diff --git a/.gitignore b/.gitignore index 32858aad3..829bbcee7 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +/target/ diff --git a/README.md b/README.md index 6da27ecb5..42c5d14f5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

项目介绍

-       Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 8全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。 +       Redkale (中文名: 红菜苔,一种湖北特产蔬菜) 是基于Java 11全新的微服务框架, 包含HTTP、WebSocket、TCP/UDP、数据序列化、数据缓存、依赖注入等功能。 本框架致力于简化集中式和微服务架构的开发,在增强开发敏捷性的同时保持高性能。

RedKale 有如下主要特点:
    diff --git a/bin/apidoc.bat b/bin/apidoc.bat index 786c57d26..4f99006f5 100644 --- a/bin/apidoc.bat +++ b/bin/apidoc.bat @@ -1,7 +1,7 @@ -@ECHO OFF - -SET APP_HOME=%~dp0 - -IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. - -java -DCMD=APIDOC -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application +@ECHO OFF + +SET APP_HOME=%~dp0 + +IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. + +java -DCMD=APIDOC -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application diff --git a/bin/command.bat b/bin/command.bat index 93ad7ab8b..3fb08d1d2 100644 --- a/bin/command.bat +++ b/bin/command.bat @@ -1,7 +1,7 @@ -@ECHO OFF - -SET APP_HOME=%~dp0 - -IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. - -java -DCMD=%1 -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application +@ECHO OFF + +SET APP_HOME=%~dp0 + +IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. + +java -DCMD=%1 -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application diff --git a/bin/restart.bat b/bin/restart.bat index 3a1247088..f8f029afa 100644 --- a/bin/restart.bat +++ b/bin/restart.bat @@ -1,9 +1,9 @@ -@ECHO OFF - -SET APP_HOME=%~dp0 - -IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. - -call "%APP_HOME%\bin\shutdown.bat" - +@ECHO OFF + +SET APP_HOME=%~dp0 + +IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. + +call "%APP_HOME%\bin\shutdown.bat" + call "%APP_HOME%\bin\start.bat" \ No newline at end of file diff --git a/bin/shutdown.bat b/bin/shutdown.bat index 7246f6f0b..714e75505 100644 --- a/bin/shutdown.bat +++ b/bin/shutdown.bat @@ -1,7 +1,7 @@ -@ECHO OFF - -SET APP_HOME=%~dp0 - -IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. - -java -DCMD=SHUTDOWN -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application +@ECHO OFF + +SET APP_HOME=%~dp0 + +IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. + +java -DCMD=SHUTDOWN -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application diff --git a/bin/start.bat b/bin/start.bat index bcebe18e1..e0ad68fc2 100644 --- a/bin/start.bat +++ b/bin/start.bat @@ -1,8 +1,8 @@ -@ECHO OFF - -SET APP_HOME=%~dp0 - -IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. - -java -server -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application - +@ECHO OFF + +SET APP_HOME=%~dp0 + +IF NOT EXIST "%APP_HOME%\conf\application.xml" SET APP_HOME=%~dp0.. + +java -server -DAPP_HOME="%APP_HOME%" -classpath "%APP_HOME%"\lib\* org.redkale.boot.Application + diff --git a/conf/application.xml b/conf/application.xml index 21182108c..f89296ce4 100644 --- a/conf/application.xml +++ b/conf/application.xml @@ -1,14 +1,12 @@ - - - - + + - + @@ -24,7 +22,7 @@ - + diff --git a/conf/persistence.xml b/conf/persistence.xml index b678fa9b9..280c07dce 100644 --- a/conf/persistence.xml +++ b/conf/persistence.xml @@ -1,6 +1,5 @@ - + ALL diff --git a/my/pom.xml b/my/pom.xml index f2c87ed21..723f63a72 100644 --- a/my/pom.xml +++ b/my/pom.xml @@ -4,16 +4,33 @@ org.redkale redkale jar + RedkaleProject http://redkale.org redkale -- java framework - 2.2.0 + 2.5.0 + + + UTF-8 + 11 + 11 + + + + + org.junit.jupiter + junit-jupiter + 5.7.0 + test + + + Apache 2 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ repo Apache License - + @@ -21,13 +38,13 @@ Redkale redkale redkale@qq.com - http://redkale.org + https://redkale.org Project Manager Architect redkale - http://redkale.org + https://redkale.org No @@ -35,12 +52,6 @@ - - UTF-8 - 11 - 11 - - Redkale ossrh @@ -51,11 +62,13 @@ https://oss.sonatype.org/service/local/staging/deploy/maven2/ + https://github.com/redkale/redkale scm:git:git@github.com/redkale/redkale.git scm:git:git@github.com:redkale/redkale.git + @@ -99,6 +112,7 @@ + org.apache.maven.plugins maven-source-plugin @@ -111,6 +125,7 @@ + org.apache.maven.plugins maven-javadoc-plugin diff --git a/my/settings.xml b/my/settings.xml index ec3807690..4f003e0ba 100644 --- a/my/settings.xml +++ b/my/settings.xml @@ -1,97 +1,99 @@ - - - - ossrh - redkale - xxxxxxxxxxxxxxxxxxxxxxxxx - - - - - - ossrh - - true - - - gpg2 - xxxxxxxxxxxxxxxxxxxxxxxxx - - - - - release - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.6 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - true - false - release - deploy - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - org.apache.maven.plugins - maven-source-plugin - 3.0.0 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.3 - - - attach-javadocs - - jar - - - - - - - - + + + + ossrh + redkale + xxxxxxxxxxxxxxxxxxxxxxxxx + + + + + + ossrh + + true + + + gpg2 + xxxxxxxxxxxxxxxxxxxxxxxxx + + + + + release + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..0f1202c9a --- /dev/null +++ b/pom.xml @@ -0,0 +1,138 @@ + + + 4.0.0 + org.redkale + redkale + jar + RedkaleProject + https://redkale.org + redkale -- java framework + 2.5.0 + + + UTF-8 + 11 + 11 + + 5.7.0 + 3.2.0 + 3.8.0 + 3.0.0-M5 + 3.0.0-M5 + + + + + Apache 2 + https://www.apache.org/licenses/ + repo + Apache License + + + + + + org.junit.jupiter + junit-jupiter + ${junit.version} + test + + + + + + central + Central Repository + https://repo.maven.apache.org/maven2 + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots + + + + + + Redkale + redkale + redkale@qq.com + https://redkale.org + + Project Manager + Architect + + redkale + https://redkale.org + + No + + 8 + + + + + https://github.com/redkale/redkale + scm:git:git@github.com/redkale/redkale.git + scm:git:git@github.com:redkale/redkale.git + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + UTF-8 + + -parameters + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + false + + org.redkale.boot.Application + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven-failsafe-plugin.version} + + + + + \ No newline at end of file diff --git a/src/META-INF/application-template.xml b/src/main/java/META-INF/application-template.xml similarity index 91% rename from src/META-INF/application-template.xml rename to src/main/java/META-INF/application-template.xml index 82cc4812b..17243142c 100644 --- a/src/META-INF/application-template.xml +++ b/src/main/java/META-INF/application-template.xml @@ -136,12 +136,12 @@ 如果name是mimetype.property.开头的值将会在进程启动时进行MimeType.add("yyyy", "YYYYYY")操作。 load: 加载文件,多个用;隔开。 默认置入的system.property.的有: - System.setProperty("net.transport.poolmaxconns", "100"); - System.setProperty("net.transport.pinginterval", "30"); - System.setProperty("net.transport.checkinterval", "30"); - System.setProperty("convert.tiny", "true"); - System.setProperty("convert.pool.size", "128"); - System.setProperty("convert.writer.buffer.defsize", "4096"); + System.setProperty("redkale.net.transport.poolmaxconns", "100"); + System.setProperty("redkale.net.transport.pinginterval", "30"); + System.setProperty("redkale.net.transport.checkinterval", "30"); + System.setProperty("redkale.convert.tiny", "true"); + System.setProperty("redkale.convert.pool.size", "128"); + System.setProperty("redkale.convert.writer.buffer.defsize", "4096"); 节点下也可包含非节点. 非其节点可以通过@Resource(name="properties.xxxxxx")进行注入, 被注解的字段类型只能是AnyValue、AnyValue[] @@ -164,30 +164,38 @@ excludelibs: 排除lib.path与excludes中的正则表达式匹配的路径, 多个正则表达式用分号;隔开 charset: 文本编码, 默认: UTF-8 backlog: 默认10K - threads: 线程数, 默认: CPU核数*2,最小8个【已废弃 @since 2.3.0】 + threads【已废弃】: 线程数, 默认: CPU核数*2,最小8个【已废弃 @since 2.3.0】 maxconns: 最大连接数, 小于1表示无限制, 默认: 0 maxbody: request.body最大值, 默认: 64K bufferCapacity: ByteBuffer的初始化大小, TCP默认: 32K; (HTTP 2.0、WebSocket,必须要16k以上); UDP默认: 1350B bufferPoolSize: ByteBuffer池的大小,默认: 线程数*4 - responsePoolSize: Response池的大小,默认: 线程数*2 + responsePoolSize: Response池的大小,默认: 1024 aliveTimeoutSeconds: KeepAlive读操作超时秒数, 默认30, 0表示永久不超时; -1表示禁止KeepAlive readTimeoutSeconds: 读操作超时秒数, 默认0, 表示永久不超时 writeTimeoutSeconds: 写操作超时秒数, 默认0, 表示永久不超时 - iogroup: 流线程组AsyncGroup对象,如果值为client,表示和Application.asyncGroup对象共用 interceptor: 启动/关闭NodeServer时被调用的拦截器实现类,必须是org.redkale.boot.NodeInterceptor的子类,默认为null --> - + - + diff --git a/src/META-INF/logging-template.properties b/src/main/java/META-INF/logging-template.properties similarity index 100% rename from src/META-INF/logging-template.properties rename to src/main/java/META-INF/logging-template.properties diff --git a/src/META-INF/persistence-template.xml b/src/main/java/META-INF/persistence-template.xml similarity index 58% rename from src/META-INF/persistence-template.xml rename to src/main/java/META-INF/persistence-template.xml index c4a16a117..82935101a 100644 --- a/src/META-INF/persistence-template.xml +++ b/src/main/java/META-INF/persistence-template.xml @@ -12,29 +12,18 @@ 是否开启缓存(标记为@Cacheable的Entity类),值目前只支持两种: ALL: 所有开启缓存。 NONE: 关闭所有缓存, 非NONE字样统一视为ALL --> + + - - - + + @@ -42,7 +31,7 @@ - + diff --git a/src/javax/annotation/Priority.java b/src/main/java/javax/annotation/Priority.java similarity index 94% rename from src/javax/annotation/Priority.java rename to src/main/java/javax/annotation/Priority.java index 0d84bf5ea..a23830532 100644 --- a/src/javax/annotation/Priority.java +++ b/src/main/java/javax/annotation/Priority.java @@ -1,33 +1,39 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package javax.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * 值越大,优先级越高 - * - * @since Common Annotations 1.2 - */ -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Priority { - int value(); -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package javax.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 值越大,优先级越高 + * + * @since Common Annotations 1.2 + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Priority { + + /** + * 优先级值 + * + * @return int + */ + int value(); +} diff --git a/src/javax/annotation/Resource.java b/src/main/java/javax/annotation/Resource.java similarity index 61% rename from src/javax/annotation/Resource.java rename to src/main/java/javax/annotation/Resource.java index bec630b5b..2be9de072 100644 --- a/src/javax/annotation/Resource.java +++ b/src/main/java/javax/annotation/Resource.java @@ -1,32 +1,83 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package javax.annotation; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -/** - * @since Common Annotations 1.0 - */ -@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) -@Retention(RetentionPolicy.RUNTIME) -public @interface Resource { - public enum AuthenticationType { - CONTAINER, - APPLICATION - } - public String name() default ""; - - public Class type() default Object.class; - public AuthenticationType authenticationType() default AuthenticationType.CONTAINER; - public boolean shareable() default true; - public String description() default ""; - public String mappedName() default ""; - - public String lookup() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package javax.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @since Common Annotations 1.0 + */ +@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface Resource { + + /** + * AuthenticationType + */ + @Deprecated + public enum AuthenticationType { + /** + * @deprecated + */ + CONTAINER, + /** + * @deprecated + */ + APPLICATION + } + + /** + * 资源名称 + * + * @return String + */ + public String name() default ""; + + /** + * 依赖注入的类型 + * + * @return Class + */ + public Class type() default Object.class; + + /** + * + * @return AuthenticationType + */ + @Deprecated + public AuthenticationType authenticationType() default AuthenticationType.CONTAINER; + + /** + * + * @return boolean + */ + @Deprecated + public boolean shareable() default true; + + /** + * + * @return String + */ + @Deprecated + public String description() default ""; + + /** + * + * @return String + */ + @Deprecated + public String mappedName() default ""; + + /** + * + * @return String + */ + @Deprecated + public String lookup() default ""; +} diff --git a/src/javax/persistence/Cacheable.java b/src/main/java/javax/persistence/Cacheable.java similarity index 97% rename from src/javax/persistence/Cacheable.java rename to src/main/java/javax/persistence/Cacheable.java index 45238dd98..fd6f6b181 100644 --- a/src/javax/persistence/Cacheable.java +++ b/src/main/java/javax/persistence/Cacheable.java @@ -1,62 +1,62 @@ -/** ***************************************************************************** - * Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 - * which accompanies this distribution. - * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Linda DeMichiel - Java Persistence 2.1 - * Linda DeMichiel - Java Persistence 2.0 - * - ***************************************************************************** */ -package javax.persistence; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * Specifies whether an entity should be cached if caching is enabled - * when the value of the persistence.xml caching element - * is ENABLE_SELECTIVE or DISABLE_SELECTIVE. - * The value of the Cacheable annotation is inherited by - * subclasses; it can be overridden by specifying - * Cacheable on a subclass. - * - *

    - * Cacheable(false) means that the entity and its state must - * not be cached by the provider. - * - * @since Java Persistence 2.0 - */ -@Target({TYPE}) -@Retention(RUNTIME) -public @interface Cacheable { - - /** - * (Optional) Whether or not the entity should be cached. - * - * @return boolean - */ - boolean value() default true; - - /** - * (Optional) 定时自动更新缓存的周期秒数,为0表示不做定时更新, 大于0表示每经过interval秒后会自动从数据库中拉取数据更新Cache - * - * @return int - */ - int interval() default 0; - - /** - * DataSource是否直接返回对象的真实引用, 而不是copy一份 - * - * @return boolean - */ - boolean direct() default false; - -} +/** ***************************************************************************** + * Copyright (c) 2008 - 2013 Oracle Corporation. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 + * which accompanies this distribution. + * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Linda DeMichiel - Java Persistence 2.1 + * Linda DeMichiel - Java Persistence 2.0 + * + ***************************************************************************** */ +package javax.persistence; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * Specifies whether an entity should be cached if caching is enabled + * when the value of the persistence.xml caching element + * is ENABLE_SELECTIVE or DISABLE_SELECTIVE. + * The value of the Cacheable annotation is inherited by + * subclasses; it can be overridden by specifying + * Cacheable on a subclass. + * + *

    + * Cacheable(false) means that the entity and its state must + * not be cached by the provider. + * + * @since Java Persistence 2.0 + */ +@Target({TYPE}) +@Retention(RUNTIME) +public @interface Cacheable { + + /** + * (Optional) Whether or not the entity should be cached. + * + * @return boolean + */ + boolean value() default true; + + /** + * (Optional) 定时自动更新缓存的周期秒数,为0表示不做定时更新, 大于0表示每经过interval秒后会自动从数据库中拉取数据更新Cache + * + * @return int + */ + int interval() default 0; + + /** + * DataSource是否直接返回对象的真实引用, 而不是copy一份 + * + * @return boolean + */ + boolean direct() default false; + +} diff --git a/src/javax/persistence/Column.java b/src/main/java/javax/persistence/Column.java similarity index 93% rename from src/javax/persistence/Column.java rename to src/main/java/javax/persistence/Column.java index 9ae01c923..5e3f7ec54 100644 --- a/src/javax/persistence/Column.java +++ b/src/main/java/javax/persistence/Column.java @@ -81,12 +81,19 @@ public @interface Column { boolean unique() default false; /** - * (Optional) Whether the database column is nullable. + * (Optional) Whether the database column is required. * * @return boolean */ boolean nullable() default true; + /** + * for OpenAPI Specification 3 + * + * @return String + */ + String example() default ""; + /** * (Optional) Whether the column is included in SQL INSERT * statements generated by the persistence provider. @@ -109,11 +116,13 @@ public @interface Column { * * @return String */ + @Deprecated String table() default ""; /** * (Optional) The column length. (Applies only if a * string-valued column is used.) + * if type==String and length == 65535 then sqltype is text * * @return int */ diff --git a/src/javax/persistence/Entity.java b/src/main/java/javax/persistence/Entity.java similarity index 92% rename from src/javax/persistence/Entity.java rename to src/main/java/javax/persistence/Entity.java index a4bc1b912..d3f4716a7 100644 --- a/src/javax/persistence/Entity.java +++ b/src/main/java/javax/persistence/Entity.java @@ -15,9 +15,7 @@ ******************************************************************************/ package javax.persistence; -import java.lang.annotation.Target; -import java.lang.annotation.Retention; -import java.lang.annotation.Documented; +import java.lang.annotation.*; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @@ -27,6 +25,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; * * @since Java Persistence 1.0 */ +@Inherited @Documented @Target(TYPE) @Retention(RUNTIME) diff --git a/src/javax/persistence/Id.java b/src/main/java/javax/persistence/Id.java similarity index 100% rename from src/javax/persistence/Id.java rename to src/main/java/javax/persistence/Id.java diff --git a/src/javax/persistence/Index.java b/src/main/java/javax/persistence/Index.java similarity index 100% rename from src/javax/persistence/Index.java rename to src/main/java/javax/persistence/Index.java diff --git a/src/javax/persistence/Table.java b/src/main/java/javax/persistence/Table.java similarity index 97% rename from src/javax/persistence/Table.java rename to src/main/java/javax/persistence/Table.java index e0be2eaa3..9da7348aa 100644 --- a/src/javax/persistence/Table.java +++ b/src/main/java/javax/persistence/Table.java @@ -82,6 +82,11 @@ public @interface Table { */ Index[] indexes() default {}; + /** + * comment + * + * @return String + */ String comment() default ""; } diff --git a/src/javax/persistence/Transient.java b/src/main/java/javax/persistence/Transient.java similarity index 100% rename from src/javax/persistence/Transient.java rename to src/main/java/javax/persistence/Transient.java diff --git a/src/javax/persistence/UniqueConstraint.java b/src/main/java/javax/persistence/UniqueConstraint.java similarity index 100% rename from src/javax/persistence/UniqueConstraint.java rename to src/main/java/javax/persistence/UniqueConstraint.java diff --git a/src/module-info.txt b/src/main/java/module-info.java similarity index 74% rename from src/module-info.txt rename to src/main/java/module-info.java index 95daeaccd..aad1ec72a 100644 --- a/src/module-info.txt +++ b/src/main/java/module-info.java @@ -4,20 +4,22 @@ * * @author zhangjx * -module org.redkale { + */ +module redkale { requires java.base; - requires java.logging; - requires java.xml; + requires java.logging; + requires java.net.http; requires java.sql; - requires jdk.unsupported; //sun.misc.Unsafe exports javax.annotation; exports javax.persistence; + exports org.redkale.asm; exports org.redkale.boot; exports org.redkale.boot.watch; + exports org.redkale.cluster; exports org.redkale.convert; exports org.redkale.convert.bson; exports org.redkale.convert.ext; @@ -32,10 +34,11 @@ module org.redkale { exports org.redkale.util; exports org.redkale.watch; - uses org.redkale.cluster.ClusterAgent; uses org.redkale.mq.MessageAgent; - uses org.redkale.source.CacheSource; - uses org.redkale.source.SourceLoader; + uses org.redkale.cluster.ClusterAgent; + uses org.redkale.convert.ConvertProvider; + uses org.redkale.source.CacheSourceProvider; + uses org.redkale.source.DataSourceProvider; uses org.redkale.util.ResourceInjectLoader; + } -*/ \ No newline at end of file diff --git a/src/org/redkale/asm/AnnotationVisitor.java b/src/main/java/org/redkale/asm/AnnotationVisitor.java similarity index 98% rename from src/org/redkale/asm/AnnotationVisitor.java rename to src/main/java/org/redkale/asm/AnnotationVisitor.java index 5b3496de7..96ec56a20 100644 --- a/src/org/redkale/asm/AnnotationVisitor.java +++ b/src/main/java/org/redkale/asm/AnnotationVisitor.java @@ -102,9 +102,6 @@ public abstract class AnnotationVisitor { * method calls. May be null. */ public AnnotationVisitor(final int api, final AnnotationVisitor av) { - if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { - throw new IllegalArgumentException(); - } this.api = api; this.av = av; } diff --git a/src/org/redkale/asm/AnnotationWriter.java b/src/main/java/org/redkale/asm/AnnotationWriter.java similarity index 100% rename from src/org/redkale/asm/AnnotationWriter.java rename to src/main/java/org/redkale/asm/AnnotationWriter.java diff --git a/src/org/redkale/asm/Attribute.java b/src/main/java/org/redkale/asm/Attribute.java similarity index 99% rename from src/org/redkale/asm/Attribute.java rename to src/main/java/org/redkale/asm/Attribute.java index e2ddb7451..8a7d60d0e 100644 --- a/src/org/redkale/asm/Attribute.java +++ b/src/main/java/org/redkale/asm/Attribute.java @@ -286,8 +286,13 @@ public class Attribute { //The stuff below is temporary - once proper support for nestmate attribute has been added, it can be safely removed. //see also changes in ClassReader.accept. - + /** + * + */ public static class NestMembers extends Attribute { + /** + * + */ public NestMembers() { super("NestMembers"); } @@ -321,11 +326,16 @@ public class Attribute { } } + /** + * + */ public static class NestHost extends Attribute { byte[] bytes; String clazz; - + /** + * + */ public NestHost() { super("NestHost"); } diff --git a/src/org/redkale/asm/ByteVector.java b/src/main/java/org/redkale/asm/ByteVector.java similarity index 100% rename from src/org/redkale/asm/ByteVector.java rename to src/main/java/org/redkale/asm/ByteVector.java diff --git a/src/org/redkale/asm/ClassReader.java b/src/main/java/org/redkale/asm/ClassReader.java similarity index 100% rename from src/org/redkale/asm/ClassReader.java rename to src/main/java/org/redkale/asm/ClassReader.java diff --git a/src/org/redkale/asm/ClassVisitor.java b/src/main/java/org/redkale/asm/ClassVisitor.java similarity index 98% rename from src/org/redkale/asm/ClassVisitor.java rename to src/main/java/org/redkale/asm/ClassVisitor.java index 57b96a95d..137a5079b 100644 --- a/src/org/redkale/asm/ClassVisitor.java +++ b/src/main/java/org/redkale/asm/ClassVisitor.java @@ -104,9 +104,6 @@ public abstract class ClassVisitor { * calls. May be null. */ public ClassVisitor(final int api, final ClassVisitor cv) { - if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { - throw new IllegalArgumentException(); - } this.api = api; this.cv = cv; } @@ -244,9 +241,6 @@ public abstract class ClassVisitor { */ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } if (cv != null) { return cv.visitTypeAnnotation(typeRef, typePath, desc, visible); } diff --git a/src/org/redkale/asm/ClassWriter.java b/src/main/java/org/redkale/asm/ClassWriter.java similarity index 97% rename from src/org/redkale/asm/ClassWriter.java rename to src/main/java/org/redkale/asm/ClassWriter.java index 25da6f65a..b16fad438 100644 --- a/src/org/redkale/asm/ClassWriter.java +++ b/src/main/java/org/redkale/asm/ClassWriter.java @@ -1297,38 +1297,6 @@ public class ClassWriter extends ClassVisitor { return result; } - /** - * Adds a handle to the constant pool of the class being build. Does nothing - * if the constant pool already contains a similar item. This method is - * intended for {@link Attribute} sub classes, and is normally not needed by - * class generators or adapters. - * - * @param tag - * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, - * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, - * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, - * {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or - * {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner - * the internal name of the field or method owner class. - * @param name - * the name of the field or method. - * @param desc - * the descriptor of the field or method. - * @return the index of a new or already existing method type reference - * item. - * - * @deprecated this method is superseded by - * {@link #newHandle(int, String, String, String, boolean)}. - */ - @Deprecated - public int newHandle(final int tag, final String owner, final String name, - final String desc) { - return newHandle(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE); - } - /** * Adds a handle to the constant pool of the class being build. Does nothing * if the constant pool already contains a similar item. This method is diff --git a/src/org/redkale/asm/Context.java b/src/main/java/org/redkale/asm/Context.java similarity index 100% rename from src/org/redkale/asm/Context.java rename to src/main/java/org/redkale/asm/Context.java diff --git a/src/org/redkale/asm/CurrentFrame.java b/src/main/java/org/redkale/asm/CurrentFrame.java similarity index 100% rename from src/org/redkale/asm/CurrentFrame.java rename to src/main/java/org/redkale/asm/CurrentFrame.java diff --git a/src/org/redkale/asm/Edge.java b/src/main/java/org/redkale/asm/Edge.java similarity index 100% rename from src/org/redkale/asm/Edge.java rename to src/main/java/org/redkale/asm/Edge.java diff --git a/src/org/redkale/asm/FieldVisitor.java b/src/main/java/org/redkale/asm/FieldVisitor.java similarity index 96% rename from src/org/redkale/asm/FieldVisitor.java rename to src/main/java/org/redkale/asm/FieldVisitor.java index 51a954dfa..999139ad6 100644 --- a/src/org/redkale/asm/FieldVisitor.java +++ b/src/main/java/org/redkale/asm/FieldVisitor.java @@ -101,9 +101,6 @@ public abstract class FieldVisitor { * calls. May be null. */ public FieldVisitor(final int api, final FieldVisitor fv) { - if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { - throw new IllegalArgumentException(); - } this.api = api; this.fv = fv; } @@ -145,9 +142,6 @@ public abstract class FieldVisitor { */ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } if (fv != null) { return fv.visitTypeAnnotation(typeRef, typePath, desc, visible); } diff --git a/src/org/redkale/asm/FieldWriter.java b/src/main/java/org/redkale/asm/FieldWriter.java similarity index 100% rename from src/org/redkale/asm/FieldWriter.java rename to src/main/java/org/redkale/asm/FieldWriter.java diff --git a/src/org/redkale/asm/Frame.java b/src/main/java/org/redkale/asm/Frame.java similarity index 100% rename from src/org/redkale/asm/Frame.java rename to src/main/java/org/redkale/asm/Frame.java diff --git a/src/org/redkale/asm/Handle.java b/src/main/java/org/redkale/asm/Handle.java similarity index 84% rename from src/org/redkale/asm/Handle.java rename to src/main/java/org/redkale/asm/Handle.java index 6a35cf7d9..c09447d20 100644 --- a/src/org/redkale/asm/Handle.java +++ b/src/main/java/org/redkale/asm/Handle.java @@ -99,35 +99,6 @@ public final class Handle { */ final boolean itf; - /** - * Constructs a new field or method handle. - * - * @param tag - * the kind of field or method designated by this Handle. Must be - * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, - * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, - * {@link Opcodes#H_INVOKEVIRTUAL}, - * {@link Opcodes#H_INVOKESTATIC}, - * {@link Opcodes#H_INVOKESPECIAL}, - * {@link Opcodes#H_NEWINVOKESPECIAL} or - * {@link Opcodes#H_INVOKEINTERFACE}. - * @param owner - * the internal name of the class that owns the field or method - * designated by this handle. - * @param name - * the name of the field or method designated by this handle. - * @param desc - * the descriptor of the field or method designated by this - * handle. - * - * @deprecated this constructor has been superseded - * by {@link #Handle(int, String, String, String, boolean)}. - */ - @Deprecated - public Handle(int tag, String owner, String name, String desc) { - this(tag, owner, name, desc, tag == Opcodes.H_INVOKEINTERFACE); - } - /** * Constructs a new field or method handle. * diff --git a/src/org/redkale/asm/Handler.java b/src/main/java/org/redkale/asm/Handler.java similarity index 100% rename from src/org/redkale/asm/Handler.java rename to src/main/java/org/redkale/asm/Handler.java diff --git a/src/org/redkale/asm/Item.java b/src/main/java/org/redkale/asm/Item.java similarity index 100% rename from src/org/redkale/asm/Item.java rename to src/main/java/org/redkale/asm/Item.java diff --git a/src/org/redkale/asm/Label.java b/src/main/java/org/redkale/asm/Label.java similarity index 100% rename from src/org/redkale/asm/Label.java rename to src/main/java/org/redkale/asm/Label.java diff --git a/src/org/redkale/asm/MethodDebugVisitor.java b/src/main/java/org/redkale/asm/MethodDebugVisitor.java similarity index 67% rename from src/org/redkale/asm/MethodDebugVisitor.java rename to src/main/java/org/redkale/asm/MethodDebugVisitor.java index c1b810fcf..3045c4b8d 100644 --- a/src/org/redkale/asm/MethodDebugVisitor.java +++ b/src/main/java/org/redkale/asm/MethodDebugVisitor.java @@ -1,192 +1,274 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.asm; - -import java.util.*; - -/** - * MethodVisitor 的调试类 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class MethodDebugVisitor { - - private final MethodVisitor visitor; - - private boolean debug = false; - - public MethodDebugVisitor setDebug(boolean d) { - debug = d; - return this; - } - - public void debugLine() { - if (!debug) return; - System.out.println(); - System.out.println(); - System.out.println(); - } - - private final Map labels = new LinkedHashMap<>(); - - private static final String[] opcodes = new String[200]; //0 -18 - - static { - try { - for (java.lang.reflect.Field field : Opcodes.class.getFields()) { - String name = field.getName(); - if (name.startsWith("ASM")) continue; - if (name.startsWith("V1_")) continue; - if (name.startsWith("ACC_")) continue; - if (name.startsWith("T_")) continue; - if (name.startsWith("H_")) continue; - if (name.startsWith("F_")) continue; - if (field.getType() != int.class) continue; - opcodes[(int) (Integer) field.get(null)] = name; - } - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - } - - public MethodDebugVisitor(MethodVisitor visitor) { - //super(Opcodes.ASM5, visitor); - this.visitor = visitor; - } - - public AnnotationVisitor visitParameterAnnotation(int i, String string, boolean bln) { - AnnotationVisitor av = visitor.visitParameterAnnotation(i, string, bln); - if (debug) System.out.println("mv.visitParameterAnnotation(" + i + ", \"" + string + "\", " + bln + ");"); - return av; - } - - public AnnotationVisitor visitAnnotation(String desc, boolean flag) { - AnnotationVisitor av = visitor.visitAnnotation(desc, flag); - if (debug) System.out.println("mv.visitAnnotation(\"" + desc + "\", " + flag + ");"); - return av; - } - - public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { - AnnotationVisitor av = visitor.visitTypeAnnotation(typeRef, typePath, desc, visible); - if (debug) System.out.println("mv.visitTypeAnnotation(" + typeRef + ", " + typePath + ", \"" + desc + "\", " + visible + ");"); - return av; - } - - public void visitParameter(String name, int access) { - visitor.visitParameter(name, access); - if (debug) System.out.println("mv.visitParameter(" + name + ", " + access + ");"); - } - - public void visitVarInsn(int opcode, int var) { - visitor.visitVarInsn(opcode, var); - if (debug) System.out.println("mv.visitVarInsn(" + opcodes[opcode] + ", " + var + ");"); - } - - public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { - visitor.visitFrame(type, nLocal, local, nStack, stack); - if (debug) { - String typestr = "" + type; - if (type == -1) { - typestr = "Opcodes.F_NEW"; - } else if (type == 1) { - typestr = "Opcodes.F_APPEND"; - } else if (type == 2) { - typestr = "Opcodes.F_CHOP"; - } else if (type == 3) { - typestr = "Opcodes.F_SAME"; - } else if (type == 4) { - typestr = "Opcodes.F_SAME1"; - } - System.out.println("mv.visitFrame(" + typestr + ", " + nLocal + ", " + Arrays.toString(local) + ", " + nStack + ", " + Arrays.toString(stack) + ");"); - } - } - - public void visitJumpInsn(int opcode, Label var) { //调用此方法的 ClassWriter 必须由 COMPUTE_FRAMES 构建 - visitor.visitJumpInsn(opcode, var); - if (debug) { - Integer index = labels.get(var); - if (index == null) { - index = labels.size(); - labels.put(var, index); - System.out.println("Label l" + index + " = new Label();"); - } - System.out.println("mv.visitJumpInsn(" + opcodes[opcode] + ", l" + index + ");"); - } - } - - public void visitCode() { - visitor.visitCode(); - if (debug) System.out.println("mv.visitCode();"); - } - - public void visitLabel(Label var) { - visitor.visitLabel(var); - if (debug) { - Integer index = labels.get(var); - if (index == null) { - index = labels.size(); - labels.put(var, index); - System.out.println("Label l" + index + " = new Label();"); - } - System.out.println("mv.visitLabel(l" + index + ");"); - } - } - - public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - visitor.visitMethodInsn(opcode, owner, name, desc, itf); - if (debug) System.out.println("mv.visitMethodInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\", " + itf + ");"); - } - - public void visitFieldInsn(int opcode, String owner, String name, String desc) { - visitor.visitFieldInsn(opcode, owner, name, desc); - if (debug) System.out.println("mv.visitFieldInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\");"); - } - - public void visitTypeInsn(int opcode, String type) { - visitor.visitTypeInsn(opcode, type); - if (debug) System.out.println("mv.visitTypeInsn(" + opcodes[opcode] + ", \"" + type + "\");"); - } - - public void visitInsn(int opcode) { - visitor.visitInsn(opcode); - if (debug) System.out.println("mv.visitInsn(" + opcodes[opcode] + ");"); - } - - public void visitIntInsn(int opcode, int value) { - visitor.visitIntInsn(opcode, value); - if (debug) System.out.println("mv.visitIntInsn(" + opcodes[opcode] + ", " + value + ");"); - } - - public void visitIincInsn(int opcode, int value) { - visitor.visitIincInsn(opcode, value); - if (debug) System.out.println("mv.visitIincInsn(" + opcode + ", " + value + ");"); - } - - public void visitLdcInsn(Object o) { - visitor.visitLdcInsn(o); - if (debug) { - if (o instanceof CharSequence) { - System.out.println("mv.visitLdcInsn(\"" + o + "\");"); - } else if (o instanceof org.redkale.asm.Type) { - System.out.println("mv.visitLdcInsn(Type.getType(\"" + o + "\"));"); - } else { - System.out.println("mv.visitLdcInsn(" + o + ");"); - } - } - } - - public void visitMaxs(int maxStack, int maxLocals) { - visitor.visitMaxs(maxStack, maxLocals); - if (debug) System.out.println("mv.visitMaxs(" + maxStack + ", " + maxLocals + ");"); - } - - public void visitEnd() { - visitor.visitEnd(); - if (debug) System.out.println("mv.visitEnd();\r\n\r\n\r\n"); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.asm; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.*; +import static org.redkale.asm.Opcodes.*; + +/** + * MethodVisitor 的调试类 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class MethodDebugVisitor { + + private final MethodVisitor visitor; + + private boolean debug = false; + + public MethodDebugVisitor setDebug(boolean d) { + debug = d; + return this; + } + + public void debugLine() { + if (!debug) return; + System.out.println(); + System.out.println(); + System.out.println(); + } + + private final Map labels = new LinkedHashMap<>(); + + private static final String[] opcodes = new String[200]; //0 -18 + + static { + try { + for (java.lang.reflect.Field field : Opcodes.class.getFields()) { + String name = field.getName(); + if (name.startsWith("ASM")) continue; + if (name.startsWith("V1_")) continue; + if (name.startsWith("ACC_")) continue; + if (name.startsWith("T_")) continue; + if (name.startsWith("H_")) continue; + if (name.startsWith("F_")) continue; + if (field.getType() != int.class) continue; + opcodes[(int) (Integer) field.get(null)] = name; + } + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + + /** + * + * @param visitor MethodVisitor + */ + public MethodDebugVisitor(MethodVisitor visitor) { + //super(Opcodes.ASM5, visitor); + this.visitor = visitor; + } + + public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + visitor.visitTryCatchBlock(start, end, handler, type); + if (debug) System.out.println("mv.visitTryCatchBlock(label0, label1, label2, \"" + type + "\");"); + } + + public AnnotationVisitor visitParameterAnnotation(int i, String string, boolean bln) { + AnnotationVisitor av = visitor.visitParameterAnnotation(i, string, bln); + if (debug) System.out.println("mv.visitParameterAnnotation(" + i + ", \"" + string + "\", " + bln + ");"); + return av; + } + + public AnnotationVisitor visitAnnotation(String desc, boolean flag) { + AnnotationVisitor av = visitor.visitAnnotation(desc, flag); + if (debug) System.out.println("mv.visitAnnotation(\"" + desc + "\", " + flag + ");"); + return av; + } + + public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + AnnotationVisitor av = visitor.visitTypeAnnotation(typeRef, typePath, desc, visible); + if (debug) System.out.println("mv.visitTypeAnnotation(" + typeRef + ", " + typePath + ", \"" + desc + "\", " + visible + ");"); + return av; + } + + public void visitParameter(String name, int access) { + visitor.visitParameter(name, access); + if (debug) System.out.println("mv.visitParameter(" + name + ", " + access + ");"); + } + + public void visitVarInsn(int opcode, int var) { + visitor.visitVarInsn(opcode, var); + if (debug) System.out.println("mv.visitVarInsn(" + opcodes[opcode] + ", " + var + ");"); + } + + public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { + visitor.visitFrame(type, nLocal, local, nStack, stack); + if (debug) { + String typestr = "" + type; + if (type == -1) { + typestr = "Opcodes.F_NEW"; + } else if (type == 1) { + typestr = "Opcodes.F_APPEND"; + } else if (type == 2) { + typestr = "Opcodes.F_CHOP"; + } else if (type == 3) { + typestr = "Opcodes.F_SAME"; + } else if (type == 4) { + typestr = "Opcodes.F_SAME1"; + } + System.out.println("mv.visitFrame(" + typestr + ", " + nLocal + ", " + Arrays.toString(local) + ", " + nStack + ", " + Arrays.toString(stack) + ");"); + } + } + + public void visitJumpInsn(int opcode, Label var) { //调用此方法的 ClassWriter 必须由 COMPUTE_FRAMES 构建 + visitor.visitJumpInsn(opcode, var); + if (debug) { + Integer index = labels.get(var); + if (index == null) { + index = labels.size(); + labels.put(var, index); + System.out.println("Label l" + index + " = new Label();"); + } + System.out.println("mv.visitJumpInsn(" + opcodes[opcode] + ", l" + index + ");"); + } + } + + public void visitCode() { + visitor.visitCode(); + if (debug) System.out.println("mv.visitCode();"); + } + + public void visitLabel(Label var) { + visitor.visitLabel(var); + if (debug) { + Integer index = labels.get(var); + if (index == null) { + index = labels.size(); + labels.put(var, index); + System.out.println("Label l" + index + " = new Label();"); + } + System.out.println("mv.visitLabel(l" + index + ");"); + } + } + + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + visitor.visitMethodInsn(opcode, owner, name, desc, itf); + if (debug) System.out.println("mv.visitMethodInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\", " + itf + ");"); + } + + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + visitor.visitFieldInsn(opcode, owner, name, desc); + if (debug) System.out.println("mv.visitFieldInsn(" + opcodes[opcode] + ", \"" + owner + "\", \"" + name + "\", \"" + desc + "\");"); + } + + public void visitTypeInsn(int opcode, String type) { + visitor.visitTypeInsn(opcode, type); + if (debug) System.out.println("mv.visitTypeInsn(" + opcodes[opcode] + ", \"" + type + "\");"); + } + + public void visitInsn(int opcode) { + visitor.visitInsn(opcode); + if (debug) System.out.println("mv.visitInsn(" + opcodes[opcode] + ");"); + } + + public void visitIntInsn(int opcode, int value) { + visitor.visitIntInsn(opcode, value); + if (debug) System.out.println("mv.visitIntInsn(" + opcodes[opcode] + ", " + value + ");"); + } + + public void visitIincInsn(int opcode, int value) { + visitor.visitIincInsn(opcode, value); + if (debug) System.out.println("mv.visitIincInsn(" + opcode + ", " + value + ");"); + } + + public void visitLdcInsn(Object o) { + visitor.visitLdcInsn(o); + if (debug) { + if (o instanceof CharSequence) { + System.out.println("mv.visitLdcInsn(\"" + o + "\");"); + } else if (o instanceof org.redkale.asm.Type) { + System.out.println("mv.visitLdcInsn(Type.getType(\"" + o + "\"));"); + } else { + System.out.println("mv.visitLdcInsn(" + o + ");"); + } + } + } + + public void visitMaxs(int maxStack, int maxLocals) { + visitor.visitMaxs(maxStack, maxLocals); + if (debug) System.out.println("mv.visitMaxs(" + maxStack + ", " + maxLocals + ");"); + } + + public void visitEnd() { + visitor.visitEnd(); + if (debug) System.out.println("mv.visitEnd();\r\n\r\n\r\n"); + } + + public static void pushInt(MethodDebugVisitor mv, int num) { + if (num < 6) { + mv.visitInsn(ICONST_0 + num); + } else if (num <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, num); + } else if (num <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, num); + } else { + mv.visitLdcInsn(num); + } + } + + public static void pushInt(MethodVisitor mv, int num) { + if (num < 6) { + mv.visitInsn(ICONST_0 + num); + } else if (num <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, num); + } else if (num <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, num); + } else { + mv.visitLdcInsn(num); + } + } + + public static void visitAnnotation(final AnnotationVisitor av, final Annotation ann) { + try { + for (Method anm : ann.annotationType().getMethods()) { + final String mname = anm.getName(); + if ("equals".equals(mname) || "hashCode".equals(mname) || "toString".equals(mname) || "annotationType".equals(mname)) continue; + final Object r = anm.invoke(ann); + if (r instanceof String[]) { + AnnotationVisitor av1 = av.visitArray(mname); + for (String item : (String[]) r) { + av1.visit(null, item); + } + av1.visitEnd(); + } else if (r instanceof Class[]) { + AnnotationVisitor av1 = av.visitArray(mname); + for (Class item : (Class[]) r) { + av1.visit(null, Type.getType(item)); + } + av1.visitEnd(); + } else if (r instanceof Enum[]) { + AnnotationVisitor av1 = av.visitArray(mname); + for (Enum item : (Enum[]) r) { + av1.visitEnum(null, Type.getDescriptor(item.getClass()), ((Enum) item).name()); + } + av1.visitEnd(); + } else if (r instanceof Annotation[]) { + AnnotationVisitor av1 = av.visitArray(mname); + for (Annotation item : (Annotation[]) r) { + visitAnnotation(av1.visitAnnotation(null, Type.getDescriptor(((Annotation) item).annotationType())), item); + } + av1.visitEnd(); + } else if (r instanceof Class) { + av.visit(mname, Type.getType((Class) r)); + } else if (r instanceof Enum) { + av.visitEnum(mname, Type.getDescriptor(r.getClass()), ((Enum) r).name()); + } else if (r instanceof Annotation) { + visitAnnotation(av.visitAnnotation(null, Type.getDescriptor(((Annotation) r).annotationType())), (Annotation) r); + } else { + av.visit(mname, r); + } + } + av.visitEnd(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/src/org/redkale/asm/MethodVisitor.java b/src/main/java/org/redkale/asm/MethodVisitor.java similarity index 94% rename from src/org/redkale/asm/MethodVisitor.java rename to src/main/java/org/redkale/asm/MethodVisitor.java index 99f8535dd..a3dfba21e 100644 --- a/src/org/redkale/asm/MethodVisitor.java +++ b/src/main/java/org/redkale/asm/MethodVisitor.java @@ -118,9 +118,6 @@ public abstract class MethodVisitor { * calls. May be null. */ public MethodVisitor(final int api, final MethodVisitor mv) { - if (api < Opcodes.ASM4 || api > Opcodes.ASM6) { - throw new IllegalArgumentException(); - } this.api = api; this.mv = mv; } @@ -140,9 +137,6 @@ public abstract class MethodVisitor { * allowed (see {@link Opcodes}). */ public void visitParameter(String name, int access) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } if (mv != null) { mv.visitParameter(name, access); } @@ -209,9 +203,6 @@ public abstract class MethodVisitor { */ public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } if (mv != null) { return mv.visitTypeAnnotation(typeRef, typePath, desc, visible); } @@ -453,35 +444,6 @@ public abstract class MethodVisitor { } } - /** - * Visits a method instruction. A method instruction is an instruction that - * invokes a method. - * - * @param opcode - * the opcode of the type instruction to be visited. This opcode - * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or - * INVOKEINTERFACE. - * @param owner - * the internal name of the method's owner class (see - * {@link Type#getInternalName() getInternalName}). - * @param name - * the method's name. - * @param desc - * the method's descriptor (see {@link Type Type}). - */ - @Deprecated - public void visitMethodInsn(int opcode, String owner, String name, - String desc) { - if (api >= Opcodes.ASM5) { - boolean itf = opcode == Opcodes.INVOKEINTERFACE; - visitMethodInsn(opcode, owner, name, desc, itf); - return; - } - if (mv != null) { - mv.visitMethodInsn(opcode, owner, name, desc); - } - } - /** * Visits a method instruction. A method instruction is an instruction that * invokes a method. @@ -502,14 +464,6 @@ public abstract class MethodVisitor { */ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { - if (api < Opcodes.ASM5) { - if (itf != (opcode == Opcodes.INVOKEINTERFACE)) { - throw new IllegalArgumentException( - "INVOKESPECIAL/STATIC on interfaces require ASM 5"); - } - visitMethodInsn(opcode, owner, name, desc); - return; - } if (mv != null) { mv.visitMethodInsn(opcode, owner, name, desc, itf); } @@ -723,9 +677,6 @@ public abstract class MethodVisitor { */ public AnnotationVisitor visitInsnAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } if (mv != null) { return mv.visitInsnAnnotation(typeRef, typePath, desc, visible); } @@ -783,9 +734,6 @@ public abstract class MethodVisitor { */ public AnnotationVisitor visitTryCatchAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } if (mv != null) { return mv.visitTryCatchAnnotation(typeRef, typePath, desc, visible); } @@ -854,9 +802,6 @@ public abstract class MethodVisitor { public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, TypePath typePath, Label[] start, Label[] end, int[] index, String desc, boolean visible) { - if (api < Opcodes.ASM5) { - throw new RuntimeException(); - } if (mv != null) { return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, index, desc, visible); diff --git a/src/org/redkale/asm/MethodWriter.java b/src/main/java/org/redkale/asm/MethodWriter.java similarity index 100% rename from src/org/redkale/asm/MethodWriter.java rename to src/main/java/org/redkale/asm/MethodWriter.java diff --git a/src/org/redkale/asm/ModuleVisitor.java b/src/main/java/org/redkale/asm/ModuleVisitor.java similarity index 100% rename from src/org/redkale/asm/ModuleVisitor.java rename to src/main/java/org/redkale/asm/ModuleVisitor.java diff --git a/src/org/redkale/asm/ModuleWriter.java b/src/main/java/org/redkale/asm/ModuleWriter.java similarity index 100% rename from src/org/redkale/asm/ModuleWriter.java rename to src/main/java/org/redkale/asm/ModuleWriter.java diff --git a/src/org/redkale/asm/Opcodes.java b/src/main/java/org/redkale/asm/Opcodes.java similarity index 99% rename from src/org/redkale/asm/Opcodes.java rename to src/main/java/org/redkale/asm/Opcodes.java index 30f9efd17..48bff123a 100644 --- a/src/org/redkale/asm/Opcodes.java +++ b/src/main/java/org/redkale/asm/Opcodes.java @@ -73,10 +73,9 @@ package org.redkale.asm; public interface Opcodes { // ASM API versions - - int ASM4 = 4 << 16 | 0 << 8 | 0; - int ASM5 = 5 << 16 | 0 << 8 | 0; - int ASM6 = 6 << 16 | 0 << 8 | 0; + int ASM4 = 4 << 16 | 0 << 8; + int ASM5 = 5 << 16 | 0 << 8; + int ASM6 = 6 << 16 | 0 << 8; // versions diff --git a/src/org/redkale/asm/Type.java b/src/main/java/org/redkale/asm/Type.java similarity index 100% rename from src/org/redkale/asm/Type.java rename to src/main/java/org/redkale/asm/Type.java diff --git a/src/org/redkale/asm/TypePath.java b/src/main/java/org/redkale/asm/TypePath.java similarity index 99% rename from src/org/redkale/asm/TypePath.java rename to src/main/java/org/redkale/asm/TypePath.java index 88830f12e..4ab44734b 100644 --- a/src/org/redkale/asm/TypePath.java +++ b/src/main/java/org/redkale/asm/TypePath.java @@ -161,7 +161,7 @@ public class TypePath { * @return the corresponding TypePath object, or null if the path is empty. */ public static TypePath fromString(final String typePath) { - if (typePath == null || typePath.length() == 0) { + if (typePath == null || typePath.isEmpty()) { return null; } int n = typePath.length(); diff --git a/src/org/redkale/asm/TypeReference.java b/src/main/java/org/redkale/asm/TypeReference.java similarity index 100% rename from src/org/redkale/asm/TypeReference.java rename to src/main/java/org/redkale/asm/TypeReference.java diff --git a/src/org/redkale/asm/asm.txt b/src/main/java/org/redkale/asm/asm.txt similarity index 93% rename from src/org/redkale/asm/asm.txt rename to src/main/java/org/redkale/asm/asm.txt index 7719a4d13..f3f74d9ab 100644 --- a/src/org/redkale/asm/asm.txt +++ b/src/main/java/org/redkale/asm/asm.txt @@ -1,27 +1,27 @@ -need copy classes: - -AnnotationVisitor.java -AnnotationWriter.java -Attribute.java -ByteVector.java -ClassReader.java -ClassVisitor.java -ClassWriter.java -Context.java -CurrentFrame.java -Edge.java -FieldVisitor.java -FieldWriter.java -Frame.java -Handle.java -Handler.java -Item.java -Label.java -MethodVisitor.java -MethodWriter.java -ModuleVisitor.java -ModuleWriter.java -Opcodes.java -Type.java -TypePath.java -TypeReference.java +need copy classes: + +AnnotationVisitor.java +AnnotationWriter.java +Attribute.java +ByteVector.java +ClassReader.java +ClassVisitor.java +ClassWriter.java +Context.java +CurrentFrame.java +Edge.java +FieldVisitor.java +FieldWriter.java +Frame.java +Handle.java +Handler.java +Item.java +Label.java +MethodVisitor.java +MethodWriter.java +ModuleVisitor.java +ModuleWriter.java +Opcodes.java +Type.java +TypePath.java +TypeReference.java diff --git a/src/org/redkale/asm/package-info.java b/src/main/java/org/redkale/asm/package-info.java similarity index 96% rename from src/org/redkale/asm/package-info.java rename to src/main/java/org/redkale/asm/package-info.java index f91083ffa..a0d8621f7 100644 --- a/src/org/redkale/asm/package-info.java +++ b/src/main/java/org/redkale/asm/package-info.java @@ -1,4 +1,4 @@ -/** - * 本包下所有代码均是从/java.base/jdk/internal/org/objectweb/asm 拷贝过来的 - */ -package org.redkale.asm; +/** + * 本包下所有代码均是从/java.base/jdk/internal/org/objectweb/asm 拷贝过来的 + */ +package org.redkale.asm; diff --git a/src/main/java/org/redkale/boot/ApiDocsService.java b/src/main/java/org/redkale/boot/ApiDocsService.java new file mode 100644 index 000000000..7ad9f0782 --- /dev/null +++ b/src/main/java/org/redkale/boot/ApiDocsService.java @@ -0,0 +1,535 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import java.io.*; +import java.lang.reflect.*; +import java.math.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.atomic.*; +import java.util.logging.*; +import javax.persistence.*; +import org.redkale.convert.*; +import org.redkale.convert.json.*; +import org.redkale.mq.MessageMultiConsumer; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import org.redkale.source.*; +import org.redkale.util.*; + +/** + * API接口文档生成类,作用:生成Application实例中所有HttpServer的可用HttpServlet的API接口方法
    + * 继承 HttpBaseServlet 是为了获取 HttpMapping 信息
    + * https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class ApiDocsService { + + private static final java.lang.reflect.Type TYPE_RETRESULT_OBJECT = new TypeToken>() { + }.getType(); + + private static final java.lang.reflect.Type TYPE_RETRESULT_STRING = new TypeToken>() { + }.getType(); + + private static final java.lang.reflect.Type TYPE_RETRESULT_INTEGER = new TypeToken>() { + }.getType(); + + private static final java.lang.reflect.Type TYPE_RETRESULT_LONG = new TypeToken>() { + }.getType(); + + private final Application app; //Application全局对象 + + public ApiDocsService(Application app) { + this.app = app; + } + + public void run(String[] args) throws Exception { + //是否跳过RPC接口 + final boolean skipRPC = Arrays.toString(args).toLowerCase().contains("skip-rpc") && !Arrays.toString(args).toLowerCase().contains("skip-rpc=false"); + + List serverList = new ArrayList<>(); + Field __prefix = HttpServlet.class.getDeclaredField("_prefix"); + __prefix.setAccessible(true); + Map>> typesMap = new LinkedHashMap<>(); + //https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md + Map swaggerPathsMap = new LinkedHashMap<>(); + List swaggerServers = new ArrayList<>(); + List swaggerTags = new ArrayList<>(); + Map> swaggerComponentsMap = new LinkedHashMap<>(); + for (NodeServer node : app.servers) { + if (!(node instanceof NodeHttpServer)) continue; + final Map map = new LinkedHashMap<>(); + serverList.add(map); + HttpServer server = node.getServer(); + map.put("address", server.getSocketAddress()); + swaggerServers.add(Utility.ofMap("url", "http://localhost:" + server.getSocketAddress().getPort())); + List> servletsList = new ArrayList<>(); + map.put("servlets", servletsList); + String plainContentType = server.getResponseConfig() == null ? "application/json" : server.getResponseConfig().plainContentType; + if (plainContentType == null || plainContentType.isEmpty()) plainContentType = "application/json"; + if (plainContentType.indexOf(';') > 0) plainContentType = plainContentType.substring(0, plainContentType.indexOf(';')); + + for (HttpServlet servlet : server.getPrepareServlet().getServlets()) { + if (!(servlet instanceof HttpServlet)) continue; + if (servlet instanceof WebSocketServlet) continue; + if (servlet.getClass().getAnnotation(MessageMultiConsumer.class) != null) { + node.logger.log(Level.INFO, servlet + " be skipped because has @MessageMultiConsumer"); + continue; + } + WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); + if (ws == null) { + node.logger.log(Level.WARNING, servlet + " not found @WebServlet"); + continue; + } + if (ws.name().isEmpty()) { + node.logger.log(Level.INFO, servlet + " be skipped because @WebServlet.name is empty"); + continue; + } + final String tag = ws.name().isEmpty() ? servlet.getClass().getSimpleName().replace("Servlet", "").toLowerCase() : ws.name(); + final Map servletMap = new LinkedHashMap<>(); + String prefix = (String) __prefix.get(servlet); + String[] urlregs = ws.value(); + if (prefix != null && !prefix.isEmpty()) { + for (int i = 0; i < urlregs.length; i++) { + urlregs[i] = prefix + urlregs[i]; + } + } + servletMap.put("urlregs", urlregs); + servletMap.put("moduleid", ws.moduleid()); + servletMap.put("name", ws.name()); + servletMap.put("comment", ws.comment()); + + List mappingsList = new ArrayList<>(); + servletMap.put("mappings", mappingsList); + final Class selfClz = servlet.getClass(); + Class clz = servlet.getClass(); + HashSet actionUrls = new HashSet<>(); + do { + if (Modifier.isAbstract(clz.getModifiers())) break; + for (Method method : clz.getMethods()) { + if (method.getParameterCount() != 2) continue; + HttpMapping action = method.getAnnotation(HttpMapping.class); + if (action == null) continue; + if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法 + if (actionUrls.contains(action.url())) continue; + if (HttpScope.class.isAssignableFrom(action.result())) continue; //忽略模板引擎的方法 + if (action.rpconly() && skipRPC) continue; //不生成RPC接口 + + final List> swaggerParamsList = new ArrayList<>(); + + final Map mappingMap = new LinkedHashMap<>(); + mappingMap.put("url", prefix + action.url()); + actionUrls.add(action.url()); + mappingMap.put("auth", action.auth()); + mappingMap.put("actionid", action.actionid()); + mappingMap.put("comment", action.comment()); + List paramsList = new ArrayList<>(); + mappingMap.put("params", paramsList); + List results = new ArrayList<>(); + Type resultType = action.result(); + if (!action.resultref().isEmpty()) { + Field f = servlet.getClass().getDeclaredField(action.resultref()); + f.setAccessible(true); + resultType = (Type) f.get(servlet); + } +// for (final Class rtype : action.results()) { +// results.add(rtype.getName()); +// if (typesMap.containsKey(rtype.getName())) continue; +// if (rtype.getName().startsWith("java.")) continue; +// if (rtype.getName().startsWith("javax.")) continue; +// final boolean filter = FilterBean.class.isAssignableFrom(rtype); +// final Map> typeMap = new LinkedHashMap<>(); +// Class loop = rtype; +// do { +// if (loop == null || loop.isInterface()) break; +// for (Field field : loop.getDeclaredFields()) { +// if (Modifier.isFinal(field.getModifiers())) continue; +// if (Modifier.isStatic(field.getModifiers())) continue; +// +// Map fieldmap = new LinkedHashMap<>(); +// fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName()); +// +// Comment comment = field.getAnnotation(Comment.class); +// Column col = field.getAnnotation(Column.class); +// FilterColumn fc = field.getAnnotation(FilterColumn.class); +// if (comment != null) { +// fieldmap.put("comment", comment.value()); +// } else if (col != null) { +// fieldmap.put("comment", col.comment()); +// } else if (fc != null) { +// fieldmap.put("comment", fc.comment()); +// } +// fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null)); +// fieldmap.put("updatable", (filter || col == null || col.updatable())); +// if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) { +// if (field.getAnnotation(RestAddress.class) != null) continue; +// } +// +// typeMap.put(field.getName(), fieldmap); +// } +// } while ((loop = loop.getSuperclass()) != Object.class); +// typesMap.put(rtype.getName(), typeMap); +// } + mappingMap.put("results", results); + boolean hasbodyparam = false; + Map swaggerRequestBody = new LinkedHashMap<>(); + for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) { + final Map oldapisParamMap = new LinkedHashMap<>(); + final boolean isarray = param.type().isArray(); + final Class ptype = isarray ? param.type().getComponentType() : param.type(); + oldapisParamMap.put("name", param.name()); + oldapisParamMap.put("radix", param.radix()); + oldapisParamMap.put("type", ptype.getName() + (isarray ? "[]" : "")); + oldapisParamMap.put("style", param.style()); + oldapisParamMap.put("comment", param.comment()); + oldapisParamMap.put("required", param.required()); + paramsList.add(oldapisParamMap); + { + final Map paramSchemaMap = new LinkedHashMap<>(); + Type paramGenericType = param.type(); + if (!param.typeref().isEmpty()) { + Field f = servlet.getClass().getDeclaredField(param.typeref()); + f.setAccessible(true); + paramGenericType = (Type) f.get(servlet); + } + simpleSchemaType(node.getLogger(), swaggerComponentsMap, param.type(), paramGenericType, paramSchemaMap, true); + if (param.style() == HttpParam.HttpParameterStyle.BODY) { + swaggerRequestBody.put("description", param.comment()); + swaggerRequestBody.put("content", Utility.ofMap(plainContentType, Utility.ofMap("schema", paramSchemaMap))); + } else { + final Map swaggerParamMap = new LinkedHashMap<>(); + swaggerParamMap.put("name", param.name()); + swaggerParamMap.put("in", param.style().name().toLowerCase()); + swaggerParamMap.put("description", param.comment()); + swaggerParamMap.put("required", param.required()); + if (param.deprecated()) { + swaggerParamMap.put("deprecated", param.deprecated()); + } + //https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#parameterStyle + swaggerParamMap.put("style", param.style() == HttpParam.HttpParameterStyle.HEADER || param.name().indexOf('#') == 0 ? "simple" : "form"); + swaggerParamMap.put("explode", true); + swaggerParamMap.put("schema", paramSchemaMap); + Object example = formatExample(param.example(), param.type(), paramGenericType); + if (example != null) swaggerParamMap.put("example", example); + if (!param.example().isEmpty()) { + swaggerParamMap.put("example", param.example()); + } + swaggerParamsList.add(swaggerParamMap); + } + } + if (param.style() == HttpParam.HttpParameterStyle.BODY) hasbodyparam = true; + if (ptype.isPrimitive() || ptype == String.class) continue; + if (typesMap.containsKey(ptype.getName())) continue; + if (ptype.getName().startsWith("java.")) continue; + if (ptype.getName().startsWith("javax.")) continue; + + final Map> typeMap = new LinkedHashMap<>(); + Class loop = ptype; + final boolean filter = FilterBean.class.isAssignableFrom(loop); + do { + if (loop == null || loop.isInterface()) break; + for (Field field : loop.getDeclaredFields()) { + if (Modifier.isFinal(field.getModifiers())) continue; + if (Modifier.isStatic(field.getModifiers())) continue; + + Map fieldmap = new LinkedHashMap<>(); + fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName()); + + Column col = field.getAnnotation(Column.class); + FilterColumn fc = field.getAnnotation(FilterColumn.class); + Comment comment = field.getAnnotation(Comment.class); + if (comment != null) { + fieldmap.put("comment", comment.value()); + } else if (col != null) { + fieldmap.put("comment", col.comment()); + } else if (fc != null) { + fieldmap.put("comment", fc.comment()); + } + fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null)); + fieldmap.put("updatable", (filter || col == null || col.updatable())); + + if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) { + if (field.getAnnotation(RestAddress.class) != null) continue; + } + + typeMap.put(field.getName(), fieldmap); + } + } while ((loop = loop.getSuperclass()) != Object.class); + + typesMap.put(ptype.getName(), typeMap); + } + mappingMap.put("result", action.result().getSimpleName().replace("void", "Object")); + mappingsList.add(mappingMap); + + final Map swaggerOperatMap = new LinkedHashMap<>(); + swaggerOperatMap.put("tags", new String[]{tag}); + swaggerOperatMap.put("operationId", action.name()); + if (method.getAnnotation(Deprecated.class) != null) { + swaggerOperatMap.put("deprecated", true); + } + Map respSchemaMap = new LinkedHashMap<>(); + simpleSchemaType(node.getLogger(), swaggerComponentsMap, action.result(), resultType, respSchemaMap, true); + + Map respMap = new LinkedHashMap<>(); + respMap.put("schema", respSchemaMap); + Object example = formatExample(action.example(), action.result(), resultType); + if (example != null) swaggerOperatMap.put("example", example); + if (!swaggerRequestBody.isEmpty()) swaggerOperatMap.put("requestBody", swaggerRequestBody); + swaggerOperatMap.put("parameters", swaggerParamsList); + String actiondesc = action.comment(); + if (action.rpconly()) actiondesc = "[Only for RPC API] " + actiondesc; + swaggerOperatMap.put("responses", Utility.ofMap("200", Utility.ofMap("description", actiondesc, "content", Utility.ofMap("application/json", respMap)))); + + String m = action.methods() == null || action.methods().length == 0 ? null : action.methods()[0].toLowerCase(); + if (m == null) { + m = hasbodyparam || TYPE_RETRESULT_STRING.equals(resultType) || TYPE_RETRESULT_INTEGER.equals(resultType) + || TYPE_RETRESULT_LONG.equals(resultType) || action.name().contains("create") || action.name().contains("insert") + || action.name().contains("update") || action.name().contains("delete") || action.name().contains("send") ? "post" : "get"; + } + swaggerPathsMap.put(prefix + action.url(), Utility.ofMap("description", action.comment(), m, swaggerOperatMap)); + } + } while ((clz = clz.getSuperclass()) != HttpServlet.class); + mappingsList.sort((o1, o2) -> ((String) o1.get("url")).compareTo((String) o2.get("url"))); + servletsList.add(servletMap); + if (!actionUrls.isEmpty()) swaggerTags.add(Utility.ofMap("name", tag, "description", ws.comment())); + } + servletsList.sort((o1, o2) -> { + String[] urlregs1 = (String[]) o1.get("urlregs"); + String[] urlregs2 = (String[]) o2.get("urlregs"); + return urlregs1.length > 0 ? (urlregs2.length > 0 ? urlregs1[0].compareTo(urlregs2[0]) : 1) : -1; + }); + } + { // https://github.com/OAI/OpenAPI-Specification + Map swaggerResultMap = new LinkedHashMap<>(); + swaggerResultMap.put("openapi", "3.0.0"); + Map infomap = new LinkedHashMap<>(); + infomap.put("title", "Redkale generate apidoc"); + infomap.put("version", "1.0.0"); + swaggerResultMap.put("info", infomap); + swaggerResultMap.put("servers", swaggerServers); + swaggerResultMap.put("paths", swaggerPathsMap); + swaggerResultMap.put("tags", swaggerTags); + if (!swaggerComponentsMap.isEmpty()) swaggerResultMap.put("components", Utility.ofMap("schemas", swaggerComponentsMap)); + final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "openapi-doc.json")); + out.write(JsonConvert.root().convertTo(swaggerResultMap).getBytes(StandardCharsets.UTF_8)); + out.close(); + } + { + Map oldapisResultMap = new LinkedHashMap<>(); + oldapisResultMap.put("servers", serverList); + oldapisResultMap.put("types", typesMap); + final String json = JsonConvert.root().convertTo(oldapisResultMap); + final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "apidoc.json")); + out.write(json.getBytes(StandardCharsets.UTF_8)); + out.close(); + File doctemplate = new File(app.getConfPath().toString(), "apidoc-template.html"); + InputStream in = null; + if (doctemplate.isFile() && doctemplate.canRead()) { + in = new FileInputStream(doctemplate); + } + if (in == null) in = ApiDocsService.class.getResourceAsStream("apidoc-template.html"); + String content = Utility.read(in).replace("'${content}'", json); + in.close(); + FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html")); + outhtml.write(content.getBytes(StandardCharsets.UTF_8)); + outhtml.close(); + } + } + + private static void simpleSchemaType(Logger logger, Map> componentsMap, Class type, Type genericType, Map schemaMap, boolean recursive) { + if (type == int.class || type == Integer.class || type == AtomicInteger.class) { + schemaMap.put("type", "integer"); + schemaMap.put("format", "int32"); + } else if (type == long.class || type == Long.class + || type == AtomicLong.class || type == LongAdder.class || type == BigInteger.class) { + schemaMap.put("type", "integer"); + schemaMap.put("format", "int64"); + } else if (type == float.class || type == Float.class) { + schemaMap.put("type", "number"); + schemaMap.put("format", "float"); + } else if (type == double.class || type == Double.class || type == BigDecimal.class) { + schemaMap.put("type", "number"); + schemaMap.put("format", "double"); + } else if (type == boolean.class || type == Boolean.class || type == AtomicBoolean.class) { + schemaMap.put("type", "boolean"); + } else if (type.isPrimitive() || Number.class.isAssignableFrom(type)) { + schemaMap.put("type", "number"); + } else if (type == String.class || CharSequence.class.isAssignableFrom(type)) { + schemaMap.put("type", "string"); + } else if (recursive && (type.isArray() || Collection.class.isAssignableFrom(type))) { + schemaMap.put("type", "array"); + Map sbumap = new LinkedHashMap<>(); + if (type.isArray()) { + simpleSchemaType(logger, componentsMap, type.getComponentType(), type.getComponentType(), sbumap, false); + } else if (genericType instanceof ParameterizedType) { + Type subpt = ((ParameterizedType) genericType).getActualTypeArguments()[0]; + if (subpt instanceof Class) { + simpleSchemaType(logger, componentsMap, (Class) subpt, subpt, sbumap, false); + } else if (subpt instanceof ParameterizedType && ((ParameterizedType) subpt).getOwnerType() instanceof Class) { + simpleSchemaType(logger, componentsMap, (Class) ((ParameterizedType) subpt).getOwnerType(), subpt, sbumap, false); + } else { + sbumap.put("type", "object"); + } + } else { + sbumap.put("type", "object"); + } + schemaMap.put("items", sbumap); + } else if (!type.getName().startsWith("java.") && !type.getName().startsWith("javax.")) { + String ct = simpleComponentType(logger, componentsMap, type, genericType); + if (ct == null) { + schemaMap.put("type", "object"); + } else { + schemaMap.put("$ref", "#/components/schemas/" + ct); + } + } else { + schemaMap.put("type", "object"); + } + } + + private static String simpleComponentType(Logger logger, Map> componentsMap, Class type, Type genericType) { + try { + Encodeable encodeable = JsonFactory.root().loadEncoder(genericType); + String ct = componentKey(logger, componentsMap, null, encodeable, true); + if (ct == null || ct.length() == 0) return null; + if (componentsMap.containsKey(ct)) return ct; + Map cmap = new LinkedHashMap<>(); + componentsMap.put(ct, cmap); //必须在调用simpleSchemaType之前put,不然嵌套情况下死循环 + + cmap.put("type", "object"); + List requireds = new ArrayList<>(); + Map properties = new LinkedHashMap<>(); + if (encodeable instanceof ObjectEncoder) { + for (EnMember member : ((ObjectEncoder) encodeable).getMembers()) { + Map schemaMap = new LinkedHashMap<>(); + simpleSchemaType(logger, componentsMap, TypeToken.typeToClassOrElse(member.getEncoder().getType(), Object.class), member.getEncoder().getType(), schemaMap, true); + String desc = ""; + if (member.getField() != null) { + Column col = member.getField().getAnnotation(Column.class); + if (col == null) { + FilterColumn fcol = member.getField().getAnnotation(FilterColumn.class); + if (fcol != null) { + desc = fcol.comment(); + if (fcol.required()) requireds.add(member.getAttribute().field()); + } + } else { + desc = col.comment(); + if (!col.nullable()) requireds.add(member.getAttribute().field()); + } + if (desc.isEmpty() && member.getField().getAnnotation(Comment.class) != null) { + desc = member.getField().getAnnotation(Comment.class).value(); + } + } else if (member.getMethod() != null) { + Column col = member.getMethod().getAnnotation(Column.class); + if (col == null) { + FilterColumn fcol = member.getMethod().getAnnotation(FilterColumn.class); + if (fcol != null) { + desc = fcol.comment(); + if (fcol.required()) requireds.add(member.getAttribute().field()); + } + } else { + desc = col.comment(); + if (!col.nullable()) requireds.add(member.getAttribute().field()); + } + if (desc.isEmpty() && member.getMethod().getAnnotation(Comment.class) != null) { + desc = member.getMethod().getAnnotation(Comment.class).value(); + } + } + if (!desc.isEmpty()) schemaMap.put("description", desc); + properties.put(member.getAttribute().field(), schemaMap); + } + } + if (!requireds.isEmpty()) cmap.put("required", requireds); + cmap.put("properties", properties); + return ct; + } catch (Exception e) { + logger.log(Level.WARNING, genericType + " generate component info error", e); + return null; + } + } + + private static String componentKey(Logger logger, Map> componentsMap, EnMember field, Encodeable encodeable, boolean first) { + if (encodeable instanceof ObjectEncoder) { + StringBuilder sb = new StringBuilder(); + sb.append(((ObjectEncoder) encodeable).getTypeClass().getSimpleName()); + for (EnMember member : ((ObjectEncoder) encodeable).getMembers()) { + if (member.getEncoder() instanceof ArrayEncoder + || member.getEncoder() instanceof CollectionEncoder) { + String subsb = componentKey(logger, componentsMap, member, member.getEncoder(), false); + if (subsb == null) return null; + AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField(); + if (real == null) continue; + Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType(); + Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType(); + if (cz == ct) continue; + if (sb.length() > 0 && subsb.length() > 0) sb.append("_"); + sb.append(subsb); + } else if (member.getEncoder() instanceof ObjectEncoder || member.getEncoder() instanceof SimpledCoder) { + AccessibleObject real = member.getField() == null ? member.getMethod() : member.getField(); + if (real == null) continue; + if (member.getEncoder() instanceof SimpledCoder) { + simpleSchemaType(logger, componentsMap, ((SimpledCoder) member.getEncoder()).getType(), ((SimpledCoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true); + } else { + simpleSchemaType(logger, componentsMap, ((ObjectEncoder) member.getEncoder()).getTypeClass(), ((ObjectEncoder) member.getEncoder()).getType(), new LinkedHashMap<>(), true); + } + Class cz = real instanceof Field ? ((Field) real).getType() : ((Method) real).getReturnType(); + Type ct = real instanceof Field ? ((Field) real).getGenericType() : ((Method) real).getGenericReturnType(); + if (cz == ct) continue; + String subsb = componentKey(logger, componentsMap, member, member.getEncoder(), false); + if (subsb == null) return null; + if (sb.length() > 0 && subsb.length() > 0) sb.append("_"); + sb.append(subsb); + } else if (member.getEncoder() instanceof MapEncoder) { + continue; + } else { + return null; + } + } + return sb.toString(); + } else if (encodeable instanceof ArrayEncoder || encodeable instanceof CollectionEncoder) { + final boolean array = (encodeable instanceof ArrayEncoder); + Encodeable subEncodeable = array ? ((ArrayEncoder) encodeable).getComponentEncoder() : ((CollectionEncoder) encodeable).getComponentEncoder(); + if (subEncodeable instanceof SimpledCoder && field != null) return ""; + final String sb = componentKey(logger, componentsMap, null, subEncodeable, false); + if (sb == null || sb.isEmpty()) return sb; + if (field != null && field.getField() != null && field.getField().getDeclaringClass() == Sheet.class) { + return sb; + } + return sb + (array ? "_Array" : "_Collection"); + } else if (encodeable instanceof SimpledCoder) { + Class stype = ((SimpledCoder) encodeable).getType(); + if (stype.isPrimitive() || stype == Boolean.class || Number.class.isAssignableFrom(stype) || CharSequence.class.isAssignableFrom(stype)) { + return stype.getSimpleName(); + } + return ""; + } else if (encodeable instanceof MapEncoder) { + return first ? null : ""; + } else { + return null; + } + } + + private static Object formatExample(String example, Class type, Type genericType) { + if (example == null || example.isEmpty()) return null; + if (type == Flipper.class) { + return new Flipper(); + } else if (TYPE_RETRESULT_OBJECT.equals(genericType)) { + return RetResult.success(); + } else if (TYPE_RETRESULT_STRING.equals(genericType)) { + return RetResult.success(); + } else if (TYPE_RETRESULT_INTEGER.equals(genericType)) { + return RetResult.success(0); + } else if (TYPE_RETRESULT_LONG.equals(genericType)) { + return RetResult.success(0L); + } + return example; + } + +} diff --git a/src/org/redkale/boot/Application.java b/src/main/java/org/redkale/boot/Application.java similarity index 71% rename from src/org/redkale/boot/Application.java rename to src/main/java/org/redkale/boot/Application.java index d45850bc8..f212df1fa 100644 --- a/src/org/redkale/boot/Application.java +++ b/src/main/java/org/redkale/boot/Application.java @@ -1,1370 +1,1505 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import org.redkale.cluster.ClusterAgent; -import org.redkale.util.RedkaleClassLoader; -import org.redkale.net.TransportGroupInfo; -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.nio.file.Path; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.logging.*; -import javax.annotation.Resource; -import javax.net.ssl.SSLContext; -import javax.xml.parsers.*; -import org.redkale.boot.ClassFilter.FilterEntry; -import org.redkale.cluster.*; -import org.redkale.convert.Convert; -import org.redkale.convert.bson.BsonFactory; -import org.redkale.convert.json.*; -import org.redkale.mq.*; -import org.redkale.net.*; -import org.redkale.net.http.*; -import org.redkale.net.sncp.*; -import org.redkale.service.Service; -import org.redkale.source.*; -import org.redkale.util.AnyValue.DefaultAnyValue; -import org.redkale.util.*; -import org.redkale.watch.*; -import org.w3c.dom.*; - -/** - * - * 进程启动类,全局对象。
    - *

    - * 程序启动执行步骤:
    - *     1、读取application.xml
    - *     2、进行classpath扫描动态加载Service、WebSocket与Servlet
    - *     3、优先加载所有SNCP协议的服务,再加载其他协议服务, 最后加载WATCH协议的服务
    - *     4、最后进行Service、Servlet与其他资源之间的依赖注入
    - * 
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class Application { - - /** - * 当前进程启动的时间, 类型: long - */ - public static final String RESNAME_APP_TIME = "APP_TIME"; - - /** - * 当前进程的名称, 类型:String - */ - public static final String RESNAME_APP_NAME = "APP_NAME"; - - /** - * 当前进程的根目录, 类型:String、File、Path、URI - */ - public static final String RESNAME_APP_HOME = "APP_HOME"; - - /** - * 当前进程的配置目录,如果不是绝对路径则视为HOME目录下的相对路径 类型:String、File、Path、URI - */ - public static final String RESNAME_APP_CONF = "APP_CONF"; - - /** - * application.xml 文件中resources节点的内容, 类型: AnyValue - */ - public static final String RESNAME_APP_GRES = "APP_GRES"; - - /** - * 当前进程节点的nodeid, 类型:int - */ - public static final String RESNAME_APP_NODEID = "APP_NODEID"; - - /** - * 当前进程节点的IP地址, 类型:InetSocketAddress、InetAddress、String - */ - public static final String RESNAME_APP_ADDR = "APP_ADDR"; - - /** - * 当前进程的work线程池, 类型:Executor、ExecutorService - * - * @since 2.3.0 - */ - public static final String RESNAME_APP_EXECUTOR = "APP_EXECUTOR"; - - /** - * 当前进程的客户端组, 类型:AsyncGroup - * - * @since 2.3.0 - */ - public static final String RESNAME_APP_GROUP = "APP_GROUP"; - - /** - * 当前Service所属的SNCP Server的地址 类型: SocketAddress、InetSocketAddress、String
    - */ - public static final String RESNAME_SNCP_ADDR = "SNCP_ADDR"; - - /** - * 当前Service所属的SNCP Server所属的组 类型: String
    - */ - public static final String RESNAME_SNCP_GROUP = "SNCP_GROUP"; - - /** - * "SERVER_ROOT" 当前Server的ROOT目录类型:String、File、Path - */ - public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT; - - /** - * 当前Server的线程池 - * - * @deprecated 2.3.0 使用RESNAME_APP_EXECUTOR - */ - //@Deprecated - //public static final String RESNAME_SERVER_EXECUTOR2 = Server.RESNAME_SERVER_EXECUTOR2; - /** - * 当前Server的ResourceFactory - */ - public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY; - - //本进程节点ID - final int nodeid; - - //本进程节点ID - final String name; - - //本地IP地址 - final InetSocketAddress localAddress; - - //业务逻辑线程池 - //@since 2.3.0 - final ExecutorService workExecutor; - - //CacheSource 资源 - final List cacheSources = new CopyOnWriteArrayList<>(); - - //DataSource 资源 - final List dataSources = new CopyOnWriteArrayList<>(); - - //NodeServer 资源, 顺序必须是sncps, others, watchs - final List servers = new CopyOnWriteArrayList<>(); - - //SNCP传输端的TransportFactory, 注意: 只给SNCP使用 - final TransportFactory sncpTransportFactory; - - //给客户端使用,包含SNCP客户端、自定义数据库客户端连接池 - final AsyncGroup asyncGroup; - - //第三方服务发现管理接口 - //@since 2.1.0 - final ClusterAgent clusterAgent; - - //MQ管理接口 - //@since 2.1.0 - final MessageAgent[] messageAgents; - - //全局根ResourceFactory - final ResourceFactory resourceFactory = ResourceFactory.root(); - - //服务配置项 - final AnyValue config; - - //排除的jar路径 - final String excludelibs; - - //临时计数器 - CountDownLatch servicecdl; //会出现两次赋值 - - //是否启动了WATCH协议服务 - boolean watching; - - //-------------------------------------------------------------------------------------------- - //是否用于main方法运行 - final boolean singletonrun; - - //根WatchFactory - //private final WatchFactory watchFactory = WatchFactory.root(); - //进程根目录 - private final File home; - - //配置文件目录 - private final URI confPath; - - //日志 - private final Logger logger; - - //监听事件 - private final List listeners = new CopyOnWriteArrayList<>(); - - //服务启动时间 - private final long startTime = System.currentTimeMillis(); - - //Server启动的计数器,用于确保所有Server都启动完后再进行下一步处理 - private final CountDownLatch serversLatch; - - //根ClassLoader - private final RedkaleClassLoader classLoader; - - //Server根ClassLoader - private final RedkaleClassLoader serverClassLoader; - - private Application(final AnyValue config) { - this(false, config); - } - - private Application(final boolean singletonrun, final AnyValue config) { - this.singletonrun = singletonrun; - this.config = config; - System.setProperty("redkale.version", Redkale.getDotedVersion()); - - final File root = new File(System.getProperty(RESNAME_APP_HOME)); - this.resourceFactory.register(RESNAME_APP_TIME, long.class, this.startTime); - this.resourceFactory.register(RESNAME_APP_HOME, Path.class, root.toPath()); - this.resourceFactory.register(RESNAME_APP_HOME, File.class, root); - this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI()); - try { - this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath()); - if (System.getProperty(RESNAME_APP_HOME) == null) System.setProperty(RESNAME_APP_HOME, root.getCanonicalPath()); - this.home = root.getCanonicalFile(); - String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf"); - if (confsubpath.contains("://")) { - this.confPath = new URI(confsubpath); - } else if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) { - this.confPath = new File(confsubpath).getCanonicalFile().toURI(); - } else { - this.confPath = new File(this.home, confsubpath).getCanonicalFile().toURI(); - } - } catch (Exception e) { - throw new RuntimeException(e); - } - String localaddr = config.getValue("address", "").trim(); - InetAddress addr = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress(); - this.localAddress = new InetSocketAddress(addr, config.getIntValue("port")); - this.resourceFactory.register(RESNAME_APP_ADDR, addr.getHostAddress()); - this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, addr); - this.resourceFactory.register(RESNAME_APP_ADDR, InetSocketAddress.class, this.localAddress); - - { - int nid = config.getIntValue("nodeid", 0); - this.nodeid = nid; - this.resourceFactory.register(RESNAME_APP_NODEID, nid); - System.setProperty(RESNAME_APP_NODEID, "" + nid); - } - { - this.name = checkName(config.getValue("name", "")); - this.resourceFactory.register(RESNAME_APP_NAME, name); - System.setProperty(RESNAME_APP_NAME, name); - } - //以下是初始化日志配置 - final URI logConfURI = "file".equals(confPath.getScheme()) ? new File(new File(confPath), "logging.properties").toURI() - : URI.create(confPath.toString() + (confPath.toString().endsWith("/") ? "" : "/") + "logging.properties"); - if (!"file".equals(confPath.getScheme()) || (new File(logConfURI).isFile() && new File(logConfURI).canRead())) { - try { - final String rootpath = root.getCanonicalPath().replace('\\', '/'); - InputStream fin = logConfURI.toURL().openStream(); - Properties properties = new Properties(); - properties.load(fin); - fin.close(); - properties.entrySet().stream().forEach(x -> { - x.setValue(x.getValue().toString().replace("${APP_HOME}", rootpath)); - }); - - if (properties.getProperty("java.util.logging.FileHandler.formatter") == null) { - properties.setProperty("java.util.logging.FileHandler.formatter", LogFileHandler.LoggingFormater.class.getName()); - } - if (properties.getProperty("java.util.logging.ConsoleHandler.formatter") == null) { - properties.setProperty("java.util.logging.ConsoleHandler.formatter", LogFileHandler.LoggingFormater.class.getName()); - } - String fileHandlerPattern = properties.getProperty("java.util.logging.FileHandler.pattern"); - if (fileHandlerPattern != null && fileHandlerPattern.contains("%d")) { - final String fileHandlerClass = LogFileHandler.class.getName(); - Properties prop = new Properties(); - final String handlers = properties.getProperty("handlers"); - if (handlers != null && handlers.contains("java.util.logging.FileHandler")) { - //singletonrun模式下不输出文件日志 - prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", singletonrun ? "" : fileHandlerClass)); - } - if (!prop.isEmpty()) { - String prefix = fileHandlerClass + "."; - properties.entrySet().stream().forEach(x -> { - if (x.getKey().toString().startsWith("java.util.logging.FileHandler.")) { - prop.put(x.getKey().toString().replace("java.util.logging.FileHandler.", prefix), x.getValue()); - } - }); - prop.entrySet().stream().forEach(x -> { - properties.put(x.getKey(), x.getValue()); - }); - } - properties.put(SncpClient.class.getSimpleName() + ".handlers", LogFileHandler.SncpLogFileHandler.class.getName()); - } - ByteArrayOutputStream out = new ByteArrayOutputStream(); - final PrintStream ps = new PrintStream(out); - properties.forEach((x, y) -> ps.println(x + "=" + y)); - LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray())); - } catch (Exception e) { - Logger.getLogger(this.getClass().getSimpleName()).log(Level.WARNING, "init logger configuration error", e); - } - } - this.logger = Logger.getLogger(this.getClass().getSimpleName()); - this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1); - this.classLoader = new RedkaleClassLoader(Thread.currentThread().getContextClassLoader()); - logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------"); - //------------------配置 节点 ------------------ - final AnyValue resources = config.getAnyValue("resources"); - TransportStrategy strategy = null; - String excludelib0 = null; - ClusterAgent cluster = null; - MessageAgent[] mqs = null; - int bufferCapacity = 32 * 1024; - int bufferPoolSize = Runtime.getRuntime().availableProcessors() * 8; - int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS; - int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS; - AnyValue executorConf = null; - if (resources != null) { - executorConf = resources.getAnyValue("executor"); - AnyValue excludelibConf = resources.getAnyValue("excludelibs"); - if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value"); - AnyValue transportConf = resources.getAnyValue("transport"); - int groupsize = resources.getAnyValues("group").length; - if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue(); - if (transportConf != null) { - //--------------transportBufferPool----------- - bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 8 * 1024); - readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds); - writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds); - final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Runtime.getRuntime().availableProcessors() * 2); - bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4); - } - - AnyValue clusterConf = resources.getAnyValue("cluster"); - if (clusterConf != null) { - try { - String classval = clusterConf.getValue("value"); - if (classval == null || classval.isEmpty()) { - Iterator it = ServiceLoader.load(ClusterAgentLoader.class, classLoader).iterator(); - while (it.hasNext()) { - ClusterAgentLoader agent = it.next(); - if (agent != null && agent.match(clusterConf)) { - cluster = agent.agentClass().getConstructor().newInstance(); - cluster.setConfig(clusterConf); - break; - } - } - if (cluster == null) { - ClusterAgent cacheClusterAgent = new CacheClusterAgent(); - if (cacheClusterAgent.match(clusterConf)) { - cluster = cacheClusterAgent; - cluster.setConfig(clusterConf); - } - } - if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='value' value error: " + clusterConf); - } else { - Class type = classLoader.loadClass(clusterConf.getValue("value")); - if (!ClusterAgent.class.isAssignableFrom(type)) { - logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf); - } else { - cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance(); - cluster.setConfig(clusterConf); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "load application cluster resource error: " + clusterConf, e); - } - } - - AnyValue[] mqConfs = resources.getAnyValues("mq"); - if (mqConfs != null && mqConfs.length > 0) { - mqs = new MessageAgent[mqConfs.length]; - Set mqnames = new HashSet<>(); - for (int i = 0; i < mqConfs.length; i++) { - AnyValue mqConf = mqConfs[i]; - String mqname = mqConf.getValue("name", ""); - if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat"); - mqnames.add(mqname); - String namex = mqConf.getValue("names"); - if (namex != null && !namex.isEmpty()) { - for (String n : namex.split(";")) { - if (n.trim().isEmpty()) continue; - if (mqnames.contains(n.trim())) throw new RuntimeException("mq.name(" + n.trim() + ") is repeat"); - mqnames.add(n.trim()); - } - } - try { - String classval = mqConf.getValue("value"); - if (classval == null || classval.isEmpty()) { - Iterator it = ServiceLoader.load(MessageAgentLoader.class, classLoader).iterator(); - while (it.hasNext()) { - MessageAgentLoader messageAgent = it.next(); - if (messageAgent != null && messageAgent.match(mqConf)) { - mqs[i] = messageAgent.agentClass().getConstructor().newInstance(); - mqs[i].setConfig(mqConf); - break; - } - } - if (mqs[i] == null) logger.log(Level.SEVERE, "load application mq resource, but not found name='value' value error: " + mqConf); - } else { - Class type = classLoader.loadClass(classval); - if (!MessageAgent.class.isAssignableFrom(type)) { - logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf); - } else { - mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance(); - mqs[i].setConfig(mqConf); - } - } - } catch (Exception e) { - logger.log(Level.SEVERE, "load application mq resource error: " + mqs[i], e); - } - } - } - } - - ExecutorService workExecutor0 = null; - if (executorConf != null) { - final AtomicReference workref = new AtomicReference<>(); - int executorThreads = executorConf.getIntValue("threads", Math.max(2, Runtime.getRuntime().availableProcessors())); - boolean executorHash = executorConf.getBoolValue("hash"); - if (executorThreads > 0) { - final AtomicInteger workcounter = new AtomicInteger(); - if (executorHash) { - workExecutor0 = new ThreadHashExecutor(executorThreads, (Runnable r) -> { - int c = workcounter.incrementAndGet(); - String threadname = "Redkale-HashWorkThread-" + (c > 9 ? c : ("0" + c)); - Thread t = new WorkThread(threadname, workref.get(), r); - return t; - }); - } else { - workExecutor0 = Executors.newFixedThreadPool(executorThreads, (Runnable r) -> { - int c = workcounter.incrementAndGet(); - String threadname = "Redkale-WorkThread-" + (c > 9 ? c : ("0" + c)); - Thread t = new WorkThread(threadname, workref.get(), r); - return t; - }); - } - workref.set(workExecutor0); - } - } - this.workExecutor = workExecutor0; - this.resourceFactory.register(RESNAME_APP_EXECUTOR, Executor.class, this.workExecutor); - this.resourceFactory.register(RESNAME_APP_EXECUTOR, ExecutorService.class, this.workExecutor); - - this.asyncGroup = new AsyncIOGroup(this.workExecutor, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize); - this.resourceFactory.register(RESNAME_APP_GROUP, AsyncGroup.class, this.asyncGroup); - - this.excludelibs = excludelib0; - this.sncpTransportFactory = TransportFactory.create(this.asyncGroup, (SSLContext) null, Transport.DEFAULT_NETPROTOCOL, readTimeoutSeconds, writeTimeoutSeconds, strategy); - DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("net.transport.pool.maxconns", "100")) - .addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("net.transport.ping.interval", "30")) - .addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("net.transport.check.interval", "30")); - this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining()); - this.clusterAgent = cluster; - this.messageAgents = mqs; - Thread.currentThread().setContextClassLoader(this.classLoader); - this.serverClassLoader = new RedkaleClassLoader(this.classLoader); - } - - private String checkName(String name) { //不能含特殊字符 - if (name.isEmpty()) return name; - if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); - for (char ch : name.toCharArray()) { - if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 - throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); - } - } - return name; - } - - public ExecutorService getWorkExecutor() { - return workExecutor; - } - - public AsyncGroup getAsyncGroup() { - return asyncGroup; - } - - public ResourceFactory getResourceFactory() { - return resourceFactory; - } - - public TransportFactory getSncpTransportFactory() { - return sncpTransportFactory; - } - - public ClusterAgent getClusterAgent() { - return clusterAgent; - } - - public MessageAgent getMessageAgent(String name) { - if (messageAgents == null) return null; - for (MessageAgent agent : messageAgents) { - if (agent.getName().equals(name)) return agent; - } - return null; - } - - public MessageAgent[] getMessageAgents() { - return messageAgents; - } - - public RedkaleClassLoader getClassLoader() { - return classLoader; - } - - public RedkaleClassLoader getServerClassLoader() { - return serverClassLoader; - } - - public List getNodeServers() { - return new ArrayList<>(servers); - } - - public List getDataSources() { - return new ArrayList<>(dataSources); - } - - public List getCacheSources() { - return new ArrayList<>(cacheSources); - } - - public int getNodeid() { - return nodeid; - } - - public String getName() { - return name; - } - - public File getHome() { - return home; - } - - public URI getConfPath() { - return confPath; - } - - public long getStartTime() { - return startTime; - } - - public AnyValue getAppConfig() { - return config; - } - - public void init() throws Exception { - System.setProperty("net.transport.poolmaxconns", "100"); - System.setProperty("net.transport.pinginterval", "30"); - System.setProperty("net.transport.checkinterval", "30"); - System.setProperty("convert.tiny", "true"); - System.setProperty("convert.pool.size", "128"); - System.setProperty("convert.writer.buffer.defsize", "4096"); - - final String confpath = this.confPath.toString(); - final String homepath = this.home.getCanonicalPath(); - if ("file".equals(this.confPath.getScheme())) { - File persist = new File(new File(confPath), "persistence.xml"); - if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath()); - } else { - System.setProperty(DataSources.DATASOURCE_CONFPATH, confpath + (confpath.endsWith("/") ? "" : "/") + "persistence.xml"); - } - String pidstr = ""; - try { //JDK 9+ - Class phclass = Class.forName("java.lang.ProcessHandle"); - Object phobj = phclass.getMethod("current").invoke(null); - Object pid = phclass.getMethod("pid").invoke(phobj); - pidstr = "APP_PID = " + pid + "\r\n"; - } catch (Throwable t) { - } - logger.log(Level.INFO, pidstr + "APP_JAVA = " + System.getProperty("java.version") + "\r\n" + RESNAME_APP_NODEID + " = " + this.nodeid + "\r\n" + RESNAME_APP_ADDR + " = " + this.localAddress.getHostString() + ":" + this.localAddress.getPort() + "\r\n" + RESNAME_APP_HOME + " = " + homepath + "\r\n" + RESNAME_APP_CONF + " = " + confpath); - String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath); - lib = lib.isEmpty() ? confpath : (lib + ";" + confpath); - Server.loadLib(classLoader, logger, lib); - - //------------------------------------------------------------------------ - final AnyValue resources = config.getAnyValue("resources"); - if (resources != null) { - resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources); - final AnyValue properties = resources.getAnyValue("properties"); - if (properties != null) { - String dfloads = properties.getValue("load"); - if (dfloads != null) { - for (String dfload : dfloads.split(";")) { - if (dfload.trim().isEmpty()) continue; - final URI df = (dfload.indexOf('/') < 0) ? URI.create(confpath + (confpath.endsWith("/") ? "" : "/") + dfload) : new File(dfload).toURI(); - if (!"file".equals(df.getScheme()) || new File(df).isFile()) { - Properties ps = new Properties(); - try { - InputStream in = df.toURL().openStream(); - ps.load(in); - in.close(); - ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath))); - } catch (Exception e) { - logger.log(Level.WARNING, "load properties(" + dfload + ") error", e); - } - } - } - } - for (AnyValue prop : properties.getAnyValues("property")) { - String name = prop.getValue("name"); - String value = prop.getValue("value"); - if (name == null || value == null) continue; - value = value.replace("${APP_HOME}", homepath); - if (name.startsWith("system.property.")) { - System.setProperty(name.substring("system.property.".length()), value); - } else if (name.startsWith("mimetype.property.")) { - MimeType.add(name.substring("mimetype.property.".length()), value); - } else if (name.startsWith("property.")) { - resourceFactory.register(name, value); - } else { - resourceFactory.register("property." + name, value); - } - } - } - } - this.resourceFactory.register(BsonFactory.root()); - this.resourceFactory.register(JsonFactory.root()); - this.resourceFactory.register(BsonFactory.root().getConvert()); - this.resourceFactory.register(JsonFactory.root().getConvert()); - this.resourceFactory.register("bsonconvert", Convert.class, BsonFactory.root().getConvert()); - this.resourceFactory.register("jsonconvert", Convert.class, JsonFactory.root().getConvert()); - //只有WatchService才能加载Application、WatchFactory - final Application application = this; - this.resourceFactory.register(new ResourceFactory.ResourceLoader() { - - @Override - public void load(ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) { - try { - Resource res = field.getAnnotation(Resource.class); - if (res == null) return; - if (src instanceof Service && Sncp.isRemote((Service) src)) return; //远程模式不得注入 - Class type = field.getType(); - if (type == Application.class) { - field.set(src, application); - } else if (type == ResourceFactory.class) { - boolean serv = RESNAME_SERVER_RESFACTORY.equals(res.name()) || res.name().equalsIgnoreCase("server"); - field.set(src, serv ? rf : (res.name().isEmpty() ? application.resourceFactory : null)); - } else if (type == TransportFactory.class) { - field.set(src, application.sncpTransportFactory); - } else if (type == NodeSncpServer.class) { - NodeServer server = null; - for (NodeServer ns : application.getNodeServers()) { - if (ns.getClass() != NodeSncpServer.class) continue; - if (res.name().equals(ns.server.getName())) { - server = ns; - break; - } - } - field.set(src, server); - } else if (type == NodeHttpServer.class) { - NodeServer server = null; - for (NodeServer ns : application.getNodeServers()) { - if (ns.getClass() != NodeHttpServer.class) continue; - if (res.name().equals(ns.server.getName())) { - server = ns; - break; - } - } - field.set(src, server); - } else if (type == NodeWatchServer.class) { - NodeServer server = null; - for (NodeServer ns : application.getNodeServers()) { - if (ns.getClass() != NodeWatchServer.class) continue; - if (res.name().equals(ns.server.getName())) { - server = ns; - break; - } - } - field.set(src, server); - } -// if (type == WatchFactory.class) { -// field.set(src, application.watchFactory); -// } - } catch (Exception e) { - logger.log(Level.SEVERE, "Resource inject error", e); - } - } - - @Override - public boolean autoNone() { - return false; - } - - }, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class); - - //------------------------------------- 注册 HttpClient -------------------------------------------------------- - resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { - try { - if (field.getAnnotation(Resource.class) == null) return; - HttpClient httpClient = HttpClient.create(asyncGroup); - field.set(src, httpClient); - rf.inject(httpClient, null); // 给其可能包含@Resource的字段赋值; - rf.register(resourceName, HttpClient.class, httpClient); - } catch (Exception e) { - logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpClient inject error", e); - } - }, HttpClient.class); - //-------------------------------------------------------------------------- - if (this.asyncGroup != null) { - ((AsyncIOGroup) this.asyncGroup).start(); - } - if (this.clusterAgent != null) { - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing"); - long s = System.currentTimeMillis(); - if (this.clusterAgent instanceof CacheClusterAgent) { - String sourceName = ((CacheClusterAgent) clusterAgent).getSourceName(); //必须在inject前调用,需要赋值Resourcable.name - loadCacheSource(sourceName); - } - clusterAgent.setTransportFactory(this.sncpTransportFactory); - this.resourceFactory.inject(clusterAgent); - clusterAgent.init(clusterAgent.getConfig()); - this.resourceFactory.register(ClusterAgent.class, clusterAgent); - logger.info("ClusterAgent init in " + (System.currentTimeMillis() - s) + " ms"); - } - if (this.messageAgents != null) { - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent initing"); - long s = System.currentTimeMillis(); - for (MessageAgent agent : this.messageAgents) { - this.resourceFactory.inject(agent); - agent.init(agent.getConfig()); - this.resourceFactory.register(agent.getName(), MessageAgent.class, agent); - this.resourceFactory.register(agent.getName(), HttpMessageClient.class, agent.getHttpMessageClient()); - //this.resourceFactory.register(agent.getName(), SncpMessageClient.class, agent.getSncpMessageClient()); //不需要给开发者使用 - } - logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms"); - - } - //------------------------------------- 注册 HttpMessageClient -------------------------------------------------------- - resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { - try { - if (field.getAnnotation(Resource.class) == null) return; - if (clusterAgent == null) { - NodeHttpServer nodeHttpServer = null; - for (NodeServer n : getNodeServers()) { - if (n.getClass() == NodeHttpServer.class && Objects.equals(resourceName, ((NodeHttpServer) n).getHttpServer().getName())) { - nodeHttpServer = (NodeHttpServer) n; - break; - } - } - if (nodeHttpServer == null) { - for (NodeServer n : getNodeServers()) { - if (n.getClass() == NodeHttpServer.class) { - nodeHttpServer = (NodeHttpServer) n; - break; - } - } - } - if (nodeHttpServer == null) return; - HttpMessageClient messageClient = new HttpMessageLocalClient(nodeHttpServer.getHttpServer()); - field.set(src, messageClient); - rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值; - rf.register(resourceName, HttpMessageClient.class, messageClient); - return; - } - HttpMessageClient messageClient = new HttpMessageClusterClient(clusterAgent); - field.set(src, messageClient); - rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值; - rf.register(resourceName, HttpMessageClient.class, messageClient); - } catch (Exception e) { - logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpMessageClient inject error", e); - } - }, HttpMessageClient.class); - initResources(); - } - - private void loadCacheSource(final String sourceName) { - final AnyValue resources = config.getAnyValue("resources"); - for (AnyValue sourceConf : resources.getAnyValues("source")) { - if (!sourceName.equals(sourceConf.getValue("name"))) continue; - String classval = sourceConf.getValue("value"); - try { - Class sourceType = CacheMemorySource.class; - if (classval == null || classval.isEmpty()) { - Iterator it = ServiceLoader.load(CacheSourceLoader.class, serverClassLoader).iterator(); - while (it.hasNext()) { - CacheSourceLoader s = it.next(); - if (s != null && s.match(sourceConf)) { - sourceType = s.sourceClass(); - break; - } - } - } else { - sourceType = serverClassLoader.loadClass(classval); - } - CacheSource source = Modifier.isFinal(sourceType.getModifiers()) ? (CacheSource) sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, sourceName, sourceType, null, resourceFactory, sncpTransportFactory, null, null, sourceConf); - cacheSources.add((CacheSource) source); - resourceFactory.register(sourceName, CacheSource.class, source); - resourceFactory.inject(source); - if (source instanceof Service) ((Service) source).init(sourceConf); - logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + sourceName + ", source = " + source); - } catch (Exception e) { - logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e); - } - return; - } - } - - private void initResources() throws Exception { - //------------------------------------------------------------------------- - final AnyValue resources = config.getAnyValue("resources"); - if (resources != null) { - //------------------------------------------------------------------------ - for (AnyValue conf : resources.getAnyValues("group")) { - final String group = conf.getValue("name", ""); - final String protocol = conf.getValue("protocol", Transport.DEFAULT_NETPROTOCOL).toUpperCase(); - if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) { - throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol")); - } - TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, new LinkedHashSet<>()); - for (AnyValue node : conf.getAnyValues("node")) { - final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port")); - ginfo.putAddress(addr); - } - sncpTransportFactory.addGroupInfo(ginfo); - } - for (AnyValue conf : resources.getAnyValues("listener")) { - final String listenClass = conf.getValue("value", ""); - if (listenClass.isEmpty()) continue; - Class clazz = classLoader.loadClass(listenClass); - if (!ApplicationListener.class.isAssignableFrom(clazz)) continue; - @SuppressWarnings("unchecked") - ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance(); - resourceFactory.inject(listener); - listener.init(config); - this.listeners.add(listener); - } - } - //------------------------------------------------------------------------ - } - - private void startSelfServer() throws Exception { - final Application application = this; - new Thread() { - { - setName("Redkale-Application-SelfServer-Thread"); - } - - @Override - public void run() { - try { - final DatagramChannel channel = DatagramChannel.open(); - channel.configureBlocking(true); - channel.socket().setSoTimeout(3000); - channel.bind(new InetSocketAddress("127.0.0.1", config.getIntValue("port"))); - boolean loop = true; - ByteBuffer buffer = ByteBuffer.allocateDirect(1024); - while (loop) { - buffer.clear(); - SocketAddress address = channel.receive(buffer); - buffer.flip(); - byte[] bytes = new byte[buffer.remaining()]; - buffer.get(bytes); - final String cmd = new String(bytes); - if ("SHUTDOWN".equalsIgnoreCase(cmd)) { - try { - long s = System.currentTimeMillis(); - logger.info(application.getClass().getSimpleName() + " shutdowning"); - application.shutdown(); - buffer.clear(); - buffer.put("SHUTDOWN OK".getBytes()); - buffer.flip(); - channel.send(buffer, address); - long e = System.currentTimeMillis() - s; - logger.info(application.getClass().getSimpleName() + " shutdown in " + e + " ms"); - application.serversLatch.countDown(); - System.exit(0); - } catch (Exception ex) { - logger.log(Level.INFO, "SHUTDOWN FAIL", ex); - buffer.clear(); - buffer.put("SHUTDOWN FAIL".getBytes()); - buffer.flip(); - channel.send(buffer, address); - } - } else if ("APIDOC".equalsIgnoreCase(cmd)) { - try { - new ApiDocsService(application).run(); - buffer.clear(); - buffer.put("APIDOC OK".getBytes()); - buffer.flip(); - channel.send(buffer, address); - } catch (Exception ex) { - buffer.clear(); - buffer.put("APIDOC FAIL".getBytes()); - buffer.flip(); - channel.send(buffer, address); - } - } else { - long s = System.currentTimeMillis(); - logger.info(application.getClass().getSimpleName() + " command " + cmd); - application.command(cmd); - buffer.clear(); - buffer.put("COMMAND OK".getBytes()); - buffer.flip(); - channel.send(buffer, address); - long e = System.currentTimeMillis() - s; - logger.info(application.getClass().getSimpleName() + " command in " + e + " ms"); - } - } - } catch (Exception e) { - logger.log(Level.INFO, "Control fail", e); - System.exit(1); - } - } - }.start(); - } - - private static void sendCommand(Logger logger, int port, String command) throws Exception { - final DatagramChannel channel = DatagramChannel.open(); - channel.configureBlocking(true); - channel.connect(new InetSocketAddress("127.0.0.1", port)); - ByteBuffer buffer = ByteBuffer.allocate(128); - buffer.put(command.getBytes()); - buffer.flip(); - channel.write(buffer); - buffer.clear(); - channel.configureBlocking(true); - try { - channel.read(buffer); - buffer.flip(); - byte[] bytes = new byte[buffer.remaining()]; - buffer.get(bytes); - channel.close(); - if (logger != null) logger.info("Send: " + command + ", Reply: " + new String(bytes)); - Thread.sleep(1000); - } catch (Exception e) { - if (e instanceof PortUnreachableException) { - if ("APIDOC".equalsIgnoreCase(command)) { - final Application application = Application.create(true); - application.init(); - application.start(); - new ApiDocsService(application).run(); - if (logger != null) logger.info("APIDOC OK"); - return; - } - } - throw e; - } - } - - public void start() throws Exception { - if (!singletonrun && this.clusterAgent != null) { - this.clusterAgent.register(this); - } - final AnyValue[] entrys = config.getAnyValues("server"); - CountDownLatch timecd = new CountDownLatch(entrys.length); - final List sncps = new ArrayList<>(); - final List others = new ArrayList<>(); - final List watchs = new ArrayList<>(); - for (final AnyValue entry : entrys) { - if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) { - sncps.add(entry); - } else if (entry.getValue("protocol", "").toUpperCase().startsWith("WATCH")) { - watchs.add(entry); - } else { - others.add(entry); - } - } - if (watchs.size() > 1) throw new RuntimeException("Found one more WATCH Server"); - this.watching = !watchs.isEmpty(); - - runServers(timecd, sncps); //必须确保SNCP服务都启动后再启动其他服务 - runServers(timecd, others); - runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务 - timecd.await(); - if (this.clusterAgent != null) this.clusterAgent.start(); - if (this.messageAgents != null) { - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent starting"); - long s = System.currentTimeMillis(); - final StringBuffer sb = new StringBuffer(); - Set names = new HashSet<>(); - for (MessageAgent agent : this.messageAgents) { - names.add(agent.getName()); - Map map = agent.start().join(); - AtomicInteger maxlen = new AtomicInteger(); - map.keySet().forEach(str -> { - if (str.length() > maxlen.get()) maxlen.set(str.length()); - }); - new TreeMap<>(map).forEach((topic, ms) -> sb.append("MessageConsumer(topic=").append(alignString(topic, maxlen.get())).append(") init and start in ").append(ms).append(" ms\r\n") - ); - } - if (sb.length() > 0) logger.info(sb.toString().trim()); - logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") start in " + (System.currentTimeMillis() - s) + " ms"); - } - //if (!singletonrun) signalHandle(); - //if (!singletonrun) clearPersistData(); - logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n"); - for (ApplicationListener listener : this.listeners) { - listener.postStart(this); - } - if (!singletonrun) this.serversLatch.await(); - } - - private static String alignString(String value, int maxlen) { - StringBuilder sb = new StringBuilder(maxlen); - sb.append(value); - for (int i = 0; i < maxlen - value.length(); i++) { - sb.append(' '); - } - return sb.toString(); - } - -// private void clearPersistData() { -// File cachedir = new File(home, "cache"); -// if (!cachedir.isDirectory()) return; -// File[] lfs = cachedir.listFiles(); -// if (lfs != null) { -// for (File file : lfs) { -// if (file.getName().startsWith("persist-")) file.delete(); -// } -// } -// } -// private void signalHandle() { -// //http://www.comptechdoc.org/os/linux/programming/linux_pgsignals.html -// String[] sigs = new String[]{"HUP", "TERM", "INT", "QUIT", "KILL", "TSTP", "USR1", "USR2", "STOP"}; -// List list = new ArrayList<>(); -// for (String sig : sigs) { -// try { -// list.add(new sun.misc.Signal(sig)); -// } catch (Exception e) { -// } -// } -// sun.misc.SignalHandler handler = new sun.misc.SignalHandler() { -// -// private volatile boolean runed; -// -// @Override -// public void handle(Signal sig) { -// if (runed) return; -// runed = true; -// logger.info(Application.this.getClass().getSimpleName() + " stoped\r\n"); -// System.exit(0); -// } -// }; -// for (Signal sig : list) { -// try { -// Signal.handle(sig, handler); -// } catch (Exception e) { -// } -// } -// } - @SuppressWarnings("unchecked") - private void runServers(CountDownLatch timecd, final List serconfs) throws Exception { - this.servicecdl = new CountDownLatch(serconfs.size()); - CountDownLatch sercdl = new CountDownLatch(serconfs.size()); - final AtomicBoolean inited = new AtomicBoolean(false); - final Map> nodeClasses = new HashMap<>(); - for (final AnyValue serconf : serconfs) { - Thread thread = new Thread() { - { - setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase().replaceFirst("\\..+", "") + ":" + serconf.getIntValue("port") + "-Thread"); - this.setDaemon(true); - } - - @Override - public void run() { - try { - //Thread ctd = Thread.currentThread(); - //ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader())); - final String protocol = serconf.getValue("protocol", "").replaceFirst("\\..+", "").toUpperCase(); - NodeServer server = null; - if ("SNCP".equals(protocol)) { - server = NodeSncpServer.createNodeServer(Application.this, serconf); - } else if ("WATCH".equalsIgnoreCase(protocol)) { - DefaultAnyValue serconf2 = (DefaultAnyValue) serconf; - DefaultAnyValue rest = (DefaultAnyValue) serconf2.getAnyValue("rest"); - if (rest == null) { - rest = new DefaultAnyValue(); - serconf2.addValue("rest", rest); - } - rest.setValue("base", WatchServlet.class.getName()); - server = new NodeWatchServer(Application.this, serconf); - } else if ("HTTP".equalsIgnoreCase(protocol)) { - server = new NodeHttpServer(Application.this, serconf); - } else { - if (!inited.get()) { - synchronized (nodeClasses) { - if (!inited.getAndSet(true)) { //加载自定义的协议,如:SOCKS - ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null); - ClassFilter.Loader.load(home, ((excludelibs != null ? (excludelibs + ";") : "") + serconf.getValue("excludelibs", "")).split(";"), profilter); - final Set> entrys = profilter.getFilterEntrys(); - for (FilterEntry entry : entrys) { - final Class type = entry.getType(); - NodeProtocol pros = type.getAnnotation(NodeProtocol.class); - String p = pros.value().toUpperCase(); - if ("SNCP".equals(p) || "HTTP".equals(p)) continue; - final Class old = nodeClasses.get(p); - if (old != null && old != type) { - throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")"); - } - nodeClasses.put(p, type); - } - } - } - } - Class nodeClass = nodeClasses.get(protocol); - if (nodeClass != null) server = NodeServer.create(nodeClass, Application.this, serconf); - } - if (server == null) { - logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", serconf.getValue("protocol")); - System.exit(0); - } - servers.add(server); - server.init(serconf); - if (!singletonrun) server.start(); - timecd.countDown(); - sercdl.countDown(); - } catch (Exception ex) { - logger.log(Level.WARNING, serconf + " runServers error", ex); - Application.this.serversLatch.countDown(); - } - } - }; - thread.start(); - } - sercdl.await(); - } - - public static T singleton(Class serviceClass, Class... extServiceClasses) throws Exception { - return singleton("", serviceClass, extServiceClasses); - } - - public static T singleton(String name, Class serviceClass, Class... extServiceClasses) throws Exception { - if (serviceClass == null) throw new IllegalArgumentException("serviceClass is null"); - final Application application = Application.create(true); - System.setProperty("red" + "kale.singleton.serviceclass", serviceClass.getName()); - if (extServiceClasses != null && extServiceClasses.length > 0) { - StringBuilder sb = new StringBuilder(); - for (Class clazz : extServiceClasses) { - if (sb.length() > 0) sb.append(','); - sb.append(clazz.getName()); - } - System.setProperty("red" + "kale.singleton.extserviceclasses", sb.toString()); - } - application.init(); - application.start(); - for (NodeServer server : application.servers) { - T service = server.resourceFactory.find(name, serviceClass); - if (service != null) return service; - } - if (Modifier.isAbstract(serviceClass.getModifiers())) throw new IllegalArgumentException("abstract class not allowed"); - if (serviceClass.isInterface()) throw new IllegalArgumentException("interface class not allowed"); - throw new IllegalArgumentException(serviceClass.getName() + " maybe have zero not-final public method"); - } - - public static Application create(final boolean singleton) throws IOException { - return new Application(singleton, loadAppXml()); - } - - public void reloadConfig() throws IOException { - AnyValue newconfig = loadAppXml(); - final String confpath = this.confPath.toString(); - final String homepath = this.home.getCanonicalPath(); - final AnyValue resources = newconfig.getAnyValue("resources"); - if (resources != null) { - resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources); - final AnyValue properties = resources.getAnyValue("properties"); - if (properties != null) { - String dfloads = properties.getValue("load"); - if (dfloads != null) { - for (String dfload : dfloads.split(";")) { - if (dfload.trim().isEmpty()) continue; - final URI df = (dfload.indexOf('/') < 0) ? URI.create(confpath + (confpath.endsWith("/") ? "" : "/") + dfload) : new File(dfload).toURI(); - if (!"file".equals(df.getScheme()) || new File(df).isFile()) { - Properties ps = new Properties(); - try { - InputStream in = df.toURL().openStream(); - ps.load(in); - in.close(); - ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath))); - } catch (Exception e) { - logger.log(Level.WARNING, "load properties(" + dfload + ") error", e); - } - } - } - } - for (AnyValue prop : properties.getAnyValues("property")) { - String name = prop.getValue("name"); - String value = prop.getValue("value"); - if (name == null || value == null) continue; - value = value.replace("${APP_HOME}", homepath); - if (name.startsWith("system.property.")) { - System.setProperty(name.substring("system.property.".length()), value); - } else if (name.startsWith("mimetype.property.")) { - MimeType.add(name.substring("mimetype.property.".length()), value); - } else if (name.startsWith("property.")) { - resourceFactory.register(name, value); - } else { - resourceFactory.register("property." + name, value); - } - } - } - } - } - - private static AnyValue loadAppXml() throws IOException { - final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/'); - System.setProperty(RESNAME_APP_HOME, home); - String confsubpath = System.getProperty(RESNAME_APP_CONF, "conf"); - URI appconf; - if (confsubpath.contains("://")) { - appconf = URI.create(confsubpath + (confsubpath.endsWith("/") ? "" : "/") + "application.xml"); - } else if (confsubpath.charAt(0) == '/' || confsubpath.indexOf(':') > 0) { - appconf = new File(confsubpath, "application.xml").toURI(); - } else { - appconf = new File(new File(home, confsubpath), "application.xml").toURI(); - } - return load(appconf.toURL().openStream()); - } - - public static void main(String[] args) throws Exception { - Utility.midnight(); //先初始化一下Utility - Thread.currentThread().setName("Redkale-Application-Main-Thread"); - //运行主程序 - if (System.getProperty("CMD") != null) { - AnyValue config = loadAppXml(); - Application.sendCommand(null, config.getIntValue("port"), System.getProperty("CMD")); - return; - } - final Application application = Application.create(false); - application.init(); - application.startSelfServer(); - try { - for (ApplicationListener listener : application.listeners) { - listener.preStart(application); - } - application.start(); - } catch (Exception e) { - application.logger.log(Level.SEVERE, "Application start error", e); - System.exit(0); - } - System.exit(0); - } - - NodeSncpServer findNodeSncpServer(final InetSocketAddress sncpAddr) { - for (NodeServer node : servers) { - if (node.isSNCP() && sncpAddr.equals(node.getSncpAddress())) { - return (NodeSncpServer) node; - } - } - return null; - } - - public void command(String cmd) { - List localServers = new ArrayList<>(servers); //顺序sncps, others, watchs - localServers.stream().forEach((server) -> { - try { - server.command(cmd); - } catch (Exception t) { - logger.log(Level.WARNING, " command server(" + server.getSocketAddress() + ") error", t); - } - }); - } - - public void shutdown() throws Exception { - for (ApplicationListener listener : this.listeners) { - try { - listener.preShutdown(this); - } catch (Exception e) { - logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e); - } - } - List localServers = new ArrayList<>(servers); //顺序sncps, others, watchs - Collections.reverse(localServers); //倒序, 必须让watchs先关闭,watch包含服务发现和注销逻辑 - if (this.messageAgents != null) { - Set names = new HashSet<>(); - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent stopping"); - long s = System.currentTimeMillis(); - for (MessageAgent agent : this.messageAgents) { - names.add(agent.getName()); - agent.stop().join(); - } - logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") stop in " + (System.currentTimeMillis() - s) + " ms"); - } - if (clusterAgent != null) { - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent destroying"); - long s = System.currentTimeMillis(); - clusterAgent.deregister(this); - clusterAgent.destroy(clusterAgent.getConfig()); - logger.info("ClusterAgent destroy in " + (System.currentTimeMillis() - s) + " ms"); - } - localServers.stream().forEach((server) -> { - try { - server.shutdown(); - } catch (Exception t) { - logger.log(Level.WARNING, " shutdown server(" + server.getSocketAddress() + ") error", t); - } finally { - serversLatch.countDown(); - } - }); - if (this.messageAgents != null) { - Set names = new HashSet<>(); - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent destroying"); - long s = System.currentTimeMillis(); - for (MessageAgent agent : this.messageAgents) { - names.add(agent.getName()); - agent.destroy(agent.getConfig()); - } - logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") destroy in " + (System.currentTimeMillis() - s) + " ms"); - } - for (DataSource source : dataSources) { - if (source == null) continue; - try { - source.getClass().getMethod("close").invoke(source); - } catch (Exception e) { - logger.log(Level.FINER, source.getClass() + " close DataSource erroneous", e); - } - } - for (CacheSource source : cacheSources) { - if (source == null) continue; - try { - source.getClass().getMethod("close").invoke(source); - } catch (Exception e) { - logger.log(Level.FINER, source.getClass() + " close CacheSource erroneous", e); - } - } - this.sncpTransportFactory.shutdownNow(); - } - - private static int parseLenth(String value, int defValue) { - if (value == null) return defValue; - value = value.toUpperCase().replace("B", ""); - if (value.endsWith("G")) return Integer.decode(value.replace("G", "")) * 1024 * 1024 * 1024; - if (value.endsWith("M")) return Integer.decode(value.replace("M", "")) * 1024 * 1024; - if (value.endsWith("K")) return Integer.decode(value.replace("K", "")) * 1024; - return Integer.decode(value); - } - - private static AnyValue load(final InputStream in0) { - final DefaultAnyValue any = new DefaultAnyValue(); - try (final InputStream in = in0) { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - DocumentBuilder builder = factory.newDocumentBuilder(); - Document doc = builder.parse(in); - Element root = doc.getDocumentElement(); - load(any, root); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - return any; - } - - private static void load(final DefaultAnyValue any, final Node root) { - final String home = System.getProperty(RESNAME_APP_HOME); - NamedNodeMap nodes = root.getAttributes(); - if (nodes == null) return; - for (int i = 0; i < nodes.getLength(); i++) { - Node node = nodes.item(i); - any.addValue(node.getNodeName(), node.getNodeValue().replace("${APP_HOME}", home)); - } - NodeList children = root.getChildNodes(); - if (children == null) return; - for (int i = 0; i < children.getLength(); i++) { - Node node = children.item(i); - if (node.getNodeType() != Node.ELEMENT_NODE) continue; - DefaultAnyValue sub = new DefaultAnyValue(); - load(sub, node); - any.addValue(node.getNodeName(), sub); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import org.redkale.cluster.ClusterAgent; +import org.redkale.util.RedkaleClassLoader; +import org.redkale.net.TransportGroupInfo; +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.logging.*; +import javax.annotation.*; +import javax.net.ssl.SSLContext; +import org.redkale.boot.ClassFilter.FilterEntry; +import org.redkale.cluster.*; +import org.redkale.convert.Convert; +import org.redkale.convert.bson.BsonFactory; +import org.redkale.convert.json.*; +import org.redkale.mq.*; +import org.redkale.net.*; +import org.redkale.net.http.*; +import org.redkale.net.sncp.*; +import org.redkale.service.Service; +import org.redkale.source.*; +import static org.redkale.source.DataSources.DATASOURCE_CONFPATH; +import org.redkale.util.AnyValue.DefaultAnyValue; +import org.redkale.util.*; +import org.redkale.watch.*; + +/** + * + * 进程启动类,全局对象。
    + *

    + * 程序启动执行步骤:
    + *     1、读取application.xml
    + *     2、进行classpath扫描动态加载Service、WebSocket与Servlet
    + *     3、优先加载所有SNCP协议的服务,再加载其他协议服务, 最后加载WATCH协议的服务
    + *     4、最后进行Service、Servlet与其他资源之间的依赖注入
    + * 
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class Application { + + /** + * 当前进程启动的时间, 类型: long + */ + public static final String RESNAME_APP_TIME = "APP_TIME"; + + /** + * 当前进程的名称, 类型:String + */ + public static final String RESNAME_APP_NAME = "APP_NAME"; + + /** + * 当前进程的根目录, 类型:String、File、Path、URI + */ + public static final String RESNAME_APP_HOME = "APP_HOME"; + + /** + * 当前进程的配置目录URI,如果不是绝对路径则视为HOME目录下的相对路径 类型:String、URI、File、Path
    + * 若配置目录不是本地文件夹, 则File、Path类型的值为null + */ + public static final String RESNAME_APP_CONF = "APP_CONF"; + + /** + * application.xml 文件中resources节点的内容, 类型: AnyValue + */ + public static final String RESNAME_APP_GRES = "APP_GRES"; + + /** + * 当前进程节点的nodeid, 类型:int + */ + public static final String RESNAME_APP_NODEID = "APP_NODEID"; + + /** + * 当前进程节点的IP地址, 类型:InetSocketAddress、InetAddress、String + */ + public static final String RESNAME_APP_ADDR = "APP_ADDR"; + + /** + * 当前进程的work线程池, 类型:Executor、ExecutorService + * + * @since 2.3.0 + */ + public static final String RESNAME_APP_EXECUTOR = "APP_EXECUTOR"; + + /** + * 当前进程的客户端组, 类型:AsyncGroup + * + * @since 2.3.0 + */ + public static final String RESNAME_APP_ASYNCGROUP = "APP_ASYNCGROUP"; + + /** + * 当前Service所属的SNCP Server的地址 类型: SocketAddress、InetSocketAddress、String
    + */ + public static final String RESNAME_SNCP_ADDR = "SNCP_ADDR"; + + /** + * 当前Service所属的SNCP Server所属的组 类型: String
    + */ + public static final String RESNAME_SNCP_GROUP = "SNCP_GROUP"; + + /** + * "SERVER_ROOT" 当前Server的ROOT目录类型:String、File、Path + */ + public static final String RESNAME_SERVER_ROOT = Server.RESNAME_SERVER_ROOT; + + /** + * 当前Server的ResourceFactory + */ + public static final String RESNAME_SERVER_RESFACTORY = Server.RESNAME_SERVER_RESFACTORY; + + //本进程节点ID + final int nodeid; + + //本进程节点ID + final String name; + + //本地IP地址 + final InetSocketAddress localAddress; + + //业务逻辑线程池 + //@since 2.3.0 + final ExecutorService workExecutor; + + //CacheSource 资源 + final List cacheSources = new CopyOnWriteArrayList<>(); + + //DataSource 资源 + final List dataSources = new CopyOnWriteArrayList<>(); + + //NodeServer 资源, 顺序必须是sncps, others, watchs + final List servers = new CopyOnWriteArrayList<>(); + + //SNCP传输端的TransportFactory, 注意: 只给SNCP使用 + final TransportFactory sncpTransportFactory; + + //给客户端使用,包含SNCP客户端、自定义数据库客户端连接池 + final AsyncGroup asyncGroup; + + //第三方服务发现管理接口 + //@since 2.1.0 + final ClusterAgent clusterAgent; + + //MQ管理接口 + //@since 2.1.0 + final MessageAgent[] messageAgents; + + //全局根ResourceFactory + final ResourceFactory resourceFactory = ResourceFactory.create(); + + //服务配置项 + final AnyValue config; + + //是否从/META-INF中读取配置 + final boolean configFromCache; + + //排除的jar路径 + final String excludelibs; + + //临时计数器 + CountDownLatch servicecdl; //会出现两次赋值 + + //是否启动了WATCH协议服务 + boolean watching; + + //-------------------------------------------------------------------------------------------- + //是否用于main方法运行 + private final boolean singletonMode; + + //是否用于编译模式运行 + private final boolean compileMode; + + //根WatchFactory + //private final WatchFactory watchFactory = WatchFactory.root(); + //进程根目录 + private final File home; + + //配置文件目录 + private final URI confPath; + + //日志 + private final Logger logger; + + //监听事件 + final List listeners = new CopyOnWriteArrayList<>(); + + //服务启动时间 + private final long startTime = System.currentTimeMillis(); + + //Server启动的计数器,用于确保所有Server都启动完后再进行下一步处理 + private final CountDownLatch serversLatch; + + //根ClassLoader + private final RedkaleClassLoader classLoader; + + //Server根ClassLoader + private final RedkaleClassLoader serverClassLoader; + + Application(final AnyValue config) { + this(false, false, config); + } + + @SuppressWarnings("UseSpecificCatch") + Application(final boolean singletonMode, boolean compileMode, final AnyValue config) { + this.singletonMode = singletonMode; + this.compileMode = compileMode; + this.config = config; + this.configFromCache = "true".equals(config.getValue("[config-from-cache]")); + System.setProperty("redkale.version", Redkale.getDotedVersion()); + + final File root = new File(System.getProperty(RESNAME_APP_HOME)); + this.resourceFactory.register(RESNAME_APP_TIME, long.class, this.startTime); + this.resourceFactory.register(RESNAME_APP_HOME, Path.class, root.toPath()); + this.resourceFactory.register(RESNAME_APP_HOME, File.class, root); + this.resourceFactory.register(RESNAME_APP_HOME, URI.class, root.toURI()); + File confFile = null; + try { + this.resourceFactory.register(RESNAME_APP_HOME, root.getCanonicalPath()); + if (System.getProperty(RESNAME_APP_HOME) == null) { + System.setProperty(RESNAME_APP_HOME, root.getCanonicalPath()); + } + this.home = root.getCanonicalFile(); + String confDir = System.getProperty(RESNAME_APP_CONF, "conf"); + if (confDir.contains("://") || confDir.startsWith("file:") || confDir.startsWith("resource:") || confDir.contains("!")) { //graalvm native-image startwith resource:META-INF + this.confPath = new URI(confDir); + if (confDir.startsWith("file:")) { + confFile = new File(this.confPath.getPath()).getCanonicalFile(); + } + } else if (confDir.charAt(0) == '/' || confDir.indexOf(':') > 0) { + confFile = new File(confDir).getCanonicalFile(); + this.confPath = confFile.toURI(); + } else { + confFile = new File(this.home, confDir).getCanonicalFile(); + this.confPath = confFile.toURI(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + String localaddr = config.getValue("address", "").trim(); + InetAddress addr = localaddr.isEmpty() ? Utility.localInetAddress() : new InetSocketAddress(localaddr, config.getIntValue("port")).getAddress(); + this.localAddress = new InetSocketAddress(addr, config.getIntValue("port")); + this.resourceFactory.register(RESNAME_APP_ADDR, addr.getHostAddress()); + this.resourceFactory.register(RESNAME_APP_ADDR, InetAddress.class, addr); + this.resourceFactory.register(RESNAME_APP_ADDR, InetSocketAddress.class, this.localAddress); + + this.resourceFactory.register(RESNAME_APP_CONF, URI.class, this.confPath); + this.resourceFactory.register(RESNAME_APP_CONF, String.class, this.confPath.toString()); + if (confFile != null) { + this.resourceFactory.register(RESNAME_APP_CONF, File.class, confFile); + this.resourceFactory.register(RESNAME_APP_CONF, Path.class, confFile.toPath()); + } + + { + int nid = config.getIntValue("nodeid", 0); + this.nodeid = nid; + this.resourceFactory.register(RESNAME_APP_NODEID, nid); + System.setProperty(RESNAME_APP_NODEID, "" + nid); + } + { + this.name = checkName(config.getValue("name", "")); + this.resourceFactory.register(RESNAME_APP_NAME, name); + System.setProperty(RESNAME_APP_NAME, name); + } + { + ClassLoader currClassLoader = Thread.currentThread().getContextClassLoader(); + if (currClassLoader instanceof RedkaleClassLoader) { + this.classLoader = (RedkaleClassLoader) currClassLoader; + } else { + Set cacheClasses = null; + if (!singletonMode && !compileMode) { + try { + InputStream in = Application.class.getResourceAsStream(RedkaleClassLoader.RESOURCE_CACHE_CLASSES_PATH); + if (in != null) { + BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8), 1024); + List list = new ArrayList<>(); + reader.lines().forEach(v -> list.add(v)); + Collections.sort(list); + if (!list.isEmpty()) cacheClasses = new LinkedHashSet<>(list); + in.close(); + } + } catch (Exception e) { + } + } + if (cacheClasses == null) { + this.classLoader = new RedkaleClassLoader(currClassLoader); + } else { + this.classLoader = new RedkaleClassLoader.RedkaleCacheClassLoader(currClassLoader, cacheClasses); + } + Thread.currentThread().setContextClassLoader(this.classLoader); + } + } + //以下是初始化日志配置 + { + URI logConfURI; + File logConfFile = null; + if (configFromCache) { + logConfURI = RedkaleClassLoader.getConfResourceAsURI(null, "logging.properties"); + } else if ("file".equals(confPath.getScheme())) { + logConfFile = new File(confPath.getPath(), "logging.properties"); + logConfURI = logConfFile.toURI(); + if (!logConfFile.isFile() || !logConfFile.canRead()) logConfFile = null; + } else { + logConfURI = URI.create(confPath + (confPath.toString().endsWith("/") ? "" : "/") + "logging.properties"); + } + if (!"file".equals(confPath.getScheme()) || logConfFile != null) { + try { + final String rootpath = root.getCanonicalPath().replace('\\', '/'); + InputStream fin = logConfURI.toURL().openStream(); + Properties properties = new Properties(); + properties.load(fin); + fin.close(); + properties.entrySet().forEach(x -> x.setValue(x.getValue().toString().replace("${APP_HOME}", rootpath))); + + if (properties.getProperty("java.util.logging.FileHandler.formatter") == null) { + if (compileMode) { + properties.setProperty("java.util.logging.FileHandler.formatter", SimpleFormatter.class.getName()); + if (properties.getProperty("java.util.logging.SimpleFormatter.format") == null) { + properties.setProperty("java.util.logging.SimpleFormatter.format", LoggingFileHandler.FORMATTER_FORMAT.replaceAll("\r\n", "%n")); + } + } else { + properties.setProperty("java.util.logging.FileHandler.formatter", LoggingFileHandler.LoggingFormater.class.getName()); + } + } + if (properties.getProperty("java.util.logging.ConsoleHandler.formatter") == null) { + if (compileMode) { + properties.setProperty("java.util.logging.ConsoleHandler.formatter", SimpleFormatter.class.getName()); + if (properties.getProperty("java.util.logging.SimpleFormatter.format") == null) { + properties.setProperty("java.util.logging.SimpleFormatter.format", LoggingFileHandler.FORMATTER_FORMAT.replaceAll("\r\n", "%n")); + } + } else { + properties.setProperty("java.util.logging.ConsoleHandler.formatter", LoggingFileHandler.LoggingFormater.class.getName()); + } + } + String fileHandlerPattern = properties.getProperty("java.util.logging.FileHandler.pattern"); + if (fileHandlerPattern != null && fileHandlerPattern.contains("%d")) { + final String fileHandlerClass = LoggingFileHandler.class.getName(); + Properties prop = new Properties(); + final String handlers = properties.getProperty("handlers"); + if (handlers != null && handlers.contains("java.util.logging.FileHandler")) { + //singletonrun模式下不输出文件日志 + prop.setProperty("handlers", handlers.replace("java.util.logging.FileHandler", singletonMode || compileMode ? "" : fileHandlerClass)); + } + if (!prop.isEmpty()) { + String prefix = fileHandlerClass + "."; + properties.entrySet().forEach(x -> { + if (x.getKey().toString().startsWith("java.util.logging.FileHandler.")) { + prop.put(x.getKey().toString().replace("java.util.logging.FileHandler.", prefix), x.getValue()); + } + }); + prop.entrySet().forEach(x -> { + properties.put(x.getKey(), x.getValue()); + }); + } + if (!compileMode) { + properties.put(SncpClient.class.getSimpleName() + ".handlers", LoggingFileHandler.LoggingSncpFileHandler.class.getName()); + } + } + if (compileMode) { + properties.put("handlers", "java.util.logging.ConsoleHandler"); + Map newprop = new HashMap(properties); + newprop.forEach((k, v) -> { + if (k.toString().startsWith("java.util.logging.FileHandler.")) { + properties.remove(k); + } + }); + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + final PrintStream ps = new PrintStream(out); + properties.forEach((x, y) -> ps.println(x + "=" + y)); + LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray())); + } catch (Exception e) { + Logger.getLogger(this.getClass().getSimpleName()).log(Level.WARNING, "init logger configuration error", e); + } + } + } + this.logger = Logger.getLogger(this.getClass().getSimpleName()); + this.serversLatch = new CountDownLatch(config.getAnyValues("server").length + 1); + logger.log(Level.INFO, "------------------------- Redkale " + Redkale.getDotedVersion() + " -------------------------"); + //------------------配置 节点 ------------------ + final AnyValue resources = config.getAnyValue("resources"); + TransportStrategy strategy = null; + String excludelib0 = null; + ClusterAgent cluster = null; + MessageAgent[] mqs = null; + int bufferCapacity = 32 * 1024; + int bufferPoolSize = Utility.cpus() * 8; + int readTimeoutSeconds = TransportFactory.DEFAULT_READTIMEOUTSECONDS; + int writeTimeoutSeconds = TransportFactory.DEFAULT_WRITETIMEOUTSECONDS; + AnyValue executorConf = null; + if (resources != null) { + executorConf = resources.getAnyValue("executor"); + AnyValue excludelibConf = resources.getAnyValue("excludelibs"); + if (excludelibConf != null) excludelib0 = excludelibConf.getValue("value"); + AnyValue transportConf = resources.getAnyValue("transport"); + int groupsize = resources.getAnyValues("group").length; + if (groupsize > 0 && transportConf == null) transportConf = new DefaultAnyValue(); + if (transportConf != null) { + //--------------transportBufferPool----------- + bufferCapacity = Math.max(parseLenth(transportConf.getValue("bufferCapacity"), bufferCapacity), 32 * 1024); + readTimeoutSeconds = transportConf.getIntValue("readTimeoutSeconds", readTimeoutSeconds); + writeTimeoutSeconds = transportConf.getIntValue("writeTimeoutSeconds", writeTimeoutSeconds); + final int threads = parseLenth(transportConf.getValue("threads"), groupsize * Utility.cpus() * 2); + bufferPoolSize = parseLenth(transportConf.getValue("bufferPoolSize"), threads * 4); + } + + AnyValue clusterConf = resources.getAnyValue("cluster"); + if (clusterConf != null) { + try { + String classval = clusterConf.getValue("value"); + if (classval == null || classval.isEmpty()) { + Iterator it = ServiceLoader.load(ClusterAgentProvider.class, classLoader).iterator(); + RedkaleClassLoader.putServiceLoader(ClusterAgentProvider.class); + while (it.hasNext()) { + ClusterAgentProvider agent = it.next(); + if (agent != null) RedkaleClassLoader.putReflectionPublicConstructors(agent.getClass(), agent.getClass().getName()); //loader class + if (agent != null && agent.acceptsConf(clusterConf)) { + RedkaleClassLoader.putReflectionPublicConstructors(agent.getClass(), agent.agentClass().getName()); //agent class + cluster = agent.agentClass().getConstructor().newInstance(); + cluster.setConfig(clusterConf); + break; + } + } + if (cluster == null) { + ClusterAgent cacheClusterAgent = new CacheClusterAgent(); + if (cacheClusterAgent.acceptsConf(clusterConf)) { + cluster = cacheClusterAgent; + cluster.setConfig(clusterConf); + } + } + if (cluster == null) logger.log(Level.SEVERE, "load application cluster resource, but not found name='value' value error: " + clusterConf); + } else { + Class type = classLoader.loadClass(clusterConf.getValue("value")); + if (!ClusterAgent.class.isAssignableFrom(type)) { + logger.log(Level.SEVERE, "load application cluster resource, but not found " + ClusterAgent.class.getSimpleName() + " implements class error: " + clusterConf); + } else { + RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName()); + cluster = (ClusterAgent) type.getDeclaredConstructor().newInstance(); + cluster.setConfig(clusterConf); + } + } + } catch (Exception e) { + logger.log(Level.SEVERE, "load application cluster resource error: " + clusterConf, e); + } + } + + AnyValue[] mqConfs = resources.getAnyValues("mq"); + if (mqConfs != null && mqConfs.length > 0) { + mqs = new MessageAgent[mqConfs.length]; + Set mqnames = new HashSet<>(); + for (int i = 0; i < mqConfs.length; i++) { + AnyValue mqConf = mqConfs[i]; + String mqname = mqConf.getValue("name", ""); + if (mqnames.contains(mqname)) throw new RuntimeException("mq.name(" + mqname + ") is repeat"); + mqnames.add(mqname); + String namex = mqConf.getValue("names"); + if (namex != null && !namex.isEmpty()) { + for (String n : namex.split(";")) { + if (n.trim().isEmpty()) continue; + if (mqnames.contains(n.trim())) throw new RuntimeException("mq.name(" + n.trim() + ") is repeat"); + mqnames.add(n.trim()); + } + } + try { + String classval = mqConf.getValue("value"); + if (classval == null || classval.isEmpty()) { + Iterator it = ServiceLoader.load(MessageAgentProvider.class, classLoader).iterator(); + RedkaleClassLoader.putServiceLoader(MessageAgentProvider.class); + while (it.hasNext()) { + MessageAgentProvider messageAgent = it.next(); + if (messageAgent != null) RedkaleClassLoader.putReflectionPublicConstructors(messageAgent.getClass(), messageAgent.getClass().getName()); //loader class + if (messageAgent != null && messageAgent.acceptsConf(mqConf)) { + RedkaleClassLoader.putReflectionPublicConstructors(messageAgent.getClass(), messageAgent.agentClass().getName()); //agent class + mqs[i] = messageAgent.agentClass().getConstructor().newInstance(); + mqs[i].setConfig(mqConf); + break; + } + } + if (mqs[i] == null) logger.log(Level.SEVERE, "load application mq resource, but not found name='value' value error: " + mqConf); + } else { + Class type = classLoader.loadClass(classval); + if (!MessageAgent.class.isAssignableFrom(type)) { + logger.log(Level.SEVERE, "load application mq resource, but not found " + MessageAgent.class.getSimpleName() + " implements class error: " + mqConf); + } else { + RedkaleClassLoader.putReflectionDeclaredConstructors(type, type.getName()); + mqs[i] = (MessageAgent) type.getDeclaredConstructor().newInstance(); + mqs[i].setConfig(mqConf); + } + } + } catch (Exception e) { + logger.log(Level.SEVERE, "load application mq resource error: " + mqs[i], e); + } + } + } + } + + ExecutorService workExecutor0 = null; + if (executorConf != null) { + final AtomicReference workref = new AtomicReference<>(); + final int executorThreads = executorConf.getIntValue("threads", Math.max(2, Utility.cpus())); + boolean executorHash = executorConf.getBoolValue("hash"); + if (executorThreads > 0) { + final AtomicInteger workcounter = new AtomicInteger(); + if (executorHash) { + workExecutor0 = new ThreadHashExecutor(executorThreads, (Runnable r) -> { + int i = workcounter.get(); + int c = workcounter.incrementAndGet(); + String threadname = "Redkale-HashWorkThread-" + (c > 9 ? c : ("0" + c)); + Thread t = new WorkThread(threadname, i, executorThreads, workref.get(), r); + return t; + }); + } else { + workExecutor0 = Executors.newFixedThreadPool(executorThreads, (Runnable r) -> { + int i = workcounter.get(); + int c = workcounter.incrementAndGet(); + String threadname = "Redkale-WorkThread-" + (c > 9 ? c : ("0" + c)); + Thread t = new WorkThread(threadname, i, executorThreads, workref.get(), r); + return t; + }); + } + workref.set(workExecutor0); + } + } + this.workExecutor = workExecutor0; + this.resourceFactory.register(RESNAME_APP_EXECUTOR, Executor.class, this.workExecutor); + this.resourceFactory.register(RESNAME_APP_EXECUTOR, ExecutorService.class, this.workExecutor); + + this.asyncGroup = new AsyncIOGroup(true, null, this.workExecutor, bufferCapacity, bufferPoolSize); + this.resourceFactory.register(RESNAME_APP_ASYNCGROUP, AsyncGroup.class, this.asyncGroup); + + this.excludelibs = excludelib0; + this.sncpTransportFactory = TransportFactory.create(this.asyncGroup, (SSLContext) null, Transport.DEFAULT_NETPROTOCOL, readTimeoutSeconds, writeTimeoutSeconds, strategy); + DefaultAnyValue tarnsportConf = DefaultAnyValue.create(TransportFactory.NAME_POOLMAXCONNS, System.getProperty("redkale.net.transport.pool.maxconns", "100")) + .addValue(TransportFactory.NAME_PINGINTERVAL, System.getProperty("redkale.net.transport.ping.interval", "30")) + .addValue(TransportFactory.NAME_CHECKINTERVAL, System.getProperty("redkale.net.transport.check.interval", "30")); + this.sncpTransportFactory.init(tarnsportConf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining()); + this.clusterAgent = cluster; + this.messageAgents = mqs; + if (compileMode || this.classLoader instanceof RedkaleClassLoader.RedkaleCacheClassLoader) { + this.serverClassLoader = this.classLoader; + } else { + this.serverClassLoader = new RedkaleClassLoader(this.classLoader); + } + } + + private String checkName(String name) { //不能含特殊字符 + if (name == null || name.isEmpty()) return name; + if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); + for (char ch : name.toCharArray()) { + if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 + throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); + } + } + return name; + } + + public ExecutorService getWorkExecutor() { + return workExecutor; + } + + public AsyncGroup getAsyncGroup() { + return asyncGroup; + } + + public ResourceFactory getResourceFactory() { + return resourceFactory; + } + + public TransportFactory getSncpTransportFactory() { + return sncpTransportFactory; + } + + public ClusterAgent getClusterAgent() { + return clusterAgent; + } + + public MessageAgent getMessageAgent(String name) { + if (messageAgents == null) return null; + for (MessageAgent agent : messageAgents) { + if (agent.getName().equals(name)) return agent; + } + return null; + } + + public MessageAgent[] getMessageAgents() { + return messageAgents; + } + + public RedkaleClassLoader getClassLoader() { + return classLoader; + } + + public RedkaleClassLoader getServerClassLoader() { + return serverClassLoader; + } + + public List getNodeServers() { + return new ArrayList<>(servers); + } + + public List getDataSources() { + return new ArrayList<>(dataSources); + } + + public List getCacheSources() { + return new ArrayList<>(cacheSources); + } + + public int getNodeid() { + return nodeid; + } + + public String getName() { + return name; + } + + public File getHome() { + return home; + } + + public URI getConfPath() { + return confPath; + } + + public long getStartTime() { + return startTime; + } + + public AnyValue getAppConfig() { + return config; + } + + public boolean isCompileMode() { + return compileMode; + } + + public boolean isSingletonMode() { + return singletonMode; + } + + public void init() throws Exception { + System.setProperty("redkale.net.transport.poolmaxconns", "100"); + System.setProperty("redkale.net.transport.pinginterval", "30"); + System.setProperty("redkale.net.transport.checkinterval", "30"); + System.setProperty("redkale.convert.tiny", "true"); + System.setProperty("redkale.convert.pool.size", "128"); + System.setProperty("redkale.convert.writer.buffer.defsize", "4096"); + + final String confDir = this.confPath.toString(); + final String homepath = this.home.getCanonicalPath(); + if ("file".equals(this.confPath.getScheme())) { + File persist = new File(new File(confPath), "persistence.xml"); + if (persist.isFile()) System.setProperty(DataSources.DATASOURCE_CONFPATH, persist.getCanonicalPath()); + } else { + System.setProperty(DataSources.DATASOURCE_CONFPATH, confDir + (confDir.endsWith("/") ? "" : "/") + "persistence.xml"); + } +// String pidstr = ""; +// try { //JDK 9+ +// Class phclass = Thread.currentThread().getContextClassLoader().loadClass("java.lang.ProcessHandle"); +// Object phobj = phclass.getMethod("current").invoke(null); +// Object pid = phclass.getMethod("pid").invoke(phobj); +// pidstr = "APP_PID = " + pid + "\r\n"; +// } catch (Throwable t) { +// } + + logger.log(Level.INFO, "APP_OSNAME = " + System.getProperty("os.name") + " " + System.getProperty("os.version") + " " + System.getProperty("os.arch") + "\r\n" + + "APP_JAVA = " + System.getProperty("java.runtime.name", System.getProperty("org.graalvm.nativeimage.kind") != null ? "Nativeimage" : "") + + " " + System.getProperty("java.runtime.version", System.getProperty("java.vendor.version", System.getProperty("java.vm.version"))) + "\r\n" //graalvm.nativeimage 模式下无 java.runtime.xxx 属性 + + "APP_PID = " + ProcessHandle.current().pid() + "\r\n" + + RESNAME_APP_NODEID + " = " + this.nodeid + "\r\n" + + "APP_LOADER = " + this.classLoader.getClass().getSimpleName() + "\r\n" + + RESNAME_APP_ADDR + " = " + this.localAddress.getHostString() + ":" + this.localAddress.getPort() + "\r\n" + + RESNAME_APP_HOME + " = " + homepath + "\r\n" + + RESNAME_APP_CONF + " = " + confDir.substring(confDir.indexOf('!') + 1)); + + if (!compileMode && !(classLoader instanceof RedkaleClassLoader.RedkaleCacheClassLoader)) { + String lib = config.getValue("lib", "${APP_HOME}/libs/*").trim().replace("${APP_HOME}", homepath); + lib = lib.isEmpty() ? confDir : (lib + ";" + confDir); + Server.loadLib(classLoader, logger, lib); + } + + //------------------------------------------------------------------------ + final AnyValue resources = config.getAnyValue("resources"); + if (resources != null) { + resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources); + final AnyValue properties = resources.getAnyValue("properties"); + if (properties != null) { + String dfloads = properties.getValue("load"); + if (dfloads != null) { + for (String dfload : dfloads.split(";")) { + if (dfload.trim().isEmpty()) continue; + final URI df = RedkaleClassLoader.getConfResourceAsURI(configFromCache ? null : confDir, dfload.trim()); + if (df != null && (!"file".equals(df.getScheme()) || df.toString().contains("!") || new File(df).isFile())) { + Properties ps = new Properties(); + try { + InputStream in = df.toURL().openStream(); + ps.load(in); + in.close(); + ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath))); + } catch (Exception e) { + logger.log(Level.WARNING, "load properties(" + dfload + ") error", e); + } + } + } + } + for (AnyValue prop : properties.getAnyValues("property")) { + String name = prop.getValue("name"); + String value = prop.getValue("value"); + if (name == null || value == null) continue; + value = value.replace("${APP_HOME}", homepath); + if (name.startsWith("system.property.")) { + System.setProperty(name.substring("system.property.".length()), value); + } else if (name.startsWith("mimetype.property.")) { + MimeType.add(name.substring("mimetype.property.".length()), value); + } else if (name.startsWith("property.")) { + resourceFactory.register(name, value); + } else { + resourceFactory.register("property." + name, value); + } + } + } + } + this.resourceFactory.register(BsonFactory.root()); + this.resourceFactory.register(JsonFactory.root()); + this.resourceFactory.register(BsonFactory.root().getConvert()); + this.resourceFactory.register(JsonFactory.root().getConvert()); + this.resourceFactory.register("bsonconvert", Convert.class, BsonFactory.root().getConvert()); + this.resourceFactory.register("jsonconvert", Convert.class, JsonFactory.root().getConvert()); + //只有WatchService才能加载Application、WatchFactory + final Application application = this; + this.resourceFactory.register(new ResourceFactory.ResourceLoader() { + + @Override + public void load(ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) { + try { + Resource res = field.getAnnotation(Resource.class); + if (res == null) return; + if (src instanceof Service && Sncp.isRemote((Service) src)) return; //远程模式不得注入 + Class type = field.getType(); + if (type == Application.class) { + field.set(src, application); + } else if (type == ResourceFactory.class) { + boolean serv = RESNAME_SERVER_RESFACTORY.equals(res.name()) || res.name().equalsIgnoreCase("server"); + field.set(src, serv ? rf : (res.name().isEmpty() ? application.resourceFactory : null)); + } else if (type == TransportFactory.class) { + field.set(src, application.sncpTransportFactory); + } else if (type == NodeSncpServer.class) { + NodeServer server = null; + for (NodeServer ns : application.getNodeServers()) { + if (ns.getClass() != NodeSncpServer.class) continue; + if (res.name().equals(ns.server.getName())) { + server = ns; + break; + } + } + field.set(src, server); + } else if (type == NodeHttpServer.class) { + NodeServer server = null; + for (NodeServer ns : application.getNodeServers()) { + if (ns.getClass() != NodeHttpServer.class) continue; + if (res.name().equals(ns.server.getName())) { + server = ns; + break; + } + } + field.set(src, server); + } else if (type == NodeWatchServer.class) { + NodeServer server = null; + for (NodeServer ns : application.getNodeServers()) { + if (ns.getClass() != NodeWatchServer.class) continue; + if (res.name().equals(ns.server.getName())) { + server = ns; + break; + } + } + field.set(src, server); + } +// if (type == WatchFactory.class) { +// field.set(src, application.watchFactory); +// } + } catch (Exception e) { + logger.log(Level.SEVERE, "Resource inject error", e); + } + } + + @Override + public boolean autoNone() { + return false; + } + + }, Application.class, ResourceFactory.class, TransportFactory.class, NodeSncpServer.class, NodeHttpServer.class, NodeWatchServer.class); + + //------------------------------------- 注册 HttpClient -------------------------------------------------------- + resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { + try { + if (field.getAnnotation(Resource.class) == null) return; + HttpClient httpClient = HttpClient.create(asyncGroup); + field.set(src, httpClient); + rf.inject(httpClient, null); // 给其可能包含@Resource的字段赋值; + rf.register(resourceName, HttpClient.class, httpClient); + } catch (Exception e) { + logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpClient inject error", e); + } + }, HttpClient.class); + //-------------------------------------------------------------------------- + if (this.asyncGroup != null) { + ((AsyncIOGroup) this.asyncGroup).start(); + } + if (this.clusterAgent != null) { + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent initing"); + long s = System.currentTimeMillis(); + if (this.clusterAgent instanceof CacheClusterAgent) { + String sourceName = ((CacheClusterAgent) clusterAgent).getSourceName(); //必须在inject前调用,需要赋值Resourcable.name + loadCacheSource(sourceName); + } + clusterAgent.setTransportFactory(this.sncpTransportFactory); + this.resourceFactory.inject(clusterAgent); + clusterAgent.init(clusterAgent.getConfig()); + this.resourceFactory.register(ClusterAgent.class, clusterAgent); + logger.info("ClusterAgent init in " + (System.currentTimeMillis() - s) + " ms"); + } + if (this.messageAgents != null) { + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent initing"); + long s = System.currentTimeMillis(); + for (MessageAgent agent : this.messageAgents) { + this.resourceFactory.inject(agent); + agent.init(agent.getConfig()); + this.resourceFactory.register(agent.getName(), MessageAgent.class, agent); + this.resourceFactory.register(agent.getName(), HttpMessageClient.class, agent.getHttpMessageClient()); + //this.resourceFactory.register(agent.getName(), SncpMessageClient.class, agent.getSncpMessageClient()); //不需要给开发者使用 + } + logger.info("MessageAgent init in " + (System.currentTimeMillis() - s) + " ms"); + + } + //------------------------------------- 注册 HttpMessageClient -------------------------------------------------------- + resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { + try { + if (field.getAnnotation(Resource.class) == null) return; + if (clusterAgent == null) { + HttpMessageClient messageClient = new HttpMessageLocalClient(application, resourceName); + field.set(src, messageClient); + rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值; + rf.register(resourceName, HttpMessageClient.class, messageClient); + return; + } + HttpMessageClient messageClient = new HttpMessageClusterClient(clusterAgent); + field.set(src, messageClient); + rf.inject(messageClient, null); // 给其可能包含@Resource的字段赋值; + rf.register(resourceName, HttpMessageClient.class, messageClient); + } catch (Exception e) { + logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] HttpMessageClient inject error", e); + } + }, HttpMessageClient.class); + initResources(); + } + + private void loadCacheSource(final String sourceName) { + final AnyValue resources = config.getAnyValue("resources"); + for (AnyValue sourceConf : resources.getAnyValues("source")) { + if (!sourceName.equals(sourceConf.getValue("name"))) continue; + String classval = sourceConf.getValue("value"); + try { + Class sourceType = CacheMemorySource.class; + if (classval == null || classval.isEmpty()) { + RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class); + List providers = new ArrayList<>(); + Iterator it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator(); + while (it.hasNext()) { + CacheSourceProvider s = it.next(); + if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName()); + if (s != null && s.acceptsConf(sourceConf)) { + providers.add(s); + } + } + Collections.sort(providers, (a, b) -> { + Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class); + Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class); + return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); + }); + for (CacheSourceProvider provider : providers) { + sourceType = provider.sourceClass(); + if (sourceType != null) break; + } + } else { + sourceType = serverClassLoader.loadClass(classval); + } + RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName()); + CacheSource source = sourceType == CacheMemorySource.class ? new CacheMemorySource(sourceName) + : Modifier.isFinal(sourceType.getModifiers()) ? (CacheSource) sourceType.getConstructor().newInstance() + : (CacheSource) Sncp.createLocalService(serverClassLoader, sourceName, sourceType, null, resourceFactory, sncpTransportFactory, null, null, sourceConf); + cacheSources.add((CacheSource) source); + resourceFactory.register(sourceName, CacheSource.class, source); + resourceFactory.inject(source); + if (!compileMode && source instanceof Service) ((Service) source).init(sourceConf); + logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + sourceName + ", source = " + source); + } catch (Exception e) { + logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e); + } + return; + } + } + + private void initResources() throws Exception { + //------------------------------------------------------------------------- + final AnyValue resources = config.getAnyValue("resources"); + if (!compileMode && !singletonMode && configFromCache) { + System.setProperty(DATASOURCE_CONFPATH, ""); //必须要清空 + } + if (resources != null) { + //------------------------------------------------------------------------ + for (AnyValue conf : resources.getAnyValues("group")) { + final String group = conf.getValue("name", ""); + final String protocol = conf.getValue("protocol", Transport.DEFAULT_NETPROTOCOL).toUpperCase(); + if (!"TCP".equalsIgnoreCase(protocol) && !"UDP".equalsIgnoreCase(protocol)) { + throw new RuntimeException("Not supported Transport Protocol " + conf.getValue("protocol")); + } + TransportGroupInfo ginfo = new TransportGroupInfo(group, protocol, new LinkedHashSet<>()); + for (AnyValue node : conf.getAnyValues("node")) { + final InetSocketAddress addr = new InetSocketAddress(node.getValue("addr"), node.getIntValue("port")); + ginfo.putAddress(addr); + } + sncpTransportFactory.addGroupInfo(ginfo); + } + for (AnyValue conf : resources.getAnyValues("listener")) { + final String listenClass = conf.getValue("value", ""); + if (listenClass.isEmpty()) continue; + Class clazz = classLoader.loadClass(listenClass); + if (!ApplicationListener.class.isAssignableFrom(clazz)) continue; + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName()); + @SuppressWarnings("unchecked") + ApplicationListener listener = (ApplicationListener) clazz.getDeclaredConstructor().newInstance(); + resourceFactory.inject(listener); + listener.init(config); + this.listeners.add(listener); + } + } + //------------------------------------------------------------------------ + } + + private void startSelfServer() throws Exception { + final Application application = this; + new Thread() { + { + setName("Redkale-Application-SelfServer-Thread"); + } + + @Override + public void run() { + try { + final DatagramChannel channel = DatagramChannel.open(); + channel.configureBlocking(true); + channel.socket().setSoTimeout(3000); + channel.bind(new InetSocketAddress("127.0.0.1", config.getIntValue("port"))); + boolean loop = true; + ByteBuffer buffer = ByteBuffer.allocateDirect(1024); + while (loop) { + buffer.clear(); + SocketAddress address = channel.receive(buffer); + buffer.flip(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + String[] param = JsonConvert.root().convertFrom(String[].class, bytes); + final String cmd = param[0]; + if ("SHUTDOWN".equalsIgnoreCase(cmd)) { + try { + long s = System.currentTimeMillis(); + logger.info(application.getClass().getSimpleName() + " shutdowning"); + application.shutdown(); + buffer.clear(); + buffer.put("SHUTDOWN OK".getBytes()); + buffer.flip(); + channel.send(buffer, address); + long e = System.currentTimeMillis() - s; + logger.info(application.getClass().getSimpleName() + " shutdown in " + e + " ms"); + application.serversLatch.countDown(); + System.exit(0); + } catch (Exception ex) { + logger.log(Level.INFO, "SHUTDOWN FAIL", ex); + buffer.clear(); + buffer.put("SHUTDOWN FAIL".getBytes()); + buffer.flip(); + channel.send(buffer, address); + } + } else if ("APIDOC".equalsIgnoreCase(cmd)) { + try { + String[] args = new String[param.length - 1]; + if (param.length > 1) System.arraycopy(param, 1, args, 0, args.length); + new ApiDocsService(application).run(args); + buffer.clear(); + buffer.put("APIDOC OK".getBytes()); + buffer.flip(); + channel.send(buffer, address); + } catch (Exception ex) { + buffer.clear(); + buffer.put("APIDOC FAIL".getBytes()); + buffer.flip(); + channel.send(buffer, address); + } + } else { + long s = System.currentTimeMillis(); + logger.info(application.getClass().getSimpleName() + " command " + cmd); + application.command(cmd); + buffer.clear(); + buffer.put("COMMAND OK".getBytes()); + buffer.flip(); + channel.send(buffer, address); + long e = System.currentTimeMillis() - s; + logger.info(application.getClass().getSimpleName() + " command in " + e + " ms"); + } + } + } catch (Exception e) { + logger.log(Level.INFO, "Control fail", e); + System.exit(1); + } + } + }.start(); + } + + private static void sendCommand(Logger logger, int port, String command, String[] args) throws Exception { + final DatagramChannel channel = DatagramChannel.open(); + channel.configureBlocking(true); + channel.connect(new InetSocketAddress("127.0.0.1", port)); + ByteBuffer buffer = ByteBuffer.allocate(1024); + String[] param = new String[1 + (args == null ? 0 : args.length)]; + param[0] = command; + if (args != null) System.arraycopy(args, 0, param, 1, args.length); + buffer.put(JsonConvert.root().convertToBytes(param)); + buffer.flip(); + channel.write(buffer); + buffer.clear(); + channel.configureBlocking(true); + try { + channel.read(buffer); + buffer.flip(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + channel.close(); + if (logger != null) logger.info("Send: " + command + ", Reply: " + new String(bytes)); + Thread.sleep(1000); + } catch (Exception e) { + if (e instanceof PortUnreachableException) { + if ("APIDOC".equalsIgnoreCase(command)) { + final Application application = Application.create(true); + application.init(); + application.start(); + new ApiDocsService(application).run(args); + if (logger != null) logger.info("APIDOC OK"); + return; + } + } + throw e; + } + } + + public void start() throws Exception { + if (!singletonMode && !compileMode && this.clusterAgent != null) { + this.clusterAgent.register(this); + } + final AnyValue[] entrys = config.getAnyValues("server"); + CountDownLatch timecd = new CountDownLatch(entrys.length); + final List sncps = new ArrayList<>(); + final List others = new ArrayList<>(); + final List watchs = new ArrayList<>(); + for (final AnyValue entry : entrys) { + if (entry.getValue("protocol", "").toUpperCase().startsWith("SNCP")) { + sncps.add(entry); + } else if (entry.getValue("protocol", "").toUpperCase().startsWith("WATCH")) { + watchs.add(entry); + } else { + others.add(entry); + } + } + if (watchs.size() > 1) throw new RuntimeException("Found one more WATCH Server"); + this.watching = !watchs.isEmpty(); + + runServers(timecd, sncps); //必须确保SNCP服务都启动后再启动其他服务 + runServers(timecd, others); + runServers(timecd, watchs); //必须在所有服务都启动后再启动WATCH服务 + timecd.await(); + if (this.clusterAgent != null) this.clusterAgent.start(); + if (this.messageAgents != null) { + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent starting"); + long s = System.currentTimeMillis(); + final StringBuffer sb = new StringBuffer(); + Set names = new HashSet<>(); + for (MessageAgent agent : this.messageAgents) { + names.add(agent.getName()); + Map map = agent.start().join(); + AtomicInteger maxlen = new AtomicInteger(); + map.keySet().forEach(str -> { + if (str.length() > maxlen.get()) maxlen.set(str.length()); + }); + new TreeMap<>(map).forEach((topic, ms) -> sb.append("MessageConsumer(topic=").append(alignString(topic, maxlen.get())).append(") init and start in ").append(ms).append(" ms\r\n") + ); + } + if (sb.length() > 0) logger.info(sb.toString().trim()); + logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") start in " + (System.currentTimeMillis() - s) + " ms"); + } + //if (!singletonrun) signalHandle(); + //if (!singletonrun) clearPersistData(); + logger.info(this.getClass().getSimpleName() + " started in " + (System.currentTimeMillis() - startTime) + " ms\r\n"); + for (ApplicationListener listener : this.listeners) { + listener.postStart(this); + } + if (!singletonMode && !compileMode) this.serversLatch.await(); + } + + private static String alignString(String value, int maxlen) { + StringBuilder sb = new StringBuilder(maxlen); + sb.append(value); + for (int i = 0; i < maxlen - value.length(); i++) { + sb.append(' '); + } + return sb.toString(); + } + +// private void clearPersistData() { +// File cachedir = new File(home, "cache"); +// if (!cachedir.isDirectory()) return; +// File[] lfs = cachedir.listFiles(); +// if (lfs != null) { +// for (File file : lfs) { +// if (file.getName().startsWith("persist-")) file.delete(); +// } +// } +// } +// private void signalHandle() { +// //http://www.comptechdoc.org/os/linux/programming/linux_pgsignals.html +// String[] sigs = new String[]{"HUP", "TERM", "INT", "QUIT", "KILL", "TSTP", "USR1", "USR2", "STOP"}; +// List list = new ArrayList<>(); +// for (String sig : sigs) { +// try { +// list.add(new sun.misc.Signal(sig)); +// } catch (Exception e) { +// } +// } +// sun.misc.SignalHandler handler = new sun.misc.SignalHandler() { +// +// private volatile boolean runed; +// +// @Override +// public void handle(Signal sig) { +// if (runed) return; +// runed = true; +// logger.info(Application.this.getClass().getSimpleName() + " stoped\r\n"); +// System.exit(0); +// } +// }; +// for (Signal sig : list) { +// try { +// Signal.handle(sig, handler); +// } catch (Exception e) { +// } +// } +// } + @SuppressWarnings("unchecked") + private void runServers(CountDownLatch timecd, final List serconfs) throws Exception { + this.servicecdl = new CountDownLatch(serconfs.size()); + CountDownLatch sercdl = new CountDownLatch(serconfs.size()); + final AtomicBoolean inited = new AtomicBoolean(false); + final Map> nodeClasses = new HashMap<>(); + for (final AnyValue serconf : serconfs) { + Thread thread = new Thread() { + { + setName("Redkale-" + serconf.getValue("protocol", "Server").toUpperCase().replaceFirst("\\..+", "") + ":" + serconf.getIntValue("port") + "-Thread"); + this.setDaemon(true); + } + + @Override + public void run() { + try { + //Thread ctd = Thread.currentThread(); + //ctd.setContextClassLoader(new URLClassLoader(new URL[0], ctd.getContextClassLoader())); + final String protocol = serconf.getValue("protocol", "").replaceFirst("\\..+", "").toUpperCase(); + NodeServer server = null; + if ("SNCP".equals(protocol)) { + server = NodeSncpServer.createNodeServer(Application.this, serconf); + } else if ("WATCH".equalsIgnoreCase(protocol)) { + DefaultAnyValue serconf2 = (DefaultAnyValue) serconf; + DefaultAnyValue rest = (DefaultAnyValue) serconf2.getAnyValue("rest"); + if (rest == null) { + rest = new DefaultAnyValue(); + serconf2.addValue("rest", rest); + } + rest.setValue("base", WatchServlet.class.getName()); + server = new NodeWatchServer(Application.this, serconf); + } else if ("HTTP".equalsIgnoreCase(protocol)) { + server = new NodeHttpServer(Application.this, serconf); + } else { + if (!inited.get()) { + synchronized (nodeClasses) { + if (!inited.getAndSet(true)) { //加载自定义的协议,如:SOCKS + ClassFilter profilter = new ClassFilter(classLoader, NodeProtocol.class, NodeServer.class, (Class[]) null); + ClassFilter.Loader.load(home, classLoader, ((excludelibs != null ? (excludelibs + ";") : "") + serconf.getValue("excludelibs", "")).split(";"), profilter); + final Set> entrys = profilter.getFilterEntrys(); + for (FilterEntry entry : entrys) { + final Class type = entry.getType(); + NodeProtocol pros = type.getAnnotation(NodeProtocol.class); + String p = pros.value().toUpperCase(); + if ("SNCP".equals(p) || "HTTP".equals(p)) continue; + final Class old = nodeClasses.get(p); + if (old != null && old != type) { + throw new RuntimeException("Protocol(" + p + ") had NodeServer-Class(" + old.getName() + ") but repeat NodeServer-Class(" + type.getName() + ")"); + } + nodeClasses.put(p, type); + } + } + } + } + Class nodeClass = nodeClasses.get(protocol); + if (nodeClass != null) server = NodeServer.create(nodeClass, Application.this, serconf); + } + if (server == null) { + logger.log(Level.SEVERE, "Not found Server Class for protocol({0})", serconf.getValue("protocol")); + System.exit(0); + } + servers.add(server); + server.init(serconf); + if (!singletonMode && !compileMode) { + server.start(); + } else if (compileMode) { + server.getServer().getPrepareServlet().init(server.getServer().getContext(), serconf); + } + timecd.countDown(); + sercdl.countDown(); + } catch (Exception ex) { + logger.log(Level.WARNING, serconf + " runServers error", ex); + Application.this.serversLatch.countDown(); + } + } + }; + thread.start(); + } + sercdl.await(); + } + + public static T singleton(Class serviceClass, Class... extServiceClasses) throws Exception { + return singleton("", serviceClass, extServiceClasses); + } + + public static T singleton(String name, Class serviceClass, Class... extServiceClasses) throws Exception { + if (serviceClass == null) throw new IllegalArgumentException("serviceClass is null"); + final Application application = Application.create(true); + System.setProperty("red" + "kale.singleton.serviceclass", serviceClass.getName()); + if (extServiceClasses != null && extServiceClasses.length > 0) { + StringBuilder sb = new StringBuilder(); + for (Class clazz : extServiceClasses) { + if (sb.length() > 0) sb.append(','); + sb.append(clazz.getName()); + } + System.setProperty("red" + "kale.singleton.extserviceclasses", sb.toString()); + } + application.init(); + application.start(); + for (NodeServer server : application.servers) { + T service = server.resourceFactory.find(name, serviceClass); + if (service != null) return service; + } + if (Modifier.isAbstract(serviceClass.getModifiers())) throw new IllegalArgumentException("abstract class not allowed"); + if (serviceClass.isInterface()) throw new IllegalArgumentException("interface class not allowed"); + throw new IllegalArgumentException(serviceClass.getName() + " maybe have zero not-final public method"); + } + + public static Application create(final boolean singleton) throws IOException { + return new Application(singleton, false, loadAppConfig()); + } + + public void reloadConfig() throws IOException { + AnyValue newconfig = loadAppConfig(); + final String confpath = this.confPath.toString(); + final String homepath = this.home.getCanonicalPath(); + final AnyValue resources = newconfig.getAnyValue("resources"); + if (resources != null) { + resourceFactory.register(RESNAME_APP_GRES, AnyValue.class, resources); + final AnyValue properties = resources.getAnyValue("properties"); + if (properties != null) { + String dfloads = properties.getValue("load"); + if (dfloads != null) { + for (String dfload : dfloads.split(";")) { + if (dfload.trim().isEmpty()) continue; + final URI df = (dfload.indexOf('/') < 0) ? URI.create(confpath + (confpath.endsWith("/") ? "" : "/") + dfload) : new File(dfload).toURI(); + if (!"file".equals(df.getScheme()) || new File(df).isFile()) { + Properties ps = new Properties(); + try { + InputStream in = df.toURL().openStream(); + ps.load(in); + in.close(); + ps.forEach((x, y) -> resourceFactory.register("property." + x, y.toString().replace("${APP_HOME}", homepath))); + } catch (Exception e) { + logger.log(Level.WARNING, "load properties(" + dfload + ") error", e); + } + } + } + } + for (AnyValue prop : properties.getAnyValues("property")) { + String name = prop.getValue("name"); + String value = prop.getValue("value"); + if (name == null || value == null) continue; + value = value.replace("${APP_HOME}", homepath); + if (name.startsWith("system.property.")) { + System.setProperty(name.substring("system.property.".length()), value); + } else if (name.startsWith("mimetype.property.")) { + MimeType.add(name.substring("mimetype.property.".length()), value); + } else if (name.startsWith("property.")) { + resourceFactory.register(name, value); + } else { + resourceFactory.register("property." + name, value); + } + } + } + } + } + + static AnyValue loadAppConfig() throws IOException { + final String home = new File(System.getProperty(RESNAME_APP_HOME, "")).getCanonicalPath().replace('\\', '/'); + System.setProperty(RESNAME_APP_HOME, home); + String confDir = System.getProperty(RESNAME_APP_CONF, "conf"); + URI appConfFile; + boolean fromcache = false; + if (confDir.contains("://")) { + appConfFile = URI.create(confDir + (confDir.endsWith("/") ? "" : "/") + "application.xml"); + } else if (confDir.charAt(0) == '/' || confDir.indexOf(':') > 0) { + File f = new File(confDir, "application.xml"); + if (f.isFile() && f.canRead()) { + appConfFile = f.toURI(); + confDir = f.getParentFile().getCanonicalPath(); + } else { + appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.xml"); //不能传confDir + confDir = appConfFile.toString().replace("/application.xml", ""); + fromcache = true; + } + } else { + File f = new File(new File(home, confDir), "application.xml"); + if (f.isFile() && f.canRead()) { + appConfFile = f.toURI(); + confDir = f.getParentFile().getCanonicalPath(); + } else { + appConfFile = RedkaleClassLoader.getConfResourceAsURI(null, "application.xml"); //不能传confDir + confDir = appConfFile.toString().replace("/application.xml", ""); + fromcache = true; + } + } + System.setProperty(RESNAME_APP_CONF, confDir); + AnyValue conf = AnyValue.loadFromXml(appConfFile.toURL().openStream(), StandardCharsets.UTF_8, (k, v) -> v.replace("${APP_HOME}", home)).getAnyValue("application"); + if (fromcache) ((DefaultAnyValue) conf).addValue("[config-from-cache]", "true"); + return conf; + } + + public static void main(String[] args) throws Exception { + Utility.midnight(); //先初始化一下Utility + Thread.currentThread().setName("Redkale-Application-Main-Thread"); + //运行主程序 + { + String cmd = System.getProperty("cmd", System.getProperty("CMD")); + String[] params = args; + if (cmd == null && args != null && args.length > 0 + && args[0] != null && !args[0].trim().isEmpty() && !"start".equalsIgnoreCase(args[0])) { + cmd = args[0]; + params = Arrays.copyOfRange(args, 1, args.length); + } + if (cmd != null) { + AnyValue config = loadAppConfig(); + Application.sendCommand(null, config.getIntValue("port"), cmd, params); + return; + } + } + //PrepareCompiler.main(args); //测试代码 + + final Application application = Application.create(false); + application.init(); + application.startSelfServer(); + try { + for (ApplicationListener listener : application.listeners) { + listener.preStart(application); + } + application.start(); + } catch (Exception e) { + application.logger.log(Level.SEVERE, "Application start error", e); + System.exit(0); + } + System.exit(0); + } + + NodeSncpServer findNodeSncpServer(final InetSocketAddress sncpAddr) { + for (NodeServer node : servers) { + if (node.isSNCP() && sncpAddr.equals(node.getSncpAddress())) { + return (NodeSncpServer) node; + } + } + return null; + } + + public void command(String cmd) { + List localServers = new ArrayList<>(servers); //顺序sncps, others, watchs + localServers.stream().forEach((server) -> { + try { + server.command(cmd); + } catch (Exception t) { + logger.log(Level.WARNING, " command server(" + server.getSocketAddress() + ") error", t); + } + }); + } + + public void shutdown() throws Exception { + for (ApplicationListener listener : this.listeners) { + try { + listener.preShutdown(this); + } catch (Exception e) { + logger.log(Level.WARNING, listener.getClass() + " preShutdown erroneous", e); + } + } + List localServers = new ArrayList<>(servers); //顺序sncps, others, watchs + Collections.reverse(localServers); //倒序, 必须让watchs先关闭,watch包含服务发现和注销逻辑 + if (isCompileMode() && this.messageAgents != null) { + Set names = new HashSet<>(); + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent stopping"); + long s = System.currentTimeMillis(); + for (MessageAgent agent : this.messageAgents) { + names.add(agent.getName()); + agent.stop().join(); + } + logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") stop in " + (System.currentTimeMillis() - s) + " ms"); + } + if (!isCompileMode() && clusterAgent != null) { + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "ClusterAgent destroying"); + long s = System.currentTimeMillis(); + clusterAgent.deregister(this); + clusterAgent.destroy(clusterAgent.getConfig()); + logger.info("ClusterAgent destroy in " + (System.currentTimeMillis() - s) + " ms"); + } + localServers.stream().forEach((server) -> { + try { + server.shutdown(); + } catch (Exception t) { + logger.log(Level.WARNING, " shutdown server(" + server.getSocketAddress() + ") error", t); + } finally { + serversLatch.countDown(); + } + }); + if (this.messageAgents != null) { + Set names = new HashSet<>(); + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "MessageAgent destroying"); + long s = System.currentTimeMillis(); + for (MessageAgent agent : this.messageAgents) { + names.add(agent.getName()); + agent.destroy(agent.getConfig()); + } + logger.info("MessageAgent(names=" + JsonConvert.root().convertTo(names) + ") destroy in " + (System.currentTimeMillis() - s) + " ms"); + } + for (DataSource source : dataSources) { + if (source == null) continue; + try { + if (source instanceof Service) { + ((Service) source).destroy(Sncp.isSncpDyn((Service) source) ? Sncp.getConf((Service) source) : null); +// } else { +// source.getClass().getMethod("close").invoke(source); +// RedkaleClassLoader.putReflectionMethod(source.getClass().getName(), source.getClass().getMethod("close")); + } + } catch (Exception e) { + logger.log(Level.FINER, source.getClass() + " close DataSource erroneous", e); + } + } + for (CacheSource source : cacheSources) { + if (source == null) continue; + try { + if (source instanceof Service) { + ((Service) source).destroy(Sncp.isSncpDyn((Service) source) ? Sncp.getConf((Service) source) : null); +// } else { +// source.getClass().getMethod("close").invoke(source); +// RedkaleClassLoader.putReflectionMethod(source.getClass().getName(), source.getClass().getMethod("close")); + } + } catch (Exception e) { + logger.log(Level.FINER, source.getClass() + " close CacheSource erroneous", e); + } + } + if (this.asyncGroup != null) { + ((AsyncIOGroup) this.asyncGroup).close(); + } + this.sncpTransportFactory.shutdownNow(); + } + + private static int parseLenth(String value, int defValue) { + if (value == null) return defValue; + value = value.toUpperCase().replace("B", ""); + if (value.endsWith("G")) return Integer.decode(value.replace("G", "")) * 1024 * 1024 * 1024; + if (value.endsWith("M")) return Integer.decode(value.replace("M", "")) * 1024 * 1024; + if (value.endsWith("K")) return Integer.decode(value.replace("K", "")) * 1024; + return Integer.decode(value); + } + +} diff --git a/src/org/redkale/boot/ApplicationListener.java b/src/main/java/org/redkale/boot/ApplicationListener.java similarity index 76% rename from src/org/redkale/boot/ApplicationListener.java rename to src/main/java/org/redkale/boot/ApplicationListener.java index eff8ec5bc..dd1ce9274 100644 --- a/src/org/redkale/boot/ApplicationListener.java +++ b/src/main/java/org/redkale/boot/ApplicationListener.java @@ -1,53 +1,69 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import org.redkale.util.AnyValue; - -/** - * Application启动和关闭时的监听事件
    - * 只能通过application.xml配置 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface ApplicationListener { - - /** - * 初始化方法 - * - * @param config 配置参数 - */ - default void init(AnyValue config) { - - } - - /** - * Application 在运行start前调用 - * - * @param application Application - */ - default void preStart(Application application) { - } - - /** - * Application 在运行start后调用 - * - * @param application Application - */ - default void postStart(Application application) { - } - - /** - * Application 在运行shutdown前调用 - * - * @param application Application - */ - default void preShutdown(Application application) { - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import org.redkale.util.AnyValue; + +/** + * Application启动和关闭时的监听事件
    + * 只能通过application.xml配置 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface ApplicationListener { + + /** + * 初始化方法 + * + * @param config 配置参数 + */ + default void init(AnyValue config) { + + } + + /** + * Application 在运行start前调用 + * + * @param application Application + */ + default void preStart(Application application) { + } + + /** + * Application 在运行start后调用 + * + * @param application Application + */ + default void postStart(Application application) { + } + + /** + * Application 在运行Compile前调用 + * + * @param application Application + */ + default void preCompile(Application application) { + } + + /** + * Application 在运行Compile后调用 + * + * @param application Application + */ + default void postCompile(Application application) { + } + + /** + * Application 在运行shutdown前调用 + * + * @param application Application + */ + default void preShutdown(Application application) { + } +} diff --git a/src/org/redkale/boot/ClassFilter.java b/src/main/java/org/redkale/boot/ClassFilter.java similarity index 89% rename from src/org/redkale/boot/ClassFilter.java rename to src/main/java/org/redkale/boot/ClassFilter.java index e454712f0..c9b946926 100644 --- a/src/org/redkale/boot/ClassFilter.java +++ b/src/main/java/org/redkale/boot/ClassFilter.java @@ -1,594 +1,606 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import java.io.*; -import java.lang.annotation.*; -import java.lang.reflect.Modifier; -import java.net.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.Predicate; -import java.util.jar.*; -import java.util.logging.*; -import java.util.regex.*; -import org.redkale.util.*; -import org.redkale.util.AnyValue.DefaultAnyValue; - -/** - * class过滤器, 符合条件的class会保留下来存入FilterEntry。 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 泛型 - */ -@SuppressWarnings("unchecked") -public final class ClassFilter { - - private static final Logger logger = Logger.getLogger(ClassFilter.class.getName()); //日志对象 - - private static final boolean finest = logger.isLoggable(Level.FINEST); //日志级别 - - private final Set> entrys = new HashSet<>(); //符合条件的结果 - - private final Set> expectEntrys = new HashSet<>(); //准备符合条件的结果 - - private Predicate expectPredicate; - - private boolean refused; //是否拒绝所有数据,设置true,则其他规则失效,都是拒绝. - - private Class superClass; //符合的父类型。不为空时,扫描结果的class必须是superClass的子类 - - private Class[] excludeSuperClasses; //不符合的父类型。 - - private Class annotationClass;//符合的注解。不为空时,扫描结果的class必须包含该注解 - - private Pattern[] includePatterns; //符合的classname正则表达式 - - private Pattern[] excludePatterns;//拒绝的classname正则表达式 - - private Set privilegeIncludes; //特批符合条件的classname - - private Set privilegeExcludes;//特批拒绝条件的classname - - private List ors; //或关系的其他ClassFilter - - private List ands;//与关系的其他ClassFilter - - private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。 - - private final ClassLoader classLoader; - - public ClassFilter(ClassLoader classLoader, Class annotationClass, Class superClass, Class[] excludeSuperClasses) { - this(classLoader, annotationClass, superClass, excludeSuperClasses, null); - } - - public ClassFilter(ClassLoader classLoader, Class annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) { - this.annotationClass = annotationClass; - this.superClass = superClass; - this.excludeSuperClasses = excludeSuperClasses; - this.conf = conf; - this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; - } - - public static ClassFilter create(Class[] excludeSuperClasses, String includeregs, String excluderegs, Set includeValues, Set excludeValues) { - ClassFilter filter = new ClassFilter(null, null, null, excludeSuperClasses); - filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";")); - filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";")); - filter.setPrivilegeIncludes(includeValues); - filter.setPrivilegeExcludes(excludeValues); - return filter; - } - - public ClassFilter or(ClassFilter filter) { - if (ors == null) ors = new ArrayList<>(); - ors.add(filter); - return this; - } - - public ClassFilter and(ClassFilter filter) { - if (ands == null) ands = new ArrayList<>(); - ands.add(filter); - return this; - } - - /** - * 获取符合条件的class集合 - * - * @return Set<FilterEntry<T>> - */ - public final Set> getFilterEntrys() { - HashSet> set = new HashSet<>(); - set.addAll(entrys); - if (ors != null) ors.forEach(f -> set.addAll(f.getFilterEntrys())); - if (ands != null) ands.forEach(f -> set.addAll(f.getFilterEntrys())); - return set; - } - - /** - * 获取预留的class集合 - * - * @return Set<FilterEntry<T>> - */ - public final Set> getFilterExpectEntrys() { - HashSet> set = new HashSet<>(); - set.addAll(expectEntrys); - if (ors != null) ors.forEach(f -> set.addAll(f.getFilterExpectEntrys())); - if (ands != null) ands.forEach(f -> set.addAll(f.getFilterExpectEntrys())); - return set; - } - - /** - * 获取所有的class集合 - * - * @return Set<FilterEntry<T>> - */ - public final Set> getAllFilterEntrys() { - HashSet> rs = new HashSet<>(); - rs.addAll(getFilterEntrys()); - rs.addAll(getFilterExpectEntrys()); - return rs; - } - - /** - * 自动扫描地过滤指定的class - * - * @param property AnyValue - * @param clazzname String - * @param url URL - */ - @SuppressWarnings("unchecked") - public final void filter(AnyValue property, String clazzname, URL url) { - filter(property, clazzname, true, url); - } - - /** - * 过滤指定的class - * - * @param property application.xml中对应class节点下的property属性项 - * @param clazzname class名称 - * @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略 - */ - public final void filter(AnyValue property, String clazzname, boolean autoscan) { - filter(property, clazzname, autoscan, null); - } - - /** - * 过滤指定的class - * - * @param property application.xml中对应class节点下的property属性项 - * @param clazzname class名称 - * @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略 - * @param url URL - */ - public final void filter(AnyValue property, String clazzname, boolean autoscan, URL url) { - boolean r = accept0(property, clazzname); - ClassFilter cf = r ? this : null; - if (r && ands != null) { - for (ClassFilter filter : ands) { - if (!filter.accept(property, clazzname)) return; - } - } - if (!r && ors != null) { - for (ClassFilter filter : ors) { - if (filter.accept(filter.conf, clazzname)) { - cf = filter; - property = cf.conf; - break; - } - } - } - if (cf == null || clazzname.startsWith("sun.") || clazzname.contains("module-info")) return; - try { - Class clazz = classLoader.loadClass(clazzname); - if (!cf.accept(property, clazz, autoscan)) return; - if (cf.conf != null) { - if (property == null) { - property = cf.conf; - } else if (property instanceof DefaultAnyValue) { - ((DefaultAnyValue) property).addAllStringSet(cf.conf); - } else { - DefaultAnyValue dav = new DefaultAnyValue(); - dav.addAllStringSet(property); - dav.addAllStringSet(cf.conf); - property = dav; - } - } - - AutoLoad auto = (AutoLoad) clazz.getAnnotation(AutoLoad.class); - if ((expectPredicate != null && expectPredicate.test(clazzname)) || (autoscan && auto != null && !auto.value())) { //自动扫描且被标记为@AutoLoad(false)的 - expectEntrys.add(new FilterEntry(clazz, autoscan, true, property)); - } else { - entrys.add(new FilterEntry(clazz, autoscan, false, property)); - } - } catch (Throwable cfe) { - if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.") - && !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF") - && !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.") && !clazzname.startsWith("freemarker.") - && !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) { - //&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) { - logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe); - } - } - } - - /** - * 判断class是否有效 - * - * @param classname String - * - * @return boolean - */ - public boolean accept(String classname) { - return accept(null, classname); - } - - /** - * 判断class是否有效 - * - * @param property AnyValue - * @param classname String - * - * @return boolean - */ - public boolean accept(AnyValue property, String classname) { - boolean r = accept0(property, classname); - if (r && ands != null) { - for (ClassFilter filter : ands) { - if (!filter.accept(property, classname)) return false; - } - } - if (!r && ors != null) { - for (ClassFilter filter : ors) { - if (filter.accept(filter.conf, classname)) return true; - } - } - return r; - } - - private boolean accept0(AnyValue property, String classname) { - if (this.refused) return false; - if (this.privilegeIncludes != null && this.privilegeIncludes.contains(classname)) return true; - if (this.privilegeExcludes != null && this.privilegeExcludes.contains(classname)) return false; - if (classname.startsWith("java.") || classname.startsWith("javax.")) return false; - if (excludePatterns != null) { - for (Pattern reg : excludePatterns) { - if (reg.matcher(classname).matches()) return false; - } - } - if (includePatterns != null) { - for (Pattern reg : includePatterns) { - if (reg.matcher(classname).matches()) return true; - } - } - return includePatterns == null; - } - - /** - * 判断class是否有效 - * - * @param property AnyValue - * @param clazz Class - * @param autoscan boolean - * - * @return boolean - */ - @SuppressWarnings("unchecked") - public boolean accept(AnyValue property, Class clazz, boolean autoscan) { - if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false; - if (annotationClass != null && clazz.getAnnotation(annotationClass) == null) return false; - boolean rs = superClass == null || (clazz != superClass && superClass.isAssignableFrom(clazz)); - if (rs && this.excludeSuperClasses != null && this.excludeSuperClasses.length > 0) { - for (Class c : this.excludeSuperClasses) { - if (c != null && (clazz == c || c.isAssignableFrom(clazz))) return false; - } - } - return rs; - } - - public static Pattern[] toPattern(String[] regs) { - if (regs == null || regs.length == 0) return null; - int i = 0; - Pattern[] rs = new Pattern[regs.length]; - for (String reg : regs) { - if (reg == null || reg.trim().isEmpty()) continue; - rs[i++] = Pattern.compile(reg.trim()); - } - if (i == 0) return null; - if (i == rs.length) return rs; - Pattern[] ps = new Pattern[i]; - System.arraycopy(rs, 0, ps, 0, i); - return ps; - } - - public void setSuperClass(Class superClass) { - this.superClass = superClass; - } - - public Class getSuperClass() { - return superClass; - } - - public Class[] getExcludeSuperClasses() { - return excludeSuperClasses; - } - - public void setExcludeSuperClasses(Class[] excludeSuperClasses) { - this.excludeSuperClasses = excludeSuperClasses; - } - - public void setAnnotationClass(Class annotationClass) { - this.annotationClass = annotationClass; - } - - public Pattern[] getIncludePatterns() { - return includePatterns; - } - - public void setIncludePatterns(String[] includePatterns) { - this.includePatterns = toPattern(includePatterns); - } - - public Pattern[] getExcludePatterns() { - return excludePatterns; - } - - public void setExcludePatterns(String[] excludePatterns) { - this.excludePatterns = toPattern(excludePatterns); - } - - public Class getAnnotationClass() { - return annotationClass; - } - - public boolean isRefused() { - return refused; - } - - public void setRefused(boolean refused) { - this.refused = refused; - } - - public Predicate getExpectPredicate() { - return expectPredicate; - } - - public void setExpectPredicate(Predicate predicate) { - this.expectPredicate = predicate; - } - - public Set getPrivilegeIncludes() { - return privilegeIncludes; - } - - public void setPrivilegeIncludes(Set privilegeIncludes) { - this.privilegeIncludes = privilegeIncludes == null || privilegeIncludes.isEmpty() ? null : privilegeIncludes; - } - - public Set getPrivilegeExcludes() { - return privilegeExcludes; - } - - public void setPrivilegeExcludes(Set privilegeExcludes) { - this.privilegeExcludes = privilegeExcludes == null || privilegeExcludes.isEmpty() ? null : privilegeExcludes; - - } - - /** - * 存放符合条件的class与class指定的属性项 - * - * @param 泛型 - */ - public static final class FilterEntry { - - private final HashSet groups = new LinkedHashSet<>(); - - private final String name; - - private final Class type; - - private final AnyValue property; - - private final boolean autoload; - - private final boolean expect; - - public FilterEntry(Class type, AnyValue property) { - this(type, false, false, property); - } - - public FilterEntry(Class type, final boolean autoload, boolean expect, AnyValue property) { - this.type = type; - String str = property == null ? null : property.getValue("groups"); - if (str != null) { - str = str.trim(); - if (str.endsWith(";")) str = str.substring(0, str.length() - 1); - } - if (str != null) this.groups.addAll(Arrays.asList(str.split(";"))); - this.property = property; - this.autoload = autoload; - this.expect = expect; - this.name = property == null ? "" : property.getValue("name", ""); - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName() - + ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + this.groups + "]"; - } - - @Override - public int hashCode() { - return this.type.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - return (this.type == ((FilterEntry) obj).type && this.groups.equals(((FilterEntry) obj).groups) && this.name.equals(((FilterEntry) obj).name)); - } - - public Class getType() { - return type; - } - - public String getName() { - return name; - } - - public AnyValue getProperty() { - return property; - } - - public boolean containsGroup(String group) { - return groups != null && groups.contains(group); - } - - public boolean isEmptyGroups() { - return groups == null || groups.isEmpty(); - } - - public HashSet getGroups() { - return groups; - } - - public boolean isAutoload() { - return autoload; - } - - public boolean isExpect() { - return expect; - } - } - - /** - * class加载类 - */ - public static class Loader { - - protected static final Logger logger = Logger.getLogger(Loader.class.getName()); - - protected static final ConcurrentMap> cache = new ConcurrentHashMap<>(); - - public static void close() { - cache.clear(); - } - - /** - * 加载当前线程的classpath扫描所有class进行过滤 - * - * @param excludeFile 不需要扫描的文件夹, 可以为null - * @param excludeRegs 包含此关键字的文件将被跳过, 可以为null - * @param filters 过滤器 - * - * @throws IOException 异常 - */ - public static void load(final File excludeFile, final String[] excludeRegs, final ClassFilter... filters) throws IOException { - RedkaleClassLoader loader = (RedkaleClassLoader) Thread.currentThread().getContextClassLoader(); - List urlfiles = new ArrayList<>(2); - List urljares = new ArrayList<>(2); - final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null; - final Pattern[] excludePatterns = toPattern(excludeRegs); - for (URL url : loader.getAllURLs()) { - if (exurl != null && exurl.sameFile(url)) continue; - if (excludePatterns != null) { - boolean skip = false; - for (Pattern p : excludePatterns) { - if (p.matcher(url.toString()).matches()) { - skip = true; - break; - } - } - if (skip) continue; - } - if (url.getPath().endsWith(".jar")) { - urljares.add(url); - } else { - urlfiles.add(url); - } - } - List files = new ArrayList<>(); - boolean debug = logger.isLoggable(Level.FINEST); - StringBuilder debugstr = new StringBuilder(); - for (final URL url : urljares) { - Set classes = cache.get(url); - if (classes == null) { - classes = new LinkedHashSet<>(); - try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), "UTF-8"))) { - Enumeration it = jar.entries(); - while (it.hasMoreElements()) { - String entryname = it.nextElement().getName().replace('/', '.'); - if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) { - String classname = entryname.substring(0, entryname.length() - 6); - if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; - //常见的jar跳过 - if (classname.startsWith("com.mysql.")) break; - if (classname.startsWith("org.mariadb.")) break; - if (classname.startsWith("oracle.jdbc.")) break; - if (classname.startsWith("org.postgresql.")) break; - if (classname.startsWith("com.microsoft.sqlserver.")) break; - classes.add(classname); - if (debug) debugstr.append(classname).append("\r\n"); - for (final ClassFilter filter : filters) { - if (filter != null) filter.filter(null, classname, url); - } - } - } - } - cache.put(url, classes); - } else { - for (String classname : classes) { - for (final ClassFilter filter : filters) { - if (filter != null) filter.filter(null, classname, url); - } - } - } - } - for (final URL url : urlfiles) { - Set classes = cache.get(url); - if (classes == null) { - classes = new LinkedHashSet<>(); - files.clear(); - File root = new File(url.getFile()); - String rootpath = root.getPath(); - loadClassFiles(excludeFile, root, files); - for (File f : files) { - String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.'); - if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; - classes.add(classname); - if (debug) debugstr.append(classname).append("\r\n"); - for (final ClassFilter filter : filters) { - if (filter != null) filter.filter(null, classname, url); - } - } - cache.put(url, classes); - } else { - for (String classname : classes) { - for (final ClassFilter filter : filters) { - if (filter != null) filter.filter(null, classname, url); - } - } - } - } - //if (debug) logger.log(Level.INFO, "scan classes: \r\n{0}", debugstr); - } - - private static void loadClassFiles(File exclude, File root, List files) { - if (root.isFile() && root.getName().endsWith(".class")) { - files.add(root); - } else if (root.isDirectory()) { - if (exclude != null && exclude.equals(root)) return; - File[] lfs = root.listFiles(); - if (lfs == null) throw new RuntimeException("File(" + root + ") cannot listFiles()"); - for (File f : lfs) { - loadClassFiles(exclude, f, files); - } - } - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import java.io.*; +import java.lang.annotation.*; +import java.lang.reflect.Modifier; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Predicate; +import java.util.jar.*; +import java.util.logging.*; +import java.util.regex.*; +import org.redkale.util.*; +import org.redkale.util.AnyValue.DefaultAnyValue; + +/** + * class过滤器, 符合条件的class会保留下来存入FilterEntry。 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 泛型 + */ +@SuppressWarnings("unchecked") +public final class ClassFilter { + + private static final Logger logger = Logger.getLogger(ClassFilter.class.getName()); //日志对象 + + private static final boolean finest = logger.isLoggable(Level.FINEST); //日志级别 + + private final Set> entrys = new HashSet<>(); //符合条件的结果 + + private final Set> expectEntrys = new HashSet<>(); //准备符合条件的结果 + + private Predicate expectPredicate; + + private boolean refused; //是否拒绝所有数据,设置true,则其他规则失效,都是拒绝. + + private Class superClass; //符合的父类型。不为空时,扫描结果的class必须是superClass的子类 + + private Class[] excludeSuperClasses; //不符合的父类型。 + + private Class annotationClass;//符合的注解。不为空时,扫描结果的class必须包含该注解 + + private Pattern[] includePatterns; //符合的classname正则表达式 + + private Pattern[] excludePatterns;//拒绝的classname正则表达式 + + private Set privilegeIncludes; //特批符合条件的classname + + private Set privilegeExcludes;//特批拒绝条件的classname + + private List ors; //或关系的其他ClassFilter + + private List ands;//与关系的其他ClassFilter + + private AnyValue conf; //基本配置信息, 当符合条件时将conf的属性赋值到FilterEntry中去。 + + private final ClassLoader classLoader; + + public ClassFilter(RedkaleClassLoader classLoader, Class annotationClass, Class superClass, Class[] excludeSuperClasses) { + this(classLoader, annotationClass, superClass, excludeSuperClasses, null); + } + + public ClassFilter(RedkaleClassLoader classLoader, Class annotationClass, Class superClass, Class[] excludeSuperClasses, AnyValue conf) { + this.annotationClass = annotationClass; + this.superClass = superClass; + this.excludeSuperClasses = excludeSuperClasses; + this.conf = conf; + this.classLoader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; + } + + public static ClassFilter create(RedkaleClassLoader classLoader, Class[] excludeSuperClasses, String includeregs, String excluderegs, Set includeValues, Set excludeValues) { + ClassFilter filter = new ClassFilter(classLoader, null, null, excludeSuperClasses); + filter.setIncludePatterns(includeregs == null ? null : includeregs.split(";")); + filter.setExcludePatterns(excluderegs == null ? null : excluderegs.split(";")); + filter.setPrivilegeIncludes(includeValues); + filter.setPrivilegeExcludes(excludeValues); + return filter; + } + + public ClassFilter or(ClassFilter filter) { + if (ors == null) ors = new ArrayList<>(); + ors.add(filter); + return this; + } + + public ClassFilter and(ClassFilter filter) { + if (ands == null) ands = new ArrayList<>(); + ands.add(filter); + return this; + } + + /** + * 获取符合条件的class集合 + * + * @return Set<FilterEntry<T>> + */ + public final Set> getFilterEntrys() { + HashSet> set = new HashSet<>(); + set.addAll(entrys); + if (ors != null) ors.forEach(f -> set.addAll(f.getFilterEntrys())); + if (ands != null) ands.forEach(f -> set.addAll(f.getFilterEntrys())); + return set; + } + + /** + * 获取预留的class集合 + * + * @return Set<FilterEntry<T>> + */ + public final Set> getFilterExpectEntrys() { + HashSet> set = new HashSet<>(); + set.addAll(expectEntrys); + if (ors != null) ors.forEach(f -> set.addAll(f.getFilterExpectEntrys())); + if (ands != null) ands.forEach(f -> set.addAll(f.getFilterExpectEntrys())); + return set; + } + + /** + * 获取所有的class集合 + * + * @return Set<FilterEntry<T>> + */ + public final Set> getAllFilterEntrys() { + HashSet> rs = new HashSet<>(); + rs.addAll(getFilterEntrys()); + rs.addAll(getFilterExpectEntrys()); + return rs; + } + + /** + * 自动扫描地过滤指定的class + * + * @param property AnyValue + * @param clazzname String + * @param url URL + */ + @SuppressWarnings("unchecked") + public final void filter(AnyValue property, String clazzname, URL url) { + filter(property, clazzname, true, url); + } + + /** + * 过滤指定的class + * + * @param property application.xml中对应class节点下的property属性项 + * @param clazzname class名称 + * @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略 + */ + public final void filter(AnyValue property, String clazzname, boolean autoscan) { + filter(property, clazzname, autoscan, null); + } + + /** + * 过滤指定的class + * + * @param property application.xml中对应class节点下的property属性项 + * @param clazzname class名称 + * @param autoscan 为true表示自动扫描的, false表示显著调用filter, AutoLoad的注解将被忽略 + * @param url URL + */ + public final void filter(AnyValue property, String clazzname, boolean autoscan, URL url) { + boolean r = accept0(property, clazzname); + ClassFilter cf = r ? this : null; + if (r && ands != null) { + for (ClassFilter filter : ands) { + if (!filter.accept(property, clazzname)) return; + } + } + if (!r && ors != null) { + for (ClassFilter filter : ors) { + if (filter.accept(filter.conf, clazzname)) { + cf = filter; + property = cf.conf; + break; + } + } + } + if (cf == null || clazzname.startsWith("sun.") || clazzname.contains("module-info")) return; + try { + Class clazz = classLoader.loadClass(clazzname); + if (!cf.accept(property, clazz, autoscan)) return; + if (cf.conf != null) { + if (property == null) { + property = cf.conf; + } else if (property instanceof DefaultAnyValue) { + ((DefaultAnyValue) property).addAllStringSet(cf.conf); + } else { + DefaultAnyValue dav = new DefaultAnyValue(); + dav.addAllStringSet(property); + dav.addAllStringSet(cf.conf); + property = dav; + } + } + + AutoLoad auto = (AutoLoad) clazz.getAnnotation(AutoLoad.class); + if ((expectPredicate != null && expectPredicate.test(clazzname)) || (autoscan && auto != null && !auto.value())) { //自动扫描且被标记为@AutoLoad(false)的 + expectEntrys.add(new FilterEntry(clazz, autoscan, true, property)); + } else { + entrys.add(new FilterEntry(clazz, autoscan, false, property)); + } + } catch (Throwable cfe) { + if (finest && !clazzname.startsWith("sun.") && !clazzname.startsWith("javax.") + && !clazzname.startsWith("com.sun.") && !clazzname.startsWith("jdk.") && !clazzname.startsWith("META-INF") + && !clazzname.startsWith("com.mysql.") && !clazzname.startsWith("com.microsoft.") && !clazzname.startsWith("freemarker.") + && !clazzname.startsWith("org.redkale") && (clazzname.contains("Service") || clazzname.contains("Servlet"))) { + //&& (!(cfe instanceof NoClassDefFoundError) || (cfe instanceof UnsupportedClassVersionError) || ((NoClassDefFoundError) cfe).getMessage().startsWith("java.lang.NoClassDefFoundError: java"))) { + logger.log(Level.FINEST, ClassFilter.class.getSimpleName() + " filter error for class: " + clazzname + (url == null ? "" : (" in " + url)), cfe); + } + } + } + + /** + * 判断class是否有效 + * + * @param classname String + * + * @return boolean + */ + public boolean accept(String classname) { + return accept(null, classname); + } + + /** + * 判断class是否有效 + * + * @param property AnyValue + * @param classname String + * + * @return boolean + */ + public boolean accept(AnyValue property, String classname) { + boolean r = accept0(property, classname); + if (r && ands != null) { + for (ClassFilter filter : ands) { + if (!filter.accept(property, classname)) return false; + } + } + if (!r && ors != null) { + for (ClassFilter filter : ors) { + if (filter.accept(filter.conf, classname)) return true; + } + } + return r; + } + + private boolean accept0(AnyValue property, String classname) { + if (this.refused) return false; + if (this.privilegeIncludes != null && this.privilegeIncludes.contains(classname)) return true; + if (this.privilegeExcludes != null && this.privilegeExcludes.contains(classname)) return false; + if (classname.startsWith("java.") || classname.startsWith("javax.")) return false; + if (excludePatterns != null) { + for (Pattern reg : excludePatterns) { + if (reg.matcher(classname).matches()) return false; + } + } + if (includePatterns != null) { + for (Pattern reg : includePatterns) { + if (reg.matcher(classname).matches()) return true; + } + } + return includePatterns == null; + } + + /** + * 判断class是否有效 + * + * @param property AnyValue + * @param clazz Class + * @param autoscan boolean + * + * @return boolean + */ + @SuppressWarnings("unchecked") + public boolean accept(AnyValue property, Class clazz, boolean autoscan) { + if (this.refused || !Modifier.isPublic(clazz.getModifiers())) return false; + if (annotationClass != null && clazz.getAnnotation(annotationClass) == null) return false; + boolean rs = superClass == null || (clazz != superClass && superClass.isAssignableFrom(clazz)); + if (rs && this.excludeSuperClasses != null && this.excludeSuperClasses.length > 0) { + for (Class c : this.excludeSuperClasses) { + if (c != null && (clazz == c || c.isAssignableFrom(clazz))) return false; + } + } + return rs; + } + + public static Pattern[] toPattern(String[] regs) { + if (regs == null || regs.length == 0) return null; + int i = 0; + Pattern[] rs = new Pattern[regs.length]; + for (String reg : regs) { + if (reg == null || reg.trim().isEmpty()) continue; + rs[i++] = Pattern.compile(reg.trim()); + } + if (i == 0) return null; + if (i == rs.length) return rs; + Pattern[] ps = new Pattern[i]; + System.arraycopy(rs, 0, ps, 0, i); + return ps; + } + + public void setSuperClass(Class superClass) { + this.superClass = superClass; + } + + public Class getSuperClass() { + return superClass; + } + + public Class[] getExcludeSuperClasses() { + return excludeSuperClasses; + } + + public void setExcludeSuperClasses(Class[] excludeSuperClasses) { + this.excludeSuperClasses = excludeSuperClasses; + } + + public void setAnnotationClass(Class annotationClass) { + this.annotationClass = annotationClass; + } + + public Pattern[] getIncludePatterns() { + return includePatterns; + } + + public void setIncludePatterns(String[] includePatterns) { + this.includePatterns = toPattern(includePatterns); + } + + public Pattern[] getExcludePatterns() { + return excludePatterns; + } + + public void setExcludePatterns(String[] excludePatterns) { + this.excludePatterns = toPattern(excludePatterns); + } + + public Class getAnnotationClass() { + return annotationClass; + } + + public boolean isRefused() { + return refused; + } + + public void setRefused(boolean refused) { + this.refused = refused; + } + + public Predicate getExpectPredicate() { + return expectPredicate; + } + + public void setExpectPredicate(Predicate predicate) { + this.expectPredicate = predicate; + } + + public Set getPrivilegeIncludes() { + return privilegeIncludes; + } + + public void setPrivilegeIncludes(Set privilegeIncludes) { + this.privilegeIncludes = privilegeIncludes == null || privilegeIncludes.isEmpty() ? null : privilegeIncludes; + } + + public Set getPrivilegeExcludes() { + return privilegeExcludes; + } + + public void setPrivilegeExcludes(Set privilegeExcludes) { + this.privilegeExcludes = privilegeExcludes == null || privilegeExcludes.isEmpty() ? null : privilegeExcludes; + + } + + /** + * 存放符合条件的class与class指定的属性项 + * + * @param 泛型 + */ + public static final class FilterEntry { + + private final HashSet groups = new LinkedHashSet<>(); + + private final String name; + + private final Class type; + + private final AnyValue property; + + private final boolean autoload; + + private final boolean expect; + + public FilterEntry(Class type, AnyValue property) { + this(type, false, false, property); + } + + public FilterEntry(Class type, final boolean autoload, boolean expect, AnyValue property) { + this.type = type; + String str = property == null ? null : property.getValue("groups"); + if (str != null) { + str = str.trim(); + if (str.endsWith(";")) str = str.substring(0, str.length() - 1); + } + if (str != null) this.groups.addAll(Arrays.asList(str.split(";"))); + this.property = property; + this.autoload = autoload; + this.expect = expect; + this.name = property == null ? "" : property.getValue("name", ""); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "[thread=" + Thread.currentThread().getName() + + ", type=" + this.type.getSimpleName() + ", name=" + name + ", groups=" + this.groups + "]"; + } + + @Override + public int hashCode() { + return this.type.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + return (this.type == ((FilterEntry) obj).type && this.groups.equals(((FilterEntry) obj).groups) && this.name.equals(((FilterEntry) obj).name)); + } + + public Class getType() { + return type; + } + + public String getName() { + return name; + } + + public AnyValue getProperty() { + return property; + } + + public boolean containsGroup(String group) { + return groups != null && groups.contains(group); + } + + public boolean isEmptyGroups() { + return groups == null || groups.isEmpty(); + } + + public HashSet getGroups() { + return groups; + } + + public boolean isAutoload() { + return autoload; + } + + public boolean isExpect() { + return expect; + } + } + + /** + * class加载类 + */ + public static class Loader { + + protected static final Logger logger = Logger.getLogger(Loader.class.getName()); + + protected static final ConcurrentMap> cache = new ConcurrentHashMap<>(); + + public static void close() { + cache.clear(); + } + + /** + * 加载当前线程的classpath扫描所有class进行过滤 + * + * @param excludeFile 不需要扫描的文件夹, 可以为null + * @param loader RedkaleClassloader, 不可为null + * @param excludeRegs 包含此关键字的文件将被跳过, 可以为null + * @param filters 过滤器 + * + * @throws IOException 异常 + */ + public static void load(final File excludeFile, RedkaleClassLoader loader, final String[] excludeRegs, final ClassFilter... filters) throws IOException { + List urlfiles = new ArrayList<>(2); + List urljares = new ArrayList<>(2); + final URL exurl = excludeFile != null ? excludeFile.toURI().toURL() : null; + final Pattern[] excludePatterns = toPattern(excludeRegs); + for (URL url : loader.getAllURLs()) { + if (exurl != null && exurl.sameFile(url)) continue; + if (excludePatterns != null && url != RedkaleClassLoader.URL_NONE) { + boolean skip = false; + for (Pattern p : excludePatterns) { + if (p.matcher(url.toString()).matches()) { + skip = true; + break; + } + } + if (skip) continue; + } + if (url.getPath().endsWith(".jar")) { + urljares.add(url); + } else { + urlfiles.add(url); + } + } + List files = new ArrayList<>(); + boolean debug = logger.isLoggable(Level.FINEST); + StringBuilder debugstr = new StringBuilder(); + for (final URL url : urljares) { + Set classes = cache.get(url); + if (classes == null) { + classes = new LinkedHashSet<>(); + try (JarFile jar = new JarFile(URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8))) { + Enumeration it = jar.entries(); + while (it.hasMoreElements()) { + String entryname = it.nextElement().getName().replace('/', '.'); + if (entryname.endsWith(".class") && entryname.indexOf('$') < 0) { + String classname = entryname.substring(0, entryname.length() - 6); + if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; + //常见的jar跳过 + if (classname.startsWith("com.redkaledyn.")) break; //redkale动态生成的类 + if (classname.startsWith("com.mysql.")) break; + if (classname.startsWith("org.mariadb.")) break; + if (classname.startsWith("oracle.jdbc.")) break; + if (classname.startsWith("org.postgresql.")) break; + if (classname.startsWith("com.microsoft.sqlserver.")) break; + classes.add(classname); + if (debug) debugstr.append(classname).append("\r\n"); + for (final ClassFilter filter : filters) { + if (filter != null) filter.filter(null, classname, url); + } + } + } + } + cache.put(url, classes); + } else { + for (String classname : classes) { + for (final ClassFilter filter : filters) { + if (filter != null) filter.filter(null, classname, url); + } + } + } + } + for (final URL url : urlfiles) { + Set classes = cache.get(url); + if (classes == null) { + classes = new LinkedHashSet<>(); + final Set cs = classes; + if (url == RedkaleClassLoader.URL_NONE) loader.forEachCacheClass(v -> cs.add(v)); + if (cs.isEmpty()) { + files.clear(); + File root = new File(url.getFile()); + String rootpath = root.getPath(); + loadClassFiles(excludeFile, root, files); + for (File f : files) { + String classname = f.getPath().substring(rootpath.length() + 1, f.getPath().length() - 6).replace(File.separatorChar, '.'); + if (classname.startsWith("javax.") || classname.startsWith("com.sun.")) continue; + classes.add(classname); + if (debug) debugstr.append(classname).append("\r\n"); + for (final ClassFilter filter : filters) { + if (filter != null) filter.filter(null, classname, url); + } + } + } else { + for (String classname : classes) { + for (final ClassFilter filter : filters) { + if (filter != null) filter.filter(null, classname, url); + } + } + } + cache.put(url, classes); + } else { + for (String classname : classes) { + for (final ClassFilter filter : filters) { + if (filter != null) filter.filter(null, classname, url); + } + } + } + } + //if (debug) logger.log(Level.INFO, "scan classes: \r\n{0}", debugstr); + } + + private static void loadClassFiles(File exclude, File root, List files) { + if (root.isFile() && root.getName().endsWith(".class")) { + files.add(root); + } else if (root.isDirectory()) { + if (exclude != null && exclude.equals(root)) return; + File[] lfs = root.listFiles(); + if (lfs == null) throw new RuntimeException("File(" + root + ") cannot listFiles()"); + for (File f : lfs) { + loadClassFiles(exclude, f, files); + } + } + } + } +} diff --git a/src/org/redkale/boot/LogFileHandler.java b/src/main/java/org/redkale/boot/LoggingFileHandler.java similarity index 94% rename from src/org/redkale/boot/LogFileHandler.java rename to src/main/java/org/redkale/boot/LoggingFileHandler.java index f966035a9..e5f616faf 100644 --- a/src/org/redkale/boot/LogFileHandler.java +++ b/src/main/java/org/redkale/boot/LoggingFileHandler.java @@ -1,366 +1,372 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import java.io.*; -import java.nio.file.*; -import static java.nio.file.StandardCopyOption.*; -import java.time.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.logging.*; -import java.util.logging.Formatter; -import java.util.regex.Pattern; - -/** - * 自定义的日志输出类 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public class LogFileHandler extends Handler { - - /** - * SNCP的日志输出Handler - */ - public static class SncpLogFileHandler extends LogFileHandler { - - @Override - public String getPrefix() { - return "sncp-"; - } - } - - /** - * 默认的日志时间格式化类 - * - */ - public static class LoggingFormater extends Formatter { - - private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n"; - - @Override - public String format(LogRecord log) { - String source; - if (log.getSourceClassName() != null) { - source = log.getSourceClassName(); - if (log.getSourceMethodName() != null) { - source += " " + log.getSourceMethodName(); - } - } else { - source = log.getLoggerName(); - } - String message = formatMessage(log); - String throwable = ""; - if (log.getThrown() != null) { - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw) { - @Override - public void println() { - super.print("\r\n"); - } - }; - pw.println(); - log.getThrown().printStackTrace(pw); - pw.close(); - throwable = sw.toString(); - } - return String.format(format, - System.currentTimeMillis(), - source, - log.getLoggerName(), - log.getLevel().getName(), - message, - throwable); - } - } - - public static void initDebugLogConfig() { - try { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - final PrintStream ps = new PrintStream(out); - ps.println("handlers = java.util.logging.ConsoleHandler"); - ps.println(".level = FINEST"); - ps.println("jdk.level = INFO"); - ps.println("sun.level = INFO"); - ps.println("com.sun.level = INFO"); - ps.println("javax.level = INFO"); - ps.println("java.util.logging.ConsoleHandler.level = FINEST"); - ps.println("java.util.logging.ConsoleHandler.formatter = " + LogFileHandler.LoggingFormater.class.getName()); - LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray())); - } catch (Exception e) { - } - } - - protected final LinkedBlockingQueue logqueue = new LinkedBlockingQueue(); - - private String pattern; - - private String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中 - - private int limit; //文件大小限制 - - private final AtomicInteger logindex = new AtomicInteger(); - - private final AtomicInteger logunusualindex = new AtomicInteger(); - - private int count = 1; //文件限制 - - private long tomorrow; - - private boolean append; - - private Pattern denyreg; - - private final AtomicLong loglength = new AtomicLong(); - - private final AtomicLong logunusuallength = new AtomicLong(); - - private File logfile; - - private File logunusualfile; - - private OutputStream logstream; - - private OutputStream logunusualstream; - - public LogFileHandler() { - updateTomorrow(); - configure(); - open(); - } - - private void updateTomorrow() { - Calendar cal = Calendar.getInstance(); - cal.set(Calendar.HOUR_OF_DAY, 0); - cal.set(Calendar.MINUTE, 0); - cal.set(Calendar.SECOND, 0); - cal.set(Calendar.MILLISECOND, 0); - cal.add(Calendar.DAY_OF_YEAR, 1); - long t = cal.getTimeInMillis(); - if (this.tomorrow != t) logindex.set(0); - this.tomorrow = t; - } - - private void open() { - final String name = "Redkale-Logging-" + getClass().getSimpleName() + "-Thread"; - new Thread() { - { - setName(name); - setDaemon(true); - } - - @Override - public void run() { - while (true) { - try { - LogRecord log = logqueue.take(); - final boolean bigger = (limit > 0 && limit <= loglength.get()); - final boolean changeday = tomorrow <= log.getMillis(); - if (bigger || changeday) { - updateTomorrow(); - if (logstream != null) { - logstream.close(); - if (bigger) { - for (int i = Math.min(count - 2, logindex.get() - 1); i > 0; i--) { - File greater = new File(logfile.getPath() + "." + i); - if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE); - } - Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE); - } else { - if (logfile.exists() && logfile.length() < 1) logfile.delete(); - } - logstream = null; - } - } - if (unusual != null && changeday && logunusualstream != null) { - logunusualstream.close(); - if (limit > 0 && limit <= logunusuallength.get()) { - for (int i = Math.min(count - 2, logunusualindex.get() - 1); i > 0; i--) { - File greater = new File(logunusualfile.getPath() + "." + i); - if (greater.exists()) Files.move(greater.toPath(), new File(logunusualfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE); - } - Files.move(logunusualfile.toPath(), new File(logunusualfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE); - } else { - if (logunusualfile.exists() && logunusualfile.length() < 1) logunusualfile.delete(); - } - logunusualstream = null; - } - if (logstream == null) { - logindex.incrementAndGet(); - java.time.LocalDate date = LocalDate.now(); - logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth())))); - logfile.getParentFile().mkdirs(); - loglength.set(logfile.length()); - logstream = new FileOutputStream(logfile, append); - } - if (unusual != null && logunusualstream == null) { - logunusualindex.incrementAndGet(); - java.time.LocalDate date = LocalDate.now(); - logunusualfile = new File(unusual.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth())))); - logunusualfile.getParentFile().mkdirs(); - logunusuallength.set(logunusualfile.length()); - logunusualstream = new FileOutputStream(logunusualfile, append); - } - //----------------------写日志------------------------- - String message = getFormatter().format(log); - String encoding = getEncoding(); - byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding); - logstream.write(bytes); - loglength.addAndGet(bytes.length); - if (unusual != null && (log.getLevel() == Level.WARNING || log.getLevel() == Level.SEVERE)) { - logunusualstream.write(bytes); - logunusuallength.addAndGet(bytes.length); - } - } catch (Exception e) { - ErrorManager err = getErrorManager(); - if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE); - } - } - - } - }.start(); - } - - public String getPrefix() { - return ""; - } - - private void configure() { - LogManager manager = LogManager.getLogManager(); - String cname = LogFileHandler.class.getName(); - this.pattern = manager.getProperty(cname + ".pattern"); - if (this.pattern == null) { - this.pattern = "logs-%m/" + getPrefix() + "log-%d.log"; - } else { - int pos = this.pattern.lastIndexOf('/'); - if (pos > 0) { - this.pattern = this.pattern.substring(0, pos + 1) + getPrefix() + this.pattern.substring(pos + 1); - } else { - this.pattern = getPrefix() + this.pattern; - } - } - String unusualstr = manager.getProperty(cname + ".unusual"); - if (unusualstr != null) { - int pos = unusualstr.lastIndexOf('/'); - if (pos > 0) { - this.unusual = unusualstr.substring(0, pos + 1) + getPrefix() + unusualstr.substring(pos + 1); - } else { - this.unusual = getPrefix() + unusualstr; - } - } - String limitstr = manager.getProperty(cname + ".limit"); - try { - if (limitstr != null) { - limitstr = limitstr.toUpperCase(); - boolean g = limitstr.indexOf('G') > 0; - boolean m = limitstr.indexOf('M') > 0; - boolean k = limitstr.indexOf('K') > 0; - int ls = Math.abs(Integer.decode(limitstr.replace("G", "").replace("M", "").replace("K", "").replace("B", ""))); - if (g) { - ls *= 1024 * 1024 * 1024; - } else if (m) { - ls *= 1024 * 1024; - } else if (k) { - ls *= 1024; - } - this.limit = ls; - } - } catch (Exception e) { - } - String countstr = manager.getProperty(cname + ".count"); - try { - if (countstr != null) this.count = Math.max(1, Math.abs(Integer.decode(countstr))); - } catch (Exception e) { - } - String appendstr = manager.getProperty(cname + ".append"); - try { - if (appendstr != null) this.append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr); - } catch (Exception e) { - } - String levelstr = manager.getProperty(cname + ".level"); - try { - if (levelstr != null) { - Level l = Level.parse(levelstr); - setLevel(l != null ? l : Level.ALL); - } - } catch (Exception e) { - } - String filterstr = manager.getProperty(cname + ".filter"); - try { - if (filterstr != null) { - Class clz = ClassLoader.getSystemClassLoader().loadClass(filterstr); - setFilter((Filter) clz.getDeclaredConstructor().newInstance()); - } - } catch (Exception e) { - } - String formatterstr = manager.getProperty(cname + ".formatter"); - try { - if (formatterstr != null) { - Class clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr); - setFormatter((Formatter) clz.getDeclaredConstructor().newInstance()); - } - } catch (Exception e) { - } - if (getFormatter() == null) setFormatter(new SimpleFormatter()); - - String encodingstr = manager.getProperty(cname + ".encoding"); - try { - if (encodingstr != null) setEncoding(encodingstr); - } catch (Exception e) { - } - - String denyregstr = manager.getProperty(cname + ".denyreg"); - try { - if (denyregstr != null && !denyregstr.trim().isEmpty()) { - denyreg = Pattern.compile(denyregstr); - } - } catch (Exception e) { - } - } - - @Override - public void publish(LogRecord log) { - final String sourceClassName = log.getSourceClassName(); - if (sourceClassName == null || true) { - StackTraceElement[] ses = new Throwable().getStackTrace(); - for (int i = 2; i < ses.length; i++) { - if (ses[i].getClassName().startsWith("java.util.logging")) continue; - log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName()); - log.setSourceMethodName(ses[i].getMethodName()); - break; - } - } else { - log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName); - } - if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return; - logqueue.offer(log); - } - - @Override - public void flush() { - try { - if (logstream != null) logstream.flush(); - } catch (Exception e) { - ErrorManager err = getErrorManager(); - if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE); - } - } - - @Override - public void close() throws SecurityException { - try { - if (logstream != null) logstream.close(); - } catch (Exception e) { - ErrorManager err = getErrorManager(); - if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import org.redkale.util.RedkaleClassLoader; + +import java.io.*; +import java.nio.file.*; +import static java.nio.file.StandardCopyOption.*; +import java.time.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.logging.*; +import java.util.logging.Formatter; +import java.util.regex.Pattern; + +/** + * 自定义的日志输出类 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public class LoggingFileHandler extends Handler { + + //public static final String FORMATTER_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s%n%5$s%6$s%n"; + public static final String FORMATTER_FORMAT = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL %4$s %2$s\r\n%5$s%6$s\r\n"; + + /** + * SNCP的日志输出Handler + */ + public static class LoggingSncpFileHandler extends LoggingFileHandler { + + @Override + public String getPrefix() { + return "sncp-"; + } + } + + /** + * 默认的日志时间格式化类 + * 与SimpleFormatter的区别在于level不使用本地化 + * + */ + public static class LoggingFormater extends Formatter { + + @Override + public String format(LogRecord log) { + String source; + if (log.getSourceClassName() != null) { + source = log.getSourceClassName(); + if (log.getSourceMethodName() != null) { + source += " " + log.getSourceMethodName(); + } + } else { + source = log.getLoggerName(); + } + String message = formatMessage(log); + String throwable = ""; + if (log.getThrown() != null) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw) { + @Override + public void println() { + super.print("\r\n"); + } + }; + pw.println(); + log.getThrown().printStackTrace(pw); + pw.close(); + throwable = sw.toString(); + } + return String.format(FORMATTER_FORMAT, + System.currentTimeMillis(), + source, + log.getLoggerName(), + log.getLevel().getName(), + message, + throwable); + } + } + + public static void initDebugLogConfig() { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + final PrintStream ps = new PrintStream(out); + ps.println("handlers = java.util.logging.ConsoleHandler"); + ps.println(".level = FINEST"); + ps.println("jdk.level = INFO"); + ps.println("sun.level = INFO"); + ps.println("com.sun.level = INFO"); + ps.println("javax.level = INFO"); + ps.println("java.util.logging.ConsoleHandler.level = FINEST"); + ps.println("java.util.logging.ConsoleHandler.formatter = " + LoggingFileHandler.LoggingFormater.class.getName()); + LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray())); + } catch (Exception e) { + } + } + + protected final LinkedBlockingQueue logqueue = new LinkedBlockingQueue(); + + private String pattern; + + private String unusual; //不为null表示将 WARNING、SEVERE 级别的日志写入单独的文件中 + + private int limit; //文件大小限制 + + private final AtomicInteger logindex = new AtomicInteger(); + + private final AtomicInteger logunusualindex = new AtomicInteger(); + + private int count = 1; //文件限制 + + private long tomorrow; + + private boolean append; + + private Pattern denyreg; + + private final AtomicLong loglength = new AtomicLong(); + + private final AtomicLong logunusuallength = new AtomicLong(); + + private File logfile; + + private File logunusualfile; + + private OutputStream logstream; + + private OutputStream logunusualstream; + + public LoggingFileHandler() { + updateTomorrow(); + configure(); + open(); + } + + private void updateTomorrow() { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + cal.add(Calendar.DAY_OF_YEAR, 1); + long t = cal.getTimeInMillis(); + if (this.tomorrow != t) logindex.set(0); + this.tomorrow = t; + } + + private void open() { + final String name = "Redkale-Logging-" + getClass().getSimpleName() + "-Thread"; + new Thread() { + { + setName(name); + setDaemon(true); + } + + @Override + public void run() { + while (true) { + try { + LogRecord log = logqueue.take(); + final boolean bigger = (limit > 0 && limit <= loglength.get()); + final boolean changeday = tomorrow <= log.getMillis(); + if (bigger || changeday) { + updateTomorrow(); + if (logstream != null) { + logstream.close(); + if (bigger) { + for (int i = Math.min(count - 2, logindex.get() - 1); i > 0; i--) { + File greater = new File(logfile.getPath() + "." + i); + if (greater.exists()) Files.move(greater.toPath(), new File(logfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE); + } + Files.move(logfile.toPath(), new File(logfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE); + } else { + if (logfile.exists() && logfile.length() < 1) logfile.delete(); + } + logstream = null; + } + } + if (unusual != null && changeday && logunusualstream != null) { + logunusualstream.close(); + if (limit > 0 && limit <= logunusuallength.get()) { + for (int i = Math.min(count - 2, logunusualindex.get() - 1); i > 0; i--) { + File greater = new File(logunusualfile.getPath() + "." + i); + if (greater.exists()) Files.move(greater.toPath(), new File(logunusualfile.getPath() + "." + (i + 1)).toPath(), REPLACE_EXISTING, ATOMIC_MOVE); + } + Files.move(logunusualfile.toPath(), new File(logunusualfile.getPath() + ".1").toPath(), REPLACE_EXISTING, ATOMIC_MOVE); + } else { + if (logunusualfile.exists() && logunusualfile.length() < 1) logunusualfile.delete(); + } + logunusualstream = null; + } + if (logstream == null) { + logindex.incrementAndGet(); + java.time.LocalDate date = LocalDate.now(); + logfile = new File(pattern.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth())))); + logfile.getParentFile().mkdirs(); + loglength.set(logfile.length()); + logstream = new FileOutputStream(logfile, append); + } + if (unusual != null && logunusualstream == null) { + logunusualindex.incrementAndGet(); + java.time.LocalDate date = LocalDate.now(); + logunusualfile = new File(unusual.replace("%m", String.valueOf((date.getYear() * 100 + date.getMonthValue()))).replace("%d", String.valueOf((date.getYear() * 10000 + date.getMonthValue() * 100 + date.getDayOfMonth())))); + logunusualfile.getParentFile().mkdirs(); + logunusuallength.set(logunusualfile.length()); + logunusualstream = new FileOutputStream(logunusualfile, append); + } + //----------------------写日志------------------------- + String message = getFormatter().format(log); + String encoding = getEncoding(); + byte[] bytes = encoding == null ? message.getBytes() : message.getBytes(encoding); + logstream.write(bytes); + loglength.addAndGet(bytes.length); + if (unusual != null && (log.getLevel() == Level.WARNING || log.getLevel() == Level.SEVERE)) { + logunusualstream.write(bytes); + logunusuallength.addAndGet(bytes.length); + } + } catch (Exception e) { + ErrorManager err = getErrorManager(); + if (err != null) err.error(null, e, ErrorManager.WRITE_FAILURE); + } + } + + } + }.start(); + } + + public String getPrefix() { + return ""; + } + + private void configure() { + LogManager manager = LogManager.getLogManager(); + String cname = LoggingFileHandler.class.getName(); + this.pattern = manager.getProperty(cname + ".pattern"); + if (this.pattern == null) { + this.pattern = "logs-%m/" + getPrefix() + "log-%d.log"; + } else { + int pos = this.pattern.lastIndexOf('/'); + if (pos > 0) { + this.pattern = this.pattern.substring(0, pos + 1) + getPrefix() + this.pattern.substring(pos + 1); + } else { + this.pattern = getPrefix() + this.pattern; + } + } + String unusualstr = manager.getProperty(cname + ".unusual"); + if (unusualstr != null) { + int pos = unusualstr.lastIndexOf('/'); + if (pos > 0) { + this.unusual = unusualstr.substring(0, pos + 1) + getPrefix() + unusualstr.substring(pos + 1); + } else { + this.unusual = getPrefix() + unusualstr; + } + } + String limitstr = manager.getProperty(cname + ".limit"); + try { + if (limitstr != null) { + limitstr = limitstr.toUpperCase(); + boolean g = limitstr.indexOf('G') > 0; + boolean m = limitstr.indexOf('M') > 0; + boolean k = limitstr.indexOf('K') > 0; + int ls = Math.abs(Integer.decode(limitstr.replace("G", "").replace("M", "").replace("K", "").replace("B", ""))); + if (g) { + ls *= 1024 * 1024 * 1024; + } else if (m) { + ls *= 1024 * 1024; + } else if (k) { + ls *= 1024; + } + this.limit = ls; + } + } catch (Exception e) { + } + String countstr = manager.getProperty(cname + ".count"); + try { + if (countstr != null) this.count = Math.max(1, Math.abs(Integer.decode(countstr))); + } catch (Exception e) { + } + String appendstr = manager.getProperty(cname + ".append"); + try { + if (appendstr != null) this.append = "true".equalsIgnoreCase(appendstr) || "1".equals(appendstr); + } catch (Exception e) { + } + String levelstr = manager.getProperty(cname + ".level"); + try { + if (levelstr != null) { + Level l = Level.parse(levelstr); + setLevel(l != null ? l : Level.ALL); + } + } catch (Exception e) { + } + String filterstr = manager.getProperty(cname + ".filter"); + try { + if (filterstr != null) { + Class clz = ClassLoader.getSystemClassLoader().loadClass(filterstr); + RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName()); + setFilter((Filter) clz.getDeclaredConstructor().newInstance()); + } + } catch (Exception e) { + } + String formatterstr = manager.getProperty(cname + ".formatter"); + try { + if (formatterstr != null) { + Class clz = ClassLoader.getSystemClassLoader().loadClass(formatterstr); + RedkaleClassLoader.putReflectionDeclaredConstructors(clz, clz.getName()); + setFormatter((Formatter) clz.getDeclaredConstructor().newInstance()); + } + } catch (Exception e) { + } + if (getFormatter() == null) setFormatter(new SimpleFormatter()); + + String encodingstr = manager.getProperty(cname + ".encoding"); + try { + if (encodingstr != null) setEncoding(encodingstr); + } catch (Exception e) { + } + + String denyregstr = manager.getProperty(cname + ".denyreg"); + try { + if (denyregstr != null && !denyregstr.trim().isEmpty()) { + denyreg = Pattern.compile(denyregstr); + } + } catch (Exception e) { + } + } + + @Override + public void publish(LogRecord log) { + final String sourceClassName = log.getSourceClassName(); + if (sourceClassName == null || true) { + StackTraceElement[] ses = new Throwable().getStackTrace(); + for (int i = 2; i < ses.length; i++) { + if (ses[i].getClassName().startsWith("java.util.logging")) continue; + log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + ses[i].getClassName()); + log.setSourceMethodName(ses[i].getMethodName()); + break; + } + } else { + log.setSourceClassName('[' + Thread.currentThread().getName() + "] " + sourceClassName); + } + if (denyreg != null && denyreg.matcher(log.getMessage()).find()) return; + logqueue.offer(log); + } + + @Override + public void flush() { + try { + if (logstream != null) logstream.flush(); + } catch (Exception e) { + ErrorManager err = getErrorManager(); + if (err != null) err.error(null, e, ErrorManager.FLUSH_FAILURE); + } + } + + @Override + public void close() throws SecurityException { + try { + if (logstream != null) logstream.close(); + } catch (Exception e) { + ErrorManager err = getErrorManager(); + if (err != null) err.error(null, e, ErrorManager.CLOSE_FAILURE); + } + } + +} diff --git a/src/org/redkale/boot/NodeHttpServer.java b/src/main/java/org/redkale/boot/NodeHttpServer.java similarity index 77% rename from src/org/redkale/boot/NodeHttpServer.java rename to src/main/java/org/redkale/boot/NodeHttpServer.java index 27f591a44..27f547d1b 100644 --- a/src/org/redkale/boot/NodeHttpServer.java +++ b/src/main/java/org/redkale/boot/NodeHttpServer.java @@ -1,399 +1,446 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import java.lang.annotation.Annotation; -import java.lang.reflect.*; -import java.net.*; -import java.util.*; -import java.util.concurrent.CountDownLatch; -import java.util.logging.Level; -import javax.annotation.*; -import static org.redkale.boot.Application.RESNAME_SNCP_ADDR; -import org.redkale.boot.ClassFilter.FilterEntry; -import org.redkale.cluster.ClusterAgent; -import org.redkale.mq.MessageAgent; -import org.redkale.net.*; -import org.redkale.net.http.*; -import org.redkale.net.sncp.Sncp; -import org.redkale.service.*; -import org.redkale.util.AnyValue.DefaultAnyValue; -import org.redkale.util.*; -import org.redkale.watch.*; - -/** - * HTTP Server节点的配置Server - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@NodeProtocol("HTTP") -public class NodeHttpServer extends NodeServer { - - protected final boolean rest; //是否加载REST服务, 为true加载rest节点信息并将所有可REST化的Service生成RestServlet - - protected final HttpServer httpServer; - - public NodeHttpServer(Application application, AnyValue serconf) { - super(application, createServer(application, serconf)); - this.httpServer = (HttpServer) server; - this.rest = serconf == null ? false : serconf.getAnyValue("rest") != null; - } - - private static Server createServer(Application application, AnyValue serconf) { - return new HttpServer(application, application.getStartTime(), application.getResourceFactory().createChild()); - } - - public HttpServer getHttpServer() { - return httpServer; - } - - @Override - public InetSocketAddress getSocketAddress() { - return httpServer == null ? null : httpServer.getSocketAddress(); - } - - @Override - @SuppressWarnings("unchecked") - protected ClassFilter createServiceClassFilter() { - return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service"); - } - - @Override - @SuppressWarnings("unchecked") - protected ClassFilter createFilterClassFilter() { - return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter"); - } - - @Override - @SuppressWarnings("unchecked") - protected ClassFilter createServletClassFilter() { - return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet"); - } - - @Override - protected ClassFilter createOtherClassFilter() { - return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket"); - } - - @Override - protected void loadService(ClassFilter serviceFilter, ClassFilter otherFilter) throws Exception { - super.loadService(serviceFilter, otherFilter); - initWebSocketService(); - } - - @Override - protected void loadFilter(ClassFilter filterFilter, ClassFilter otherFilter) throws Exception { - if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter); - } - - @Override - @SuppressWarnings("unchecked") - protected void loadServlet(ClassFilter servletFilter, ClassFilter otherFilter) throws Exception { - if (httpServer != null) loadHttpServlet(servletFilter, otherFilter); - } - - private void initWebSocketService() { - final NodeServer self = this; - final ResourceFactory regFactory = application.getResourceFactory(); - resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务 - try { - if (field.getAnnotation(Resource.class) == null) return; - if (!(src instanceof WebSocketServlet)) return; - ResourceFactory.ResourceLoader loader = null; - ResourceFactory sncpResFactory = null; - for (NodeServer ns : application.servers) { - if (!ns.isSNCP()) continue; - sncpResFactory = ns.resourceFactory; - loader = sncpResFactory.findLoader(WebSocketNode.class, field); - if (loader != null) break; - } - if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment); - synchronized (regFactory) { - Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class); - if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) { - resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class)); - resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class)); - resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class)); - } - if (nodeService == null) { - MessageAgent messageAgent = null; - try { - Field c = WebSocketServlet.class.getDeclaredField("messageAgent"); - c.setAccessible(true); - messageAgent = (MessageAgent) c.get(src); - } catch (Exception ex) { - logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex); - } - nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, messageAgent, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set) null, (AnyValue) null); - regFactory.register(resourceName, WebSocketNode.class, nodeService); - } - resourceFactory.inject(nodeService, self); - field.set(src, nodeService); - logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "WebSocketNode inject error", e); - } - }, WebSocketNode.class); - } - - @SuppressWarnings("unchecked") - protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter classFilter) throws Exception { - final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; - final String localThreadName = "[" + Thread.currentThread().getName() + "] "; - List> list = new ArrayList(classFilter.getFilterEntrys()); - for (FilterEntry en : list) { - Class clazz = (Class) en.getType(); - if (Modifier.isAbstract(clazz.getModifiers())) continue; - final HttpFilter filter = clazz.getDeclaredConstructor().newInstance(); - resourceFactory.inject(filter, this); - DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); - this.httpServer.addHttpFilter(filter, filterConf); - if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR); - } - if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); - } - - @SuppressWarnings("unchecked") - protected void loadHttpServlet(final ClassFilter servletFilter, ClassFilter webSocketFilter) throws Exception { - final AnyValue servletsConf = this.serverConf.getAnyValue("servlets"); - final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; - String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", ""); - if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1); - if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; - final String prefix = prefix0; - final String localThreadName = "[" + Thread.currentThread().getName() + "] "; - List> list = new ArrayList(servletFilter.getFilterEntrys()); - list.sort((FilterEntry o1, FilterEntry o2) -> { //必须保证WebSocketServlet优先加载, 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode - boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType()); - boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType()); - if (ws1 == ws2) { - Priority p1 = o1.getType().getAnnotation(Priority.class); - Priority p2 = o2.getType().getAnnotation(Priority.class); - int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); - return v == 0 ? o1.getType().getName().compareTo(o2.getType().getName()) : 0; - } - return ws1 ? -1 : 1; - }); - final List> ss = sb == null ? null : new ArrayList<>(); - for (FilterEntry en : list) { - Class clazz = (Class) en.getType(); - if (Modifier.isAbstract(clazz.getModifiers())) continue; - WebServlet ws = clazz.getAnnotation(WebServlet.class); - if (ws == null) continue; - if (ws.value().length == 0) { - logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName()); - continue; - } - final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance(); - resourceFactory.inject(servlet, this); - final String[] mappings = ws.value(); - String pref = ws.repair() ? prefix : ""; - DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty(); - this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings); - if (ss != null) { - for (int i = 0; i < mappings.length; i++) { - mappings[i] = pref + mappings[i]; - } - ss.add(new AbstractMap.SimpleEntry<>(clazz.getName(), mappings)); - } - } - int max = 0; - if (ss != null && sb != null) { - ss.sort((AbstractMap.SimpleEntry o1, AbstractMap.SimpleEntry o2) -> o1.getKey().compareTo(o2.getKey())); - for (AbstractMap.SimpleEntry as : ss) { - if (as.getKey().length() > max) max = as.getKey().length(); - } - for (AbstractMap.SimpleEntry as : ss) { - sb.append(localThreadName).append(" Load ").append(as.getKey()); - for (int i = 0; i < max - as.getKey().length(); i++) { - sb.append(' '); - } - sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); - } - } - if (rest && serverConf != null) { - final List restedObjects = new ArrayList<>(); - for (AnyValue restConf : serverConf.getAnyValues("rest")) { - loadRestServlet(webSocketFilter, restConf, restedObjects, sb); - } - } - if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim()); - } - - @SuppressWarnings("unchecked") - protected void loadRestServlet(final ClassFilter webSocketFilter, final AnyValue restConf, final List restedObjects, final StringBuilder sb) throws Exception { - if (!rest) return; - if (restConf == null) return; //不存在REST服务 - - final long starts = System.currentTimeMillis(); - String prefix0 = restConf.getValue("path", ""); - if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1); - if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; - - final String localThreadName = "[" + Thread.currentThread().getName() + "] "; - final List> ss = sb == null ? null : new ArrayList<>(); - String mqname = restConf.getValue("mq"); - MessageAgent agent0 = null; - if (mqname != null) { - agent0 = application.getMessageAgent(mqname); - if (agent0 == null) throw new RuntimeException("not found " + MessageAgent.class.getSimpleName() + " config for (name=" + mqname + ")"); - } - final MessageAgent messageAgent = agent0; - if (messageAgent != null) prefix0 = ""; //开启MQ时,prefix字段失效 - final String prefix = prefix0; - final boolean autoload = restConf.getBoolValue("autoload", true); - { //加载RestService - String userTypeStr = restConf.getValue("usertype"); - final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr); - - final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName())); - final Set includeValues = new HashSet<>(); - final Set excludeValues = new HashSet<>(); - for (AnyValue item : restConf.getAnyValues("service")) { - if (item.getBoolValue("ignore", false)) { - excludeValues.add(item.getValue("value", "")); - } else { - includeValues.add(item.getValue("value", "")); - } - } - - final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues); - final boolean finest = logger.isLoggable(Level.FINEST); - final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size()); - super.interceptorServices.stream().parallel().forEach((service) -> { - try { - final Class stype = Sncp.getServiceType(service); - final String name = Sncp.getResourceName(service); - RestService rs = (RestService) stype.getAnnotation(RestService.class); - if (rs == null || rs.ignore()) return; - - final String stypename = stype.getName(); - if (!autoload && !includeValues.contains(stypename)) return; - if (!restFilter.accept(stypename)) return; - synchronized (restedObjects) { - if (restedObjects.contains(service)) { - logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore"); - return; - } - restedObjects.add(service); //避免重复创建Rest对象 - } - HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix); - if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null - String prefix2 = prefix; - WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); - if (ws != null && !ws.repair()) prefix2 = ""; - resourceFactory.inject(servlet, NodeHttpServer.this); - dynServletMap.put(service, servlet); - if (messageAgent != null) messageAgent.putService(this, service, servlet); - //if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet); - if (ss != null) { - String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); - for (int i = 0; i < mappings.length; i++) { - mappings[i] = prefix2 + mappings[i]; - } - synchronized (ss) { - ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName() + "(rest.name='" + name + "')", mappings)); - } - } - } finally { - scdl.countDown(); - } - }); - scdl.await(); - } - if (webSocketFilter != null) { //加载RestWebSocket - final Set includeValues = new HashSet<>(); - final Set excludeValues = new HashSet<>(); - for (AnyValue item : restConf.getAnyValues("websocket")) { - if (item.getBoolValue("ignore", false)) { - excludeValues.add(item.getValue("value", "")); - } else { - includeValues.add(item.getValue("value", "")); - } - } - - final ClassFilter restFilter = ClassFilter.create(null, restConf.getValue("includes", ""), restConf.getValue("excludes", ""), includeValues, excludeValues); - final boolean finest = logger.isLoggable(Level.FINEST); - - List> list = new ArrayList(webSocketFilter.getFilterEntrys()); - for (FilterEntry en : list) { - Class clazz = (Class) en.getType(); - if (Modifier.isAbstract(clazz.getModifiers())) { - logger.log(Level.FINE, clazz.getName() + " cannot abstract on rest websocket, so ignore"); - continue; - } - if (Modifier.isFinal(clazz.getModifiers())) { - logger.log(Level.FINE, clazz.getName() + " cannot final on rest websocket, so ignore"); - continue; - } - final Class stype = en.getType(); - RestWebSocket rs = stype.getAnnotation(RestWebSocket.class); - if (rs == null || rs.ignore()) return; - - final String stypename = stype.getName(); - if (!autoload && !includeValues.contains(stypename)) return; - if (!restFilter.accept(stypename)) return; - if (restedObjects.contains(stype)) { - logger.log(Level.WARNING, stype.getName() + " repeat create rest websocket, so ignore"); - return; - } - restedObjects.add(stype); //避免重复创建Rest对象 - WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty()); - if (servlet == null) return; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null - String prefix2 = prefix; - WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); - if (ws != null && !ws.repair()) prefix2 = ""; - resourceFactory.inject(servlet, NodeHttpServer.this); - if (finest) logger.finest(localThreadName + " " + stype.getName() + " create a RestWebSocketServlet"); - if (ss != null) { - String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); - for (int i = 0; i < mappings.length; i++) { - mappings[i] = prefix2 + mappings[i]; - } - ss.add(new AbstractMap.SimpleEntry<>(servlet.getClass().getName(), mappings)); - } - } - } - if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent); - //输出信息 - if (ss != null && !ss.isEmpty() && sb != null) { - ss.sort((AbstractMap.SimpleEntry o1, AbstractMap.SimpleEntry o2) -> o1.getKey().compareTo(o2.getKey())); - int max = 0; - for (AbstractMap.SimpleEntry as : ss) { - if (as.getKey().length() > max) max = as.getKey().length(); - } - for (AbstractMap.SimpleEntry as : ss) { - sb.append(localThreadName).append(" Load ").append(as.getKey()); - for (int i = 0; i < max - as.getKey().length(); i++) { - sb.append(' '); - } - sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); - } - sb.append(localThreadName).append(" All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR); - } - } - - @Override //loadServlet执行之后调用 - protected void postLoadServlets() { - final ClusterAgent cluster = application.clusterAgent; - if (cluster != null) { - NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class); - String protocol = pros.value().toUpperCase(); - if (!cluster.containsProtocol(protocol)) return; - if (!cluster.containsPort(server.getSocketAddress().getPort())) return; - cluster.register(this, protocol, dynServletMap.keySet(), new HashSet<>()); - } - } - - @Override - protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) { - cluster.deregister(this, protocol, dynServletMap.keySet(), new HashSet<>()); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import java.util.logging.Level; +import java.util.stream.Stream; +import javax.annotation.*; +import static org.redkale.boot.Application.RESNAME_SNCP_ADDR; +import org.redkale.boot.ClassFilter.FilterEntry; +import org.redkale.cluster.ClusterAgent; +import org.redkale.mq.MessageAgent; +import org.redkale.net.*; +import org.redkale.net.http.*; +import org.redkale.net.sncp.Sncp; +import org.redkale.service.*; +import org.redkale.util.AnyValue.DefaultAnyValue; +import org.redkale.util.*; +import org.redkale.watch.*; + +/** + * HTTP Server节点的配置Server + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@NodeProtocol("HTTP") +public class NodeHttpServer extends NodeServer { + + protected final boolean rest; //是否加载REST服务, 为true加载rest节点信息并将所有可REST化的Service生成RestServlet + + protected final HttpServer httpServer; + + public NodeHttpServer(Application application, AnyValue serconf) { + super(application, createServer(application, serconf)); + this.httpServer = (HttpServer) server; + this.rest = serconf == null ? false : serconf.getAnyValue("rest") != null; + } + + private static Server createServer(Application application, AnyValue serconf) { + return new HttpServer(application, application.getStartTime(), application.getResourceFactory().createChild()); + } + + public HttpServer getHttpServer() { + return httpServer; + } + + @Override + public InetSocketAddress getSocketAddress() { + return httpServer == null ? null : httpServer.getSocketAddress(); + } + + @Override + @SuppressWarnings("unchecked") + protected ClassFilter createServiceClassFilter() { + return createClassFilter(this.sncpGroup, null, Service.class, new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service"); + } + + @Override + @SuppressWarnings("unchecked") + protected ClassFilter createFilterClassFilter() { + return createClassFilter(null, null, HttpFilter.class, new Class[]{WatchFilter.class}, null, "filters", "filter"); + } + + @Override + @SuppressWarnings("unchecked") + protected ClassFilter createServletClassFilter() { + return createClassFilter(null, WebServlet.class, HttpServlet.class, new Class[]{WatchServlet.class}, null, "servlets", "servlet"); + } + + @Override + protected ClassFilter createOtherClassFilter() { + return createClassFilter(null, RestWebSocket.class, WebSocket.class, null, null, "rest", "websocket"); + } + + @Override + protected void loadService(ClassFilter serviceFilter, ClassFilter otherFilter) throws Exception { + super.loadService(serviceFilter, otherFilter); + initWebSocketService(); + } + + @Override + protected void loadFilter(ClassFilter filterFilter, ClassFilter otherFilter) throws Exception { + if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter); + } + + @Override + @SuppressWarnings("unchecked") + protected void loadServlet(ClassFilter servletFilter, ClassFilter otherFilter) throws Exception { + if (httpServer != null) loadHttpServlet(servletFilter, otherFilter); + } + + private void initWebSocketService() { + final NodeServer self = this; + final ResourceFactory regFactory = application.getResourceFactory(); + resourceFactory.register((ResourceFactory rf, final Object src, final String resourceName, Field field, Object attachment) -> { //主要用于单点的服务 + try { + if (field.getAnnotation(Resource.class) == null) return; + if (!(src instanceof WebSocketServlet)) return; + ResourceFactory.ResourceLoader loader = null; + ResourceFactory sncpResFactory = null; + for (NodeServer ns : application.servers) { + if (!ns.isSNCP()) continue; + sncpResFactory = ns.resourceFactory; + loader = sncpResFactory.findLoader(WebSocketNode.class, field); + if (loader != null) break; + } + if (loader != null) loader.load(sncpResFactory, src, resourceName, field, attachment); + synchronized (regFactory) { + Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class); + if (sncpResFactory != null && resourceFactory.find(RESNAME_SNCP_ADDR, String.class) == null) { + resourceFactory.register(RESNAME_SNCP_ADDR, InetSocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, InetSocketAddress.class)); + resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, sncpResFactory.find(RESNAME_SNCP_ADDR, SocketAddress.class)); + resourceFactory.register(RESNAME_SNCP_ADDR, String.class, sncpResFactory.find(RESNAME_SNCP_ADDR, String.class)); + } + if (nodeService == null) { + MessageAgent messageAgent = null; + try { + Field c = WebSocketServlet.class.getDeclaredField("messageAgent"); + c.setAccessible(true); + messageAgent = (MessageAgent) c.get(src); + } catch (Exception ex) { + logger.log(Level.WARNING, "WebSocketServlet getMessageAgent error", ex); + } + nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, messageAgent, application.getResourceFactory(), application.getSncpTransportFactory(), (InetSocketAddress) null, (Set) null, (AnyValue) null); + regFactory.register(resourceName, WebSocketNode.class, nodeService); + } + resourceFactory.inject(nodeService, self); + field.set(src, nodeService); + logger.fine("[" + Thread.currentThread().getName() + "] Load Service " + nodeService); + } + } catch (Exception e) { + logger.log(Level.SEVERE, "WebSocketNode inject error", e); + } + }, WebSocketNode.class); + } + + @SuppressWarnings("unchecked") + protected void loadHttpFilter(final AnyValue filtersConf, final ClassFilter classFilter) throws Exception { + final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; + final String localThreadName = "[" + Thread.currentThread().getName() + "] "; + List> list = new ArrayList(classFilter.getFilterEntrys()); + for (FilterEntry en : list) { + Class clazz = (Class) en.getType(); + if (Modifier.isAbstract(clazz.getModifiers())) continue; + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName()); + final HttpFilter filter = clazz.getDeclaredConstructor().newInstance(); + resourceFactory.inject(filter, this); + DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); + this.httpServer.addHttpFilter(filter, filterConf); + if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR); + } + if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); + } + + @SuppressWarnings("unchecked") + protected void loadHttpServlet(final ClassFilter servletFilter, ClassFilter webSocketFilter) throws Exception { + RedkaleClassLoader.putReflectionPublicClasses(HttpServlet.class.getName()); + RedkaleClassLoader.putReflectionPublicClasses(HttpPrepareServlet.class.getName()); + RedkaleClassLoader.putReflectionDeclaredConstructors(HttpResourceServlet.class, HttpResourceServlet.class.getName()); + final AnyValue servletsConf = this.serverConf.getAnyValue("servlets"); + final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; + String prefix0 = servletsConf == null ? "" : servletsConf.getValue("path", ""); + if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1); + if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; + final String prefix = prefix0; + final String localThreadName = "[" + Thread.currentThread().getName() + "] "; + List> list = new ArrayList(servletFilter.getFilterEntrys()); + list.sort((FilterEntry o1, FilterEntry o2) -> { //必须保证WebSocketServlet优先加载, 因为要确保其他的HttpServlet可以注入本地模式的WebSocketNode + boolean ws1 = WebSocketServlet.class.isAssignableFrom(o1.getType()); + boolean ws2 = WebSocketServlet.class.isAssignableFrom(o2.getType()); + if (ws1 == ws2) { + Priority p1 = o1.getType().getAnnotation(Priority.class); + Priority p2 = o2.getType().getAnnotation(Priority.class); + int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); + return v == 0 ? o1.getType().getName().compareTo(o2.getType().getName()) : 0; + } + return ws1 ? -1 : 1; + }); + final long starts = System.currentTimeMillis(); + final List> ss = sb == null ? null : new ArrayList<>(); + for (FilterEntry en : list) { + Class clazz = (Class) en.getType(); + if (Modifier.isAbstract(clazz.getModifiers())) continue; + if (clazz.getAnnotation(Rest.RestDyn.class) != null) continue; //动态生成的跳过 + WebServlet ws = clazz.getAnnotation(WebServlet.class); + if (ws == null) continue; + if (ws.value().length == 0) { + logger.log(Level.INFO, "not found @WebServlet.value in " + clazz.getName()); + continue; + } + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName()); + final HttpServlet servlet = clazz.getDeclaredConstructor().newInstance(); + resourceFactory.inject(servlet, this); + final String[] mappings = ws.value(); + String pref = ws.repair() ? prefix : ""; + DefaultAnyValue servletConf = (DefaultAnyValue) en.getProperty(); + this.httpServer.addHttpServlet(servlet, pref, servletConf, mappings); + if (ss != null) { + for (int i = 0; i < mappings.length; i++) { + mappings[i] = pref + mappings[i]; + } + ss.add(new AbstractMap.SimpleEntry<>("HttpServlet (type=" + clazz.getName() + ")", mappings)); + } + } + final List> rests = sb == null ? null : new ArrayList<>(); + final List> webss = sb == null ? null : new ArrayList<>(); + if (rest && serverConf != null) { + final List restedObjects = new ArrayList<>(); + for (AnyValue restConf : serverConf.getAnyValues("rest")) { + loadRestServlet(webSocketFilter, restConf, restedObjects, sb, rests, webss); + } + } + int max = 0; + if (ss != null && sb != null) { + int maxTypeLength = 0; + int maxNameLength = 0; + if (rests != null) { + for (AbstractMap.SimpleEntry en : rests) { + int pos = en.getKey().indexOf('#'); + if (pos > maxTypeLength) maxTypeLength = pos; + int len = en.getKey().length() - pos - 1; + if (len > maxNameLength) maxNameLength = len; + } + } + if (webss != null) { + for (AbstractMap.SimpleEntry en : webss) { + int pos = en.getKey().indexOf('#'); + if (pos > maxTypeLength) maxTypeLength = pos; + int len = en.getKey().length() - pos - 1; + if (len > maxNameLength) maxNameLength = len; + } + } + if (rests != null) { + for (AbstractMap.SimpleEntry en : rests) { + StringBuilder sub = new StringBuilder(); + int pos = en.getKey().indexOf('#'); + sub.append("RestDynServlet (type=").append(en.getKey().substring(0, pos)); + for (int i = 0; i < maxTypeLength - pos; i++) { + sub.append(' '); + } + sub.append(", name='").append(en.getKey().substring(pos + 1)); + for (int i = 0; i < maxNameLength - pos; i++) { + sub.append(' '); + } + sub.append("')"); + ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue())); + } + } + if (webss != null) { + for (AbstractMap.SimpleEntry en : webss) { + StringBuilder sub = new StringBuilder(); + int pos = en.getKey().indexOf('#'); + sub.append("RestWebSocket (type=").append(en.getKey().substring(0, pos)); + for (int i = 0; i < maxTypeLength - pos; i++) { + sub.append(' '); + } + sub.append(", name='").append(en.getKey().substring(pos + 1)); + for (int i = 0; i < maxNameLength - pos; i++) { + sub.append(' '); + } + sub.append("')"); + ss.add(new AbstractMap.SimpleEntry<>(sub.toString(), en.getValue())); + } + } + ss.sort((AbstractMap.SimpleEntry o1, AbstractMap.SimpleEntry o2) -> o1.getKey().compareTo(o2.getKey())); + for (AbstractMap.SimpleEntry as : ss) { + if (as.getKey().length() > max) max = as.getKey().length(); + } + for (AbstractMap.SimpleEntry as : ss) { + sb.append(localThreadName).append("Load ").append(as.getKey()); + for (int i = 0; i < max - as.getKey().length(); i++) { + sb.append(' '); + } + sb.append(" mapping to ").append(Arrays.toString(as.getValue())).append(LINE_SEPARATOR); + } + sb.append(localThreadName).append("All HttpServlets load cost ").append(System.currentTimeMillis() - starts).append(" ms").append(LINE_SEPARATOR); + } + if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString().trim()); + } + + @SuppressWarnings("unchecked") + protected void loadRestServlet(final ClassFilter webSocketFilter, final AnyValue restConf, final List restedObjects, final StringBuilder sb, + final List> rests, final List> webss) throws Exception { + if (!rest) return; + if (restConf == null) return; //不存在REST服务 + + String prefix0 = restConf.getValue("path", ""); + if (!prefix0.isEmpty() && prefix0.charAt(prefix0.length() - 1) == '/') prefix0 = prefix0.substring(0, prefix0.length() - 1); + if (!prefix0.isEmpty() && prefix0.charAt(0) != '/') prefix0 = '/' + prefix0; + + final String localThreadName = "[" + Thread.currentThread().getName() + "] "; + String mqname = restConf.getValue("mq"); + MessageAgent agent0 = null; + if (mqname != null) { + agent0 = application.getMessageAgent(mqname); + if (agent0 == null) throw new RuntimeException("not found " + MessageAgent.class.getSimpleName() + " config for (name=" + mqname + ")"); + } + final MessageAgent messageAgent = agent0; + if (messageAgent != null) prefix0 = ""; //开启MQ时,prefix字段失效 + final String prefix = prefix0; + final boolean autoload = restConf.getBoolValue("autoload", true); + { //加载RestService + String userTypeStr = restConf.getValue("usertype"); + final Class userType = userTypeStr == null ? null : this.serverClassLoader.loadClass(userTypeStr); + + final Class baseServletType = this.serverClassLoader.loadClass(restConf.getValue("base", HttpServlet.class.getName())); + final Set includeValues = new HashSet<>(); + final Set excludeValues = new HashSet<>(); + for (AnyValue item : restConf.getAnyValues("service")) { + if (item.getBoolValue("ignore", false)) { + excludeValues.add(item.getValue("value", "")); + } else { + includeValues.add(item.getValue("value", "")); + } + } + + final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues); + final CountDownLatch scdl = new CountDownLatch(super.interceptorServices.size()); + Stream stream = super.interceptorServices.stream(); + if (!application.isCompileMode()) stream = stream.parallel(); //不能并行,否则在maven plugin运行环境下ClassLoader不对 + stream.forEach((service) -> { + try { + final Class stype = Sncp.getServiceType(service); + final String name = Sncp.getResourceName(service); + RestService rs = (RestService) stype.getAnnotation(RestService.class); + if (rs == null || rs.ignore()) return; + + final String stypename = stype.getName(); + if (!autoload && !includeValues.contains(stypename)) return; + if (!restFilter.accept(stypename)) return; + synchronized (restedObjects) { + if (restedObjects.contains(service)) { + logger.log(Level.WARNING, stype.getName() + " repeat create rest servlet, so ignore"); + return; + } + restedObjects.add(service); //避免重复创建Rest对象 + } + HttpServlet servlet = httpServer.addRestServlet(serverClassLoader, service, userType, baseServletType, prefix); + if (servlet == null) return; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null + String prefix2 = prefix; + WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); + if (ws != null && !ws.repair()) prefix2 = ""; + resourceFactory.inject(servlet, NodeHttpServer.this); + dynServletMap.put(service, servlet); + if (messageAgent != null) messageAgent.putService(this, service, servlet); + //if (finest) logger.finest(localThreadName + " Create RestServlet(resource.name='" + name + "') = " + servlet); + if (rests != null) { + String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); + for (int i = 0; i < mappings.length; i++) { + mappings[i] = prefix2 + mappings[i]; + } + synchronized (rests) { + rests.add(new AbstractMap.SimpleEntry<>(Sncp.getServiceType(service).getName() + "#" + name, mappings)); + } + } + } finally { + scdl.countDown(); + } + }); + scdl.await(); + } + if (webSocketFilter != null) { //加载RestWebSocket + final Set includeValues = new HashSet<>(); + final Set excludeValues = new HashSet<>(); + for (AnyValue item : restConf.getAnyValues("websocket")) { + if (item.getBoolValue("ignore", false)) { + excludeValues.add(item.getValue("value", "")); + } else { + includeValues.add(item.getValue("value", "")); + } + } + final ClassFilter restFilter = ClassFilter.create(serverClassLoader, null, application.isCompileMode() ? "" : restConf.getValue("includes", ""), application.isCompileMode() ? "" : restConf.getValue("excludes", ""), includeValues, excludeValues); + final boolean finest = logger.isLoggable(Level.FINEST); + + List> list = new ArrayList(webSocketFilter.getFilterEntrys()); + for (FilterEntry en : list) { + Class clazz = (Class) en.getType(); + if (Modifier.isAbstract(clazz.getModifiers())) { + logger.log(Level.FINE, clazz.getName() + " cannot abstract on rest websocket, so ignore"); + continue; + } + if (Modifier.isFinal(clazz.getModifiers())) { + logger.log(Level.FINE, clazz.getName() + " cannot final on rest websocket, so ignore"); + continue; + } + final Class stype = en.getType(); + if (stype.getAnnotation(Rest.RestDyn.class) != null) continue; + RestWebSocket rs = stype.getAnnotation(RestWebSocket.class); + if (rs == null || rs.ignore()) continue; + + final String stypename = stype.getName(); + if (!autoload && !includeValues.contains(stypename)) continue; + if (!restFilter.accept(stypename)) continue; + if (restedObjects.contains(stype)) { + logger.log(Level.WARNING, stype.getName() + " repeat create rest websocket, so ignore"); + continue; + } + restedObjects.add(stype); //避免重复创建Rest对象 + WebSocketServlet servlet = httpServer.addRestWebSocketServlet(serverClassLoader, stype, messageAgent, prefix, en.getProperty()); + if (servlet == null) continue; //没有RestOnMessage方法的HttpServlet调用Rest.createRestWebSocketServlet就会返回null + String prefix2 = prefix; + WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); + if (ws != null && !ws.repair()) prefix2 = ""; + resourceFactory.inject(servlet, NodeHttpServer.this); + if (finest) logger.finest(localThreadName + " " + stype.getName() + " create a RestWebSocketServlet"); + if (webss != null) { + String[] mappings = servlet.getClass().getAnnotation(WebServlet.class).value(); + for (int i = 0; i < mappings.length; i++) { + mappings[i] = prefix2 + mappings[i]; + } + synchronized (webss) { + webss.add(new AbstractMap.SimpleEntry<>(stype.getName() + "#" + rs.name(), mappings)); + } + } + } + } + if (messageAgent != null) this.messageAgents.put(messageAgent.getName(), messageAgent); + } + + @Override //loadServlet执行之后调用 + protected void postLoadServlets() { + final ClusterAgent cluster = application.clusterAgent; + if (!application.isCompileMode() && cluster != null) { + NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class); + String protocol = pros.value().toUpperCase(); + if (!cluster.containsProtocol(protocol)) return; + if (!cluster.containsPort(server.getSocketAddress().getPort())) return; + cluster.register(this, protocol, dynServletMap.keySet(), new HashSet<>()); + } + } + + @Override + protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) { + cluster.deregister(this, protocol, dynServletMap.keySet(), new HashSet<>()); + } +} diff --git a/src/org/redkale/boot/NodeInterceptor.java b/src/main/java/org/redkale/boot/NodeInterceptor.java similarity index 95% rename from src/org/redkale/boot/NodeInterceptor.java rename to src/main/java/org/redkale/boot/NodeInterceptor.java index a6edf764b..138e41743 100644 --- a/src/org/redkale/boot/NodeInterceptor.java +++ b/src/main/java/org/redkale/boot/NodeInterceptor.java @@ -1,38 +1,38 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -/** - * NodeServer的拦截类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class NodeInterceptor { - - /** * - * Server.start之前调用
    - * NodeServer.start的部署是先执行NodeInterceptor.preStart,再执行 Server.start 方法 - * - * @param server NodeServer - */ - public void preStart(NodeServer server) { - - } - - /** - * Server.shutdown之前调用
    - * NodeServer.shutdown的部署是先执行NodeInterceptor.preShutdown,再执行 Server.sshutdown 方法 - * - * @param server NodeServer - */ - public void preShutdown(NodeServer server) { - - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +/** + * NodeServer的拦截类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class NodeInterceptor { + + /** * + * Server.start之前调用
    + * NodeServer.start的部署是先执行NodeInterceptor.preStart,再执行 Server.start 方法 + * + * @param server NodeServer + */ + public void preStart(NodeServer server) { + + } + + /** + * Server.shutdown之前调用
    + * NodeServer.shutdown的部署是先执行NodeInterceptor.preShutdown,再执行 Server.sshutdown 方法 + * + * @param server NodeServer + */ + public void preShutdown(NodeServer server) { + + } + +} diff --git a/src/org/redkale/boot/NodeProtocol.java b/src/main/java/org/redkale/boot/NodeProtocol.java similarity index 96% rename from src/org/redkale/boot/NodeProtocol.java rename to src/main/java/org/redkale/boot/NodeProtocol.java index f37f8da57..ab72e3cef 100644 --- a/src/org/redkale/boot/NodeProtocol.java +++ b/src/main/java/org/redkale/boot/NodeProtocol.java @@ -1,24 +1,24 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import java.lang.annotation.*; - -/** - * 根据application.xml中的server节点中的protocol值来适配Server的加载逻辑, 只能注解在NodeServer子类上 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface NodeProtocol { - - String value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import java.lang.annotation.*; + +/** + * 根据application.xml中的server节点中的protocol值来适配Server的加载逻辑, 只能注解在NodeServer子类上 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface NodeProtocol { + + String value(); +} diff --git a/src/org/redkale/boot/NodeServer.java b/src/main/java/org/redkale/boot/NodeServer.java similarity index 81% rename from src/org/redkale/boot/NodeServer.java rename to src/main/java/org/redkale/boot/NodeServer.java index 449fd86fe..e413dcbd7 100644 --- a/src/org/redkale/boot/NodeServer.java +++ b/src/main/java/org/redkale/boot/NodeServer.java @@ -1,856 +1,910 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import org.redkale.cluster.ClusterAgent; -import org.redkale.mq.MessageAgent; -import org.redkale.util.RedkaleClassLoader; -import java.io.*; -import java.lang.annotation.Annotation; -import java.lang.reflect.*; -import java.net.*; -import java.nio.file.*; -import java.util.*; -import java.util.AbstractMap.SimpleEntry; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.logging.*; -import javax.annotation.*; -import static org.redkale.boot.Application.*; -import org.redkale.boot.ClassFilter.FilterEntry; -import org.redkale.net.Filter; -import org.redkale.net.*; -import org.redkale.net.http.*; -import org.redkale.net.sncp.*; -import org.redkale.service.*; -import org.redkale.source.*; -import org.redkale.util.*; - -/** - * Server节点的初始化配置类 - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public abstract class NodeServer { - - //INFO日志的换行符 - public static final String LINE_SEPARATOR = "\r\n"; - - //日志输出对象 - protected final Logger logger; - - //进程主类 - protected final Application application; - - //依赖注入工厂类 - protected final ResourceFactory resourceFactory; - - //当前Server对象 - protected final Server server; - - //ClassLoader - protected RedkaleClassLoader serverClassLoader; - - protected final Thread serverThread; - - //当前Server的SNCP协议的组 - protected String sncpGroup = null; - - //SNCP服务的地址, 非SNCP为null - private InetSocketAddress sncpAddress; - - //加载Service时的处理函数 - protected BiConsumer consumer; - - //server节点的配置 - protected AnyValue serverConf; - - protected final String threadName; - - //加载server节点后的拦截器 - protected NodeInterceptor interceptor; - - //供interceptor使用的Service对象集合 - protected final Set interceptorServices = new LinkedHashSet<>(); - - //本地模式的Service对象集合 - protected final Set localServices = new LinkedHashSet<>(); - - //远程模式的Service对象集合 - protected final Set remoteServices = new LinkedHashSet<>(); - - protected final Map dynServletMap = new LinkedHashMap<>(); - - //MessageAgent对象集合 - protected final Map messageAgents = new HashMap<>(); - - //需要远程模式Service的MessageAgent对象集合 - protected final Map sncpRemoteAgents = new HashMap<>(); - - private volatile int maxClassNameLength = 0; - - private volatile int maxNameLength = 0; - - public NodeServer(Application application, Server server) { - this.threadName = Thread.currentThread().getName(); - this.application = application; - this.server = server; - this.resourceFactory = server.getResourceFactory(); - this.logger = Logger.getLogger(this.getClass().getSimpleName()); - this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader()); - Thread.currentThread().setContextClassLoader(this.serverClassLoader); - this.serverThread = Thread.currentThread(); - this.server.setServerClassLoader(serverClassLoader); - } - - public static NodeServer create(Class clazz, Application application, AnyValue serconf) { - try { - return clazz.getConstructor(Application.class, AnyValue.class).newInstance(application, serconf); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public void init(AnyValue config) throws Exception { - this.serverConf = config == null ? AnyValue.create() : config; - if (isSNCP()) { // SNCP协议 - String host = this.serverConf.getValue("host", isWATCH() ? "127.0.0.1" : "0.0.0.0").replace("0.0.0.0", ""); - this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getAddress().getHostAddress() : host, this.serverConf.getIntValue("port")); - this.sncpGroup = application.sncpTransportFactory.findGroupName(this.sncpAddress); - //单向SNCP服务不需要对等group - //if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found info"); - } - //单点服务不会有 sncpAddress、sncpGroup - if (this.sncpAddress != null) { - this.resourceFactory.register(RESNAME_SNCP_ADDR, this.sncpAddress); - this.resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, this.sncpAddress); - this.resourceFactory.register(RESNAME_SNCP_ADDR, String.class, this.sncpAddress.getHostString() + ":" + this.sncpAddress.getPort()); - } - if (this.sncpGroup != null) this.resourceFactory.register(RESNAME_SNCP_GROUP, this.sncpGroup); - { - //设置root文件夹 - String webroot = this.serverConf.getValue("root", "root"); - File myroot = new File(webroot); - if (!webroot.contains(":") && !webroot.startsWith("/")) { - myroot = new File(System.getProperty(Application.RESNAME_APP_HOME), webroot); - } - - resourceFactory.register(Server.RESNAME_SERVER_ROOT, String.class, myroot.getCanonicalPath()); - resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile()); - resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath()); - - //加入指定的classpath - Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "")); - this.serverThread.setContextClassLoader(this.serverClassLoader); - } - //必须要进行初始化, 构建Service时需要使用Context中的ExecutorService - server.init(this.serverConf); - //init之后才有Executor - //废弃 @since 2.3.0 -// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor()); -// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor()); -// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor()); - - initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。 - String interceptorClass = this.serverConf.getValue("interceptor", ""); - if (!interceptorClass.isEmpty()) { - Class clazz = serverClassLoader.loadClass(interceptorClass); - this.interceptor = (NodeInterceptor) clazz.getDeclaredConstructor().newInstance(); - } - - ClassFilter serviceFilter = createServiceClassFilter(); - if (application.singletonrun) { //singleton模式下只加载指定的Service - final String ssc = System.getProperty("red" + "kale.singleton.serviceclass"); - final String extssc = System.getProperty("red" + "kale.singleton.extserviceclasses"); - if (ssc != null) { - final List sscList = new ArrayList<>(); - sscList.add(ssc); - if (extssc != null && !extssc.isEmpty()) { - for (String s : extssc.split(",")) { - if (!s.isEmpty()) sscList.add(s); - } - } - serviceFilter.setExpectPredicate(c -> !sscList.contains(c)); - } - } - ClassFilter filterFilter = createFilterClassFilter(); - ClassFilter servletFilter = createServletClassFilter(); - ClassFilter otherFilter = createOtherClassFilter(); - long s = System.currentTimeMillis(); - ClassFilter.Loader.load(application.getHome(), ((application.excludelibs != null ? (application.excludelibs + ";") : "") + serverConf.getValue("excludelibs", "")).split(";"), serviceFilter, filterFilter, servletFilter, otherFilter); - long e = System.currentTimeMillis() - s; - logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms"); - loadService(serviceFilter, otherFilter); //必须在servlet之前 - if (!application.singletonrun) { //非singleton模式下才加载Filter、Servlet - loadFilter(filterFilter, otherFilter); - loadServlet(servletFilter, otherFilter); - postLoadServlets(); - } - if (this.interceptor != null) this.resourceFactory.inject(this.interceptor); - } - - protected abstract void loadFilter(ClassFilter filterFilter, ClassFilter otherFilter) throws Exception; - - protected abstract void loadServlet(ClassFilter servletFilter, ClassFilter otherFilter) throws Exception; - - private void initResource() { - final NodeServer self = this; - //--------------------------------------------------------------------------------------------- - final ResourceFactory appResFactory = application.getResourceFactory(); - final TransportFactory appSncpTranFactory = application.getSncpTransportFactory(); - final AnyValue resources = application.config.getAnyValue("resources"); - final Map> cacheResource = new HashMap<>(); - final Map> dataResources = new HashMap<>(); - if (resources != null) { - for (AnyValue sourceConf : resources.getAnyValues("source")) { - try { - String classval = sourceConf.getValue("value"); - Class type = null; - if (classval == null || classval.isEmpty()) { - Iterator it = ServiceLoader.load(CacheSourceLoader.class, serverClassLoader).iterator(); - while (it.hasNext()) { - CacheSourceLoader s = it.next(); - if (s != null && s.match(sourceConf)) { - type = s.sourceClass(); - break; - } - } - } else { - type = serverClassLoader.loadClass(classval); - } - if (type == DataSource.class) { - type = DataMemorySource.class; - for (AnyValue itemConf : sourceConf.getAnyValues("property")) { - if (itemConf.getValue("name", "").contains(DataSources.JDBC_URL)) { - type = DataJdbcSource.class; - break; - } - } - } - if (!Service.class.isAssignableFrom(type)) { - logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf); - } else if (CacheSource.class.isAssignableFrom(type)) { - cacheResource.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf)); - } else if (DataSource.class.isAssignableFrom(type)) { - dataResources.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf)); - } else { - logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + sourceConf); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e); - } - } - } - //------------------------------------- 注册 Resource -------------------------------------------------------- - resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { - try { - Resource res = field.getAnnotation(Resource.class); - if (res == null || !res.name().startsWith("properties.")) return; - if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource - Class type = field.getType(); - if (type != AnyValue.class && type != AnyValue[].class) return; - Object resource = null; - final AnyValue properties = resources == null ? null : resources.getAnyValue("properties"); - if (properties != null && type == AnyValue.class) { - resource = properties.getAnyValue(res.name().substring("properties.".length())); - appResFactory.register(resourceName, AnyValue.class, resource); - } else if (properties != null && type == AnyValue[].class) { - resource = properties.getAnyValues(res.name().substring("properties.".length())); - appResFactory.register(resourceName, AnyValue[].class, resource); - } - field.set(src, resource); - } catch (Exception e) { - logger.log(Level.SEVERE, "Resource inject error", e); - } - }, AnyValue.class, AnyValue[].class); - - //------------------------------------- 注册 Local AutoLoad(false) Service -------------------------------------------------------- - resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { - Class resServiceType = Service.class; - try { - if (field.getAnnotation(Resource.class) == null) return; - if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 AutoLoad Service - if (!Service.class.isAssignableFrom(field.getType())) return; - resServiceType = (Class) field.getType(); - if (resServiceType.getAnnotation(Local.class) == null) return; - AutoLoad al = resServiceType.getAnnotation(AutoLoad.class); - if (al == null || al.value()) return; - - //ResourceFactory resfactory = (isSNCP() ? appResFactory : resourceFactory); - SncpClient client = src instanceof Service ? Sncp.getSncpClient((Service) src) : null; - final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); - final Set groups = new HashSet<>(); - Service service = Sncp.createLocalService(serverClassLoader, resourceName, resServiceType, null, appResFactory, appSncpTranFactory, sncpAddr, groups, null); - appResFactory.register(resourceName, resServiceType, service); - - field.set(src, service); - rf.inject(service, self); // 给其可能包含@Resource的字段赋值; - service.init(null); - logger.info("[" + Thread.currentThread().getName() + "] Load Service(@Local @AutoLoad) resourceName = " + resourceName + ", service = " + service); - } catch (Exception e) { - logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Load @Local @AutoLoad(false) Service inject " + resServiceType + " to " + src + " error", e); - } - }, Service.class); - - //------------------------------------- 注册 DataSource -------------------------------------------------------- - resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { - try { - if (field.getAnnotation(Resource.class) == null) return; - if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource - SimpleEntry resEntry = dataResources.get(resourceName); - AnyValue sourceConf = resEntry == null ? null : resEntry.getValue(); - DataSource source = null; - if (sourceConf != null) { - final Class sourceType = resEntry.getKey(); - if (sourceType == DataJdbcSource.class) { - source = DataSources.createDataSource(resourceName, sourceConf); - } else { - boolean can = false; - for (Constructor cr : sourceType.getConstructors()) { - if (cr.getParameterCount() == 0) { - can = true; - break; - } - } - if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数 - final Service srcService = (Service) src; - SncpClient client = Sncp.getSncpClient(srcService); - final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); - final Set groups = new HashSet<>(); - source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService)); - } - } - } - if (source == null) { - source = DataSources.createDataSource(resourceName); //从persistence.xml配置中创建 - } - - application.dataSources.add(source); - if (source instanceof SearchSource) { - appResFactory.register(resourceName, SearchSource.class, source); - } - appResFactory.register(resourceName, DataSource.class, source); - - field.set(src, source); - rf.inject(source, self); // 给AsyncGroup和其他@Resource的字段赋值; - //NodeServer.this.watchFactory.inject(src); - if (source instanceof Service) ((Service) source).init(sourceConf); - } catch (Exception e) { - logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e); - } - }, DataSource.class); - - //------------------------------------- 注册 CacheSource -------------------------------------------------------- - resourceFactory.register(new ResourceFactory.ResourceLoader() { - @Override - public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) { - try { - if (field.getAnnotation(Resource.class) == null) return; - if (!(src instanceof Service)) throw new RuntimeException("CacheSource must be inject in Service, cannot " + src); - if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource - final Service srcService = (Service) src; - SncpClient client = Sncp.getSncpClient(srcService); - final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); - SimpleEntry resEntry = cacheResource.get(resourceName); - AnyValue sourceConf = resEntry == null ? null : resEntry.getValue(); - if (sourceConf == null) { - SimpleEntry resEntry2 = dataResources.get(resourceName); - sourceConf = resEntry2 == null ? null : resEntry2.getValue(); - } - Class sourceType0 = CacheMemorySource.class; - if (sourceConf != null) { - String classval = sourceConf.getValue("value"); - if (classval == null || classval.isEmpty()) { - Iterator it = ServiceLoader.load(CacheSourceLoader.class, serverClassLoader).iterator(); - while (it.hasNext()) { - CacheSourceLoader s = it.next(); - if (s != null && s.match(sourceConf)) { - sourceType0 = s.sourceClass(); - break; - } - } - } else { - sourceType0 = serverClassLoader.loadClass(classval); - } - } - final Class sourceType = sourceType0; - Object source = null; - if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource - source = Modifier.isFinal(sourceType.getModifiers()) ? sourceType.getConstructor().newInstance() : (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService)); - Type genericType = field.getGenericType(); - application.cacheSources.add((CacheSource) source); - appResFactory.register(resourceName, CacheSource.class, source); - if (genericType != CacheSource.class) { - appResFactory.register(resourceName, genericType, source); - } - } - field.set(src, source); - rf.inject(source, self); // - if (source instanceof Service) ((Service) source).init(sourceConf); - - if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource - NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr); - sncpServer.getSncpServer().addSncpServlet((Service) source); - //logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source); - } - logger.info("[" + Thread.currentThread().getName() + "] Load Source resourceName = " + resourceName + ", source = " + source); - } catch (Exception e) { - logger.log(Level.SEVERE, "DataSource inject error", e); - } - } - - @Override - public boolean autoNone() { - return false; - } - }, CacheSource.class); - - //------------------------------------- 注册 WebSocketNode -------------------------------------------------------- - resourceFactory.register(new ResourceFactory.ResourceLoader() { - @Override - public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) { - try { - if (field.getAnnotation(Resource.class) == null) return; - if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 WebSocketNode - Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class); - if (nodeService == null) { - final HashSet groups = new HashSet<>(); - if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup); - nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, Sncp.getMessageAgent((Service) src), application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null); - (isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService); - ((WebSocketNodeService) nodeService).setName(resourceName); - } - resourceFactory.inject(nodeService, self); - MessageAgent messageAgent = Sncp.getMessageAgent((Service) src); - if (messageAgent != null && Sncp.getMessageAgent(nodeService) == null) Sncp.setMessageAgent(nodeService, messageAgent); - field.set(src, nodeService); - if (Sncp.isRemote(nodeService)) { - remoteServices.add(nodeService); - } else { - rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源 - localServices.add(nodeService); - interceptorServices.add(nodeService); - if (consumer != null) consumer.accept(null, nodeService); - } - } catch (Exception e) { - logger.log(Level.SEVERE, "WebSocketNode inject error", e); - } - } - - @Override - public boolean autoNone() { - return false; - } - }, WebSocketNode.class); - } - - @SuppressWarnings("unchecked") - protected void loadService(ClassFilter serviceFilter, ClassFilter otherFilter) throws Exception { - if (serviceFilter == null) return; - final long starts = System.currentTimeMillis(); - final String localThreadName = "[" + Thread.currentThread().getName() + "] "; - final Set> entrys = (Set) serviceFilter.getAllFilterEntrys(); - ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory; - final ResourceFactory appResourceFactory = application.getResourceFactory(); - final TransportFactory appSncpTransFactory = application.getSncpTransportFactory(); - for (FilterEntry entry : entrys) { //service实现类 - final Class serviceImplClass = entry.getType(); - if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过 - if (!Modifier.isPublic(serviceImplClass.getModifiers())) continue; - if (entry.isExpect()) { - if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过 - if (DataSource.class.isAssignableFrom(serviceImplClass)) continue; - if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue; - } - if (entry.getName().contains("$")) throw new RuntimeException(" value cannot contains '$' in " + entry.getProperty()); - Service oldother = resourceFactory.find(entry.getName(), serviceImplClass); - if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。 - if (!Sncp.isRemote(oldother)) interceptorServices.add(oldother); - continue; - } - final HashSet groups = entry.getGroups(); //groups.isEmpty()表示没有配置groups属性。 - if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup); - - final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !serviceImplClass.isInterface() && !Modifier.isAbstract(serviceImplClass.getModifiers())) //非SNCP的Server,通常是单点服务 - || groups.contains(this.sncpGroup) //本地IP含在内的 - || (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置 - || serviceImplClass.getAnnotation(Local.class) != null;//本地模式 - if (localed && (serviceImplClass.isInterface() || Modifier.isAbstract(serviceImplClass.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类 - final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> { - try { - if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的, 通常为BaseService - if (!serviceImplClass.getName().startsWith("org.redkale.") && !serviceImplClass.getSimpleName().contains("Base")) { - logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method"); - } - return; - } - - MessageAgent agent = null; - if (entry.getProperty() != null && entry.getProperty().getValue("mq") != null) { - agent = application.getMessageAgent(entry.getProperty().getValue("mq")); - if (agent != null) messageAgents.put(agent.getName(), agent); - } - - Service service; - final boolean ws = src instanceof WebSocketServlet; - if (ws || localed) { //本地模式 - service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, agent, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); - } else { - service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, agent, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); - } - if (service instanceof WebSocketNodeService) { - ((WebSocketNodeService) service).setName(resourceName); - if (agent != null) Sncp.setMessageAgent(service, agent); - } - final Class restype = Sncp.getResourceType(service); - if (rf.find(resourceName, restype) == null) { - regFactory.register(resourceName, restype, service); - } else if (isSNCP() && !entry.isAutoload()) { - throw new RuntimeException(restype.getSimpleName() + "(class:" + serviceImplClass.getName() + ", name:" + resourceName + ", group:" + groups + ") is repeat."); - } - if (Sncp.isRemote(service)) { - remoteServices.add(service); - if (agent != null) sncpRemoteAgents.put(agent.getName(), agent); - } else { - if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源 - localServices.add(service); - interceptorServices.add(service); - if (consumer != null) consumer.accept(agent, service); - } - } catch (RuntimeException ex) { - throw ex; - } catch (Exception e) { - throw new RuntimeException(e); - } - }; - if (entry.isExpect()) { - ResourceType rty = entry.getType().getAnnotation(ResourceType.class); - resourceFactory.register(resourceLoader, rty == null ? entry.getType() : rty.value()); - } else { - resourceLoader.load(resourceFactory, null, entry.getName(), null, false); - } - - } - - application.servicecdl.countDown(); - application.servicecdl.await(); - - final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; - //---------------- inject ---------------- - new ArrayList<>(localServices).forEach(y -> { - resourceFactory.inject(y, NodeServer.this); - calcMaxLength(y); - }); - new ArrayList<>(remoteServices).forEach(y -> { - resourceFactory.inject(y, NodeServer.this); - calcMaxLength(y); - }); - - if (sb != null) { - remoteServices.forEach(y -> { - sb.append(localThreadName).append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" load and inject").append(LINE_SEPARATOR); - }); - } - if (isSNCP() && !sncpRemoteAgents.isEmpty()) { - sncpRemoteAgents.values().forEach(agent -> { - // agent.putSncpResp((NodeSncpServer) this); - // agent.startSncpRespConsumer(); - }); - } - //----------------- init ----------------- - List swlist = new ArrayList<>(localServices); - swlist.sort((o1, o2) -> { - Priority p1 = o1.getClass().getAnnotation(Priority.class); - Priority p2 = o2.getClass().getAnnotation(Priority.class); - int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); - if (v != 0) return v; - int rs = Sncp.getResourceType(o1).getName().compareTo(Sncp.getResourceType(o2).getName()); - if (rs == 0) rs = Sncp.getResourceName(o1).compareTo(Sncp.getResourceName(o2)); - return rs; - }); - localServices.clear(); - localServices.addAll(swlist); - //this.loadPersistData(); - long preinits = System.currentTimeMillis(); - preInitServices(localServices, remoteServices); - long preinite = System.currentTimeMillis() - preinits; - final List slist = sb == null ? null : new CopyOnWriteArrayList<>(); - localServices.stream().forEach(y -> { - long s = System.currentTimeMillis(); - y.init(Sncp.getConf(y)); - long e = System.currentTimeMillis() - s; - String serstr = Sncp.toSimpleString(y, maxNameLength, maxClassNameLength); - if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load and init in ").append(e).append(" ms").append(LINE_SEPARATOR).toString()); - }); - if (slist != null && sb != null) { - List wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行) - for (String s : wlist) { - sb.append(s); - } - sb.append(localThreadName).append("All Services load cost ").append(System.currentTimeMillis() - starts).append(" ms" + LINE_SEPARATOR); - } - if (sb != null && preinite > 10) sb.append(localThreadName).append(ClusterAgent.class.getSimpleName()).append(" register ").append(preinite).append(" ms" + LINE_SEPARATOR); - if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); - } - - private void calcMaxLength(Service y) { //计算toString中的长度 - maxNameLength = Math.max(maxNameLength, Sncp.getResourceName(y).length()); - maxClassNameLength = Math.max(maxClassNameLength, Sncp.getResourceType(y).getName().length() + 1); - } - - //Service.init执行之前调用 - protected void preInitServices(Set localServices, Set remoteServices) { - final ClusterAgent cluster = application.clusterAgent; - if (cluster == null) return; - NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class); - String protocol = pros.value().toUpperCase(); - if (!cluster.containsProtocol(protocol)) return; - if (!cluster.containsPort(server.getSocketAddress().getPort())) return; - cluster.register(this, protocol, localServices, remoteServices); - } - - //loadServlet执行之后调用 - protected void postLoadServlets() { - - } - - //Service.destroy执行之前调用 - protected void preDestroyServices(Set localServices, Set remoteServices) { - if (application.clusterAgent != null) { //服务注销 - final ClusterAgent cluster = application.clusterAgent; - NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class); - String protocol = pros.value().toUpperCase(); - if (cluster.containsProtocol(protocol) && cluster.containsPort(server.getSocketAddress().getPort())) { - cluster.deregister(this, protocol, localServices, remoteServices); - afterClusterDeregisterOnPreDestroyServices(cluster, protocol); - } - } - if (!this.messageAgents.isEmpty()) { //MQ - } - } - - protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) { - } - - //Server.start执行之后调用 - protected void postStartServer(Set localServices, Set remoteServices) { - } - - protected abstract ClassFilter createFilterClassFilter(); - - protected abstract ClassFilter createServletClassFilter(); - - protected ClassFilter createOtherClassFilter() { - return null; - } - - protected ClassFilter createServiceClassFilter() { - return createClassFilter(this.sncpGroup, null, Service.class, (!isSNCP() && application.watching) ? null : new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service"); - } - - protected ClassFilter createClassFilter(final String localGroup, Class ref, - Class inter, Class[] excludeSuperClasses, Class ref2, String properties, String property) { - ClassFilter cf = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, null); - if (properties == null && properties == null) { - cf.setRefused(true); - return cf; - } - if (this.serverConf == null) { - cf.setRefused(true); - return cf; - } - AnyValue[] proplist = this.serverConf.getAnyValues(properties); - if (proplist == null || proplist.length < 1) { - cf.setRefused(true); - return cf; - } - cf = null; - for (AnyValue list : proplist) { - AnyValue.DefaultAnyValue prop = null; - String sc = list.getValue("groups"); - String mq = list.getValue("mq"); - if (sc != null) { - sc = sc.trim(); - if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1); - } - if (sc == null) sc = localGroup; - if (sc != null || mq != null) { - prop = new AnyValue.DefaultAnyValue(); - if (sc != null) prop.addValue("groups", sc); - if (mq != null) prop.addValue("mq", mq); - } - ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop); - for (AnyValue av : list.getAnyValues(property)) { // 节点 - final AnyValue[] items = av.getAnyValues("property"); - if (av instanceof AnyValue.DefaultAnyValue && items.length > 0) { //存在 节点 - AnyValue.DefaultAnyValue dav = AnyValue.DefaultAnyValue.create(); - final AnyValue.Entry[] strings = av.getStringEntrys(); - if (strings != null) { //将节点的属性值传给dav - for (AnyValue.Entry en : strings) { - dav.addValue(en.name, en.getValue()); - } - } - final AnyValue.Entry[] anys = av.getAnyEntrys(); - if (anys != null) { - for (AnyValue.Entry en : anys) { //将节点的非property属性节点传给dav - if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue()); - } - } - AnyValue.DefaultAnyValue ps = AnyValue.DefaultAnyValue.create(); - for (AnyValue item : items) { - ps.addValue(item.getValue("name"), item.getValue("value")); - } - dav.addValue("properties", ps); - av = dav; - } - if (!av.getBoolValue("ignore", false)) { - filter.filter(av, av.getValue("value"), false); - } - } - if (list.getBoolValue("autoload", true)) { - String includes = list.getValue("includes", ""); - String excludes = list.getValue("excludes", ""); - filter.setIncludePatterns(includes.split(";")); - filter.setExcludePatterns(excludes.split(";")); - } else if (ref2 == null || ref2 == Annotation.class) { //service如果是autoload=false则不需要加载 - filter.setRefused(true); - } else if (ref2 != Annotation.class) { - filter.setAnnotationClass(ref2); - } - cf = (cf == null) ? filter : cf.or(filter); - } - return cf; - } - - public abstract InetSocketAddress getSocketAddress(); - - public boolean isSNCP() { - return false; - } - - public boolean isWATCH() { - return false; - } - - public Application getApplication() { - return application; - } - - public ResourceFactory getResourceFactory() { - return resourceFactory; - } - - public RedkaleClassLoader getServerClassLoader() { - return serverClassLoader; - } - - public void setServerClassLoader(RedkaleClassLoader serverClassLoader) { - Objects.requireNonNull(this.serverClassLoader); - this.serverClassLoader = serverClassLoader; - this.serverThread.setContextClassLoader(serverClassLoader); - } - - public InetSocketAddress getSncpAddress() { - return sncpAddress; - } - - public AnyValue getServerConf() { - return serverConf; - } - - public Logger getLogger() { - return logger; - } - - public String getSncpGroup() { - return sncpGroup; - } - - public void start() throws IOException { - if (interceptor != null) interceptor.preStart(this); - server.start(application); - postStartServer(localServices, remoteServices); - } - - public void shutdown() throws IOException { - if (interceptor != null) interceptor.preShutdown(this); - final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; - final boolean finest = logger.isLoggable(Level.FINEST); - preDestroyServices(localServices, remoteServices); - localServices.forEach(y -> { - long s = System.currentTimeMillis(); - if (finest) logger.finest(y + " is destroying"); - y.destroy(Sncp.getConf(y)); - if (finest) logger.finest(y + " was destroyed"); - long e = System.currentTimeMillis() - s; - if (e > 2 && sb != null) { - sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR); - } - }); - if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); - server.shutdown(); - } - - public void command(String cmd) throws IOException { - final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; - final boolean finest = logger.isLoggable(Level.FINEST); - localServices.forEach(y -> { - Set methods = new HashSet<>(); - Class loop = y.getClass(); - //do { public方法不用递归 - for (Method m : loop.getMethods()) { - Command c = m.getAnnotation(Command.class); - if (c == null) continue; - if (Modifier.isStatic(m.getModifiers())) continue; - if (m.getReturnType() != void.class) continue; - if (m.getParameterCount() != 1) continue; - if (m.getParameterTypes()[0] != String.class) continue; - methods.add(m); - } - //} while ((loop = loop.getSuperclass()) != Object.class); - if (methods.isEmpty()) return; - long s = System.currentTimeMillis(); - Method one = null; - try { - for (Method method : methods) { - one = method; - method.invoke(y, cmd); - } - } catch (Exception ex) { - logger.log(Level.SEVERE, one + " run error, cmd = " + cmd, ex); - } - long e = System.currentTimeMillis() - s; - if (e > 10 && sb != null) { - sb.append(Sncp.toSimpleString(y, maxNameLength, maxClassNameLength)).append(" command (").append(cmd).append(") ").append(e).append("ms").append(LINE_SEPARATOR); - } - }); - if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); - } - - public T getServer() { - return (T) server; - } - - public Set getInterceptorServices() { - return new LinkedHashSet<>(interceptorServices); - } - - public Set getLocalServices() { - return new LinkedHashSet<>(localServices); - } - - public Set getRemoteServices() { - return new LinkedHashSet<>(remoteServices); - } - - public String getThreadName() { - return this.threadName; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import org.redkale.cluster.ClusterAgent; +import org.redkale.mq.MessageAgent; +import org.redkale.util.RedkaleClassLoader; +import java.io.*; +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.net.*; +import java.nio.file.*; +import java.util.*; +import java.util.AbstractMap.SimpleEntry; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.logging.*; +import javax.annotation.*; +import static org.redkale.boot.Application.*; +import org.redkale.boot.ClassFilter.FilterEntry; +import org.redkale.net.Filter; +import org.redkale.net.*; +import org.redkale.net.http.*; +import org.redkale.net.sncp.*; +import org.redkale.service.*; +import org.redkale.source.*; +import org.redkale.util.*; + +/** + * Server节点的初始化配置类 + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public abstract class NodeServer { + + //INFO日志的换行符 + public static final String LINE_SEPARATOR = "\r\n"; + + //日志输出对象 + protected final Logger logger; + + //进程主类 + protected final Application application; + + //依赖注入工厂类 + protected final ResourceFactory resourceFactory; + + //当前Server对象 + protected final Server server; + + //ClassLoader + protected RedkaleClassLoader serverClassLoader; + + protected final Thread serverThread; + + //当前Server的SNCP协议的组 + protected String sncpGroup = null; + + //SNCP服务的地址, 非SNCP为null + private InetSocketAddress sncpAddress; + + //加载Service时的处理函数 + protected BiConsumer consumer; + + //server节点的配置 + protected AnyValue serverConf; + + protected final String threadName; + + //加载server节点后的拦截器 + protected NodeInterceptor interceptor; + + //供interceptor使用的Service对象集合 + protected final Set interceptorServices = new LinkedHashSet<>(); + + //本地模式的Service对象集合 + protected final Set localServices = new LinkedHashSet<>(); + + //远程模式的Service对象集合 + protected final Set remoteServices = new LinkedHashSet<>(); + + protected final Map dynServletMap = new LinkedHashMap<>(); + + //MessageAgent对象集合 + protected final Map messageAgents = new HashMap<>(); + + //需要远程模式Service的MessageAgent对象集合 + protected final Map sncpRemoteAgents = new HashMap<>(); + + private volatile int maxTypeLength = 0; + + private volatile int maxNameLength = 0; + + public NodeServer(Application application, Server server) { + this.threadName = Thread.currentThread().getName(); + this.application = application; + this.server = server; + this.resourceFactory = server.getResourceFactory(); + this.logger = Logger.getLogger(this.getClass().getSimpleName()); + if (application.isCompileMode() || application.getServerClassLoader() instanceof RedkaleClassLoader.RedkaleCacheClassLoader) { + this.serverClassLoader = application.getServerClassLoader(); + } else { + this.serverClassLoader = new RedkaleClassLoader(application.getServerClassLoader()); + } + Thread.currentThread().setContextClassLoader(this.serverClassLoader); + this.serverThread = Thread.currentThread(); + this.server.setServerClassLoader(serverClassLoader); + } + + public static NodeServer create(Class clazz, Application application, AnyValue serconf) { + try { + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName(), Application.class, AnyValue.class); + return clazz.getDeclaredConstructor(Application.class, AnyValue.class).newInstance(application, serconf); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void init(AnyValue config) throws Exception { + this.serverConf = config == null ? AnyValue.create() : config; + if (isSNCP()) { // SNCP协议 + String host = this.serverConf.getValue("host", isWATCH() ? "127.0.0.1" : "0.0.0.0").replace("0.0.0.0", ""); + this.sncpAddress = new InetSocketAddress(host.isEmpty() ? application.localAddress.getAddress().getHostAddress() : host, this.serverConf.getIntValue("port")); + this.sncpGroup = application.sncpTransportFactory.findGroupName(this.sncpAddress); + //单向SNCP服务不需要对等group + //if (this.sncpGroup == null) throw new RuntimeException("Server (" + String.valueOf(config).replaceAll("\\s+", " ") + ") not found info"); + } + //单点服务不会有 sncpAddress、sncpGroup + if (this.sncpAddress != null) { + this.resourceFactory.register(RESNAME_SNCP_ADDR, this.sncpAddress); + this.resourceFactory.register(RESNAME_SNCP_ADDR, SocketAddress.class, this.sncpAddress); + this.resourceFactory.register(RESNAME_SNCP_ADDR, String.class, this.sncpAddress.getHostString() + ":" + this.sncpAddress.getPort()); + } + if (this.sncpGroup != null) this.resourceFactory.register(RESNAME_SNCP_GROUP, this.sncpGroup); + { + //设置root文件夹 + String webroot = this.serverConf.getValue("root", "root"); + File myroot = new File(webroot); + if (!webroot.contains(":") && !webroot.startsWith("/")) { + myroot = new File(System.getProperty(Application.RESNAME_APP_HOME), webroot); + } + + resourceFactory.register(Server.RESNAME_SERVER_ROOT, String.class, myroot.getCanonicalPath()); + resourceFactory.register(Server.RESNAME_SERVER_ROOT, File.class, myroot.getCanonicalFile()); + resourceFactory.register(Server.RESNAME_SERVER_ROOT, Path.class, myroot.toPath()); + + //加入指定的classpath + Server.loadLib(serverClassLoader, logger, this.serverConf.getValue("lib", "")); + this.serverThread.setContextClassLoader(this.serverClassLoader); + } + //必须要进行初始化, 构建Service时需要使用Context中的ExecutorService + server.init(this.serverConf); + //init之后才有Executor + //废弃 @since 2.3.0 +// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, Executor.class, server.getWorkExecutor()); +// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ExecutorService.class, server.getWorkExecutor()); +// resourceFactory.register(Server.RESNAME_SERVER_EXECUTOR, ThreadPoolExecutor.class, server.getWorkExecutor()); + + initResource(); //给 DataSource、CacheSource 注册依赖注入时的监听回调事件。 + String interceptorClass = this.serverConf.getValue("interceptor", ""); + if (!interceptorClass.isEmpty()) { + Class clazz = serverClassLoader.loadClass(interceptorClass); + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName()); + this.interceptor = (NodeInterceptor) clazz.getDeclaredConstructor().newInstance(); + } + + ClassFilter serviceFilter = createServiceClassFilter(); + if (application.isSingletonMode()) { //singleton模式下只加载指定的Service + final String ssc = System.getProperty("red" + "kale.singleton.serviceclass"); + final String extssc = System.getProperty("red" + "kale.singleton.extserviceclasses"); + if (ssc != null) { + final List sscList = new ArrayList<>(); + sscList.add(ssc); + if (extssc != null && !extssc.isEmpty()) { + for (String s : extssc.split(",")) { + if (!s.isEmpty()) sscList.add(s); + } + } + serviceFilter.setExpectPredicate(c -> !sscList.contains(c)); + } + } + ClassFilter filterFilter = createFilterClassFilter(); + ClassFilter servletFilter = createServletClassFilter(); + ClassFilter otherFilter = createOtherClassFilter(); + long s = System.currentTimeMillis(); + ClassFilter.Loader.load(application.getHome(), serverClassLoader, ((application.excludelibs != null ? (application.excludelibs + ";") : "") + serverConf.getValue("excludelibs", "")).split(";"), serviceFilter, filterFilter, servletFilter, otherFilter); + long e = System.currentTimeMillis() - s; + logger.info(this.getClass().getSimpleName() + " load filter class in " + e + " ms"); + loadService(serviceFilter, otherFilter); //必须在servlet之前 + if (!application.isSingletonMode()) { //非singleton模式下才加载Filter、Servlet + loadFilter(filterFilter, otherFilter); + loadServlet(servletFilter, otherFilter); + postLoadServlets(); + } + if (this.interceptor != null) this.resourceFactory.inject(this.interceptor); + } + + protected abstract void loadFilter(ClassFilter filterFilter, ClassFilter otherFilter) throws Exception; + + protected abstract void loadServlet(ClassFilter servletFilter, ClassFilter otherFilter) throws Exception; + + private void initResource() { + final NodeServer self = this; + //--------------------------------------------------------------------------------------------- + final ResourceFactory appResFactory = application.getResourceFactory(); + final TransportFactory appSncpTranFactory = application.getSncpTransportFactory(); + final AnyValue resources = application.config.getAnyValue("resources"); + final String confURI = appResFactory.find(RESNAME_APP_CONF, String.class); + final Map> cacheResource = new HashMap<>(); + final Map> dataResources = new HashMap<>(); + if (resources != null) { + for (AnyValue sourceConf : resources.getAnyValues("source")) { + try { + String classval = sourceConf.getValue("value"); + Class type = null; + if (classval == null || classval.isEmpty()) { + RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class); + List providers = new ArrayList<>(); + Iterator it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator(); + while (it.hasNext()) { + CacheSourceProvider s = it.next(); + if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName()); + if (s != null && s.acceptsConf(sourceConf)) { + providers.add(s); + } + } + Collections.sort(providers, (a, b) -> { + Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class); + Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class); + return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); + }); + for (CacheSourceProvider provider : providers) { + type = provider.sourceClass(); + if (type != null) break; + } + } else { + type = serverClassLoader.loadClass(classval); + } + if (type == DataSource.class) { + type = DataMemorySource.class; + for (AnyValue itemConf : sourceConf.getAnyValues("property")) { + if (itemConf.getValue("name", "").contains(DataSources.JDBC_URL)) { + type = DataJdbcSource.class; + break; + } + } + } + if (!Service.class.isAssignableFrom(type)) { + logger.log(Level.SEVERE, "load application source resource, but not Service error: " + sourceConf); + } else if (CacheSource.class.isAssignableFrom(type)) { + cacheResource.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf)); + } else if (DataSource.class.isAssignableFrom(type)) { + dataResources.put(sourceConf.getValue("name", ""), new SimpleEntry(type, sourceConf)); + } else { + logger.log(Level.SEVERE, "load application source resource, but not CacheSource error: " + sourceConf); + } + } catch (Exception e) { + logger.log(Level.SEVERE, "load application source resource error: " + sourceConf, e); + } + } + } + //------------------------------------- 注册 Resource -------------------------------------------------------- + resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { + try { + Resource res = field.getAnnotation(Resource.class); + if (res == null || !res.name().startsWith("properties.")) return; + if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource + Class type = field.getType(); + if (type != AnyValue.class && type != AnyValue[].class) return; + Object resource = null; + final AnyValue properties = resources == null ? null : resources.getAnyValue("properties"); + if (properties != null && type == AnyValue.class) { + resource = properties.getAnyValue(res.name().substring("properties.".length())); + appResFactory.register(resourceName, AnyValue.class, resource); + } else if (properties != null && type == AnyValue[].class) { + resource = properties.getAnyValues(res.name().substring("properties.".length())); + appResFactory.register(resourceName, AnyValue[].class, resource); + } + field.set(src, resource); + } catch (Exception e) { + logger.log(Level.SEVERE, "Resource inject error", e); + } + }, AnyValue.class, AnyValue[].class); + + //------------------------------------- 注册 Local AutoLoad(false) Service -------------------------------------------------------- + resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { + Class resServiceType = Service.class; + try { + if (field.getAnnotation(Resource.class) == null) return; + if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 AutoLoad Service + if (!Service.class.isAssignableFrom(field.getType())) return; + resServiceType = (Class) field.getType(); + if (resServiceType.getAnnotation(Local.class) == null) return; + AutoLoad al = resServiceType.getAnnotation(AutoLoad.class); + if (al == null || al.value()) return; + + //ResourceFactory resfactory = (isSNCP() ? appResFactory : resourceFactory); + SncpClient client = src instanceof Service ? Sncp.getSncpClient((Service) src) : null; + final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); + final Set groups = new HashSet<>(); + Service service = Modifier.isFinal(resServiceType.getModifiers()) ? (Service) resServiceType.getConstructor().newInstance() : Sncp.createLocalService(serverClassLoader, resourceName, resServiceType, null, appResFactory, appSncpTranFactory, sncpAddr, groups, null); + appResFactory.register(resourceName, resServiceType, service); + + field.set(src, service); + rf.inject(service, self); // 给其可能包含@Resource的字段赋值; + if (!application.isCompileMode()) service.init(null); + logger.info("[" + Thread.currentThread().getName() + "] Load Service(@Local @AutoLoad service = " + resServiceType.getSimpleName() + ", resourceName = '" + resourceName + "')"); + } catch (Exception e) { + logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] Load @Local @AutoLoad(false) Service inject " + resServiceType + " to " + src + " error", e); + } + }, Service.class); + + //------------------------------------- 注册 DataSource -------------------------------------------------------- + resourceFactory.register((ResourceFactory rf, final Object src, String resourceName, Field field, final Object attachment) -> { + try { + if (field.getAnnotation(Resource.class) == null) return; + if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不得注入 DataSource + SimpleEntry resEntry = dataResources.get(resourceName); + AnyValue sourceConf = resEntry == null ? null : resEntry.getValue(); + DataSource source = null; + if (sourceConf != null) { + final Class sourceType = resEntry.getKey(); + if (sourceType == DataJdbcSource.class) { + source = DataSources.createDataSource(resourceName, sourceConf); + } else { + boolean can = false; + RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName()); + for (Constructor cr : sourceType.getConstructors()) { + if (cr.getParameterCount() == 0) { + can = true; + break; + } + } + if (DataSource.class.isAssignableFrom(sourceType) && can) { // 必须有空构造函数 + if (Modifier.isFinal(sourceType.getModifiers()) || sourceType.getAnnotation(Local.class) != null) { + source = (DataSource) sourceType.getConstructor().newInstance(); + RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName()); + } else { + final Service srcService = (Service) src; + SncpClient client = Sncp.getSncpClient(srcService); + final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); + final Set groups = new HashSet<>(); + source = (DataSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, groups, Sncp.getConf(srcService)); + } + } + } + } + if (source == null) { + source = DataSources.createDataSource(confURI, resourceName); //从persistence.xml配置中创建 + } + + RedkaleClassLoader.putReflectionPublicConstructors(source.getClass(), source.getClass().getName()); + application.dataSources.add(source); + ResourceType rt = source.getClass().getAnnotation(ResourceType.class); + if (rt != null && rt.value() != DataSource.class) { + appResFactory.register(resourceName, rt.value(), source); + } else if (source instanceof SearchSource) { + appResFactory.register(resourceName, SearchSource.class, source); + } + appResFactory.register(resourceName, DataSource.class, source); + + field.set(src, source); + rf.inject(source, self); // 给AsyncGroup和其他@Resource的字段赋值; + //NodeServer.this.watchFactory.inject(src); + if (!application.isCompileMode() && source instanceof Service) ((Service) source).init(sourceConf); + logger.info("[" + Thread.currentThread().getName() + "] Load DataSource (type = " + source.getClass().getSimpleName() + ", resourceName = '" + resourceName + "')"); + } catch (Exception e) { + logger.log(Level.SEVERE, "[" + Thread.currentThread().getName() + "] DataSource inject to " + src + " error", e); + } + }, DataSource.class); + + //------------------------------------- 注册 CacheSource -------------------------------------------------------- + resourceFactory.register(new ResourceFactory.ResourceLoader() { + @Override + public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) { + try { + if (field.getAnnotation(Resource.class) == null) return; + if (!(src instanceof Service)) throw new RuntimeException("CacheSource must be inject in Service, cannot " + src); + if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 CacheSource + final Service srcService = (Service) src; + SncpClient client = Sncp.getSncpClient(srcService); + final InetSocketAddress sncpAddr = client == null ? null : client.getClientAddress(); + SimpleEntry resEntry = cacheResource.get(resourceName); + AnyValue sourceConf = resEntry == null ? null : resEntry.getValue(); + if (sourceConf == null) { + SimpleEntry resEntry2 = dataResources.get(resourceName); + sourceConf = resEntry2 == null ? null : resEntry2.getValue(); + } + Class sourceType0 = CacheMemorySource.class; + if (sourceConf != null) { + String classval = sourceConf.getValue("value"); + if (classval == null || classval.isEmpty()) { + RedkaleClassLoader.putServiceLoader(CacheSourceProvider.class); + List providers = new ArrayList<>(); + Iterator it = ServiceLoader.load(CacheSourceProvider.class, serverClassLoader).iterator(); + while (it.hasNext()) { + CacheSourceProvider s = it.next(); + if (s != null) RedkaleClassLoader.putReflectionPublicConstructors(s.getClass(), s.getClass().getName()); + if (s != null && s.acceptsConf(sourceConf)) { + providers.add(s); + } + } + Collections.sort(providers, (a, b) -> { + Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class); + Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class); + return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); + }); + for (CacheSourceProvider provider : providers) { + sourceType0 = provider.sourceClass(); + if (sourceType0 != null) break; + } + } else { + sourceType0 = serverClassLoader.loadClass(classval); + } + } + final Class sourceType = sourceType0; + Object source = null; + if (CacheSource.class.isAssignableFrom(sourceType)) { // CacheSource + RedkaleClassLoader.putReflectionPublicConstructors(sourceType, sourceType.getName()); + source = sourceType == CacheMemorySource.class ? new CacheMemorySource(resourceName) + : (Modifier.isFinal(sourceType.getModifiers()) || sourceType.getAnnotation(Local.class) != null) ? sourceType.getConstructor().newInstance() + : (CacheSource) Sncp.createLocalService(serverClassLoader, resourceName, sourceType, client == null ? null : client.getMessageAgent(), appResFactory, appSncpTranFactory, sncpAddr, null, Sncp.getConf(srcService)); + Type genericType = field.getGenericType(); + application.cacheSources.add((CacheSource) source); + appResFactory.register(resourceName, CacheSource.class, source); + if (genericType != CacheSource.class) { + appResFactory.register(resourceName, genericType, source); + } + } + field.set(src, source); + rf.inject(source, self); // + if (!application.isCompileMode() && source instanceof Service) ((Service) source).init(sourceConf); + + if ((src instanceof WebSocketNodeService) && sncpAddr != null) { //只有WebSocketNodeService的服务才需要给SNCP服务注入CacheMemorySource + NodeSncpServer sncpServer = application.findNodeSncpServer(sncpAddr); + if (source != null && source.getClass().getAnnotation(Local.class) == null) { //本地模式的Service不生成SncpServlet + sncpServer.getSncpServer().addSncpServlet((Service) source); + } + //logger.info("[" + Thread.currentThread().getName() + "] Load Service " + source); + } + logger.info("[" + Thread.currentThread().getName() + "] Load CacheSource (type = " + source.getClass().getSimpleName() + ", resourceName = '" + resourceName + "')"); + } catch (Exception e) { + logger.log(Level.SEVERE, "DataSource inject error", e); + } + } + + @Override + public boolean autoNone() { + return false; + } + }, CacheSource.class); + + //------------------------------------- 注册 WebSocketNode -------------------------------------------------------- + resourceFactory.register(new ResourceFactory.ResourceLoader() { + @Override + public void load(ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) { + try { + if (field.getAnnotation(Resource.class) == null) return; + if ((src instanceof Service) && Sncp.isRemote((Service) src)) return; //远程模式不需要注入 WebSocketNode + Service nodeService = (Service) rf.find(resourceName, WebSocketNode.class); + if (nodeService == null) { + final HashSet groups = new HashSet<>(); + if (groups.isEmpty() && isSNCP() && NodeServer.this.sncpGroup != null) groups.add(NodeServer.this.sncpGroup); + nodeService = Sncp.createLocalService(serverClassLoader, resourceName, WebSocketNodeService.class, Sncp.getMessageAgent((Service) src), application.getResourceFactory(), application.getSncpTransportFactory(), NodeServer.this.sncpAddress, groups, (AnyValue) null); + (isSNCP() ? appResFactory : resourceFactory).register(resourceName, WebSocketNode.class, nodeService); + ((WebSocketNodeService) nodeService).setName(resourceName); + } + resourceFactory.inject(nodeService, self); + MessageAgent messageAgent = Sncp.getMessageAgent((Service) src); + if (messageAgent != null && Sncp.getMessageAgent(nodeService) == null) Sncp.setMessageAgent(nodeService, messageAgent); + field.set(src, nodeService); + if (Sncp.isRemote(nodeService)) { + remoteServices.add(nodeService); + } else { + rf.inject(nodeService); //动态加载的Service也存在按需加载的注入资源 + localServices.add(nodeService); + interceptorServices.add(nodeService); + if (consumer != null) consumer.accept(null, nodeService); + } + } catch (Exception e) { + logger.log(Level.SEVERE, "WebSocketNode inject error", e); + } + } + + @Override + public boolean autoNone() { + return false; + } + }, WebSocketNode.class); + } + + @SuppressWarnings("unchecked") + protected void loadService(ClassFilter serviceFilter, ClassFilter otherFilter) throws Exception { + if (serviceFilter == null) return; + final long starts = System.currentTimeMillis(); + final String localThreadName = "[" + Thread.currentThread().getName() + "] "; + final Set> entrys = (Set) serviceFilter.getAllFilterEntrys(); + ResourceFactory regFactory = isSNCP() ? application.getResourceFactory() : resourceFactory; + final ResourceFactory appResourceFactory = application.getResourceFactory(); + final TransportFactory appSncpTransFactory = application.getSncpTransportFactory(); + for (FilterEntry entry : entrys) { //service实现类 + final Class serviceImplClass = entry.getType(); + if (Modifier.isFinal(serviceImplClass.getModifiers())) continue; //修饰final的类跳过 + if (!Modifier.isPublic(serviceImplClass.getModifiers())) continue; + if (serviceImplClass.getAnnotation(SncpDyn.class) != null) continue; //动态生成的跳过 + if (entry.isExpect()) { + if (Modifier.isAbstract(serviceImplClass.getModifiers())) continue; //修饰abstract的类跳过 + if (DataSource.class.isAssignableFrom(serviceImplClass)) continue; + if (CacheSource.class.isAssignableFrom(serviceImplClass)) continue; + } + if (entry.getName().contains("$")) throw new RuntimeException(" value cannot contains '$' in " + entry.getProperty()); + Service oldother = resourceFactory.find(entry.getName(), serviceImplClass); + if (oldother != null) { //Server加载Service时需要判断是否已经加载过了。 + if (!Sncp.isRemote(oldother)) interceptorServices.add(oldother); + continue; + } + final HashSet groups = entry.getGroups(); //groups.isEmpty()表示没有配置groups属性。 + if (groups.isEmpty() && isSNCP() && this.sncpGroup != null) groups.add(this.sncpGroup); + + final boolean localed = (this.sncpAddress == null && entry.isEmptyGroups() && !serviceImplClass.isInterface() && !Modifier.isAbstract(serviceImplClass.getModifiers())) //非SNCP的Server,通常是单点服务 + || groups.contains(this.sncpGroup) //本地IP含在内的 + || (this.sncpGroup == null && entry.isEmptyGroups()) //空的SNCP配置 + || serviceImplClass.getAnnotation(Local.class) != null;//本地模式 + if (localed && (serviceImplClass.isInterface() || Modifier.isAbstract(serviceImplClass.getModifiers()))) continue; //本地模式不能实例化接口和抽象类的Service类 + final ResourceFactory.ResourceLoader resourceLoader = (ResourceFactory rf, final Object src, final String resourceName, Field field, final Object attachment) -> { + try { + if (SncpClient.parseMethod(serviceImplClass).isEmpty() && serviceImplClass.getAnnotation(Priority.class) == null) { //class没有可用的方法且没有标记启动优先级的, 通常为BaseService + if (!serviceImplClass.getName().startsWith("org.redkale.") && !serviceImplClass.getSimpleName().contains("Base")) { + logger.log(Level.FINE, serviceImplClass + " cannot load because not found less one public non-final method"); + } + return; + } + RedkaleClassLoader.putReflectionPublicMethods(serviceImplClass.getName()); + MessageAgent agent = null; + if (entry.getProperty() != null && entry.getProperty().getValue("mq") != null) { + agent = application.getMessageAgent(entry.getProperty().getValue("mq")); + if (agent != null) messageAgents.put(agent.getName(), agent); + } + + Service service; + final boolean ws = src instanceof WebSocketServlet; + if (ws || localed) { //本地模式 + service = Sncp.createLocalService(serverClassLoader, resourceName, serviceImplClass, agent, appResourceFactory, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); + } else { + service = Sncp.createRemoteService(serverClassLoader, resourceName, serviceImplClass, agent, appSncpTransFactory, NodeServer.this.sncpAddress, groups, entry.getProperty()); + } + if (service instanceof WebSocketNodeService) { + ((WebSocketNodeService) service).setName(resourceName); + if (agent != null) Sncp.setMessageAgent(service, agent); + } + final Class restype = Sncp.getResourceType(service); + if (rf.find(resourceName, restype) == null) { + regFactory.register(resourceName, restype, service); + } else if (isSNCP() && !entry.isAutoload()) { + throw new RuntimeException(restype.getSimpleName() + "(class:" + serviceImplClass.getName() + ", name:" + resourceName + ", group:" + groups + ") is repeat."); + } + if (Sncp.isRemote(service)) { + remoteServices.add(service); + if (agent != null) sncpRemoteAgents.put(agent.getName(), agent); + } else { + if (field != null) rf.inject(service); //动态加载的Service也存在按需加载的注入资源 + localServices.add(service); + interceptorServices.add(service); + if (consumer != null) consumer.accept(agent, service); + } + } catch (RuntimeException ex) { + throw ex; + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + if (entry.isExpect()) { + ResourceType rty = entry.getType().getAnnotation(ResourceType.class); + resourceFactory.register(resourceLoader, rty == null ? entry.getType() : rty.value()); + } else { + resourceLoader.load(resourceFactory, null, entry.getName(), null, false); + } + + } + + application.servicecdl.countDown(); + application.servicecdl.await(); + + final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; + //---------------- inject ---------------- + new ArrayList<>(localServices).forEach(y -> { + resourceFactory.inject(y, NodeServer.this); + }); + new ArrayList<>(remoteServices).forEach(y -> { + resourceFactory.inject(y, NodeServer.this); + calcMaxLength(y); + }); + + if (sb != null) { + remoteServices.forEach(y -> { + sb.append(localThreadName).append(Sncp.toSimpleString(y, maxNameLength, maxTypeLength)).append(" load and inject").append(LINE_SEPARATOR); + }); + } + if (isSNCP() && !sncpRemoteAgents.isEmpty()) { + sncpRemoteAgents.values().forEach(agent -> { + // agent.putSncpResp((NodeSncpServer) this); + // agent.startSncpRespConsumer(); + }); + } + //----------------- init ----------------- + List swlist = new ArrayList<>(localServices); + swlist.forEach(y -> calcMaxLength(y)); + swlist.sort((o1, o2) -> { + Priority p1 = o1.getClass().getAnnotation(Priority.class); + Priority p2 = o2.getClass().getAnnotation(Priority.class); + int v = (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); + if (v != 0) return v; + int rs = Sncp.getResourceType(o1).getName().compareTo(Sncp.getResourceType(o2).getName()); + if (rs == 0) rs = Sncp.getResourceName(o1).compareTo(Sncp.getResourceName(o2)); + return rs; + }); + localServices.clear(); + localServices.addAll(swlist); + //this.loadPersistData(); + long preinits = System.currentTimeMillis(); + preInitServices(localServices, remoteServices); + long preinite = System.currentTimeMillis() - preinits; + final List slist = sb == null ? null : new CopyOnWriteArrayList<>(); + if (application.isCompileMode()) { + localServices.stream().forEach(y -> { + String serstr = Sncp.toSimpleString(y, maxNameLength, maxTypeLength); + if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load").append(LINE_SEPARATOR).toString()); + }); + } else { + localServices.stream().forEach(y -> { + long s = System.currentTimeMillis(); + y.init(Sncp.getConf(y)); + long e = System.currentTimeMillis() - s; + String serstr = Sncp.toSimpleString(y, maxNameLength, maxTypeLength); + if (slist != null) slist.add(new StringBuilder().append(localThreadName).append(serstr).append(" load and init in ").append(e < 10 ? " " : (e < 100 ? " " : "")).append(e).append(" ms").append(LINE_SEPARATOR).toString()); + }); + } + if (slist != null && sb != null) { + List wlist = new ArrayList<>(slist); //直接使用CopyOnWriteArrayList偶尔会出现莫名的异常(CopyOnWriteArrayList源码1185行) + for (String s : wlist) { + sb.append(s); + } + sb.append(localThreadName).append("All Services load cost ").append(System.currentTimeMillis() - starts).append(" ms" + LINE_SEPARATOR); + } + if (sb != null && preinite > 10) sb.append(localThreadName).append(ClusterAgent.class.getSimpleName()).append(" register ").append(preinite).append(" ms" + LINE_SEPARATOR); + if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); + } + + private void calcMaxLength(Service y) { //计算toString中的长度 + maxNameLength = Math.max(maxNameLength, Sncp.getResourceName(y).length()); + maxTypeLength = Math.max(maxTypeLength, Sncp.getResourceType(y).getName().length() + 1); + } + + //Service.init执行之前调用 + protected void preInitServices(Set localServices, Set remoteServices) { + final ClusterAgent cluster = application.clusterAgent; + if (!application.isCompileMode() && cluster != null) { + NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class); + String protocol = pros.value().toUpperCase(); + if (!cluster.containsProtocol(protocol)) return; + if (!cluster.containsPort(server.getSocketAddress().getPort())) return; + cluster.register(this, protocol, localServices, remoteServices); + } + } + + //loadServlet执行之后调用 + protected void postLoadServlets() { + + } + + //Service.destroy执行之前调用 + protected void preDestroyServices(Set localServices, Set remoteServices) { + if (!application.isCompileMode() && application.clusterAgent != null) { //服务注销 + final ClusterAgent cluster = application.clusterAgent; + NodeProtocol pros = getClass().getAnnotation(NodeProtocol.class); + String protocol = pros.value().toUpperCase(); + if (cluster.containsProtocol(protocol) && cluster.containsPort(server.getSocketAddress().getPort())) { + cluster.deregister(this, protocol, localServices, remoteServices); + afterClusterDeregisterOnPreDestroyServices(cluster, protocol); + } + } + if (!application.isCompileMode() && !this.messageAgents.isEmpty()) { //MQ + } + } + + protected void afterClusterDeregisterOnPreDestroyServices(ClusterAgent cluster, String protocol) { + } + + //Server.start执行之后调用 + protected void postStartServer(Set localServices, Set remoteServices) { + } + + protected abstract ClassFilter createFilterClassFilter(); + + protected abstract ClassFilter createServletClassFilter(); + + protected ClassFilter createOtherClassFilter() { + return null; + } + + protected ClassFilter createServiceClassFilter() { + return createClassFilter(this.sncpGroup, null, Service.class, (!isSNCP() && application.watching) ? null : new Class[]{org.redkale.watch.WatchService.class}, Annotation.class, "services", "service"); + } + + protected ClassFilter createClassFilter(final String localGroup, Class ref, + Class inter, Class[] excludeSuperClasses, Class ref2, String properties, String property) { + ClassFilter cf = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, null); + if (properties == null && properties == null) { + cf.setRefused(true); + return cf; + } + if (this.serverConf == null) { + cf.setRefused(true); + return cf; + } + AnyValue[] proplist = this.serverConf.getAnyValues(properties); + if (proplist == null || proplist.length < 1) { + cf.setRefused(true); + return cf; + } + cf = null; + for (AnyValue list : proplist) { + AnyValue.DefaultAnyValue prop = null; + String sc = list.getValue("groups"); + String mq = list.getValue("mq"); + if (sc != null) { + sc = sc.trim(); + if (sc.endsWith(";")) sc = sc.substring(0, sc.length() - 1); + } + if (sc == null) sc = localGroup; + if (sc != null || mq != null) { + prop = new AnyValue.DefaultAnyValue(); + if (sc != null) prop.addValue("groups", sc); + if (mq != null) prop.addValue("mq", mq); + } + ClassFilter filter = new ClassFilter(this.serverClassLoader, ref, inter, excludeSuperClasses, prop); + for (AnyValue av : list.getAnyValues(property)) { // 节点 + final AnyValue[] items = av.getAnyValues("property"); + if (av instanceof AnyValue.DefaultAnyValue && items.length > 0) { //存在 节点 + AnyValue.DefaultAnyValue dav = AnyValue.DefaultAnyValue.create(); + final AnyValue.Entry[] strings = av.getStringEntrys(); + if (strings != null) { //将节点的属性值传给dav + for (AnyValue.Entry en : strings) { + dav.addValue(en.name, en.getValue()); + } + } + final AnyValue.Entry[] anys = av.getAnyEntrys(); + if (anys != null) { + for (AnyValue.Entry en : anys) { //将节点的非property属性节点传给dav + if (!"property".equals(en.name)) dav.addValue(en.name, en.getValue()); + } + } + AnyValue.DefaultAnyValue ps = AnyValue.DefaultAnyValue.create(); + for (AnyValue item : items) { + ps.addValue(item.getValue("name"), item.getValue("value")); + } + dav.addValue("properties", ps); + av = dav; + } + if (!av.getBoolValue("ignore", false)) { + filter.filter(av, av.getValue("value"), false); + } + } + if (list.getBoolValue("autoload", true)) { + String includes = list.getValue("includes", ""); + String excludes = list.getValue("excludes", ""); + filter.setIncludePatterns(includes.split(";")); + filter.setExcludePatterns(excludes.split(";")); + } else if (ref2 == null || ref2 == Annotation.class) { //service如果是autoload=false则不需要加载 + filter.setRefused(true); + } else if (ref2 != Annotation.class) { + filter.setAnnotationClass(ref2); + } + cf = (cf == null) ? filter : cf.or(filter); + } + return cf; + } + + public abstract InetSocketAddress getSocketAddress(); + + public boolean isSNCP() { + return false; + } + + public boolean isWATCH() { + return false; + } + + public Application getApplication() { + return application; + } + + public ResourceFactory getResourceFactory() { + return resourceFactory; + } + + public RedkaleClassLoader getServerClassLoader() { + return serverClassLoader; + } + + public void setServerClassLoader(RedkaleClassLoader serverClassLoader) { + Objects.requireNonNull(this.serverClassLoader); + this.serverClassLoader = serverClassLoader; + this.serverThread.setContextClassLoader(serverClassLoader); + } + + public InetSocketAddress getSncpAddress() { + return sncpAddress; + } + + public AnyValue getServerConf() { + return serverConf; + } + + public Logger getLogger() { + return logger; + } + + public String getSncpGroup() { + return sncpGroup; + } + + public void start() throws IOException { + if (interceptor != null) interceptor.preStart(this); + server.start(); + postStartServer(localServices, remoteServices); + } + + public void shutdown() throws IOException { + if (interceptor != null) interceptor.preShutdown(this); + final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; + final boolean finest = logger.isLoggable(Level.FINEST); + preDestroyServices(localServices, remoteServices); + localServices.forEach(y -> { + long s = System.currentTimeMillis(); + if (finest) logger.finest(y + " is destroying"); + y.destroy(Sncp.getConf(y)); + if (finest) logger.finest(y + " was destroyed"); + long e = System.currentTimeMillis() - s; + if (e > 2 && sb != null) { + sb.append(Sncp.toSimpleString(y, maxNameLength, maxTypeLength)).append(" destroy ").append(e).append("ms").append(LINE_SEPARATOR); + } + }); + if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); + server.shutdown(); + } + + public void command(String cmd) throws IOException { + final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; + final boolean finest = logger.isLoggable(Level.FINEST); + localServices.forEach(y -> { + Set methods = new HashSet<>(); + Class loop = y.getClass(); + //do { public方法不用递归 + for (Method m : loop.getMethods()) { + Command c = m.getAnnotation(Command.class); + if (c == null) continue; + if (Modifier.isStatic(m.getModifiers())) continue; + if (m.getReturnType() != void.class) continue; + if (m.getParameterCount() != 1) continue; + if (m.getParameterTypes()[0] != String.class) continue; + methods.add(m); + } + //} while ((loop = loop.getSuperclass()) != Object.class); + if (methods.isEmpty()) return; + long s = System.currentTimeMillis(); + Method one = null; + try { + for (Method method : methods) { + one = method; + method.invoke(y, cmd); + } + } catch (Exception ex) { + logger.log(Level.SEVERE, one + " run error, cmd = " + cmd, ex); + } + long e = System.currentTimeMillis() - s; + if (e > 10 && sb != null) { + sb.append(Sncp.toSimpleString(y, maxNameLength, maxTypeLength)).append(" command (").append(cmd).append(") ").append(e).append("ms").append(LINE_SEPARATOR); + } + }); + if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); + } + + public T getServer() { + return (T) server; + } + + public Set getInterceptorServices() { + return new LinkedHashSet<>(interceptorServices); + } + + public Set getLocalServices() { + return new LinkedHashSet<>(localServices); + } + + public Set getRemoteServices() { + return new LinkedHashSet<>(remoteServices); + } + + public String getThreadName() { + return this.threadName; + } +} diff --git a/src/org/redkale/boot/NodeSncpServer.java b/src/main/java/org/redkale/boot/NodeSncpServer.java similarity index 91% rename from src/org/redkale/boot/NodeSncpServer.java rename to src/main/java/org/redkale/boot/NodeSncpServer.java index c3caa4254..ef2e1e173 100644 --- a/src/org/redkale/boot/NodeSncpServer.java +++ b/src/main/java/org/redkale/boot/NodeSncpServer.java @@ -1,122 +1,125 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import java.lang.reflect.Modifier; -import java.net.*; -import java.util.*; -import java.util.logging.Level; -import org.redkale.boot.ClassFilter.FilterEntry; -import org.redkale.mq.MessageAgent; -import org.redkale.net.*; -import org.redkale.net.sncp.*; -import org.redkale.service.*; -import org.redkale.util.*; -import org.redkale.util.AnyValue.DefaultAnyValue; - -/** - * SNCP Server节点的配置Server - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@NodeProtocol("SNCP") -public class NodeSncpServer extends NodeServer { - - protected final SncpServer sncpServer; - - private NodeSncpServer(Application application, AnyValue serconf) { - super(application, createServer(application, serconf)); - this.sncpServer = (SncpServer) this.server; - this.consumer = sncpServer == null || application.singletonrun ? null : (agent, x) -> {//singleton模式下不生成SncpServlet - if (x.getClass().getAnnotation(Local.class) != null) return; - SncpDynServlet servlet = sncpServer.addSncpServlet(x); - dynServletMap.put(x, servlet); - if (agent != null) agent.putService(this, x, servlet); - }; - } - - public static NodeServer createNodeServer(Application application, AnyValue serconf) { - return new NodeSncpServer(application, serconf); - } - - private static Server createServer(Application application, AnyValue serconf) { - return new SncpServer(application, application.getStartTime(), serconf, application.getResourceFactory().createChild()); - } - - @Override - public InetSocketAddress getSocketAddress() { - return sncpServer == null ? null : sncpServer.getSocketAddress(); - } - - public void consumerAccept(MessageAgent messageAgent, Service service) { - if (this.consumer != null) this.consumer.accept(messageAgent, service); - } - - @Override - public void init(AnyValue config) throws Exception { - super.init(config); - //------------------------------------------------------------------- - if (sncpServer == null) return; //调试时server才可能为null - final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null; - final String localThreadName = "[" + Thread.currentThread().getName() + "] "; - List servlets = sncpServer.getSncpServlets(); - Collections.sort(servlets); - for (SncpServlet en : servlets) { - if (sb != null) sb.append(localThreadName).append(" Load ").append(en).append(LINE_SEPARATOR); - } - if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString()); - } - - @Override - public boolean isSNCP() { - return true; - } - - public SncpServer getSncpServer() { - return sncpServer; - } - - @Override - protected void loadFilter(ClassFilter filterFilter, ClassFilter otherFilter) throws Exception { - if (sncpServer != null) loadSncpFilter(this.serverConf.getAnyValue("fliters"), filterFilter); - } - - @SuppressWarnings("unchecked") - protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter classFilter) throws Exception { - final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; - final String localThreadName = "[" + Thread.currentThread().getName() + "] "; - List> list = new ArrayList(classFilter.getFilterEntrys()); - for (FilterEntry en : list) { - Class clazz = (Class) en.getType(); - if (Modifier.isAbstract(clazz.getModifiers())) continue; - final SncpFilter filter = clazz.getDeclaredConstructor().newInstance(); - resourceFactory.inject(filter, this); - DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); - this.sncpServer.addSncpFilter(filter, filterConf); - if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR); - } - if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); - } - - @Override - protected void loadServlet(ClassFilter servletFilter, ClassFilter otherFilter) throws Exception { - } - - @Override - @SuppressWarnings("unchecked") - protected ClassFilter createFilterClassFilter() { - return createClassFilter(null, null, SncpFilter.class, new Class[]{org.redkale.watch.WatchFilter.class}, null, "filters", "filter"); - } - - @Override - protected ClassFilter createServletClassFilter() { - return null; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import java.lang.reflect.Modifier; +import java.net.*; +import java.util.*; +import java.util.logging.Level; +import org.redkale.boot.ClassFilter.FilterEntry; +import org.redkale.mq.MessageAgent; +import org.redkale.net.*; +import org.redkale.net.sncp.*; +import org.redkale.service.*; +import org.redkale.util.*; +import org.redkale.util.AnyValue.DefaultAnyValue; + +/** + * SNCP Server节点的配置Server + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@NodeProtocol("SNCP") +public class NodeSncpServer extends NodeServer { + + protected final SncpServer sncpServer; + + private NodeSncpServer(Application application, AnyValue serconf) { + super(application, createServer(application, serconf)); + this.sncpServer = (SncpServer) this.server; + this.consumer = sncpServer == null || application.isSingletonMode() ? null : (agent, x) -> {//singleton模式下不生成SncpServlet + if (x.getClass().getAnnotation(Local.class) != null) return; //本地模式的Service不生成SncpServlet + SncpDynServlet servlet = sncpServer.addSncpServlet(x); + dynServletMap.put(x, servlet); + if (agent != null) agent.putService(this, x, servlet); + }; + } + + public static NodeServer createNodeServer(Application application, AnyValue serconf) { + return new NodeSncpServer(application, serconf); + } + + private static Server createServer(Application application, AnyValue serconf) { + return new SncpServer(application, application.getStartTime(), serconf, application.getResourceFactory().createChild()); + } + + @Override + public InetSocketAddress getSocketAddress() { + return sncpServer == null ? null : sncpServer.getSocketAddress(); + } + + public void consumerAccept(MessageAgent messageAgent, Service service) { + if (this.consumer != null) this.consumer.accept(messageAgent, service); + } + + @Override + public void init(AnyValue config) throws Exception { + super.init(config); + //------------------------------------------------------------------- + if (sncpServer == null) return; //调试时server才可能为null + final StringBuilder sb = logger.isLoggable(Level.FINE) ? new StringBuilder() : null; + final String localThreadName = "[" + Thread.currentThread().getName() + "] "; + List servlets = sncpServer.getSncpServlets(); + Collections.sort(servlets); + for (SncpServlet en : servlets) { + if (sb != null) sb.append(localThreadName).append(" Load ").append(en).append(LINE_SEPARATOR); + } + if (sb != null && sb.length() > 0) logger.log(Level.FINE, sb.toString()); + } + + @Override + public boolean isSNCP() { + return true; + } + + public SncpServer getSncpServer() { + return sncpServer; + } + + @Override + protected void loadFilter(ClassFilter filterFilter, ClassFilter otherFilter) throws Exception { + if (sncpServer != null) loadSncpFilter(this.serverConf.getAnyValue("fliters"), filterFilter); + } + + @SuppressWarnings("unchecked") + protected void loadSncpFilter(final AnyValue servletsConf, final ClassFilter classFilter) throws Exception { + final StringBuilder sb = logger.isLoggable(Level.INFO) ? new StringBuilder() : null; + final String localThreadName = "[" + Thread.currentThread().getName() + "] "; + List> list = new ArrayList(classFilter.getFilterEntrys()); + for (FilterEntry en : list) { + Class clazz = (Class) en.getType(); + if (Modifier.isAbstract(clazz.getModifiers())) continue; + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName()); + final SncpFilter filter = clazz.getDeclaredConstructor().newInstance(); + resourceFactory.inject(filter, this); + DefaultAnyValue filterConf = (DefaultAnyValue) en.getProperty(); + this.sncpServer.addSncpFilter(filter, filterConf); + if (sb != null) sb.append(localThreadName).append(" Load ").append(clazz.getName()).append(LINE_SEPARATOR); + } + if (sb != null && sb.length() > 0) logger.log(Level.INFO, sb.toString()); + } + + @Override + protected void loadServlet(ClassFilter servletFilter, ClassFilter otherFilter) throws Exception { + RedkaleClassLoader.putReflectionPublicClasses(SncpServlet.class.getName()); + RedkaleClassLoader.putReflectionPublicClasses(SncpDynServlet.class.getName()); + } + + @Override + @SuppressWarnings("unchecked") + protected ClassFilter createFilterClassFilter() { + return createClassFilter(null, null, SncpFilter.class, new Class[]{org.redkale.watch.WatchFilter.class}, null, "filters", "filter"); + } + + @Override + protected ClassFilter createServletClassFilter() { + return null; + } + +} diff --git a/src/org/redkale/boot/NodeWatchServer.java b/src/main/java/org/redkale/boot/NodeWatchServer.java similarity index 96% rename from src/org/redkale/boot/NodeWatchServer.java rename to src/main/java/org/redkale/boot/NodeWatchServer.java index b7ea1f695..67760a416 100644 --- a/src/org/redkale/boot/NodeWatchServer.java +++ b/src/main/java/org/redkale/boot/NodeWatchServer.java @@ -1,53 +1,53 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import java.lang.annotation.Annotation; -import org.redkale.net.*; -import org.redkale.net.http.*; -import org.redkale.service.Service; -import org.redkale.util.AnyValue; -import org.redkale.watch.*; - -/** - * - * @author zhangjx - */ -@NodeProtocol("WATCH") -public class NodeWatchServer extends NodeHttpServer { - - public NodeWatchServer(Application application, AnyValue serconf) { - super(application, serconf); - } - - @Override - @SuppressWarnings("unchecked") - protected ClassFilter createServiceClassFilter() { - return createClassFilter(this.sncpGroup, null, WatchService.class, null, Annotation.class, "services", "service"); - } - - @Override - @SuppressWarnings("unchecked") - protected ClassFilter createFilterClassFilter() { - return createClassFilter(null, null, WatchFilter.class, null, null, "filters", "filter"); - } - - @Override - @SuppressWarnings("unchecked") - protected ClassFilter createServletClassFilter() { - return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet"); - } - - @Override - protected ClassFilter createOtherClassFilter() { - return null; - } - - @Override - public boolean isWATCH() { - return true; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import java.lang.annotation.Annotation; +import org.redkale.net.*; +import org.redkale.net.http.*; +import org.redkale.service.Service; +import org.redkale.util.AnyValue; +import org.redkale.watch.*; + +/** + * + * @author zhangjx + */ +@NodeProtocol("WATCH") +public class NodeWatchServer extends NodeHttpServer { + + public NodeWatchServer(Application application, AnyValue serconf) { + super(application, serconf); + } + + @Override + @SuppressWarnings("unchecked") + protected ClassFilter createServiceClassFilter() { + return createClassFilter(this.sncpGroup, null, WatchService.class, null, Annotation.class, "services", "service"); + } + + @Override + @SuppressWarnings("unchecked") + protected ClassFilter createFilterClassFilter() { + return createClassFilter(null, null, WatchFilter.class, null, null, "filters", "filter"); + } + + @Override + @SuppressWarnings("unchecked") + protected ClassFilter createServletClassFilter() { + return createClassFilter(null, WebServlet.class, WatchServlet.class, null, null, "servlets", "servlet"); + } + + @Override + protected ClassFilter createOtherClassFilter() { + return null; + } + + @Override + public boolean isWATCH() { + return true; + } +} diff --git a/src/main/java/org/redkale/boot/PrepareCompiler.java b/src/main/java/org/redkale/boot/PrepareCompiler.java new file mode 100644 index 000000000..ed91febcc --- /dev/null +++ b/src/main/java/org/redkale/boot/PrepareCompiler.java @@ -0,0 +1,86 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot; + +import java.lang.reflect.Modifier; +import javax.persistence.Entity; +import org.redkale.boot.ClassFilter.FilterEntry; +import org.redkale.convert.Decodeable; +import org.redkale.convert.bson.BsonFactory; +import org.redkale.convert.json.*; +import org.redkale.source.*; +import org.redkale.util.*; + +/** + * 执行一次Application.run提前获取所有动态类 + * + * @author zhangjx + * @since 2.5.0 + */ +public class PrepareCompiler { + +// public static void main(String[] args) throws Exception { +// new PrepareCompiler().run(); +// } + public Application run() throws Exception { + final Application application = new Application(false, true, Application.loadAppConfig()); + application.init(); + for (ApplicationListener listener : application.listeners) { + listener.preStart(application); + } + for (ApplicationListener listener : application.listeners) { + listener.preCompile(application); + } + application.start(); + final boolean hasSncp = application.getNodeServers().stream().filter(v -> v instanceof NodeSncpServer).findFirst().isPresent(); + final String[] exlibs = (application.excludelibs != null ? (application.excludelibs + ";") : "").split(";"); + + final ClassFilter entityFilter = new ClassFilter(application.getClassLoader(), Entity.class, Object.class, (Class[]) null); + final ClassFilter beanFilter = new ClassFilter(application.getClassLoader(), Bean.class, Object.class, (Class[]) null); + final ClassFilter filterFilter = new ClassFilter(application.getClassLoader(), null, FilterBean.class, (Class[]) null); + + ClassFilter.Loader.load(application.getHome(), application.getClassLoader(), exlibs, entityFilter, beanFilter, filterFilter); + + for (FilterEntry en : entityFilter.getFilterEntrys()) { + Class clz = en.getType(); + if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue; + try { + application.dataSources.forEach(source -> source.compile(clz)); + JsonFactory.root().loadEncoder(clz); + if (hasSncp) BsonFactory.root().loadEncoder(clz); + Decodeable decoder = JsonFactory.root().loadDecoder(clz); + if (hasSncp) BsonFactory.root().loadDecoder(clz); + decoder.convertFrom(new JsonReader("{}")); + } catch (Exception e) { //JsonFactory.loadDecoder可能会失败,因为class可能包含抽象类字段,如ColumnValue.value字段 + } + } + for (FilterEntry en : beanFilter.getFilterEntrys()) { + Class clz = en.getType(); + if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue; + try { + JsonFactory.root().loadEncoder(clz); + if (hasSncp) BsonFactory.root().loadEncoder(clz); + Decodeable decoder = JsonFactory.root().loadDecoder(clz); + if (hasSncp) BsonFactory.root().loadDecoder(clz); + decoder.convertFrom(new JsonReader("{}")); + } catch (Exception e) { //JsonFactory.loadDecoder可能会失败,因为class可能包含抽象类字段,如ColumnValue.value字段 + } + } + for (FilterEntry en : filterFilter.getFilterEntrys()) { + Class clz = en.getType(); + if (clz.isInterface() || Modifier.isAbstract(clz.getModifiers())) continue; + try { + FilterNodeBean.load(clz); + } catch (Exception e) { + } + } + for (ApplicationListener listener : application.listeners) { + listener.postCompile(application); + } + application.shutdown(); + return application; + } +} diff --git a/src/org/redkale/boot/apidoc-template.html b/src/main/java/org/redkale/boot/apidoc-template.html similarity index 95% rename from src/org/redkale/boot/apidoc-template.html rename to src/main/java/org/redkale/boot/apidoc-template.html index b6df0b565..fa0c5cdc5 100644 --- a/src/org/redkale/boot/apidoc-template.html +++ b/src/main/java/org/redkale/boot/apidoc-template.html @@ -1,110 +1,110 @@ - - - - 接口文档(apidoc生成) - - - - - - - - - + + + + 接口文档(apidoc生成) + + + + + + + + + diff --git a/src/org/redkale/boot/package-info.java b/src/main/java/org/redkale/boot/package-info.java similarity index 96% rename from src/org/redkale/boot/package-info.java rename to src/main/java/org/redkale/boot/package-info.java index 2b9eae06d..4e9f60058 100644 --- a/src/org/redkale/boot/package-info.java +++ b/src/main/java/org/redkale/boot/package-info.java @@ -1,4 +1,4 @@ -/** - * 提供Redkale服务器的启动、初始化和加载功能 - */ -package org.redkale.boot; +/** + * 提供Redkale服务器的启动、初始化和加载功能 + */ +package org.redkale.boot; diff --git a/src/org/redkale/boot/watch/AbstractWatchService.java b/src/main/java/org/redkale/boot/watch/AbstractWatchService.java similarity index 96% rename from src/org/redkale/boot/watch/AbstractWatchService.java rename to src/main/java/org/redkale/boot/watch/AbstractWatchService.java index 29d385c2e..673d7d781 100644 --- a/src/org/redkale/boot/watch/AbstractWatchService.java +++ b/src/main/java/org/redkale/boot/watch/AbstractWatchService.java @@ -1,23 +1,23 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot.watch; - -import org.redkale.service.AbstractService; -import org.redkale.util.Comment; -import org.redkale.watch.WatchService; - -/** - * - * @author zhangjx - */ -public abstract class AbstractWatchService extends AbstractService implements WatchService { - - @Comment("缺少参数") - public static final int RET_WATCH_PARAMS_ILLEGAL = 1600_0001; - - @Comment("执行异常") - public static final int RET_WATCH_RUN_EXCEPTION = 1600_0002; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot.watch; + +import org.redkale.service.AbstractService; +import org.redkale.util.Comment; +import org.redkale.watch.WatchService; + +/** + * + * @author zhangjx + */ +public abstract class AbstractWatchService extends AbstractService implements WatchService { + + @Comment("缺少参数") + public static final int RET_WATCH_PARAMS_ILLEGAL = 1600_0001; + + @Comment("执行异常") + public static final int RET_WATCH_RUN_EXCEPTION = 1600_0002; +} diff --git a/src/org/redkale/boot/watch/FilterWatchService.java b/src/main/java/org/redkale/boot/watch/FilterWatchService.java similarity index 97% rename from src/org/redkale/boot/watch/FilterWatchService.java rename to src/main/java/org/redkale/boot/watch/FilterWatchService.java index 039edbef2..e5c24137f 100644 --- a/src/org/redkale/boot/watch/FilterWatchService.java +++ b/src/main/java/org/redkale/boot/watch/FilterWatchService.java @@ -1,80 +1,80 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot.watch; - -import java.io.IOException; -import java.util.List; -import javax.annotation.Resource; -import org.redkale.boot.*; -import org.redkale.net.http.*; -import org.redkale.service.RetResult; -import org.redkale.util.Comment; - -/** - * - * @author zhangjx - */ -@RestService(name = "filter", catalog = "watch", repair = false) -public class FilterWatchService extends AbstractWatchService { - - @Comment("Filter类名不存在") - public static final int RET_FILTER_TYPE_NOT_EXISTS = 1601_0002; - - @Comment("Filter类名不合法") - public static final int RET_FILTER_TYPE_ILLEGAL = 1601_0003; - - @Comment("Filter类名已存在") - public static final int RET_FILTER_EXISTS = 1601_0004; - - @Comment("Filter的JAR包不存在") - public static final int RET_FILTER_JAR_ILLEGAL = 1601_0005; - - @Resource - protected Application application; - - @RestMapping(name = "addFilter", auth = false, comment = "动态增加Filter") - public RetResult addFilter(@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar, - @RestParam(name = "server", comment = "Server节点名") final String serverName, - @RestParam(name = "type", comment = "Filter类名") final String filterType) throws IOException { - if (filterType == null) return new RetResult(RET_FILTER_TYPE_NOT_EXISTS, "Not found Filter Type (" + filterType + ")"); - if (jar == null) return new RetResult(RET_FILTER_JAR_ILLEGAL, "Not found jar file"); - List nodes = application.getNodeServers(); - for (NodeServer node : nodes) { - if (node.getServer().containsFilter(filterType)) return new RetResult(RET_FILTER_EXISTS, "Filter(" + filterType + ") exists"); - } - return RetResult.success(); - } - - @RestMapping(name = "test1", auth = false, comment = "预留") - public RetResult test1() { - return RetResult.success(); - } - - @RestMapping(name = "test2", auth = false, comment = "预留") - public RetResult test2() { - return RetResult.success(); - } - - @RestMapping(name = "test3", auth = false, comment = "预留") - public RetResult test3() { - return RetResult.success(); - } - - @RestMapping(name = "test4", auth = false, comment = "预留") - public RetResult test4() { - return RetResult.success(); - } - - @RestMapping(name = "test5", auth = false, comment = "预留") - public RetResult test5() { - return RetResult.success(); - } - - @RestMapping(name = "test6", auth = false, comment = "预留") - public RetResult test6() { - return RetResult.success(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot.watch; + +import java.io.IOException; +import java.util.List; +import javax.annotation.Resource; +import org.redkale.boot.*; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import org.redkale.util.Comment; + +/** + * + * @author zhangjx + */ +@RestService(name = "filter", catalog = "watch", repair = false) +public class FilterWatchService extends AbstractWatchService { + + @Comment("Filter类名不存在") + public static final int RET_FILTER_TYPE_NOT_EXISTS = 1601_0002; + + @Comment("Filter类名不合法") + public static final int RET_FILTER_TYPE_ILLEGAL = 1601_0003; + + @Comment("Filter类名已存在") + public static final int RET_FILTER_EXISTS = 1601_0004; + + @Comment("Filter的JAR包不存在") + public static final int RET_FILTER_JAR_ILLEGAL = 1601_0005; + + @Resource + protected Application application; + + @RestMapping(name = "addFilter", auth = false, comment = "动态增加Filter") + public RetResult addFilter(@RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar, + @RestParam(name = "server", comment = "Server节点名") final String serverName, + @RestParam(name = "type", comment = "Filter类名") final String filterType) throws IOException { + if (filterType == null) return new RetResult(RET_FILTER_TYPE_NOT_EXISTS, "Not found Filter Type (" + filterType + ")"); + if (jar == null) return new RetResult(RET_FILTER_JAR_ILLEGAL, "Not found jar file"); + List nodes = application.getNodeServers(); + for (NodeServer node : nodes) { + if (node.getServer().containsFilter(filterType)) return new RetResult(RET_FILTER_EXISTS, "Filter(" + filterType + ") exists"); + } + return RetResult.success(); + } + + @RestMapping(name = "test1", auth = false, comment = "预留") + public RetResult test1() { + return RetResult.success(); + } + + @RestMapping(name = "test2", auth = false, comment = "预留") + public RetResult test2() { + return RetResult.success(); + } + + @RestMapping(name = "test3", auth = false, comment = "预留") + public RetResult test3() { + return RetResult.success(); + } + + @RestMapping(name = "test4", auth = false, comment = "预留") + public RetResult test4() { + return RetResult.success(); + } + + @RestMapping(name = "test5", auth = false, comment = "预留") + public RetResult test5() { + return RetResult.success(); + } + + @RestMapping(name = "test6", auth = false, comment = "预留") + public RetResult test6() { + return RetResult.success(); + } +} diff --git a/src/org/redkale/boot/watch/ServerWatchService.java b/src/main/java/org/redkale/boot/watch/ServerWatchService.java similarity index 97% rename from src/org/redkale/boot/watch/ServerWatchService.java rename to src/main/java/org/redkale/boot/watch/ServerWatchService.java index 80e7476c9..1e2c2cf1e 100644 --- a/src/org/redkale/boot/watch/ServerWatchService.java +++ b/src/main/java/org/redkale/boot/watch/ServerWatchService.java @@ -1,104 +1,104 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot.watch; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.util.*; -import java.util.stream.Stream; -import javax.annotation.Resource; -import org.redkale.boot.*; -import org.redkale.net.Server; -import org.redkale.net.http.*; -import org.redkale.service.RetResult; -import org.redkale.util.Comment; - -/** - * - * @author zhangjx - */ -@RestService(name = "server", catalog = "watch", repair = false) -public class ServerWatchService extends AbstractWatchService { - - @Comment("不存在的Server节点") - public static final int RET_SERVER_NOT_EXISTS = 1602_0001; - - @Comment("更改Server监听地址端口失败") - public static final int RET_SERVER_CHANGEPORT_ERROR = 1602_0002; - - @Resource - protected Application application; - - @RestMapping(name = "info", comment = "单个Server信息查询") - public RetResult info(@RestParam(name = "#port:") final int port) { - Stream stream = application.getNodeServers().stream(); - NodeServer node = stream.filter(ns -> ns.getServer().getSocketAddress().getPort() == port).findFirst().orElse(null); - if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + port + ") not found"); - return new RetResult(formatToMap(node)); - } - - @RestMapping(name = "infos", comment = "Server信息查询") - public RetResult infos() { - Map rs = new LinkedHashMap<>(); - for (NodeServer ns : application.getNodeServers()) { - Server server = ns.getServer(); - rs.put("" + server.getSocketAddress().getPort(), formatToMap(ns)); - } - return new RetResult(rs); - } - - @RestMapping(name = "changeAddress", comment = "更改Server的监听地址和端口") - public RetResult changeAddress(@RestParam(name = "#port:") final int oldport, - @RestParam(name = "#newhost:") final String newhost, @RestParam(name = "#newport:") final int newport) { - if (oldport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `oldport`"); - if (newport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `newport`"); - Stream stream = application.getNodeServers().stream(); - NodeServer node = stream.filter(ns -> ns.getServer().getSocketAddress().getPort() == oldport).findFirst().orElse(null); - if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + oldport + ") not found"); - final Server server = node.getServer(); - InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport); - try { - server.changeAddress(application, newAddr); - } catch (IOException e) { - e.printStackTrace(); - return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error"); - } - return RetResult.success(); - } - - private Map formatToMap(NodeServer node) { - Server server = node.getServer(); - Map rs = new LinkedHashMap<>(); - String protocol = server.getNetprotocol(); - if (node instanceof NodeSncpServer) { - protocol += "/SNCP"; - } else if (node instanceof NodeWatchServer) { - protocol += "/WATCH"; - } else if (node instanceof NodeHttpServer) { - protocol += "/HTTP"; - } else { - NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class); - protocol += "/" + np.value(); - } - rs.put("name", server.getName()); - rs.put("protocol", protocol); - rs.put("address", server.getSocketAddress()); - rs.put("backlog", server.getBacklog()); - rs.put("bufferCapacity", server.getBufferCapacity()); - rs.put("bufferPoolSize", server.getBufferPoolSize()); - rs.put("charset", server.getCharset() == null ? "UTF-8" : server.getCharset().name()); - rs.put("maxbody", server.getMaxbody()); - rs.put("maxconns", server.getMaxconns()); - rs.put("serverStartTime", server.getServerStartTime()); - rs.put("responsePoolSize", server.getResponsePoolSize()); - rs.put("readTimeoutSeconds", server.getReadTimeoutSeconds()); - rs.put("writeTimeoutSeconds", server.getWriteTimeoutSeconds()); - rs.put("createConnectionCount", server.getCreateConnectionCount()); - rs.put("livingConnectionCount", server.getLivingConnectionCount()); - rs.put("closedConnectionCount", server.getClosedConnectionCount()); - return rs; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot.watch; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.*; +import java.util.stream.Stream; +import javax.annotation.Resource; +import org.redkale.boot.*; +import org.redkale.net.Server; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import org.redkale.util.Comment; + +/** + * + * @author zhangjx + */ +@RestService(name = "server", catalog = "watch", repair = false) +public class ServerWatchService extends AbstractWatchService { + + @Comment("不存在的Server节点") + public static final int RET_SERVER_NOT_EXISTS = 1602_0001; + + @Comment("更改Server监听地址端口失败") + public static final int RET_SERVER_CHANGEPORT_ERROR = 1602_0002; + + @Resource + protected Application application; + + @RestMapping(name = "info", comment = "单个Server信息查询") + public RetResult info(@RestParam(name = "#port:") final int port) { + Stream stream = application.getNodeServers().stream(); + NodeServer node = stream.filter(ns -> ns.getServer().getSocketAddress().getPort() == port).findFirst().orElse(null); + if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + port + ") not found"); + return new RetResult(formatToMap(node)); + } + + @RestMapping(name = "infos", comment = "Server信息查询") + public RetResult infos() { + Map rs = new LinkedHashMap<>(); + for (NodeServer ns : application.getNodeServers()) { + Server server = ns.getServer(); + rs.put("" + server.getSocketAddress().getPort(), formatToMap(ns)); + } + return new RetResult(rs); + } + + @RestMapping(name = "changeAddress", comment = "更改Server的监听地址和端口") + public RetResult changeAddress(@RestParam(name = "#port:") final int oldport, + @RestParam(name = "#newhost:") final String newhost, @RestParam(name = "#newport:") final int newport) { + if (oldport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `oldport`"); + if (newport < 1) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `newport`"); + Stream stream = application.getNodeServers().stream(); + NodeServer node = stream.filter(ns -> ns.getServer().getSocketAddress().getPort() == oldport).findFirst().orElse(null); + if (node == null) return new RetResult(RET_SERVER_NOT_EXISTS, "Server(port=" + oldport + ") not found"); + final Server server = node.getServer(); + InetSocketAddress newAddr = new InetSocketAddress(newhost == null || newhost.isEmpty() ? server.getSocketAddress().getHostString() : newhost, newport); + try { + server.changeAddress(application, newAddr); + } catch (IOException e) { + e.printStackTrace(); + return new RetResult(RET_SERVER_CHANGEPORT_ERROR, "changeaddress error"); + } + return RetResult.success(); + } + + private Map formatToMap(NodeServer node) { + Server server = node.getServer(); + Map rs = new LinkedHashMap<>(); + String protocol = server.getNetprotocol(); + if (node instanceof NodeSncpServer) { + protocol += "/SNCP"; + } else if (node instanceof NodeWatchServer) { + protocol += "/WATCH"; + } else if (node instanceof NodeHttpServer) { + protocol += "/HTTP"; + } else { + NodeProtocol np = node.getClass().getAnnotation(NodeProtocol.class); + protocol += "/" + np.value(); + } + rs.put("name", server.getName()); + rs.put("protocol", protocol); + rs.put("address", server.getSocketAddress()); + rs.put("backlog", server.getBacklog()); + rs.put("bufferCapacity", server.getBufferCapacity()); + rs.put("bufferPoolSize", server.getBufferPoolSize()); + rs.put("charset", server.getCharset() == null ? "UTF-8" : server.getCharset().name()); + rs.put("maxbody", server.getMaxbody()); + rs.put("maxconns", server.getMaxconns()); + rs.put("serverStartTime", server.getServerStartTime()); + rs.put("responsePoolSize", server.getResponsePoolSize()); + rs.put("readTimeoutSeconds", server.getReadTimeoutSeconds()); + rs.put("writeTimeoutSeconds", server.getWriteTimeoutSeconds()); + rs.put("createConnectionCount", server.getCreateConnectionCount()); + rs.put("livingConnectionCount", server.getLivingConnectionCount()); + rs.put("closedConnectionCount", server.getClosedConnectionCount()); + return rs; + } +} diff --git a/src/org/redkale/boot/watch/ServiceWatchService.java b/src/main/java/org/redkale/boot/watch/ServiceWatchService.java similarity index 97% rename from src/org/redkale/boot/watch/ServiceWatchService.java rename to src/main/java/org/redkale/boot/watch/ServiceWatchService.java index 4872d0f79..27fe661a8 100644 --- a/src/org/redkale/boot/watch/ServiceWatchService.java +++ b/src/main/java/org/redkale/boot/watch/ServiceWatchService.java @@ -1,199 +1,199 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot.watch; - -import java.lang.reflect.*; -import java.util.*; -import javax.annotation.Resource; -import org.redkale.boot.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.http.*; -import org.redkale.service.RetResult; -import org.redkale.util.*; - -/** - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@RestService(name = "service", catalog = "watch", repair = false) -public class ServiceWatchService extends AbstractWatchService { - - @Comment("没有找到目标Service") - public static final int RET_SERVICE_DEST_NOT_EXISTS = 1603_0001; - - @Resource - protected Application application; - - @RestConvert(type = void.class) - @RestMapping(name = "setField", auth = false, comment = "设置Service中指定字段的内容") - public RetResult setField(@RestParam(name = "name", comment = "Service的资源名") String name, - @RestParam(name = "type", comment = "Service的类名") String type, - @RestParam(name = "field", comment = "字段名") String field, - @RestParam(name = "value", comment = "字段值") String value) { - if (name == null) name = ""; - if (type == null) type = ""; - if (field == null) field = ""; - type = type.trim(); - field = field.trim(); - if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`"); - if (field.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `field`"); - Object dest = findService(name, type); - Class clazz = dest.getClass(); - Throwable t = null; - try { - Field fieldObj = null; - do { - try { - fieldObj = clazz.getDeclaredField(field); - break; - } catch (Exception e) { - if (t == null) t = e; - } - } while ((clazz = clazz.getSuperclass()) != Object.class); - if (fieldObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")"); - fieldObj.setAccessible(true); - fieldObj.set(dest, JsonConvert.root().convertFrom(fieldObj.getGenericType(), value)); - return RetResult.success(); - } catch (Throwable t2) { - return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")"); - } - } - - @RestConvert(type = void.class) - @RestMapping(name = "getField", auth = false, comment = "查询Service中指定字段的内容") - public RetResult getField(@RestParam(name = "name", comment = "Service的资源名") String name, - @RestParam(name = "type", comment = "Service的类名") String type, - @RestParam(name = "field", comment = "字段名") String field) { - if (name == null) name = ""; - if (type == null) type = ""; - if (field == null) field = ""; - type = type.trim(); - field = field.trim(); - if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`"); - if (field.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `field`"); - Object dest = findService(name, type); - Class clazz = dest.getClass(); - Throwable t = null; - try { - Field fieldObj = null; - do { - try { - fieldObj = clazz.getDeclaredField(field); - break; - } catch (Exception e) { - if (t == null) t = e; - } - } while ((clazz = clazz.getSuperclass()) != Object.class); - if (fieldObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")"); - fieldObj.setAccessible(true); - return new RetResult(fieldObj.get(dest)); - } catch (Throwable t2) { - return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")"); - } - } - - @RestConvert(type = void.class) - @RestMapping(name = "runMethod", auth = false, comment = "调用Service中指定方法") - public RetResult runMethod(@RestParam(name = "name", comment = "Service的资源名") String name, - @RestParam(name = "type", comment = "Service的类名") String type, - @RestParam(name = "method", comment = "Service的方法名") String method, - @RestParam(name = "params", comment = "方法的参数值") List params, - @RestParam(name = "paramtypes", comment = "方法的参数数据类型") List paramtypes) { - if (name == null) name = ""; - if (type == null) type = ""; - if (method == null) method = ""; - type = type.trim(); - method = method.trim(); - if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`"); - if (method.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `method`"); - Object dest = findService(name, type); - Class clazz = dest.getClass(); - Throwable t = null; - final int paramcount = params == null ? 0 : params.size(); - if (paramtypes != null && paramcount != paramtypes.size()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "params.size not equals to paramtypes.size"); - try { - Method methodObj = null; - do { - try { - for (Method m : clazz.getDeclaredMethods()) { - if (m.getName().equals(method) && m.getParameterCount() == paramcount) { - boolean flag = true; - if (paramtypes != null) { - Class[] pts = m.getParameterTypes(); - for (int i = 0; i < pts.length; i++) { - if (!pts[i].getName().endsWith(paramtypes.get(i))) { - flag = false; - break; - } - } - } - if (flag) { - methodObj = m; - break; - } - } - } - if (methodObj != null) break; - } catch (Exception e) { - if (t == null) t = e; - } - } while ((clazz = clazz.getSuperclass()) != Object.class); - if (methodObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + (t == null ? ("not found method(" + method + ")") : String.valueOf(t)) + ")"); - methodObj.setAccessible(true); - if (paramcount < 1) return new RetResult(methodObj.invoke(dest)); - Object[] paramObjs = new Object[paramcount]; - Type[] pts = methodObj.getGenericParameterTypes(); - for (int i = 0; i < paramObjs.length; i++) { - paramObjs[i] = JsonConvert.root().convertFrom(pts[i], params.get(i)); - } - return new RetResult(methodObj.invoke(dest, paramObjs)); - } catch (Throwable t2) { - return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")"); - } - } - - protected Object findService(String name, String type) { - Object dest = null; - for (NodeServer ns : application.getNodeServers()) { - ResourceFactory resFactory = ns.getResourceFactory(); - List list = resFactory.query((n, s) -> name.equals(n) && s != null && s.getClass().getName().endsWith(type)); - if (list == null || list.isEmpty()) continue; - dest = list.get(0); - } - if (dest == null) return new RetResult(RET_SERVICE_DEST_NOT_EXISTS, "not found servie (name=" + name + ", type=" + type + ")"); - return dest; - } - - @RestMapping(name = "loadService", auth = false, comment = "动态增加Service") - public RetResult loadService(@RestParam(name = "type", comment = "Service的类名") String type, - @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) { - //待开发 - return RetResult.success(); - } - - @RestMapping(name = "reloadService", auth = false, comment = "重新加载Service") - public RetResult reloadService(@RestParam(name = "name", comment = "Service的资源名") String name, - @RestParam(name = "type", comment = "Service的类名") String type) { - //待开发 - return RetResult.success(); - } - - @RestMapping(name = "stopService", auth = false, comment = "动态停止Service") - public RetResult stopService(@RestParam(name = "name", comment = "Service的资源名") String name, - @RestParam(name = "type", comment = "Service的类名") String type) { - //待开发 - return RetResult.success(); - } - - @RestMapping(name = "findService", auth = false, comment = "查找Service") - public RetResult find(@RestParam(name = "name", comment = "Service的资源名") String name, - @RestParam(name = "type", comment = "Service的类名") String type) { - //待开发 - return RetResult.success(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot.watch; + +import java.lang.reflect.*; +import java.util.*; +import javax.annotation.Resource; +import org.redkale.boot.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import org.redkale.util.*; + +/** + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@RestService(name = "service", catalog = "watch", repair = false) +public class ServiceWatchService extends AbstractWatchService { + + @Comment("没有找到目标Service") + public static final int RET_SERVICE_DEST_NOT_EXISTS = 1603_0001; + + @Resource + protected Application application; + + @RestConvert(type = void.class) + @RestMapping(name = "setField", auth = false, comment = "设置Service中指定字段的内容") + public RetResult setField(@RestParam(name = "name", comment = "Service的资源名") String name, + @RestParam(name = "type", comment = "Service的类名") String type, + @RestParam(name = "field", comment = "字段名") String field, + @RestParam(name = "value", comment = "字段值") String value) { + if (name == null) name = ""; + if (type == null) type = ""; + if (field == null) field = ""; + type = type.trim(); + field = field.trim(); + if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`"); + if (field.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `field`"); + Object dest = findService(name, type); + Class clazz = dest.getClass(); + Throwable t = null; + try { + Field fieldObj = null; + do { + try { + fieldObj = clazz.getDeclaredField(field); + break; + } catch (Exception e) { + if (t == null) t = e; + } + } while ((clazz = clazz.getSuperclass()) != Object.class); + if (fieldObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")"); + fieldObj.setAccessible(true); + fieldObj.set(dest, JsonConvert.root().convertFrom(fieldObj.getGenericType(), value)); + return RetResult.success(); + } catch (Throwable t2) { + return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")"); + } + } + + @RestConvert(type = void.class) + @RestMapping(name = "getField", auth = false, comment = "查询Service中指定字段的内容") + public RetResult getField(@RestParam(name = "name", comment = "Service的资源名") String name, + @RestParam(name = "type", comment = "Service的类名") String type, + @RestParam(name = "field", comment = "字段名") String field) { + if (name == null) name = ""; + if (type == null) type = ""; + if (field == null) field = ""; + type = type.trim(); + field = field.trim(); + if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`"); + if (field.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `field`"); + Object dest = findService(name, type); + Class clazz = dest.getClass(); + Throwable t = null; + try { + Field fieldObj = null; + do { + try { + fieldObj = clazz.getDeclaredField(field); + break; + } catch (Exception e) { + if (t == null) t = e; + } + } while ((clazz = clazz.getSuperclass()) != Object.class); + if (fieldObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + String.valueOf(t) + ")"); + fieldObj.setAccessible(true); + return new RetResult(fieldObj.get(dest)); + } catch (Throwable t2) { + return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")"); + } + } + + @RestConvert(type = void.class) + @RestMapping(name = "runMethod", auth = false, comment = "调用Service中指定方法") + public RetResult runMethod(@RestParam(name = "name", comment = "Service的资源名") String name, + @RestParam(name = "type", comment = "Service的类名") String type, + @RestParam(name = "method", comment = "Service的方法名") String method, + @RestParam(name = "params", comment = "方法的参数值") List params, + @RestParam(name = "paramtypes", comment = "方法的参数数据类型") List paramtypes) { + if (name == null) name = ""; + if (type == null) type = ""; + if (method == null) method = ""; + type = type.trim(); + method = method.trim(); + if (type.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `type`"); + if (method.isEmpty()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param `method`"); + Object dest = findService(name, type); + Class clazz = dest.getClass(); + Throwable t = null; + final int paramcount = params == null ? 0 : params.size(); + if (paramtypes != null && paramcount != paramtypes.size()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "params.size not equals to paramtypes.size"); + try { + Method methodObj = null; + do { + try { + for (Method m : clazz.getDeclaredMethods()) { + if (m.getName().equals(method) && m.getParameterCount() == paramcount) { + boolean flag = true; + if (paramtypes != null) { + Class[] pts = m.getParameterTypes(); + for (int i = 0; i < pts.length; i++) { + if (!pts[i].getName().endsWith(paramtypes.get(i))) { + flag = false; + break; + } + } + } + if (flag) { + methodObj = m; + break; + } + } + } + if (methodObj != null) break; + } catch (Exception e) { + if (t == null) t = e; + } + } while ((clazz = clazz.getSuperclass()) != Object.class); + if (methodObj == null) return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + (t == null ? ("not found method(" + method + ")") : String.valueOf(t)) + ")"); + methodObj.setAccessible(true); + if (paramcount < 1) return new RetResult(methodObj.invoke(dest)); + Object[] paramObjs = new Object[paramcount]; + Type[] pts = methodObj.getGenericParameterTypes(); + for (int i = 0; i < paramObjs.length; i++) { + paramObjs[i] = JsonConvert.root().convertFrom(pts[i], params.get(i)); + } + return new RetResult(methodObj.invoke(dest, paramObjs)); + } catch (Throwable t2) { + return new RetResult(RET_WATCH_RUN_EXCEPTION, "run exception (" + t2.toString() + ")"); + } + } + + protected Object findService(String name, String type) { + Object dest = null; + for (NodeServer ns : application.getNodeServers()) { + ResourceFactory resFactory = ns.getResourceFactory(); + List list = resFactory.query((n, s) -> name.equals(n) && s != null && s.getClass().getName().endsWith(type)); + if (list == null || list.isEmpty()) continue; + dest = list.get(0); + } + if (dest == null) return new RetResult(RET_SERVICE_DEST_NOT_EXISTS, "not found servie (name=" + name + ", type=" + type + ")"); + return dest; + } + + @RestMapping(name = "loadService", auth = false, comment = "动态增加Service") + public RetResult loadService(@RestParam(name = "type", comment = "Service的类名") String type, + @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) { + //待开发 + return RetResult.success(); + } + + @RestMapping(name = "reloadService", auth = false, comment = "重新加载Service") + public RetResult reloadService(@RestParam(name = "name", comment = "Service的资源名") String name, + @RestParam(name = "type", comment = "Service的类名") String type) { + //待开发 + return RetResult.success(); + } + + @RestMapping(name = "stopService", auth = false, comment = "动态停止Service") + public RetResult stopService(@RestParam(name = "name", comment = "Service的资源名") String name, + @RestParam(name = "type", comment = "Service的类名") String type) { + //待开发 + return RetResult.success(); + } + + @RestMapping(name = "findService", auth = false, comment = "查找Service") + public RetResult find(@RestParam(name = "name", comment = "Service的资源名") String name, + @RestParam(name = "type", comment = "Service的类名") String type) { + //待开发 + return RetResult.success(); + } +} diff --git a/src/org/redkale/boot/watch/ServletWatchService.java b/src/main/java/org/redkale/boot/watch/ServletWatchService.java similarity index 96% rename from src/org/redkale/boot/watch/ServletWatchService.java rename to src/main/java/org/redkale/boot/watch/ServletWatchService.java index 1b7fa095c..53bf28a3b 100644 --- a/src/org/redkale/boot/watch/ServletWatchService.java +++ b/src/main/java/org/redkale/boot/watch/ServletWatchService.java @@ -1,39 +1,39 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot.watch; - -import javax.annotation.Resource; -import org.redkale.boot.Application; -import org.redkale.net.TransportFactory; -import org.redkale.net.http.*; - -/** - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@RestService(name = "servlet", catalog = "watch", repair = false) -public class ServletWatchService extends AbstractWatchService { - - @Resource - protected Application application; - - @Resource - protected TransportFactory transportFactory; -// -// @RestMapping(name = "loadServlet", auth = false, comment = "动态增加Servlet") -// public RetResult loadServlet(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) { -// //待开发 -// return RetResult.success(); -// } -// -// @RestMapping(name = "stopServlet", auth = false, comment = "动态停止Servlet") -// public RetResult stopServlet(String type) { -// //待开发 -// return RetResult.success(); -// } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot.watch; + +import javax.annotation.Resource; +import org.redkale.boot.Application; +import org.redkale.net.TransportFactory; +import org.redkale.net.http.*; + +/** + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@RestService(name = "servlet", catalog = "watch", repair = false) +public class ServletWatchService extends AbstractWatchService { + + @Resource + protected Application application; + + @Resource + protected TransportFactory transportFactory; +// +// @RestMapping(name = "loadServlet", auth = false, comment = "动态增加Servlet") +// public RetResult loadServlet(String type, @RestUploadFile(maxLength = 10 * 1024 * 1024, fileNameReg = "\\.jar$") byte[] jar) { +// //待开发 +// return RetResult.success(); +// } +// +// @RestMapping(name = "stopServlet", auth = false, comment = "动态停止Servlet") +// public RetResult stopServlet(String type) { +// //待开发 +// return RetResult.success(); +// } +} diff --git a/src/org/redkale/boot/watch/TransportWatchService.java b/src/main/java/org/redkale/boot/watch/TransportWatchService.java similarity index 97% rename from src/org/redkale/boot/watch/TransportWatchService.java rename to src/main/java/org/redkale/boot/watch/TransportWatchService.java index c849c94ac..9fad7757c 100644 --- a/src/org/redkale/boot/watch/TransportWatchService.java +++ b/src/main/java/org/redkale/boot/watch/TransportWatchService.java @@ -1,140 +1,140 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot.watch; - -import java.io.IOException; -import java.net.*; -import java.nio.channels.AsynchronousSocketChannel; -import java.util.List; -import java.util.concurrent.TimeUnit; -import javax.annotation.Resource; -import org.redkale.boot.Application; -import org.redkale.net.*; -import org.redkale.net.http.*; -import org.redkale.net.sncp.*; -import org.redkale.service.*; -import org.redkale.util.*; -import org.redkale.util.AnyValue.DefaultAnyValue; - -/** - * - * @author zhangjx - */ -@RestService(name = "transport", catalog = "watch", repair = false) -public class TransportWatchService extends AbstractWatchService { - - @Comment("不存在的Group节点") - public static final int RET_TRANSPORT_GROUP_NOT_EXISTS = 1606_0001; - - @Comment("非法的Node节点IP地址") - public static final int RET_TRANSPORT_ADDR_ILLEGAL = 1606_0002; - - @Comment("Node节点IP地址已存在") - public static final int RET_TRANSPORT_ADDR_EXISTS = 1606_0003; - - @Resource - protected Application application; - - @Resource - protected TransportFactory transportFactory; - - @RestMapping(name = "listnodes", auth = false, comment = "获取所有Node节点") - public List listNodes() { - return transportFactory.getGroupInfos(); - } - - @RestMapping(name = "addnode", auth = false, comment = "动态增加指定Group的Node节点") - public RetResult addNode(@RestParam(name = "group", comment = "Group节点名") final String group, - @RestParam(name = "addr", comment = "节点IP") final String addr, - @RestParam(name = "port", comment = "节点端口") final int port) throws IOException { - InetSocketAddress address; - try { - address = new InetSocketAddress(addr, port); - AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(); - channel.connect(address).get(2, TimeUnit.SECONDS); //连接超时2秒 - channel.close(); - } catch (Exception e) { - return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is illegal or cannot connect"); - } - if (transportFactory.findGroupName(address) != null) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is exists"); - synchronized (this) { - if (transportFactory.findGroupInfo(group) == null) { - return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")"); - } - transportFactory.addGroupInfo(group, address); - for (Service service : transportFactory.getServices()) { - if (!Sncp.isSncpDyn(service)) continue; - SncpClient client = Sncp.getSncpClient(service); - if (Sncp.isRemote(service)) { - if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) { - client.getRemoteGroupTransport().addRemoteAddresses(address); - } - } - } - DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port); - for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) { - if (group.equals(groupconf.getValue("name"))) { - ((DefaultAnyValue) groupconf).addValue("node", node); - break; - } - } - //application.restoreConfig(); - } - return RetResult.success(); - } - - @RestMapping(name = "removenode", auth = false, comment = "动态删除指定Group的Node节点") - public RetResult removeNode(@RestParam(name = "group", comment = "Group节点名") final String group, - @RestParam(name = "addr", comment = "节点IP") final String addr, - @RestParam(name = "port", comment = "节点端口") final int port) throws IOException { - if (group == null) return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")"); - final InetSocketAddress address = new InetSocketAddress(addr, port); - if (!group.equals(transportFactory.findGroupName(address))) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") not belong to group(" + group + ")"); - synchronized (this) { - if (transportFactory.findGroupInfo(group) == null) { - return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")"); - } - transportFactory.removeGroupInfo(group, address); - for (Service service : transportFactory.getServices()) { - if (!Sncp.isSncpDyn(service)) continue; - SncpClient client = Sncp.getSncpClient(service); - if (Sncp.isRemote(service)) { - if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) { - client.getRemoteGroupTransport().removeRemoteAddresses(address); - } - } - } - for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) { - if (group.equals(groupconf.getValue("name"))) { - ((DefaultAnyValue) groupconf).removeValue("node", DefaultAnyValue.create("addr", addr).addValue("port", port)); - break; - } - } - //application.restoreConfig(); - } - return RetResult.success(); - } - - @RestMapping(name = "test1", auth = false, comment = "预留") - public RetResult test1() { - return RetResult.success(); - } - - @RestMapping(name = "test2", auth = false, comment = "预留") - public RetResult test2() { - return RetResult.success(); - } - - @RestMapping(name = "test3", auth = false, comment = "预留") - public RetResult test3() { - return RetResult.success(); - } - - @RestMapping(name = "test4", auth = false, comment = "预留") - public RetResult test4() { - return RetResult.success(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.boot.watch; + +import java.io.IOException; +import java.net.*; +import java.nio.channels.AsynchronousSocketChannel; +import java.util.List; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import org.redkale.boot.Application; +import org.redkale.net.*; +import org.redkale.net.http.*; +import org.redkale.net.sncp.*; +import org.redkale.service.*; +import org.redkale.util.*; +import org.redkale.util.AnyValue.DefaultAnyValue; + +/** + * + * @author zhangjx + */ +@RestService(name = "transport", catalog = "watch", repair = false) +public class TransportWatchService extends AbstractWatchService { + + @Comment("不存在的Group节点") + public static final int RET_TRANSPORT_GROUP_NOT_EXISTS = 1606_0001; + + @Comment("非法的Node节点IP地址") + public static final int RET_TRANSPORT_ADDR_ILLEGAL = 1606_0002; + + @Comment("Node节点IP地址已存在") + public static final int RET_TRANSPORT_ADDR_EXISTS = 1606_0003; + + @Resource + protected Application application; + + @Resource + protected TransportFactory transportFactory; + + @RestMapping(name = "listnodes", auth = false, comment = "获取所有Node节点") + public List listNodes() { + return transportFactory.getGroupInfos(); + } + + @RestMapping(name = "addnode", auth = false, comment = "动态增加指定Group的Node节点") + public RetResult addNode(@RestParam(name = "group", comment = "Group节点名") final String group, + @RestParam(name = "addr", comment = "节点IP") final String addr, + @RestParam(name = "port", comment = "节点端口") final int port) throws IOException { + InetSocketAddress address; + try { + address = new InetSocketAddress(addr, port); + AsynchronousSocketChannel channel = AsynchronousSocketChannel.open(); + channel.connect(address).get(2, TimeUnit.SECONDS); //连接超时2秒 + channel.close(); + } catch (Exception e) { + return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is illegal or cannot connect"); + } + if (transportFactory.findGroupName(address) != null) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") is exists"); + synchronized (this) { + if (transportFactory.findGroupInfo(group) == null) { + return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")"); + } + transportFactory.addGroupInfo(group, address); + for (Service service : transportFactory.getServices()) { + if (!Sncp.isSncpDyn(service)) continue; + SncpClient client = Sncp.getSncpClient(service); + if (Sncp.isRemote(service)) { + if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) { + client.getRemoteGroupTransport().addRemoteAddresses(address); + } + } + } + DefaultAnyValue node = DefaultAnyValue.create("addr", addr).addValue("port", port); + for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) { + if (group.equals(groupconf.getValue("name"))) { + ((DefaultAnyValue) groupconf).addValue("node", node); + break; + } + } + //application.restoreConfig(); + } + return RetResult.success(); + } + + @RestMapping(name = "removenode", auth = false, comment = "动态删除指定Group的Node节点") + public RetResult removeNode(@RestParam(name = "group", comment = "Group节点名") final String group, + @RestParam(name = "addr", comment = "节点IP") final String addr, + @RestParam(name = "port", comment = "节点端口") final int port) throws IOException { + if (group == null) return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")"); + final InetSocketAddress address = new InetSocketAddress(addr, port); + if (!group.equals(transportFactory.findGroupName(address))) return new RetResult(RET_TRANSPORT_ADDR_ILLEGAL, "InetSocketAddress(addr=" + addr + ", port=" + port + ") not belong to group(" + group + ")"); + synchronized (this) { + if (transportFactory.findGroupInfo(group) == null) { + return new RetResult(RET_TRANSPORT_GROUP_NOT_EXISTS, "not found group (" + group + ")"); + } + transportFactory.removeGroupInfo(group, address); + for (Service service : transportFactory.getServices()) { + if (!Sncp.isSncpDyn(service)) continue; + SncpClient client = Sncp.getSncpClient(service); + if (Sncp.isRemote(service)) { + if (client.getRemoteGroups() != null && client.getRemoteGroups().contains(group)) { + client.getRemoteGroupTransport().removeRemoteAddresses(address); + } + } + } + for (AnyValue groupconf : application.getAppConfig().getAnyValue("resources").getAnyValues("group")) { + if (group.equals(groupconf.getValue("name"))) { + ((DefaultAnyValue) groupconf).removeValue("node", DefaultAnyValue.create("addr", addr).addValue("port", port)); + break; + } + } + //application.restoreConfig(); + } + return RetResult.success(); + } + + @RestMapping(name = "test1", auth = false, comment = "预留") + public RetResult test1() { + return RetResult.success(); + } + + @RestMapping(name = "test2", auth = false, comment = "预留") + public RetResult test2() { + return RetResult.success(); + } + + @RestMapping(name = "test3", auth = false, comment = "预留") + public RetResult test3() { + return RetResult.success(); + } + + @RestMapping(name = "test4", auth = false, comment = "预留") + public RetResult test4() { + return RetResult.success(); + } +} diff --git a/src/org/redkale/cluster/CacheClusterAgent.java b/src/main/java/org/redkale/cluster/CacheClusterAgent.java similarity index 97% rename from src/org/redkale/cluster/CacheClusterAgent.java rename to src/main/java/org/redkale/cluster/CacheClusterAgent.java index 9dd1b8146..aafdc0ae3 100644 --- a/src/org/redkale/cluster/CacheClusterAgent.java +++ b/src/main/java/org/redkale/cluster/CacheClusterAgent.java @@ -1,327 +1,327 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.cluster; - -import java.net.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.Level; -import javax.annotation.Resource; -import org.redkale.boot.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.service.Service; -import org.redkale.source.CacheSource; -import org.redkale.util.*; - -/** - * 使用CacheSource实现的第三方服务发现管理接口cluster - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.3.0 - */ -public class CacheClusterAgent extends ClusterAgent implements Resourcable { - - @Resource(name = "$") - private CacheSource source; - - private String sourceName; - - protected int ttls = 10; //定时检查的秒数 - - protected ScheduledThreadPoolExecutor scheduler; - - //可能被HttpMessageClient用到的服务 key: servicename - protected final ConcurrentHashMap> httpAddressMap = new ConcurrentHashMap<>(); - - //可能被mqtp用到的服务 key: servicename - protected final ConcurrentHashMap> mqtpAddressMap = new ConcurrentHashMap<>(); - - @Override - public void init(AnyValue config) { - super.init(config); - this.sourceName = getSourceName(); - - AnyValue[] properties = config.getAnyValues("property"); - for (AnyValue property : properties) { - if ("ttls".equalsIgnoreCase(property.getValue("name"))) { - this.ttls = Integer.parseInt(property.getValue("value", "").trim()); - if (this.ttls < 5) this.ttls = 10; - } - } - } - - @Override - public void destroy(AnyValue config) { - if (scheduler != null) scheduler.shutdownNow(); - } - - public String getSourceName() { - AnyValue[] properties = config.getAnyValues("property"); - for (AnyValue property : properties) { - if ("source".equalsIgnoreCase(property.getValue("name")) - && property.getValue("value") != null) { - this.sourceName = property.getValue("value"); - return this.sourceName; - } - } - return null; - } - - @Override - public String resourceName() { - return sourceName; - } - - @Override //ServiceLoader时判断配置是否符合当前实现类 - public boolean match(AnyValue config) { - if (config == null) return false; - AnyValue[] properties = config.getAnyValues("property"); - if (properties == null || properties.length == 0) return false; - for (AnyValue property : properties) { - if ("source".equalsIgnoreCase(property.getValue("name")) - && property.getValue("value") != null) return true; - } - return false; - } - - @Override - public void start() { - if (this.scheduler == null) { - this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-" + CacheClusterAgent.class.getSimpleName() + "-Task-Thread"); - t.setDaemon(true); - return t; - }); - - this.scheduler.scheduleAtFixedRate(() -> { - try { - checkApplicationHealth(); - checkHttpAddressHealth(); - loadMqtpAddressHealth(); - localEntrys.values().stream().filter(e -> !e.canceled).forEach(entry -> { - checkLocalHealth(entry); - }); - remoteEntrys.values().stream().filter(entry -> "SNCP".equalsIgnoreCase(entry.protocol)).forEach(entry -> { - updateSncpTransport(entry); - }); - } catch (Exception e) { - logger.log(Level.SEVERE, "scheduleAtFixedRate check error", e); - } - }, Math.max(2000, ttls * 1000), Math.max(2000, ttls * 1000), TimeUnit.MILLISECONDS); - } - } - - protected void loadMqtpAddressHealth() { - List keys = source.queryKeysStartsWith("cluster.mqtp:"); - keys.forEach(servicename -> { - try { - this.mqtpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS)); - } catch (Exception e) { - logger.log(Level.SEVERE, "loadMqtpAddressHealth check " + servicename + " error", e); - } - }); - } - - protected void checkHttpAddressHealth() { - try { - this.httpAddressMap.keySet().stream().forEach(servicename -> { - try { - this.httpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS)); - } catch (Exception e) { - logger.log(Level.SEVERE, "checkHttpAddressHealth check " + servicename + " error", e); - } - }); - } catch (Exception ex) { - logger.log(Level.SEVERE, "checkHttpAddressHealth check error", ex); - } - } - - protected void checkLocalHealth(final ClusterEntry entry) { - AddressEntry newaddr = new AddressEntry(); - newaddr.addr = entry.address; - newaddr.nodeid = this.nodeid; - newaddr.time = System.currentTimeMillis(); - source.hset(entry.checkname, entry.checkid, AddressEntry.class, newaddr); - } - - @Override //获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段 - public CompletableFuture>> queryMqtpAddress(String protocol, String module, String resname) { - final Map> rsmap = new ConcurrentHashMap<>(); - final String servicenamprefix = generateHttpServiceName(protocol, module, null) + ":"; - mqtpAddressMap.keySet().stream().filter(k -> k.startsWith(servicenamprefix)) - .forEach(sn -> rsmap.put(sn.substring(servicenamprefix.length()), mqtpAddressMap.get(sn))); - return CompletableFuture.completedFuture(rsmap); - } - - @Override //获取HTTP远程服务的可用ip列表 - public CompletableFuture> queryHttpAddress(String protocol, String module, String resname) { - final String servicename = generateHttpServiceName(protocol, module, resname); - Collection rs = httpAddressMap.get(servicename); - if (rs != null) return CompletableFuture.completedFuture(rs); - return queryAddress(servicename).thenApply(t -> { - httpAddressMap.put(servicename, t); - return t; - }); - } - - @Override - protected CompletableFuture> queryAddress(final ClusterEntry entry) { - return queryAddress(entry.servicename); - } - - private CompletableFuture> queryAddress(final String servicename) { - final CompletableFuture> future = source.hmapAsync(servicename, AddressEntry.class, 0, 10000); - return future.thenApply(map -> { - final Set set = new HashSet<>(); - map.forEach((n, v) -> { - if (v != null && (System.currentTimeMillis() - v.time) / 1000 < ttls) set.add(v.addr); - }); - return set; - }); - } - - protected boolean isApplicationHealth() { - String servicename = generateApplicationServiceName(); - String serviceid = generateApplicationServiceId(); - AddressEntry entry = (AddressEntry) source.hget(servicename, serviceid, AddressEntry.class); - return entry != null && (System.currentTimeMillis() - entry.time) / 1000 < ttls; - } - - protected void checkApplicationHealth() { - String checkname = generateApplicationServiceName(); - String checkid = generateApplicationCheckId(); - AddressEntry entry = new AddressEntry(); - entry.addr = this.appAddress; - entry.nodeid = this.nodeid; - entry.time = System.currentTimeMillis(); - source.hset(checkname, checkid, AddressEntry.class, entry); - } - - @Override - public void register(Application application) { - if (isApplicationHealth()) throw new RuntimeException("application.nodeid=" + nodeid + " exists in cluster"); - deregister(application); - - String serviceid = generateApplicationServiceId(); - String servicename = generateApplicationServiceName(); - AddressEntry entry = new AddressEntry(); - entry.addr = this.appAddress; - entry.nodeid = this.nodeid; - entry.time = System.currentTimeMillis(); - source.hset(servicename, serviceid, AddressEntry.class, entry); - } - - @Override - public void deregister(Application application) { - String servicename = generateApplicationServiceName(); - source.remove(servicename); - } - - @Override - protected ClusterEntry register(NodeServer ns, String protocol, Service service) { - deregister(ns, protocol, service, false); - // - ClusterEntry clusterEntry = new ClusterEntry(ns, protocol, service); - AddressEntry entry = new AddressEntry(); - entry.addr = clusterEntry.address; - entry.nodeid = this.nodeid; - entry.time = System.currentTimeMillis(); - source.hset(clusterEntry.servicename, clusterEntry.serviceid, AddressEntry.class, entry); - return clusterEntry; - } - - @Override - protected void deregister(NodeServer ns, String protocol, Service service) { - deregister(ns, protocol, service, true); - } - - protected void deregister(NodeServer ns, String protocol, Service service, boolean realcanceled) { - String servicename = generateServiceName(ns, protocol, service); - String serviceid = generateServiceId(ns, protocol, service); - ClusterEntry currEntry = null; - for (final ClusterEntry entry : localEntrys.values()) { - if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) { - currEntry = entry; - break; - } - } - if (currEntry == null) { - for (final ClusterEntry entry : remoteEntrys.values()) { - if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) { - currEntry = entry; - break; - } - } - } - source.hremove(servicename, serviceid); - if (realcanceled && currEntry != null) currEntry.canceled = true; - if (!"mqtp".equals(protocol) && currEntry != null && currEntry.submqtp) { - deregister(ns, "mqtp", service, realcanceled); - } - } - - @Override - protected String generateApplicationServiceName() { - return "cluster." + super.generateApplicationServiceName(); - } - - @Override - protected String generateServiceName(NodeServer ns, String protocol, Service service) { - return "cluster." + super.generateServiceName(ns, protocol, service); - } - - @Override - public String generateHttpServiceName(String protocol, String module, String resname) { - return "cluster." + super.generateHttpServiceName(protocol, module, resname); - } - - @Override - protected String generateApplicationCheckName() { - return generateApplicationServiceName(); - } - - @Override - protected String generateApplicationCheckId() { - return generateApplicationServiceId(); - } - - @Override - protected String generateCheckName(NodeServer ns, String protocol, Service service) { - return generateServiceName(ns, protocol, service); - } - - @Override - protected String generateCheckId(NodeServer ns, String protocol, Service service) { - return generateServiceId(ns, protocol, service); - } - - public static class AddressEntry { - - public InetSocketAddress addr; - - public int nodeid; - - public long time; - - public AddressEntry() { - } - - public AddressEntry refresh() { - this.time = System.currentTimeMillis(); - return this; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.cluster; + +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.Level; +import javax.annotation.Resource; +import org.redkale.boot.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.service.Service; +import org.redkale.source.CacheSource; +import org.redkale.util.*; + +/** + * 使用CacheSource实现的第三方服务发现管理接口cluster + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + */ +public class CacheClusterAgent extends ClusterAgent implements Resourcable { + + @Resource(name = "$") + private CacheSource source; + + private String sourceName; + + protected int ttls = 10; //定时检查的秒数 + + protected ScheduledThreadPoolExecutor scheduler; + + //可能被HttpMessageClient用到的服务 key: servicename + protected final ConcurrentHashMap> httpAddressMap = new ConcurrentHashMap<>(); + + //可能被mqtp用到的服务 key: servicename + protected final ConcurrentHashMap> mqtpAddressMap = new ConcurrentHashMap<>(); + + @Override + public void init(AnyValue config) { + super.init(config); + this.sourceName = getSourceName(); + + AnyValue[] properties = config.getAnyValues("property"); + for (AnyValue property : properties) { + if ("ttls".equalsIgnoreCase(property.getValue("name"))) { + this.ttls = Integer.parseInt(property.getValue("value", "").trim()); + if (this.ttls < 5) this.ttls = 10; + } + } + } + + @Override + public void destroy(AnyValue config) { + if (scheduler != null) scheduler.shutdownNow(); + } + + public String getSourceName() { + AnyValue[] properties = config.getAnyValues("property"); + for (AnyValue property : properties) { + if ("source".equalsIgnoreCase(property.getValue("name")) + && property.getValue("value") != null) { + this.sourceName = property.getValue("value"); + return this.sourceName; + } + } + return null; + } + + @Override + public String resourceName() { + return sourceName; + } + + @Override //ServiceLoader时判断配置是否符合当前实现类 + public boolean acceptsConf(AnyValue config) { + if (config == null) return false; + AnyValue[] properties = config.getAnyValues("property"); + if (properties == null || properties.length == 0) return false; + for (AnyValue property : properties) { + if ("source".equalsIgnoreCase(property.getValue("name")) + && property.getValue("value") != null) return true; + } + return false; + } + + @Override + public void start() { + if (this.scheduler == null) { + this.scheduler = new ScheduledThreadPoolExecutor(4, (Runnable r) -> { + final Thread t = new Thread(r, "Redkale-" + CacheClusterAgent.class.getSimpleName() + "-Task-Thread"); + t.setDaemon(true); + return t; + }); + + this.scheduler.scheduleAtFixedRate(() -> { + try { + checkApplicationHealth(); + checkHttpAddressHealth(); + loadMqtpAddressHealth(); + localEntrys.values().stream().filter(e -> !e.canceled).forEach(entry -> { + checkLocalHealth(entry); + }); + remoteEntrys.values().stream().filter(entry -> "SNCP".equalsIgnoreCase(entry.protocol)).forEach(entry -> { + updateSncpTransport(entry); + }); + } catch (Exception e) { + logger.log(Level.SEVERE, "scheduleAtFixedRate check error", e); + } + }, Math.max(2000, ttls * 1000), Math.max(2000, ttls * 1000), TimeUnit.MILLISECONDS); + } + } + + protected void loadMqtpAddressHealth() { + List keys = source.queryKeysStartsWith("cluster.mqtp:"); + keys.forEach(servicename -> { + try { + this.mqtpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS)); + } catch (Exception e) { + logger.log(Level.SEVERE, "loadMqtpAddressHealth check " + servicename + " error", e); + } + }); + } + + protected void checkHttpAddressHealth() { + try { + this.httpAddressMap.keySet().stream().forEach(servicename -> { + try { + this.httpAddressMap.put(servicename, queryAddress(servicename).get(3, TimeUnit.SECONDS)); + } catch (Exception e) { + logger.log(Level.SEVERE, "checkHttpAddressHealth check " + servicename + " error", e); + } + }); + } catch (Exception ex) { + logger.log(Level.SEVERE, "checkHttpAddressHealth check error", ex); + } + } + + protected void checkLocalHealth(final ClusterEntry entry) { + AddressEntry newaddr = new AddressEntry(); + newaddr.addr = entry.address; + newaddr.nodeid = this.nodeid; + newaddr.time = System.currentTimeMillis(); + source.hset(entry.checkname, entry.checkid, AddressEntry.class, newaddr); + } + + @Override //获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段 + public CompletableFuture>> queryMqtpAddress(String protocol, String module, String resname) { + final Map> rsmap = new ConcurrentHashMap<>(); + final String servicenamprefix = generateHttpServiceName(protocol, module, null) + ":"; + mqtpAddressMap.keySet().stream().filter(k -> k.startsWith(servicenamprefix)) + .forEach(sn -> rsmap.put(sn.substring(servicenamprefix.length()), mqtpAddressMap.get(sn))); + return CompletableFuture.completedFuture(rsmap); + } + + @Override //获取HTTP远程服务的可用ip列表 + public CompletableFuture> queryHttpAddress(String protocol, String module, String resname) { + final String servicename = generateHttpServiceName(protocol, module, resname); + Collection rs = httpAddressMap.get(servicename); + if (rs != null) return CompletableFuture.completedFuture(rs); + return queryAddress(servicename).thenApply(t -> { + httpAddressMap.put(servicename, t); + return t; + }); + } + + @Override + protected CompletableFuture> queryAddress(final ClusterEntry entry) { + return queryAddress(entry.servicename); + } + + private CompletableFuture> queryAddress(final String servicename) { + final CompletableFuture> future = source.hmapAsync(servicename, AddressEntry.class, 0, 10000); + return future.thenApply(map -> { + final Set set = new HashSet<>(); + map.forEach((n, v) -> { + if (v != null && (System.currentTimeMillis() - v.time) / 1000 < ttls) set.add(v.addr); + }); + return set; + }); + } + + protected boolean isApplicationHealth() { + String servicename = generateApplicationServiceName(); + String serviceid = generateApplicationServiceId(); + AddressEntry entry = (AddressEntry) source.hget(servicename, serviceid, AddressEntry.class); + return entry != null && (System.currentTimeMillis() - entry.time) / 1000 < ttls; + } + + protected void checkApplicationHealth() { + String checkname = generateApplicationServiceName(); + String checkid = generateApplicationCheckId(); + AddressEntry entry = new AddressEntry(); + entry.addr = this.appAddress; + entry.nodeid = this.nodeid; + entry.time = System.currentTimeMillis(); + source.hset(checkname, checkid, AddressEntry.class, entry); + } + + @Override + public void register(Application application) { + if (isApplicationHealth()) throw new RuntimeException("application.nodeid=" + nodeid + " exists in cluster"); + deregister(application); + + String serviceid = generateApplicationServiceId(); + String servicename = generateApplicationServiceName(); + AddressEntry entry = new AddressEntry(); + entry.addr = this.appAddress; + entry.nodeid = this.nodeid; + entry.time = System.currentTimeMillis(); + source.hset(servicename, serviceid, AddressEntry.class, entry); + } + + @Override + public void deregister(Application application) { + String servicename = generateApplicationServiceName(); + source.remove(servicename); + } + + @Override + protected ClusterEntry register(NodeServer ns, String protocol, Service service) { + deregister(ns, protocol, service, false); + // + ClusterEntry clusterEntry = new ClusterEntry(ns, protocol, service); + AddressEntry entry = new AddressEntry(); + entry.addr = clusterEntry.address; + entry.nodeid = this.nodeid; + entry.time = System.currentTimeMillis(); + source.hset(clusterEntry.servicename, clusterEntry.serviceid, AddressEntry.class, entry); + return clusterEntry; + } + + @Override + protected void deregister(NodeServer ns, String protocol, Service service) { + deregister(ns, protocol, service, true); + } + + protected void deregister(NodeServer ns, String protocol, Service service, boolean realcanceled) { + String servicename = generateServiceName(ns, protocol, service); + String serviceid = generateServiceId(ns, protocol, service); + ClusterEntry currEntry = null; + for (final ClusterEntry entry : localEntrys.values()) { + if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) { + currEntry = entry; + break; + } + } + if (currEntry == null) { + for (final ClusterEntry entry : remoteEntrys.values()) { + if (entry.servicename.equals(servicename) && entry.serviceid.equals(serviceid)) { + currEntry = entry; + break; + } + } + } + source.hremove(servicename, serviceid); + if (realcanceled && currEntry != null) currEntry.canceled = true; + if (!"mqtp".equals(protocol) && currEntry != null && currEntry.submqtp) { + deregister(ns, "mqtp", service, realcanceled); + } + } + + @Override + protected String generateApplicationServiceName() { + return "cluster." + super.generateApplicationServiceName(); + } + + @Override + protected String generateServiceName(NodeServer ns, String protocol, Service service) { + return "cluster." + super.generateServiceName(ns, protocol, service); + } + + @Override + public String generateHttpServiceName(String protocol, String module, String resname) { + return "cluster." + super.generateHttpServiceName(protocol, module, resname); + } + + @Override + protected String generateApplicationCheckName() { + return generateApplicationServiceName(); + } + + @Override + protected String generateApplicationCheckId() { + return generateApplicationServiceId(); + } + + @Override + protected String generateCheckName(NodeServer ns, String protocol, Service service) { + return generateServiceName(ns, protocol, service); + } + + @Override + protected String generateCheckId(NodeServer ns, String protocol, Service service) { + return generateServiceId(ns, protocol, service); + } + + public static class AddressEntry { + + public InetSocketAddress addr; + + public int nodeid; + + public long time; + + public AddressEntry() { + } + + public AddressEntry refresh() { + this.time = System.currentTimeMillis(); + return this; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } + +} diff --git a/src/org/redkale/cluster/ClusterAgent.java b/src/main/java/org/redkale/cluster/ClusterAgent.java similarity index 96% rename from src/org/redkale/cluster/ClusterAgent.java rename to src/main/java/org/redkale/cluster/ClusterAgent.java index 8e2da7d67..16991c165 100644 --- a/src/org/redkale/cluster/ClusterAgent.java +++ b/src/main/java/org/redkale/cluster/ClusterAgent.java @@ -1,342 +1,342 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.cluster; - -import java.lang.ref.WeakReference; -import java.net.InetSocketAddress; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.Logger; -import javax.annotation.Resource; -import org.redkale.boot.*; -import static org.redkale.boot.Application.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.mq.MessageMultiConsumer; -import org.redkale.net.*; -import org.redkale.net.http.*; -import org.redkale.net.sncp.*; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - * 第三方服务发现管理接口cluster - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public abstract class ClusterAgent { - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - @Resource(name = RESNAME_APP_NODEID) - protected int nodeid; - - @Resource(name = RESNAME_APP_NAME) - protected String appName = ""; - - @Resource(name = RESNAME_APP_ADDR) - protected InetSocketAddress appAddress; - - protected String name; - - protected boolean waits; - - protected String[] protocols; //必须全大写 - - protected int[] ports; - - protected AnyValue config; - - protected TransportFactory transportFactory; - - protected final ConcurrentHashMap localEntrys = new ConcurrentHashMap<>(); - - protected final ConcurrentHashMap remoteEntrys = new ConcurrentHashMap<>(); - - public void init(AnyValue config) { - this.config = config; - this.name = config.getValue("name", ""); - this.waits = config.getBoolValue("waits", false); - { - String ps = config.getValue("protocols", "").toUpperCase(); - if (ps == null || ps.isEmpty()) ps = "SNCP;HTTP"; - this.protocols = ps.split(";"); - } - String ts = config.getValue("ports", ""); - if (ts != null && !ts.isEmpty()) { - String[] its = ts.split(";"); - List list = new ArrayList<>(); - for (String str : its) { - if (str.trim().isEmpty()) continue; - list.add(Integer.parseInt(str.trim())); - } - if (!list.isEmpty()) this.ports = list.stream().mapToInt(x -> x).toArray(); - } - } - - public void destroy(AnyValue config) { - } - - //ServiceLoader时判断配置是否符合当前实现类 - public abstract boolean match(AnyValue config); - - public boolean containsProtocol(String protocol) { - if (protocol == null || protocol.isEmpty()) return false; - return protocols == null || Utility.contains(protocols, protocol.toUpperCase()); - } - - public boolean containsPort(int port) { - if (ports == null || ports.length == 0) return true; - return Utility.contains(ports, port); - } - - public abstract void register(Application application); - - public abstract void deregister(Application application); - - //注册服务, 在NodeService调用Service.init方法之前调用 - public void register(NodeServer ns, String protocol, Set localServices, Set remoteServices) { - if (localServices.isEmpty()) return; - //注册本地模式 - for (Service service : localServices) { - if (!canRegister(protocol, service)) continue; - ClusterEntry htentry = register(ns, protocol, service); - localEntrys.put(htentry.serviceid, htentry); - if (protocol.toLowerCase().startsWith("http")) { - MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); - if (mmc != null) { - ClusterEntry mqentry = register(ns, "mqtp", service); - localEntrys.put(mqentry.serviceid, mqentry); - htentry.submqtp = true; - } - } - } - //远程模式加载IP列表, 只支持SNCP协议 - if (ns.isSNCP()) { - for (Service service : remoteServices) { - ClusterEntry entry = new ClusterEntry(ns, protocol, service); - updateSncpTransport(entry); - remoteEntrys.put(entry.serviceid, entry); - } - } - } - - //注销服务, 在NodeService调用Service.destroy 方法之前调用 - public void deregister(NodeServer ns, String protocol, Set localServices, Set remoteServices) { - //注销本地模式 远程模式不注册 - for (Service service : localServices) { - if (!canRegister(protocol, service)) continue; - deregister(ns, protocol, service); - } - afterDeregister(ns, protocol); - } - - protected boolean canRegister(String protocol, Service service) { - if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false; - AutoLoad al = service.getClass().getAnnotation(AutoLoad.class); - if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return false; - if (service instanceof WebSocketNode) { - if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false; - } - return true; - } - - public void start() { - } - - protected void afterDeregister(NodeServer ns, String protocol) { - if (!this.waits) return; - int s = intervalCheckSeconds(); - if (s > 0) { //暂停,弥补其他依赖本进程服务的周期偏差 - try { - Thread.sleep(s * 1000); - } catch (InterruptedException ex) { - } - logger.info(this.getClass().getSimpleName() + " wait for " + s * 1000 + "ms after deregister"); - } - } - - public int intervalCheckSeconds() { - return 10; - } - - //获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段 - public abstract CompletableFuture>> queryMqtpAddress(String protocol, String module, String resname); - - //获取HTTP远程服务的可用ip列表 - public abstract CompletableFuture> queryHttpAddress(String protocol, String module, String resname); - - //获取远程服务的可用ip列表 - protected abstract CompletableFuture> queryAddress(ClusterEntry entry); - - //注册服务 - protected abstract ClusterEntry register(NodeServer ns, String protocol, Service service); - - //注销服务 - protected abstract void deregister(NodeServer ns, String protocol, Service service); - - //格式: protocol:classtype-resourcename - protected void updateSncpTransport(ClusterEntry entry) { - Service service = entry.serviceref.get(); - if (service == null) return; - Collection addrs = ClusterAgent.this.queryAddress(entry).join(); - Sncp.updateTransport(service, transportFactory, Sncp.getResourceType(service).getName() + "-" + Sncp.getResourceName(service), entry.netprotocol, entry.address, null, addrs); - } - - protected String generateApplicationServiceName() { - return "application" + (appName == null || appName.isEmpty() ? "" : ("." + appName)) + ".node" + this.nodeid; - } - - protected String generateApplicationServiceId() { //与servicename相同 - return generateApplicationServiceName(); - } - - protected String generateApplicationCheckName() { - return "check-" + generateApplicationServiceName(); - } - - protected String generateApplicationCheckId() { - return "check-" + generateApplicationServiceId(); - } - - //也会提供给HttpMessageClusterAgent适用 - public String generateHttpServiceName(String protocol, String module, String resname) { - return protocol.toLowerCase() + ":" + module + (resname == null || resname.isEmpty() ? "" : ("-" + resname)); - } - - //格式: protocol:classtype-resourcename - protected String generateServiceName(NodeServer ns, String protocol, Service service) { - if (protocol.toLowerCase().startsWith("http")) { //HTTP使用RestService.name方式是为了与MessageClient中的module保持一致, 因为HTTP依靠的url中的module,无法知道Service类名 - String resname = Sncp.getResourceName(service); - String module = Rest.getRestModule(service).toLowerCase(); - return protocol.toLowerCase() + ":" + module + (resname.isEmpty() ? "" : ("-" + resname)); - } - if ("mqtp".equalsIgnoreCase(protocol)) { - MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); - String selfmodule = Rest.getRestModule(service).toLowerCase(); - return protocol.toLowerCase() + ":" + mmc.module() + ":" + selfmodule; - } - if (!Sncp.isSncpDyn(service)) return protocol.toLowerCase() + ":" + service.getClass().getName(); - String resname = Sncp.getResourceName(service); - return protocol.toLowerCase() + ":" + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname)); - } - - //格式: protocol:classtype-resourcename:nodeid - protected String generateServiceId(NodeServer ns, String protocol, Service service) { - return generateServiceName(ns, protocol, service) + ":" + this.nodeid; - } - - protected String generateCheckName(NodeServer ns, String protocol, Service service) { - return "check-" + generateServiceName(ns, protocol, service); - } - - protected String generateCheckId(NodeServer ns, String protocol, Service service) { - return "check-" + generateServiceId(ns, protocol, service); - } - - protected ConcurrentHashMap getLocalEntrys() { - return localEntrys; - } - - protected ConcurrentHashMap getRemoteEntrys() { - return remoteEntrys; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public TransportFactory getTransportFactory() { - return transportFactory; - } - - public void setTransportFactory(TransportFactory transportFactory) { - this.transportFactory = transportFactory; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String[] getProtocols() { - return protocols; - } - - public void setProtocols(String[] protocols) { - this.protocols = protocols; - } - - public int[] getPorts() { - return ports; - } - - public void setPorts(int[] ports) { - this.ports = ports; - } - - public AnyValue getConfig() { - return config; - } - - public void setConfig(AnyValue config) { - this.config = config; - } - - public class ClusterEntry { - - public String serviceid; - - public String servicename; - - public String checkid; - - public String checkname; - - public String protocol; - - public String netprotocol; - - public WeakReference serviceref; - - public InetSocketAddress address; - - public boolean canceled; - - public boolean submqtp; - - public ClusterEntry(NodeServer ns, String protocol, Service service) { - this.serviceid = generateServiceId(ns, protocol, service); - this.servicename = generateServiceName(ns, protocol, service); - this.checkid = generateCheckId(ns, protocol, service); - this.checkname = generateCheckName(ns, protocol, service); - this.protocol = protocol; - InetSocketAddress addr = ns.getSocketAddress(); - String host = addr.getHostString(); - if ("0.0.0.0".equals(host)) { - host = appAddress.getHostString(); - addr = new InetSocketAddress(host, addr.getPort()); - } - this.address = addr; - this.serviceref = new WeakReference(service); - Server server = ns.getServer(); - this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.cluster; + +import java.lang.ref.WeakReference; +import java.net.InetSocketAddress; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.Logger; +import javax.annotation.Resource; +import org.redkale.boot.*; +import static org.redkale.boot.Application.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.mq.MessageMultiConsumer; +import org.redkale.net.*; +import org.redkale.net.http.*; +import org.redkale.net.sncp.*; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * 第三方服务发现管理接口cluster + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public abstract class ClusterAgent { + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + @Resource(name = RESNAME_APP_NODEID) + protected int nodeid; + + @Resource(name = RESNAME_APP_NAME) + protected String appName = ""; + + @Resource(name = RESNAME_APP_ADDR) + protected InetSocketAddress appAddress; + + protected String name; + + protected boolean waits; + + protected String[] protocols; //必须全大写 + + protected int[] ports; + + protected AnyValue config; + + protected TransportFactory transportFactory; + + protected final ConcurrentHashMap localEntrys = new ConcurrentHashMap<>(); + + protected final ConcurrentHashMap remoteEntrys = new ConcurrentHashMap<>(); + + public void init(AnyValue config) { + this.config = config; + this.name = config.getValue("name", ""); + this.waits = config.getBoolValue("waits", false); + { + String ps = config.getValue("protocols", "").toUpperCase(); + if (ps == null || ps.isEmpty()) ps = "SNCP;HTTP"; + this.protocols = ps.split(";"); + } + String ts = config.getValue("ports", ""); + if (ts != null && !ts.isEmpty()) { + String[] its = ts.split(";"); + List list = new ArrayList<>(); + for (String str : its) { + if (str.trim().isEmpty()) continue; + list.add(Integer.parseInt(str.trim())); + } + if (!list.isEmpty()) this.ports = list.stream().mapToInt(x -> x).toArray(); + } + } + + public void destroy(AnyValue config) { + } + + //ServiceLoader时判断配置是否符合当前实现类 + public abstract boolean acceptsConf(AnyValue config); + + public boolean containsProtocol(String protocol) { + if (protocol == null || protocol.isEmpty()) return false; + return protocols == null || Utility.contains(protocols, protocol.toUpperCase()); + } + + public boolean containsPort(int port) { + if (ports == null || ports.length == 0) return true; + return Utility.contains(ports, port); + } + + public abstract void register(Application application); + + public abstract void deregister(Application application); + + //注册服务, 在NodeService调用Service.init方法之前调用 + public void register(NodeServer ns, String protocol, Set localServices, Set remoteServices) { + if (localServices.isEmpty()) return; + //注册本地模式 + for (Service service : localServices) { + if (!canRegister(protocol, service)) continue; + ClusterEntry htentry = register(ns, protocol, service); + localEntrys.put(htentry.serviceid, htentry); + if (protocol.toLowerCase().startsWith("http")) { + MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); + if (mmc != null) { + ClusterEntry mqentry = register(ns, "mqtp", service); + localEntrys.put(mqentry.serviceid, mqentry); + htentry.submqtp = true; + } + } + } + //远程模式加载IP列表, 只支持SNCP协议 + if (ns.isSNCP()) { + for (Service service : remoteServices) { + ClusterEntry entry = new ClusterEntry(ns, protocol, service); + updateSncpTransport(entry); + remoteEntrys.put(entry.serviceid, entry); + } + } + } + + //注销服务, 在NodeService调用Service.destroy 方法之前调用 + public void deregister(NodeServer ns, String protocol, Set localServices, Set remoteServices) { + //注销本地模式 远程模式不注册 + for (Service service : localServices) { + if (!canRegister(protocol, service)) continue; + deregister(ns, protocol, service); + } + afterDeregister(ns, protocol); + } + + protected boolean canRegister(String protocol, Service service) { + if ("SNCP".equalsIgnoreCase(protocol) && service.getClass().getAnnotation(Local.class) != null) return false; + AutoLoad al = service.getClass().getAnnotation(AutoLoad.class); + if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return false; + if (service instanceof WebSocketNode) { + if (((WebSocketNode) service).getLocalWebSocketEngine() == null) return false; + } + return true; + } + + public void start() { + } + + protected void afterDeregister(NodeServer ns, String protocol) { + if (!this.waits) return; + int s = intervalCheckSeconds(); + if (s > 0) { //暂停,弥补其他依赖本进程服务的周期偏差 + try { + Thread.sleep(s * 1000); + } catch (InterruptedException ex) { + } + logger.info(this.getClass().getSimpleName() + " wait for " + s * 1000 + "ms after deregister"); + } + } + + public int intervalCheckSeconds() { + return 10; + } + + //获取MQTP的HTTP远程服务的可用ip列表, key = servicename的后半段 + public abstract CompletableFuture>> queryMqtpAddress(String protocol, String module, String resname); + + //获取HTTP远程服务的可用ip列表 + public abstract CompletableFuture> queryHttpAddress(String protocol, String module, String resname); + + //获取远程服务的可用ip列表 + protected abstract CompletableFuture> queryAddress(ClusterEntry entry); + + //注册服务 + protected abstract ClusterEntry register(NodeServer ns, String protocol, Service service); + + //注销服务 + protected abstract void deregister(NodeServer ns, String protocol, Service service); + + //格式: protocol:classtype-resourcename + protected void updateSncpTransport(ClusterEntry entry) { + Service service = entry.serviceref.get(); + if (service == null) return; + Collection addrs = ClusterAgent.this.queryAddress(entry).join(); + Sncp.updateTransport(service, transportFactory, Sncp.getResourceType(service).getName() + "-" + Sncp.getResourceName(service), entry.netprotocol, entry.address, null, addrs); + } + + protected String generateApplicationServiceName() { + return "application" + (appName == null || appName.isEmpty() ? "" : ("." + appName)) + ".node" + this.nodeid; + } + + protected String generateApplicationServiceId() { //与servicename相同 + return generateApplicationServiceName(); + } + + protected String generateApplicationCheckName() { + return "check-" + generateApplicationServiceName(); + } + + protected String generateApplicationCheckId() { + return "check-" + generateApplicationServiceId(); + } + + //也会提供给HttpMessageClusterAgent适用 + public String generateHttpServiceName(String protocol, String module, String resname) { + return protocol.toLowerCase() + ":" + module + (resname == null || resname.isEmpty() ? "" : ("-" + resname)); + } + + //格式: protocol:classtype-resourcename + protected String generateServiceName(NodeServer ns, String protocol, Service service) { + if (protocol.toLowerCase().startsWith("http")) { //HTTP使用RestService.name方式是为了与MessageClient中的module保持一致, 因为HTTP依靠的url中的module,无法知道Service类名 + String resname = Sncp.getResourceName(service); + String module = Rest.getRestModule(service).toLowerCase(); + return protocol.toLowerCase() + ":" + module + (resname.isEmpty() ? "" : ("-" + resname)); + } + if ("mqtp".equalsIgnoreCase(protocol)) { + MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); + String selfmodule = Rest.getRestModule(service).toLowerCase(); + return protocol.toLowerCase() + ":" + mmc.module() + ":" + selfmodule; + } + if (!Sncp.isSncpDyn(service)) return protocol.toLowerCase() + ":" + service.getClass().getName(); + String resname = Sncp.getResourceName(service); + return protocol.toLowerCase() + ":" + Sncp.getResourceType(service).getName() + (resname.isEmpty() ? "" : ("-" + resname)); + } + + //格式: protocol:classtype-resourcename:nodeid + protected String generateServiceId(NodeServer ns, String protocol, Service service) { + return generateServiceName(ns, protocol, service) + ":" + this.nodeid; + } + + protected String generateCheckName(NodeServer ns, String protocol, Service service) { + return "check-" + generateServiceName(ns, protocol, service); + } + + protected String generateCheckId(NodeServer ns, String protocol, Service service) { + return "check-" + generateServiceId(ns, protocol, service); + } + + protected ConcurrentHashMap getLocalEntrys() { + return localEntrys; + } + + protected ConcurrentHashMap getRemoteEntrys() { + return remoteEntrys; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public TransportFactory getTransportFactory() { + return transportFactory; + } + + public void setTransportFactory(TransportFactory transportFactory) { + this.transportFactory = transportFactory; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String[] getProtocols() { + return protocols; + } + + public void setProtocols(String[] protocols) { + this.protocols = protocols; + } + + public int[] getPorts() { + return ports; + } + + public void setPorts(int[] ports) { + this.ports = ports; + } + + public AnyValue getConfig() { + return config; + } + + public void setConfig(AnyValue config) { + this.config = config; + } + + public class ClusterEntry { + + public String serviceid; + + public String servicename; + + public String checkid; + + public String checkname; + + public String protocol; + + public String netprotocol; + + public WeakReference serviceref; + + public InetSocketAddress address; + + public boolean canceled; + + public boolean submqtp; + + public ClusterEntry(NodeServer ns, String protocol, Service service) { + this.serviceid = generateServiceId(ns, protocol, service); + this.servicename = generateServiceName(ns, protocol, service); + this.checkid = generateCheckId(ns, protocol, service); + this.checkname = generateCheckName(ns, protocol, service); + this.protocol = protocol; + InetSocketAddress addr = ns.getSocketAddress(); + String host = addr.getHostString(); + if ("0.0.0.0".equals(host)) { + host = appAddress.getHostString(); + addr = new InetSocketAddress(host, addr.getPort()); + } + this.address = addr; + this.serviceref = new WeakReference(service); + Server server = ns.getServer(); + this.netprotocol = server instanceof SncpServer ? ((SncpServer) server).getNetprotocol() : Transport.DEFAULT_NETPROTOCOL; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/org/redkale/cluster/ClusterAgentLoader.java b/src/main/java/org/redkale/cluster/ClusterAgentProvider.java similarity index 77% rename from src/org/redkale/cluster/ClusterAgentLoader.java rename to src/main/java/org/redkale/cluster/ClusterAgentProvider.java index f5d711b49..8c37523c8 100644 --- a/src/org/redkale/cluster/ClusterAgentLoader.java +++ b/src/main/java/org/redkale/cluster/ClusterAgentProvider.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.cluster; - -import org.redkale.util.AnyValue; - -/** - * 自定义的ClusterAgent加载器 - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.4.0 - */ -public interface ClusterAgentLoader { - - public boolean match(AnyValue config); - - public Class agentClass(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.cluster; + +import org.redkale.util.AnyValue; + +/** + * 自定义的ClusterAgent加载器 + * + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +public interface ClusterAgentProvider { + + public boolean acceptsConf(AnyValue config); + + public Class agentClass(); +} diff --git a/src/org/redkale/convert/AnyDecoder.java b/src/main/java/org/redkale/convert/AnyDecoder.java similarity index 96% rename from src/org/redkale/convert/AnyDecoder.java rename to src/main/java/org/redkale/convert/AnyDecoder.java index b270d86a7..b6708e878 100644 --- a/src/org/redkale/convert/AnyDecoder.java +++ b/src/main/java/org/redkale/convert/AnyDecoder.java @@ -1,64 +1,64 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.Type; -import java.util.*; -import org.redkale.util.*; -import org.redkale.convert.Reader.ValueType; -import static org.redkale.convert.Reader.ValueType.MAP; - -/** - * 对不明类型的对象进行反序列化。
    - * 注意: 目前只支持文本格式
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class AnyDecoder implements Decodeable { - - private static final Type collectionObjectType = new TypeToken>() { - }.getType(); - - private static final Type mapObjectType = new TypeToken>() { - }.getType(); - - private static final Creator collectionCreator = Creator.create(ArrayList.class); - - private static final Creator mapCreator = Creator.create(HashMap.class); - - protected final Decodeable stringDecoder; - - protected final CollectionDecoder collectionDecoder; - - protected final MapDecoder mapDecoder; - - public AnyDecoder(final ConvertFactory factory) { - this.stringDecoder = factory.loadDecoder(String.class); - this.collectionDecoder = new CollectionDecoder(factory, collectionObjectType, Object.class, collectionCreator, this); - this.mapDecoder = new MapDecoder(factory, mapObjectType, String.class, Object.class, mapCreator, stringDecoder, this); - } - - @Override - public Object convertFrom(Reader in) { - ValueType vt = in.readType(); - if (vt == null) return null; - switch (vt) { - case ARRAY: - return this.collectionDecoder.convertFrom(in); - case MAP: - return this.mapDecoder.convertFrom(in); - } - return this.stringDecoder.convertFrom(in); - } - - @Override - public Type getType() { - return void.class; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; +import java.util.*; +import org.redkale.util.*; +import org.redkale.convert.Reader.ValueType; +import static org.redkale.convert.Reader.ValueType.MAP; + +/** + * 对不明类型的对象进行反序列化。
    + * 注意: 目前只支持文本格式
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class AnyDecoder implements Decodeable { + + private static final Type collectionObjectType = new TypeToken>() { + }.getType(); + + private static final Type mapObjectType = new TypeToken>() { + }.getType(); + + private static final Creator collectionCreator = Creator.create(ArrayList.class); + + private static final Creator mapCreator = Creator.create(HashMap.class); + + protected final Decodeable stringDecoder; + + protected final CollectionDecoder collectionDecoder; + + protected final MapDecoder mapDecoder; + + public AnyDecoder(final ConvertFactory factory) { + this.stringDecoder = factory.loadDecoder(String.class); + this.collectionDecoder = new CollectionDecoder(factory, collectionObjectType, Object.class, collectionCreator, this); + this.mapDecoder = new MapDecoder(factory, mapObjectType, String.class, Object.class, mapCreator, stringDecoder, this); + } + + @Override + public Object convertFrom(Reader in) { + ValueType vt = in.readType(); + if (vt == null) return null; + switch (vt) { + case ARRAY: + return this.collectionDecoder.convertFrom(in); + case MAP: + return this.mapDecoder.convertFrom(in); + } + return this.stringDecoder.convertFrom(in); + } + + @Override + public Type getType() { + return void.class; + } + +} diff --git a/src/org/redkale/convert/AnyEncoder.java b/src/main/java/org/redkale/convert/AnyEncoder.java similarity index 96% rename from src/org/redkale/convert/AnyEncoder.java rename to src/main/java/org/redkale/convert/AnyEncoder.java index 310ce4633..c118fb5f0 100644 --- a/src/org/redkale/convert/AnyEncoder.java +++ b/src/main/java/org/redkale/convert/AnyEncoder.java @@ -1,73 +1,73 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.Type; -import java.util.concurrent.CompletableFuture; - -/** - * 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 序列化的泛型类型 - */ -public final class AnyEncoder implements Encodeable { - - final ConvertFactory factory; - - AnyEncoder(ConvertFactory factory) { - this.factory = factory; - } - - @Override - @SuppressWarnings("unchecked") - public void convertTo(final Writer out, final T value) { - if (value == null) { - out.writeClassName(null); - out.writeNull(); - } else { - Class clazz = value.getClass(); - if (clazz == Object.class) { - out.writeObjectB(value); - out.writeObjectE(value); - return; - } - if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clazz)); - factory.loadEncoder(clazz).convertTo(out, value); - } - } - - @SuppressWarnings("unchecked") - public void convertMapTo(final Writer out, final Object... values) { - if (values == null) { - out.writeNull(); - } else { - int count = values.length - values.length % 2; - if (out.writeMapB(count / 2, (Encodeable) this, (Encodeable) this, values) < 0) { - for (int i = 0; i < count; i += 2) { - if (i > 0) out.writeArrayMark(); - this.convertTo(out, (T) values[i]); - out.writeMapMark(); - Object val = values[i + 1]; - if (val instanceof CompletableFuture) { - this.convertTo(out, (T) ((CompletableFuture) val).join()); - } else { - this.convertTo(out, (T) val); - } - } - } - out.writeMapE(); - } - } - - @Override - public Type getType() { - return Object.class; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; +import java.util.concurrent.CompletableFuture; + +/** + * 对不明类型的对象进行序列化; BSON序列化时将对象的类名写入Writer,JSON则不写入。 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 序列化的泛型类型 + */ +public final class AnyEncoder implements Encodeable { + + final ConvertFactory factory; + + AnyEncoder(ConvertFactory factory) { + this.factory = factory; + } + + @Override + @SuppressWarnings("unchecked") + public void convertTo(final Writer out, final T value) { + if (value == null) { + out.writeClassName(null); + out.writeNull(); + } else { + Class clazz = value.getClass(); + if (clazz == Object.class) { + out.writeObjectB(value); + out.writeObjectE(value); + return; + } + if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clazz)); + factory.loadEncoder(clazz).convertTo(out, value); + } + } + + @SuppressWarnings("unchecked") + public void convertMapTo(final Writer out, final Object... values) { + if (values == null) { + out.writeNull(); + } else { + int count = values.length - values.length % 2; + if (out.writeMapB(count / 2, (Encodeable) this, (Encodeable) this, values) < 0) { + for (int i = 0; i < count; i += 2) { + if (i > 0) out.writeArrayMark(); + this.convertTo(out, (T) values[i]); + out.writeMapMark(); + Object val = values[i + 1]; + if (val instanceof CompletableFuture) { + this.convertTo(out, (T) ((CompletableFuture) val).join()); + } else { + this.convertTo(out, (T) val); + } + } + } + out.writeMapE(); + } + } + + @Override + public Type getType() { + return Object.class; + } + +} diff --git a/src/main/java/org/redkale/convert/AnyValueDecoder.java b/src/main/java/org/redkale/convert/AnyValueDecoder.java new file mode 100644 index 000000000..52f46d1a4 --- /dev/null +++ b/src/main/java/org/redkale/convert/AnyValueDecoder.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; +import org.redkale.util.AnyValue; + +/** + * AnyValue的Decoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * + * @since 2.5.0 + */ +public class AnyValueDecoder implements Decodeable { + + protected final ConvertFactory factory; + + public AnyValueDecoder(final ConvertFactory factory) { + this.factory = factory; + } + + @Override + public AnyValue convertFrom(R in) { + return null; + } + + @Override + public Type getType() { + return AnyValue.class; + } + +} diff --git a/src/main/java/org/redkale/convert/AnyValueEncoder.java b/src/main/java/org/redkale/convert/AnyValueEncoder.java new file mode 100644 index 000000000..93a7f5ecc --- /dev/null +++ b/src/main/java/org/redkale/convert/AnyValueEncoder.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; +import org.redkale.util.AnyValue; + +/** + * AnyValue的Encoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Writer输出的子类 + * + * @since 2.5.0 + */ +public class AnyValueEncoder implements Encodeable { + + @Override + public void convertTo(W out, AnyValue value) { + } + + @Override + public Type getType() { + return AnyValue.class; + } + +} diff --git a/src/org/redkale/convert/ArrayDecoder.java b/src/main/java/org/redkale/convert/ArrayDecoder.java similarity index 97% rename from src/org/redkale/convert/ArrayDecoder.java rename to src/main/java/org/redkale/convert/ArrayDecoder.java index 19ed88547..4a3f355ba 100644 --- a/src/org/redkale/convert/ArrayDecoder.java +++ b/src/main/java/org/redkale/convert/ArrayDecoder.java @@ -1,144 +1,144 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import java.util.*; - -/** - * 数组的反序列化操作类
    - * 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组。
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 反解析的数组元素类型 - */ -@SuppressWarnings("unchecked") -public class ArrayDecoder implements Decodeable { - - protected final Type type; - - protected final Type componentType; - - protected final Class componentClass; - - protected final Decodeable componentDecoder; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public ArrayDecoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof GenericArrayType) { - Type t = ((GenericArrayType) type).getGenericComponentType(); - this.componentType = t instanceof TypeVariable ? Object.class : t; - } else if ((type instanceof Class) && ((Class) type).isArray()) { - this.componentType = ((Class) type).getComponentType(); - } else { - throw new ConvertException("(" + type + ") is not a array type"); - } - if (this.componentType instanceof ParameterizedType) { - this.componentClass = (Class) ((ParameterizedType) this.componentType).getRawType(); - } else { - this.componentClass = (Class) this.componentType; - } - factory.register(type, this); - this.componentDecoder = factory.loadDecoder(this.componentType); - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - @Override - public T[] convertFrom(Reader in) { - return convertFrom(in, null); - } - - public T[] convertFrom(Reader in, DeMember member) { - byte[] typevals = new byte[1]; - int len = in.readArrayB(member, typevals, componentDecoder); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(member, componentDecoder); - len = Reader.SIGN_NOLENGTH; - } - if (this.componentDecoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - final Decodeable localdecoder = getComponentDecoder(this.componentDecoder, typevals); - final List result = new ArrayList(); - boolean first = true; - if (len == Reader.SIGN_NOLENGTH) { - int startPosition = in.position(); - while (hasNext(in, member, startPosition, contentLength, first)) { - Reader itemReader = getItemReader(in, member, first); - if (itemReader == null) break; - result.add(readMemberValue(itemReader, member, localdecoder, first)); - first = false; - } - } else { - for (int i = 0; i < len; i++) { - result.add(localdecoder.convertFrom(in)); - } - } - in.readArrayE(); - T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size()); - return result.toArray(rs); - } - - protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { - return in.hasNext(startPosition, contentLength); - } - - protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { - return decoder; - } - - protected Reader getItemReader(Reader in, DeMember member, boolean first) { - return in; - } - - protected T readMemberValue(Reader in, DeMember member, Decodeable decoder, boolean first) { - if (in == null) return null; - return decoder.convertFrom(in); - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.componentDecoder + "}"; - } - - @Override - public Type getType() { - return type; - } - - public Type getComponentType() { - return componentType; - } - - public Decodeable getComponentDecoder() { - return componentDecoder; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import java.util.*; + +/** + * 数组的反序列化操作类
    + * 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组。
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 反解析的数组元素类型 + */ +@SuppressWarnings("unchecked") +public class ArrayDecoder implements Decodeable { + + protected final Type type; + + protected final Type componentType; + + protected final Class componentClass; + + protected final Decodeable componentDecoder; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public ArrayDecoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof GenericArrayType) { + Type t = ((GenericArrayType) type).getGenericComponentType(); + this.componentType = t instanceof TypeVariable ? Object.class : t; + } else if ((type instanceof Class) && ((Class) type).isArray()) { + this.componentType = ((Class) type).getComponentType(); + } else { + throw new ConvertException("(" + type + ") is not a array type"); + } + if (this.componentType instanceof ParameterizedType) { + this.componentClass = (Class) ((ParameterizedType) this.componentType).getRawType(); + } else { + this.componentClass = (Class) this.componentType; + } + factory.register(type, this); + this.componentDecoder = factory.loadDecoder(this.componentType); + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + @Override + public T[] convertFrom(Reader in) { + return convertFrom(in, null); + } + + public T[] convertFrom(Reader in, DeMember member) { + byte[] typevals = new byte[1]; + int len = in.readArrayB(member, typevals, componentDecoder); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(member, componentDecoder); + len = Reader.SIGN_NOLENGTH; + } + if (this.componentDecoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + final Decodeable localdecoder = getComponentDecoder(this.componentDecoder, typevals); + final List result = new ArrayList(); + boolean first = true; + if (len == Reader.SIGN_NOLENGTH) { + int startPosition = in.position(); + while (hasNext(in, member, startPosition, contentLength, first)) { + Reader itemReader = getItemReader(in, member, first); + if (itemReader == null) break; + result.add(readMemberValue(itemReader, member, localdecoder, first)); + first = false; + } + } else { + for (int i = 0; i < len; i++) { + result.add(localdecoder.convertFrom(in)); + } + } + in.readArrayE(); + T[] rs = (T[]) Array.newInstance((Class) this.componentClass, result.size()); + return result.toArray(rs); + } + + protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { + return in.hasNext(startPosition, contentLength); + } + + protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { + return decoder; + } + + protected Reader getItemReader(Reader in, DeMember member, boolean first) { + return in; + } + + protected T readMemberValue(Reader in, DeMember member, Decodeable decoder, boolean first) { + if (in == null) return null; + return decoder.convertFrom(in); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", decoder:" + this.componentDecoder + "}"; + } + + @Override + public Type getType() { + return type; + } + + public Type getComponentType() { + return componentType; + } + + public Decodeable getComponentDecoder() { + return componentDecoder; + } + +} diff --git a/src/org/redkale/convert/ArrayEncoder.java b/src/main/java/org/redkale/convert/ArrayEncoder.java similarity index 97% rename from src/org/redkale/convert/ArrayEncoder.java rename to src/main/java/org/redkale/convert/ArrayEncoder.java index 8dadd3543..d19447780 100644 --- a/src/org/redkale/convert/ArrayEncoder.java +++ b/src/main/java/org/redkale/convert/ArrayEncoder.java @@ -1,133 +1,133 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; - -/** - * 数组的序列化操作类
    - * 对象数组的序列化,不包含int[]、long[]这样的primitive class数组。
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 序列化的数组元素类型 - */ -@SuppressWarnings("unchecked") -public class ArrayEncoder implements Encodeable { - - protected final Type type; - - protected final Type componentType; - - protected final Encodeable anyEncoder; - - protected final Encodeable componentEncoder; - - protected final boolean subtypefinal; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public ArrayEncoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof GenericArrayType) { - Type t = ((GenericArrayType) type).getGenericComponentType(); - this.componentType = t instanceof TypeVariable ? Object.class : t; - } else if ((type instanceof Class) && ((Class) type).isArray()) { - this.componentType = ((Class) type).getComponentType(); - } else { - throw new ConvertException("(" + type + ") is not a array type"); - } - factory.register(type, this); - this.componentEncoder = factory.loadEncoder(this.componentType); - this.anyEncoder = factory.getAnyEncoder(); - this.subtypefinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers()); - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - @Override - public void convertTo(Writer out, T[] value) { - convertTo(out, null, value); - } - - public void convertTo(Writer out, EnMember member, T[] value) { - if (value == null) { - out.writeNull(); - return; - } - int iMax = value.length - 1; - if (iMax == -1) { - out.writeArrayB(0, this, componentEncoder, value); - out.writeArrayE(); - return; - } - if (this.componentEncoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - Encodeable itemEncoder = this.componentEncoder; - if (subtypefinal) { - if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) { - for (int i = 0;; i++) { - writeMemberValue(out, member, itemEncoder, value[i], i); - if (i == iMax) break; - out.writeArrayMark(); - } - } - } else { - if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) { - final Type comp = this.componentType; - for (int i = 0;; i++) { - Object v = value[i]; - writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, i); - if (i == iMax) break; - out.writeArrayMark(); - } - } - } - out.writeArrayE(); - } - - protected void writeMemberValue(Writer out, EnMember member, Encodeable encoder, Object value, int index) { - encoder.convertTo(out, value); - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.componentEncoder + "}"; - } - - @Override - public Type getType() { - return type; - } - - public Type getComponentType() { - return componentType; - } - - public Encodeable getComponentEncoder() { - return componentEncoder; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; + +/** + * 数组的序列化操作类
    + * 对象数组的序列化,不包含int[]、long[]这样的primitive class数组。
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 序列化的数组元素类型 + */ +@SuppressWarnings("unchecked") +public class ArrayEncoder implements Encodeable { + + protected final Type type; + + protected final Type componentType; + + protected final Encodeable anyEncoder; + + protected final Encodeable componentEncoder; + + protected final boolean subtypefinal; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public ArrayEncoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof GenericArrayType) { + Type t = ((GenericArrayType) type).getGenericComponentType(); + this.componentType = t instanceof TypeVariable ? Object.class : t; + } else if ((type instanceof Class) && ((Class) type).isArray()) { + this.componentType = ((Class) type).getComponentType(); + } else { + throw new ConvertException("(" + type + ") is not a array type"); + } + factory.register(type, this); + this.componentEncoder = factory.loadEncoder(this.componentType); + this.anyEncoder = factory.getAnyEncoder(); + this.subtypefinal = (this.componentType instanceof Class) && Modifier.isFinal(((Class) this.componentType).getModifiers()); + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + @Override + public void convertTo(Writer out, T[] value) { + convertTo(out, null, value); + } + + public void convertTo(Writer out, EnMember member, T[] value) { + if (value == null) { + out.writeNull(); + return; + } + int iMax = value.length - 1; + if (iMax == -1) { + out.writeArrayB(0, this, componentEncoder, value); + out.writeArrayE(); + return; + } + if (this.componentEncoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + Encodeable itemEncoder = this.componentEncoder; + if (subtypefinal) { + if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) { + for (int i = 0;; i++) { + writeMemberValue(out, member, itemEncoder, value[i], i); + if (i == iMax) break; + out.writeArrayMark(); + } + } + } else { + if (out.writeArrayB(value.length, this, itemEncoder, value) < 0) { + final Type comp = this.componentType; + for (int i = 0;; i++) { + Object v = value[i]; + writeMemberValue(out, member, ((v != null && (v.getClass() == comp || out.specify() == comp)) ? itemEncoder : anyEncoder), v, i); + if (i == iMax) break; + out.writeArrayMark(); + } + } + } + out.writeArrayE(); + } + + protected void writeMemberValue(Writer out, EnMember member, Encodeable encoder, Object value, int index) { + encoder.convertTo(out, value); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{componentType:" + this.componentType + ", encoder:" + this.componentEncoder + "}"; + } + + @Override + public Type getType() { + return type; + } + + public Type getComponentType() { + return componentType; + } + + public Encodeable getComponentEncoder() { + return componentEncoder; + } + +} diff --git a/src/org/redkale/convert/BinaryConvert.java b/src/main/java/org/redkale/convert/BinaryConvert.java similarity index 96% rename from src/org/redkale/convert/BinaryConvert.java rename to src/main/java/org/redkale/convert/BinaryConvert.java index 6db363567..3c3c4254e 100644 --- a/src/org/redkale/convert/BinaryConvert.java +++ b/src/main/java/org/redkale/convert/BinaryConvert.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.Type; - -/** - * 二进制序列化/反序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param Writer输出的子类 - */ -public abstract class BinaryConvert extends Convert { - - protected BinaryConvert(ConvertFactory factory) { - super(factory); - } - - @Override - public final boolean isBinary() { - return true; - } - - public abstract byte[] convertTo(final Object value); - - public abstract byte[] convertTo(final Type type, final Object value); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; + +/** + * 二进制序列化/反序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param Writer输出的子类 + */ +public abstract class BinaryConvert extends Convert { + + protected BinaryConvert(ConvertFactory factory) { + super(factory); + } + + @Override + public final boolean isBinary() { + return true; + } + + public abstract byte[] convertTo(final Object value); + + public abstract byte[] convertTo(final Type type, final Object value); + +} diff --git a/src/org/redkale/convert/CollectionDecoder.java b/src/main/java/org/redkale/convert/CollectionDecoder.java similarity index 97% rename from src/org/redkale/convert/CollectionDecoder.java rename to src/main/java/org/redkale/convert/CollectionDecoder.java index a67feccdd..af5538397 100644 --- a/src/org/redkale/convert/CollectionDecoder.java +++ b/src/main/java/org/redkale/convert/CollectionDecoder.java @@ -1,149 +1,149 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import org.redkale.util.Creator; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.*; - -/** - * Collection的反序列化操作类
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 反解析的集合元素类型 - */ -@SuppressWarnings("unchecked") -public class CollectionDecoder implements Decodeable> { - - protected final Type type; - - protected final Type componentType; - - protected Creator> creator; - - protected final Decodeable componentDecoder; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public CollectionDecoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof ParameterizedType) { - final ParameterizedType pt = (ParameterizedType) type; - this.componentType = pt.getActualTypeArguments()[0]; - this.creator = factory.loadCreator((Class) pt.getRawType()); - factory.register(type, this); - this.componentDecoder = factory.loadDecoder(this.componentType); - } else if (factory.isReversible()) { - this.componentType = Object.class; - this.creator = factory.loadCreator(type instanceof Class ? (Class) type : Collection.class); - factory.register(type, this); - this.componentDecoder = factory.loadDecoder(this.componentType); - } else { - throw new ConvertException("CollectionDecoder not support the type (" + type + ")"); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - //仅供类似JsonAnyDecoder这种动态创建使用, 不得调用 factory.register - public CollectionDecoder(final ConvertFactory factory, Type type, Type componentType, - Creator> creator, final Decodeable componentDecoder) { - Objects.requireNonNull(componentDecoder); - this.type = type; - this.componentType = componentType; - this.creator = creator; - this.componentDecoder = componentDecoder; - this.inited = true; - } - - @Override - public Collection convertFrom(Reader in) { - return convertFrom(in, null); - } - - public Collection convertFrom(Reader in, DeMember member) { - byte[] typevals = new byte[1]; - int len = in.readArrayB(member, typevals, componentDecoder); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(member, componentDecoder); - len = Reader.SIGN_NOLENGTH; - } - if (this.componentDecoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - final Decodeable localdecoder = getComponentDecoder(this.componentDecoder, typevals); - final Collection result = this.creator.create(); - boolean first = true; - if (len == Reader.SIGN_NOLENGTH) { - int startPosition = in.position(); - while (hasNext(in, member, startPosition, contentLength, first)) { - Reader itemReader = getItemReader(in, member, first); - if (itemReader == null) break; - result.add(readMemberValue(itemReader, member, localdecoder, first)); - first = false; - } - } else { - for (int i = 0; i < len; i++) { - result.add(localdecoder.convertFrom(in)); - } - } - in.readArrayE(); - return result; - } - - protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { - return in.hasNext(startPosition, contentLength); - } - - protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { - return decoder; - } - - protected Reader getItemReader(Reader in, DeMember member, boolean first) { - return in; - } - - protected T readMemberValue(Reader in, DeMember member, Decodeable decoder, boolean first) { - if (in == null) return null; - return decoder.convertFrom(in); - } - - @Override - public Type getType() { - return type; - } - - public Type getComponentType() { - return componentType; - } - - public Decodeable getComponentDecoder() { - return componentDecoder; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import org.redkale.util.Creator; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; + +/** + * Collection的反序列化操作类
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 反解析的集合元素类型 + */ +@SuppressWarnings("unchecked") +public class CollectionDecoder implements Decodeable> { + + protected final Type type; + + protected final Type componentType; + + protected Creator> creator; + + protected final Decodeable componentDecoder; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public CollectionDecoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.componentType = pt.getActualTypeArguments()[0]; + this.creator = factory.loadCreator((Class) pt.getRawType()); + factory.register(type, this); + this.componentDecoder = factory.loadDecoder(this.componentType); + } else if (factory.isReversible()) { + this.componentType = Object.class; + this.creator = factory.loadCreator(type instanceof Class ? (Class) type : Collection.class); + factory.register(type, this); + this.componentDecoder = factory.loadDecoder(this.componentType); + } else { + throw new ConvertException("CollectionDecoder not support the type (" + type + ")"); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + //仅供类似JsonAnyDecoder这种动态创建使用, 不得调用 factory.register + public CollectionDecoder(final ConvertFactory factory, Type type, Type componentType, + Creator> creator, final Decodeable componentDecoder) { + Objects.requireNonNull(componentDecoder); + this.type = type; + this.componentType = componentType; + this.creator = creator; + this.componentDecoder = componentDecoder; + this.inited = true; + } + + @Override + public Collection convertFrom(Reader in) { + return convertFrom(in, null); + } + + public Collection convertFrom(Reader in, DeMember member) { + byte[] typevals = new byte[1]; + int len = in.readArrayB(member, typevals, componentDecoder); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(member, componentDecoder); + len = Reader.SIGN_NOLENGTH; + } + if (this.componentDecoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + final Decodeable localdecoder = getComponentDecoder(this.componentDecoder, typevals); + final Collection result = this.creator.create(); + boolean first = true; + if (len == Reader.SIGN_NOLENGTH) { + int startPosition = in.position(); + while (hasNext(in, member, startPosition, contentLength, first)) { + Reader itemReader = getItemReader(in, member, first); + if (itemReader == null) break; + result.add(readMemberValue(itemReader, member, localdecoder, first)); + first = false; + } + } else { + for (int i = 0; i < len; i++) { + result.add(localdecoder.convertFrom(in)); + } + } + in.readArrayE(); + return result; + } + + protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { + return in.hasNext(startPosition, contentLength); + } + + protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { + return decoder; + } + + protected Reader getItemReader(Reader in, DeMember member, boolean first) { + return in; + } + + protected T readMemberValue(Reader in, DeMember member, Decodeable decoder, boolean first) { + if (in == null) return null; + return decoder.convertFrom(in); + } + + @Override + public Type getType() { + return type; + } + + public Type getComponentType() { + return componentType; + } + + public Decodeable getComponentDecoder() { + return componentDecoder; + } + +} diff --git a/src/org/redkale/convert/CollectionEncoder.java b/src/main/java/org/redkale/convert/CollectionEncoder.java similarity index 96% rename from src/org/redkale/convert/CollectionEncoder.java rename to src/main/java/org/redkale/convert/CollectionEncoder.java index f294fd412..64233ffb9 100644 --- a/src/org/redkale/convert/CollectionEncoder.java +++ b/src/main/java/org/redkale/convert/CollectionEncoder.java @@ -1,111 +1,111 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import java.util.Collection; - -/** - * Collection的序列化操作类
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 序列化的集合元素类型 - */ -@SuppressWarnings("unchecked") -public class CollectionEncoder implements Encodeable> { - - protected final Type type; - - protected final Encodeable componentEncoder; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public CollectionEncoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof ParameterizedType) { - Type t = ((ParameterizedType) type).getActualTypeArguments()[0]; - if (t instanceof TypeVariable) { - this.componentEncoder = factory.getAnyEncoder(); - } else { - this.componentEncoder = factory.loadEncoder(t); - } - } else { - this.componentEncoder = factory.getAnyEncoder(); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - @Override - public void convertTo(Writer out, Collection value) { - convertTo(out, null, value); - } - - public void convertTo(Writer out, EnMember member, Collection value) { - if (value == null) { - out.writeNull(); - return; - } - if (value.isEmpty()) { - out.writeArrayB(0, this, componentEncoder, value); - out.writeArrayE(); - return; - } - if (this.componentEncoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - if (out.writeArrayB(value.size(), this, componentEncoder, value) < 0) { - boolean first = true; - for (Object v : value) { - if (!first) out.writeArrayMark(); - writeMemberValue(out, member, v, first); - if (first) first = false; - } - } - out.writeArrayE(); - } - - protected void writeMemberValue(Writer out, EnMember member, Object value, boolean first) { - componentEncoder.convertTo(out, value); - } - - @Override - public Type getType() { - return type; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "{componentType:" + this.type + ", encoder:" + this.componentEncoder + "}"; - } - - public Encodeable getComponentEncoder() { - return componentEncoder; - } - - public Type getComponentType() { - return componentEncoder == null ? null : componentEncoder.getType(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import java.util.Collection; + +/** + * Collection的序列化操作类
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 序列化的集合元素类型 + */ +@SuppressWarnings("unchecked") +public class CollectionEncoder implements Encodeable> { + + protected final Type type; + + protected final Encodeable componentEncoder; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public CollectionEncoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof ParameterizedType) { + Type t = ((ParameterizedType) type).getActualTypeArguments()[0]; + if (t instanceof TypeVariable) { + this.componentEncoder = factory.getAnyEncoder(); + } else { + this.componentEncoder = factory.loadEncoder(t); + } + } else { + this.componentEncoder = factory.getAnyEncoder(); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + @Override + public void convertTo(Writer out, Collection value) { + convertTo(out, null, value); + } + + public void convertTo(Writer out, EnMember member, Collection value) { + if (value == null) { + out.writeNull(); + return; + } + if (value.isEmpty()) { + out.writeArrayB(0, this, componentEncoder, value); + out.writeArrayE(); + return; + } + if (this.componentEncoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + if (out.writeArrayB(value.size(), this, componentEncoder, value) < 0) { + boolean first = true; + for (Object v : value) { + if (!first) out.writeArrayMark(); + writeMemberValue(out, member, v, first); + if (first) first = false; + } + } + out.writeArrayE(); + } + + protected void writeMemberValue(Writer out, EnMember member, Object value, boolean first) { + componentEncoder.convertTo(out, value); + } + + @Override + public Type getType() { + return type; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{componentType:" + this.type + ", encoder:" + this.componentEncoder + "}"; + } + + public Encodeable getComponentEncoder() { + return componentEncoder; + } + + public Type getComponentType() { + return componentEncoder == null ? null : componentEncoder.getType(); + } +} diff --git a/src/org/redkale/convert/Convert.java b/src/main/java/org/redkale/convert/Convert.java similarity index 94% rename from src/org/redkale/convert/Convert.java rename to src/main/java/org/redkale/convert/Convert.java index 846e1f999..cfbbdfc8e 100644 --- a/src/org/redkale/convert/Convert.java +++ b/src/main/java/org/redkale/convert/Convert.java @@ -1,76 +1,80 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.util.function.*; -import org.redkale.util.*; - -/** - * 序列化/反序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param Writer输出的子类 - */ -public abstract class Convert { - - protected final ConvertFactory factory; - - protected Convert(ConvertFactory factory) { - this.factory = factory; - } - - public ConvertFactory getFactory() { - return this.factory; - } - - protected S configWrite(S writer) { - return writer; - } - - protected S fieldFunc(S writer, BiFunction objFieldFunc, Function objExtFunc) { - writer.objFieldFunc = objFieldFunc; - writer.objExtFunc = objExtFunc; - return writer; - } - - public abstract Convert newConvert(final BiFunction objFieldFunc); - - public abstract Convert newConvert(final BiFunction objFieldFunc, Function objExtFunc); - - public abstract boolean isBinary(); - - public abstract T convertFrom(final Type type, final byte[] bytes); - - //@since 2.2.0 - public abstract T convertFrom(final Type type, final byte[] bytes, final int offset, final int length); - - public abstract T convertFrom(final Type type, final ByteBuffer... buffers); - - public abstract T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers); - - public abstract byte[] convertToBytes(final Object value); - - public abstract byte[] convertToBytes(final Type type, final Object value); - - public abstract void convertToBytes(final Object value, final ConvertBytesHandler handler); - - public abstract void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler); - - public abstract void convertToBytes(final ByteArray array, final Object value); - - public abstract void convertToBytes(final ByteArray array, final Type type, final Object value); - - public abstract ByteBuffer[] convertTo(final Supplier supplier, final Object value); - - public abstract ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; +import java.nio.ByteBuffer; +import java.util.function.*; +import org.redkale.util.*; + +/** + * 序列化/反序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param Writer输出的子类 + */ +public abstract class Convert { + + protected final ConvertFactory factory; + + protected Convert(ConvertFactory factory) { + this.factory = factory; + } + + public ConvertFactory getFactory() { + return this.factory; + } + + protected S configWrite(S writer) { + return writer; + } + + protected S fieldFunc(S writer, BiFunction objFieldFunc, Function objExtFunc) { + writer.objFieldFunc = objFieldFunc; + writer.objExtFunc = objExtFunc; + return writer; + } + + public abstract Convert newConvert(final BiFunction objFieldFunc); + + public abstract Convert newConvert(final BiFunction objFieldFunc, Function objExtFunc); + + public abstract boolean isBinary(); + + public abstract T convertFrom(final Type type, final byte[] bytes); + + //@since 2.2.0 + public abstract T convertFrom(final Type type, final byte[] bytes, final int offset, final int length); + + public abstract T convertFrom(final Type type, final ByteBuffer... buffers); + + public abstract T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers); + + public abstract void convertTo(final W writer, final Object value); + + public abstract void convertTo(final W writer, final Type type, final Object value); + + public abstract byte[] convertToBytes(final Object value); + + public abstract byte[] convertToBytes(final Type type, final Object value); + + public abstract void convertToBytes(final Object value, final ConvertBytesHandler handler); + + public abstract void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler); + + public abstract void convertToBytes(final ByteArray array, final Object value); + + public abstract void convertToBytes(final ByteArray array, final Type type, final Object value); + + public abstract ByteBuffer[] convertTo(final Supplier supplier, final Object value); + + public abstract ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value); + +} diff --git a/src/org/redkale/convert/ConvertBytesHandler.java b/src/main/java/org/redkale/convert/ConvertBytesHandler.java similarity index 95% rename from src/org/redkale/convert/ConvertBytesHandler.java rename to src/main/java/org/redkale/convert/ConvertBytesHandler.java index 320ca0dcf..c1386ce6b 100644 --- a/src/org/redkale/convert/ConvertBytesHandler.java +++ b/src/main/java/org/redkale/convert/ConvertBytesHandler.java @@ -1,23 +1,23 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.util.function.Consumer; - -/** - * - * convertToBytes系列的方法的回调 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.3.0 - */ -public interface ConvertBytesHandler { - - void completed(byte[] bs, int offset, int length, Consumer callback, A attachment); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.util.function.Consumer; + +/** + * + * convertToBytes系列的方法的回调 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + */ +public interface ConvertBytesHandler { + + void completed(byte[] bs, int offset, int length, Consumer callback, A attachment); +} diff --git a/src/org/redkale/convert/ConvertColumn.java b/src/main/java/org/redkale/convert/ConvertColumn.java similarity index 95% rename from src/org/redkale/convert/ConvertColumn.java rename to src/main/java/org/redkale/convert/ConvertColumn.java index 6769e0cf7..9d220c346 100644 --- a/src/org/redkale/convert/ConvertColumn.java +++ b/src/main/java/org/redkale/convert/ConvertColumn.java @@ -1,71 +1,71 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - -/** - * 依附在setter、getter方法、字段进行简单的配置 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({METHOD, FIELD}) -@Retention(RUNTIME) -@Repeatable(ConvertColumn.ConvertColumns.class) -public @interface ConvertColumn { - - /** - * 给字段取个别名 - * - * @return 字段别名 - */ - String name() default ""; - - /** - * 给字段取个序号ID,值小靠前 - * - * @return 字段排序ID - */ - int index() default 0; - - /** - * 解析/序列化时是否屏蔽该字段 - * - * @return 是否屏蔽该字段 - */ - boolean ignore() default false; - - /** - * 解析/序列化定制化的TYPE - * - * @return JSON or BSON or ALL - */ - ConvertType type() default ConvertType.ALL; - - /** - * ConvertColumn 的多用类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ - @Inherited - @Documented - @Target({METHOD, FIELD}) - @Retention(RUNTIME) - public static @interface ConvertColumns { - - ConvertColumn[] value(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * 依附在setter、getter方法、字段进行简单的配置 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD, FIELD}) +@Retention(RUNTIME) +@Repeatable(ConvertColumn.ConvertColumns.class) +public @interface ConvertColumn { + + /** + * 给字段取个别名 + * + * @return 字段别名 + */ + String name() default ""; + + /** + * 给字段取个序号ID,值小靠前 + * + * @return 字段排序ID + */ + int index() default 0; + + /** + * 解析/序列化时是否屏蔽该字段 + * + * @return 是否屏蔽该字段 + */ + boolean ignore() default false; + + /** + * 解析/序列化定制化的TYPE + * + * @return JSON or BSON or ALL + */ + ConvertType type() default ConvertType.ALL; + + /** + * ConvertColumn 的多用类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ + @Inherited + @Documented + @Target({METHOD, FIELD}) + @Retention(RUNTIME) + public static @interface ConvertColumns { + + ConvertColumn[] value(); + } +} diff --git a/src/org/redkale/convert/ConvertColumnEntry.java b/src/main/java/org/redkale/convert/ConvertColumnEntry.java similarity index 95% rename from src/org/redkale/convert/ConvertColumnEntry.java rename to src/main/java/org/redkale/convert/ConvertColumnEntry.java index 1b10dd09a..5b119f445 100644 --- a/src/org/redkale/convert/ConvertColumnEntry.java +++ b/src/main/java/org/redkale/convert/ConvertColumnEntry.java @@ -1,97 +1,97 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -/** - * ConvertColumn 对应的实体类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class ConvertColumnEntry { - - private int index; - - private String name = ""; - - private boolean ignore; - - private ConvertType convertType; - - public ConvertColumnEntry() { - } - - public ConvertColumnEntry(ConvertColumn column) { - if (column == null) return; - this.name = column.name(); - this.index = column.index(); - this.ignore = column.ignore(); - this.convertType = column.type(); - } - - public ConvertColumnEntry(String name) { - this(name, false); - } - - public ConvertColumnEntry(String name, boolean ignore) { - this.name = name; - this.ignore = ignore; - this.convertType = ConvertType.ALL; - } - - public ConvertColumnEntry(String name, boolean ignore, ConvertType convertType) { - this.name = name; - this.ignore = ignore; - this.convertType = convertType; - } - - public ConvertColumnEntry(String name, int index, boolean ignore, ConvertType convertType) { - this.name = name; - this.index = index; - this.ignore = ignore; - this.convertType = convertType; - } - - public String name() { - return name == null ? "" : name; - } - - public void setName(String name) { - this.name = name; - } - - public boolean ignore() { - return ignore; - } - - public void setIgnore(boolean ignore) { - this.ignore = ignore; - } - - public ConvertType type() { - return convertType == null ? ConvertType.ALL : convertType; - } - - public void setConvertType(ConvertType convertType) { - this.convertType = convertType; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - @Override - public String toString() { - return "ConvertColumnEntry{" + "index=" + index + ", name=" + name + ", ignore=" + ignore + ", convertType=" + convertType + '}'; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +/** + * ConvertColumn 对应的实体类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class ConvertColumnEntry { + + private int index; + + private String name = ""; + + private boolean ignore; + + private ConvertType convertType; + + public ConvertColumnEntry() { + } + + public ConvertColumnEntry(ConvertColumn column) { + if (column == null) return; + this.name = column.name(); + this.index = column.index(); + this.ignore = column.ignore(); + this.convertType = column.type(); + } + + public ConvertColumnEntry(String name) { + this(name, false); + } + + public ConvertColumnEntry(String name, boolean ignore) { + this.name = name; + this.ignore = ignore; + this.convertType = ConvertType.ALL; + } + + public ConvertColumnEntry(String name, boolean ignore, ConvertType convertType) { + this.name = name; + this.ignore = ignore; + this.convertType = convertType; + } + + public ConvertColumnEntry(String name, int index, boolean ignore, ConvertType convertType) { + this.name = name; + this.index = index; + this.ignore = ignore; + this.convertType = convertType; + } + + public String name() { + return name == null ? "" : name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean ignore() { + return ignore; + } + + public void setIgnore(boolean ignore) { + this.ignore = ignore; + } + + public ConvertType type() { + return convertType == null ? ConvertType.ALL : convertType; + } + + public void setConvertType(ConvertType convertType) { + this.convertType = convertType; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public String toString() { + return "ConvertColumnEntry{" + "index=" + index + ", name=" + name + ", ignore=" + ignore + ", convertType=" + convertType + '}'; + } + +} diff --git a/src/org/redkale/convert/ConvertDisabled.java b/src/main/java/org/redkale/convert/ConvertDisabled.java similarity index 95% rename from src/org/redkale/convert/ConvertDisabled.java rename to src/main/java/org/redkale/convert/ConvertDisabled.java index 4c6b7ee26..c3b86ffc2 100644 --- a/src/org/redkale/convert/ConvertDisabled.java +++ b/src/main/java/org/redkale/convert/ConvertDisabled.java @@ -1,47 +1,47 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 序列化时永久禁用该字段, 与ConvertColumn.ignore()的区别在于: ConvertDisabled不能通过ConvertEntity来解禁 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Target({METHOD, FIELD}) -@Retention(RUNTIME) -public @interface ConvertDisabled { - - /** - * 解析/序列化定制化的TYPE - * - * @return JSON or BSON or ALL - */ - ConvertType type() default ConvertType.ALL; - - /** - * ConvertDisabled 的多用类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ - @Inherited - @Documented - @Target({METHOD, FIELD}) - @Retention(RUNTIME) - public static @interface ConvertDisableds { - - ConvertDisabled[] value(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 序列化时永久禁用该字段, 与ConvertColumn.ignore()的区别在于: ConvertDisabled不能通过ConvertEntity来解禁 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Target({METHOD, FIELD}) +@Retention(RUNTIME) +public @interface ConvertDisabled { + + /** + * 解析/序列化定制化的TYPE + * + * @return JSON or BSON or ALL + */ + ConvertType type() default ConvertType.ALL; + + /** + * ConvertDisabled 的多用类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ + @Inherited + @Documented + @Target({METHOD, FIELD}) + @Retention(RUNTIME) + public static @interface ConvertDisableds { + + ConvertDisabled[] value(); + } +} diff --git a/src/org/redkale/convert/ConvertEntity.java b/src/main/java/org/redkale/convert/ConvertEntity.java similarity index 96% rename from src/org/redkale/convert/ConvertEntity.java rename to src/main/java/org/redkale/convert/ConvertEntity.java index ee735b87f..5b831c95e 100644 --- a/src/org/redkale/convert/ConvertEntity.java +++ b/src/main/java/org/redkale/convert/ConvertEntity.java @@ -1,33 +1,33 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - -/** - * 用于类名的别名, 该值必须是全局唯一
    - * 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface ConvertEntity { - - /** - * 别名值 - * - * @return String - */ - String value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; + +/** + * 用于类名的别名, 该值必须是全局唯一
    + * 使用场景: 当BSON序列化为了不指定class可以使用@ConvertEntity来取个别名。关联方法: Reader.readClassName() 和 Writer.writeClassName(String value) 。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ConvertEntity { + + /** + * 别名值 + * + * @return String + */ + String value(); +} diff --git a/src/org/redkale/convert/ConvertException.java b/src/main/java/org/redkale/convert/ConvertException.java similarity index 94% rename from src/org/redkale/convert/ConvertException.java rename to src/main/java/org/redkale/convert/ConvertException.java index 31acf0e62..4142f581b 100644 --- a/src/org/redkale/convert/ConvertException.java +++ b/src/main/java/org/redkale/convert/ConvertException.java @@ -1,32 +1,32 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -/** - * 序列化自定义异常类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class ConvertException extends RuntimeException { - - public ConvertException() { - super(); - } - - public ConvertException(String s) { - super(s); - } - - public ConvertException(String message, Throwable cause) { - super(message, cause); - } - - public ConvertException(Throwable cause) { - super(cause); - } -} +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +/** + * 序列化自定义异常类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class ConvertException extends RuntimeException { + + public ConvertException() { + super(); + } + + public ConvertException(String s) { + super(s); + } + + public ConvertException(String message, Throwable cause) { + super(message, cause); + } + + public ConvertException(Throwable cause) { + super(cause); + } +} diff --git a/src/org/redkale/convert/ConvertFactory.java b/src/main/java/org/redkale/convert/ConvertFactory.java similarity index 89% rename from src/org/redkale/convert/ConvertFactory.java rename to src/main/java/org/redkale/convert/ConvertFactory.java index dbda92726..30bae602b 100644 --- a/src/org/redkale/convert/ConvertFactory.java +++ b/src/main/java/org/redkale/convert/ConvertFactory.java @@ -1,835 +1,879 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.io.File; -import java.lang.reflect.*; -import java.math.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.*; -import java.util.regex.Pattern; -import java.util.stream.*; -import org.redkale.convert.bson.BsonConvert; -import org.redkale.convert.ext.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.*; - -/** - * 序列化模块的工厂类,用于注册自定义的序列化类型,获取Convert - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param Writer输出的子类 - */ -@SuppressWarnings("unchecked") -public abstract class ConvertFactory { - - private static final AtomicBoolean loaderInited = new AtomicBoolean(); - - private static Convert defProtobufConvert; - - private final ConvertFactory parent; - - protected Convert convert; - - protected boolean tiny; //String类型值为"",Boolean类型值为false时是否需要输出, 默认为true - - private final Encodeable anyEncoder = new AnyEncoder(this); - - //----------------------------------------------------------------------------------- - private final ConcurrentHashMap creators = new ConcurrentHashMap(); - - private final ConcurrentHashMap entitys = new ConcurrentHashMap(); - - private final ConcurrentHashMap>> fieldCoders = new ConcurrentHashMap(); - - private final ConcurrentHashMap> decoders = new ConcurrentHashMap(); - - private final ConcurrentHashMap> encoders = new ConcurrentHashMap(); - - private final ConcurrentHashMap columnEntrys = new ConcurrentHashMap(); - - private final Set skipIgnores = new HashSet(); - - //key:需要屏蔽的字段;value:排除的字段名 - private final ConcurrentHashMap> ignoreAlls = new ConcurrentHashMap(); - - private boolean skipAllIgnore = false; - - protected ConvertFactory(ConvertFactory parent, boolean tiny) { - this.tiny = tiny; - this.parent = parent; - if (parent == null) { - //--------------------------------------------------------- - - this.register(boolean.class, BoolSimpledCoder.instance); - this.register(Boolean.class, BoolSimpledCoder.instance); - - this.register(byte.class, ByteSimpledCoder.instance); - this.register(Byte.class, ByteSimpledCoder.instance); - - this.register(short.class, ShortSimpledCoder.instance); - this.register(Short.class, ShortSimpledCoder.instance); - - this.register(char.class, CharSimpledCoder.instance); - this.register(Character.class, CharSimpledCoder.instance); - - this.register(int.class, IntSimpledCoder.instance); - this.register(Integer.class, IntSimpledCoder.instance); - - this.register(long.class, LongSimpledCoder.instance); - this.register(Long.class, LongSimpledCoder.instance); - - this.register(float.class, FloatSimpledCoder.instance); - this.register(Float.class, FloatSimpledCoder.instance); - - this.register(double.class, DoubleSimpledCoder.instance); - this.register(Double.class, DoubleSimpledCoder.instance); - - this.register(Number.class, NumberSimpledCoder.instance); - this.register(String.class, StringSimpledCoder.instance); - this.register(StringWrapper.class, StringWrapperSimpledCoder.instance); - this.register(CharSequence.class, CharSequenceSimpledCoder.instance); - this.register(StringBuilder.class, CharSequenceSimpledCoder.StringBuilderSimpledCoder.instance); - this.register(java.util.Date.class, DateSimpledCoder.instance); - this.register(java.time.Duration.class, DurationSimpledCoder.instance); - this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance); - this.register(AtomicLong.class, AtomicLongSimpledCoder.instance); - this.register(BigInteger.class, BigIntegerSimpledCoder.instance); - this.register(BigDecimal.class, BigDecimalSimpledCoder.instance); - this.register(InetAddress.class, InetAddressSimpledCoder.instance); - this.register(DLong.class, DLongSimpledCoder.instance); - this.register(Class.class, TypeSimpledCoder.instance); - this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance); - this.register(Pattern.class, PatternSimpledCoder.instance); - this.register(File.class, FileSimpledCoder.instance); - this.register(Throwable.class, ThrowableSimpledCoder.instance); - this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance); - this.register(URL.class, URLSimpledCoder.instance); - this.register(URI.class, URISimpledCoder.instance); - //--------------------------------------------------------- - this.register(ByteBuffer.class, ByteBufferSimpledCoder.instance); - this.register(boolean[].class, BoolArraySimpledCoder.instance); - this.register(byte[].class, ByteArraySimpledCoder.instance); - this.register(short[].class, ShortArraySimpledCoder.instance); - this.register(char[].class, CharArraySimpledCoder.instance); - this.register(int[].class, IntArraySimpledCoder.instance); - this.register(IntStream.class, IntArraySimpledCoder.IntStreamSimpledCoder.instance); - this.register(long[].class, LongArraySimpledCoder.instance); - this.register(LongStream.class, LongArraySimpledCoder.LongStreamSimpledCoder.instance); - this.register(float[].class, FloatArraySimpledCoder.instance); - this.register(double[].class, DoubleArraySimpledCoder.instance); - this.register(DoubleStream.class, DoubleArraySimpledCoder.DoubleStreamSimpledCoder.instance); - this.register(String[].class, StringArraySimpledCoder.instance); - //--------------------------------------------------------- - this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class)); - this.register(HttpCookie.class, new Creator() { - @Override - @ConstructorParameters({"name", "value"}) - public HttpCookie create(Object... params) { - return new HttpCookie((String) params[0], (String) params[1]); - } - - }); - try { - Class sqldateClass = Class.forName("java.sql.Date"); - this.register(sqldateClass, new SimpledCoder() { - - @Override - public void convertTo(W out, java.sql.Date value) { - out.writeSmallString(value == null ? null : value.toString()); - } - - @Override - public java.sql.Date convertFrom(R in) { - String t = in.readSmallString(); - return t == null ? null : java.sql.Date.valueOf(t); - } - - }); - Class sqltimeClass = Class.forName("java.sql.Time"); - this.register(sqltimeClass, new SimpledCoder() { - - @Override - public void convertTo(W out, java.sql.Time value) { - out.writeSmallString(value == null ? null : value.toString()); - } - - @Override - public java.sql.Time convertFrom(R in) { - String t = in.readSmallString(); - return t == null ? null : java.sql.Time.valueOf(t); - } - - }); - Class timestampClass = Class.forName("java.sql.Timestamp"); - this.register(timestampClass, new SimpledCoder() { - - @Override - public void convertTo(W out, java.sql.Timestamp value) { - out.writeSmallString(value == null ? null : value.toString()); - } - - @Override - public java.sql.Timestamp convertFrom(R in) { - String t = in.readSmallString(); - return t == null ? null : java.sql.Timestamp.valueOf(t); - } - - }); - } catch (Throwable t) { - } - } - } - - public ConvertFactory parent() { - return this.parent; - } - - public static Convert findConvert(ConvertType type) { - if (type == null) return null; - if (type == ConvertType.JSON) return JsonConvert.root(); - if (type == ConvertType.BSON) return BsonConvert.root(); - if (loaderInited.get()) { - if (type == ConvertType.PROTOBUF) return defProtobufConvert; - } - synchronized (loaderInited) { - if (!loaderInited.get()) { - Iterator it = ServiceLoader.load(ConvertLoader.class).iterator(); - while (it.hasNext()) { - ConvertLoader cl = it.next(); - if (cl.type() == ConvertType.PROTOBUF) defProtobufConvert = cl.convert(); - } - loaderInited.set(true); - } - } - return type == ConvertType.PROTOBUF ? defProtobufConvert : null; - } - - protected static boolean getSystemPropertyBoolean(String key, String parentkey, boolean defvalue) { - return Boolean.parseBoolean(System.getProperty(key, System.getProperty(parentkey, String.valueOf(defvalue)))); - } - - public abstract ConvertType getConvertType(); - - public abstract boolean isReversible(); //是否可逆的 - - public abstract boolean isFieldSort(); //当ConvertColumn.index相同时是否按字段名称排序 - - public abstract ConvertFactory createChild(); - - public abstract ConvertFactory createChild(boolean tiny); - - protected SimpledCoder createEnumSimpledCoder(Class enumClass) { - return new EnumSimpledCoder(enumClass); - } - - protected Encodeable createDyncEncoder(Type type) { - return null; - } - - protected ObjectDecoder createObjectDecoder(Type type) { - return new ObjectDecoder(type); - } - - protected ObjectEncoder createObjectEncoder(Type type) { - return new ObjectEncoder(type); - } - - protected Decodeable createMapDecoder(Type type) { - return new MapDecoder(this, type); - } - - protected Encodeable createMapEncoder(Type type) { - return new MapEncoder(this, type); - } - - protected Decodeable createArrayDecoder(Type type) { - return new ArrayDecoder(this, type); - } - - protected Encodeable createArrayEncoder(Type type) { - return new ArrayEncoder(this, type); - } - - protected Decodeable createCollectionDecoder(Type type) { - return new CollectionDecoder(this, type); - } - - protected Encodeable createCollectionEncoder(Type type) { - return new CollectionEncoder(this, type); - } - - protected Decodeable createStreamDecoder(Type type) { - return new StreamDecoder(this, type); - } - - protected Encodeable createStreamEncoder(Type type) { - return new StreamEncoder(this, type); - } - - public Convert getConvert() { - return convert; - } - - public ConvertFactory tiny(boolean tiny) { - this.tiny = tiny; - return this; - } - - public boolean isConvertDisabled(AccessibleObject element) { - ConvertDisabled[] ccs = element.getAnnotationsByType(ConvertDisabled.class); - if (ccs.length == 0 && element instanceof Method) { - final Method method = (Method) element; - String fieldName = readGetSetFieldName(method); - if (fieldName != null) { - try { - ccs = method.getDeclaringClass().getDeclaredField(fieldName).getAnnotationsByType(ConvertDisabled.class); - } catch (Exception e) { //说明没有该字段,忽略异常 - } - } - } - final ConvertType ct = this.getConvertType(); - for (ConvertDisabled ref : ccs) { - if (ref.type().contains(ct)) return true; - } - return false; - } - - public ConvertColumnEntry findRef(Class clazz, AccessibleObject element) { - if (element == null) return null; - ConvertColumnEntry en = this.columnEntrys.get(element); - Set onlyColumns = ignoreAlls.get(clazz); - if (en != null && onlyColumns == null) return en; - final ConvertType ct = this.getConvertType(); - ConvertColumn[] ccs = element.getAnnotationsByType(ConvertColumn.class); - String fieldName = null; - if (ccs.length == 0 && element instanceof Method) { - final Method method = (Method) element; - fieldName = readGetSetFieldName(method); - if (fieldName != null) { - Class mclz = method.getDeclaringClass(); - do { - try { - ccs = mclz.getDeclaredField(fieldName).getAnnotationsByType(ConvertColumn.class); - break; - } catch (Exception e) { //说明没有该字段,忽略异常 - } - } while (mclz != Object.class && (mclz = mclz.getSuperclass()) != Object.class); - } - } - if (onlyColumns != null && fieldName == null) { - if (element instanceof Method) { - fieldName = readGetSetFieldName((Method) element); - } else if (element instanceof Field) { - fieldName = ((Field) element).getName(); - } - } - if (ccs.length == 0 && onlyColumns != null && fieldName != null) { - if (!onlyColumns.contains(fieldName)) return new ConvertColumnEntry(fieldName, true); - } - for (ConvertColumn ref : ccs) { - if (ref.type().contains(ct)) { - String realName = ref.name().isEmpty() ? fieldName : ref.name(); - if (onlyColumns != null && fieldName != null) { - if (!onlyColumns.contains(realName)) return new ConvertColumnEntry(realName, true); - } - ConvertColumnEntry entry = new ConvertColumnEntry(ref); - if (skipAllIgnore) { - entry.setIgnore(false); - return entry; - } - if (skipIgnores.isEmpty()) { - if (onlyColumns != null && realName != null && onlyColumns.contains(realName)) entry.setIgnore(false); - return entry; - } - if (skipIgnores.contains(((Member) element).getDeclaringClass())) entry.setIgnore(false); - return entry; - } - } - return null; - } - - static String readGetSetFieldName(Method method) { - if (method == null) return null; - String fname = method.getName(); - if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; - fname = fname.substring(fname.startsWith("is") ? 2 : 3); - if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) { - fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1); - } else if (fname.length() == 1) { - fname = "" + Character.toLowerCase(fname.charAt(0)); - } - return fname; - } - - final String getEntityAlias(Class clazz) { - if (clazz == String.class) return "A"; - if (clazz == int.class) return "I"; - if (clazz == Integer.class) return "i"; - if (clazz == long.class) return "J"; - if (clazz == Long.class) return "j"; - if (clazz == byte.class) return "B"; - if (clazz == Byte.class) return "b"; - if (clazz == boolean.class) return "Z"; - if (clazz == Boolean.class) return "z"; - if (clazz == short.class) return "S"; - if (clazz == Short.class) return "s"; - if (clazz == char.class) return "C"; - if (clazz == Character.class) return "c"; - if (clazz == float.class) return "F"; - if (clazz == Float.class) return "f"; - if (clazz == double.class) return "D"; - if (clazz == Double.class) return "d"; - - if (clazz == String[].class) return "[A"; - if (clazz == int[].class) return "[I"; - if (clazz == long[].class) return "[J"; - if (clazz == byte[].class) return "[B"; - if (clazz == boolean[].class) return "[Z"; - if (clazz == short[].class) return "[S"; - if (clazz == char[].class) return "[C"; - if (clazz == float[].class) return "[F"; - if (clazz == double[].class) return "[D"; - - ConvertEntity ce = (ConvertEntity) clazz.getAnnotation(ConvertEntity.class); - if (ce != null && findEntityAlias(ce.value()) == null) entitys.put(ce.value(), clazz); - return ce == null ? clazz.getName() : ce.value(); - } - - final Class getEntityAlias(String name) { - if ("A".equals(name)) return String.class; - if ("I".equals(name)) return int.class; - if ("i".equals(name)) return Integer.class; - if ("J".equals(name)) return long.class; - if ("j".equals(name)) return Long.class; - if ("B".equals(name)) return byte.class; - if ("b".equals(name)) return Byte.class; - if ("Z".equals(name)) return boolean.class; - if ("z".equals(name)) return Boolean.class; - if ("S".equals(name)) return short.class; - if ("s".equals(name)) return Short.class; - if ("C".equals(name)) return char.class; - if ("c".equals(name)) return Character.class; - if ("F".equals(name)) return float.class; - if ("f".equals(name)) return Float.class; - if ("D".equals(name)) return double.class; - if ("d".equals(name)) return Double.class; - - if ("[A".equals(name)) return String[].class; - if ("[I".equals(name)) return int[].class; - if ("[J".equals(name)) return long[].class; - if ("[B".equals(name)) return byte[].class; - if ("[Z".equals(name)) return boolean[].class; - if ("[S".equals(name)) return short[].class; - if ("[C".equals(name)) return char[].class; - if ("[F".equals(name)) return float[].class; - if ("[D".equals(name)) return double[].class; - - Class clazz = findEntityAlias(name); - try { - return clazz == null ? Thread.currentThread().getContextClassLoader().loadClass(name) : clazz; - } catch (Exception ex) { - throw new ConvertException("convert entity is " + name, ex); - } - } - - private Class findEntityAlias(String name) { - Class clazz = entitys.get(name); - return parent == null ? clazz : parent.findEntityAlias(name); - } - - /** - * 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false - * - * @param skipIgnore 是否忽略Ignore注解 - */ - public final void registerSkipAllIgnore(final boolean skipIgnore) { - this.skipAllIgnore = skipIgnore; - } - - /** - * 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false - * - * @param skipIgnore 忽略ignore - * - * @return 自身 - */ - public ConvertFactory skipAllIgnore(final boolean skipIgnore) { - this.skipAllIgnore = skipIgnore; - return this; - } - - /** - * 使该类所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false - * - * @param type 指定的类 - */ - public final void registerSkipIgnore(final Class type) { - skipIgnores.add(type); - } - - /** - * 屏蔽指定类所有字段,仅仅保留指定字段
    - * 注意: 该配置优先级高于skipAllIgnore和ConvertColumnEntry配置 - * - * @param type 指定的类 - * @param excludeColumns 需要排除的字段名 - */ - public final void registerIgnoreAll(final Class type, String... excludeColumns) { - Set set = ignoreAlls.get(type); - if (set == null) { - ignoreAlls.put(type, new HashSet<>(Arrays.asList(excludeColumns))); - } else { - set.addAll(Arrays.asList(excludeColumns)); - } - } - - public final void registerIgnoreAll(final Class type, Collection excludeColumns) { - Set set = ignoreAlls.get(type); - if (set == null) { - ignoreAlls.put(type, new HashSet<>(excludeColumns)); - } else { - set.addAll(new ArrayList(excludeColumns)); - } - } - - public final void register(final Class type, boolean ignore, String... columns) { - for (String column : columns) { - register(type, column, new ConvertColumnEntry(column, ignore)); - } - } - - public final void register(final Class type, boolean ignore, Collection columns) { - for (String column : columns) { - register(type, column, new ConvertColumnEntry(column, ignore)); - } - } - - public final boolean register(final Class type, String column, String alias) { - return register(type, column, new ConvertColumnEntry(alias)); - } - - public final boolean register(final Class type, String column, ConvertColumnEntry entry) { - if (type == null || column == null || entry == null) return false; - Field field = null; - try { - field = type.getDeclaredField(column); - } catch (Exception e) { - } - String get = "get"; - if (field != null && (field.getType() == boolean.class || field.getType() == Boolean.class)) get = "is"; - char[] cols = column.toCharArray(); - cols[0] = Character.toUpperCase(cols[0]); - final String bigColumn = new String(cols); - try { - register(type.getMethod(get + bigColumn), entry); - } catch (NoSuchMethodException mex) { - if (get.length() >= 3) { //get - try { - register(type.getMethod("is" + bigColumn), entry); - } catch (Exception ex) { - } - } - } catch (Exception ex) { - } - try { - register(type.getMethod("set" + bigColumn, field.getType()), entry); - } catch (Exception ex) { - } - return field == null ? true : register(field, entry); - } - - public final boolean register(final AccessibleObject field, final ConvertColumnEntry entry) { - if (field == null || entry == null) return false; - this.columnEntrys.put(field, entry); - return true; - } - - public final void reloadCoder(final Type type) { - this.register(type, this.createDecoder(type)); - this.register(type, this.createEncoder(type)); - } - - public final void reloadCoder(final Type type, final Class clazz) { - this.register(type, this.createDecoder(type, clazz)); - this.register(type, this.createEncoder(type, clazz)); - } - - public final void register(final Class clazz, final Creator creator) { - creators.put(clazz, creator); - } - - public final Creator findCreator(Class type) { - Creator creator = creators.get(type); - if (creator != null) return creator; - return this.parent == null ? null : this.parent.findCreator(type); - } - - public final Creator loadCreator(Class type) { - Creator result = findCreator(type); - if (result == null) { - result = Creator.create(type); - if (result != null) creators.put(type, result); - } - return result; - } - - //---------------------------------------------------------------------- - public final Encodeable getAnyEncoder() { - return (Encodeable) anyEncoder; - } - - public final void register(final Type clazz, final SimpledCoder coder) { - decoders.put(clazz, coder); - encoders.put(clazz, coder); - } - - public final void register(final Type clazz, final Decodeable decoder, final Encodeable encoder) { - decoders.put(clazz, decoder); - encoders.put(clazz, encoder); - } - - public final void register(final Type clazz, final Decodeable decoder) { - decoders.put(clazz, decoder); - } - - public final void register(final Type clazz, final Encodeable encoder) { - encoders.put(clazz, encoder); - } - - //coder = null表示删除该字段的指定SimpledCoder - public final void register(final Class clazz, final String field, final SimpledCoder coder) { - if (field == null || clazz == null) return; - try { - clazz.getDeclaredField(field); - } catch (Exception e) { - throw new RuntimeException(clazz + " not found field(" + field + ")"); - } - if (coder == null) { - Map map = this.fieldCoders.get(clazz); - if (map != null) map.remove(field); - } else { - this.fieldCoders.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>()).put(field, coder); - } - } - - public final SimpledCoder findFieldCoder(final Type clazz, final String field) { - if (field == null) return null; - Map> map = this.fieldCoders.get(clazz); - if (map == null) return parent == null ? null : parent.findFieldCoder(clazz, field); - return (SimpledCoder) map.get(field); - } - - public final Decodeable findDecoder(final Type type) { - Decodeable rs = (Decodeable) decoders.get(type); - if (rs != null) return rs; - return this.parent == null ? null : this.parent.findDecoder(type); - } - - public final Encodeable findEncoder(final Type type) { - Encodeable rs = (Encodeable) encoders.get(type); - if (rs != null) return rs; - return this.parent == null ? null : this.parent.findEncoder(type); - } - - public final Decodeable loadDecoder(final Type type) { - Decodeable decoder = findDecoder(type); - if (decoder != null) return decoder; - if (type instanceof GenericArrayType) return createArrayDecoder(type); - Class clazz; - if (type instanceof ParameterizedType) { - final ParameterizedType pts = (ParameterizedType) type; - clazz = (Class) (pts).getRawType(); - } else if (type instanceof TypeVariable) { // e.g. - final TypeVariable tv = (TypeVariable) type; - Class cz = tv.getBounds().length == 0 ? Object.class : null; - for (Type f : tv.getBounds()) { - if (f instanceof Class) { - cz = (Class) f; - break; - } - } - clazz = cz; - if (cz == null) throw new ConvertException("not support the type (" + type + ")"); - } else if (type instanceof WildcardType) { // e.g. - final WildcardType wt = (WildcardType) type; - Class cz = null; - for (Type f : wt.getUpperBounds()) { - if (f instanceof Class) { - cz = (Class) f; - break; - } - } - clazz = cz; - if (cz == null) throw new ConvertException("not support the type (" + type + ")"); - } else if (type instanceof Class) { - clazz = (Class) type; - } else { - throw new ConvertException("not support the type (" + type + ")"); - } - //此处不能再findDecoder,否则type与class不一致, 如: RetResult 和 RetResult - return createDecoder(type, clazz); - } - - public final Decodeable createDecoder(final Type type) { - Class clazz; - if (type instanceof ParameterizedType) { - final ParameterizedType pts = (ParameterizedType) type; - clazz = (Class) (pts).getRawType(); - } else if (type instanceof Class) { - clazz = (Class) type; - } else { - throw new ConvertException("not support the type (" + type + ")"); - } - return createDecoder(type, clazz); - } - - private Decodeable createDecoder(final Type type, final Class clazz) { - Decodeable decoder = null; - ObjectDecoder od = null; - if (clazz.isEnum()) { - decoder = createEnumSimpledCoder(clazz); - } else if (clazz.isArray()) { - decoder = createArrayDecoder(type); - } else if (Collection.class.isAssignableFrom(clazz)) { - decoder = createCollectionDecoder(type); - } else if (Stream.class.isAssignableFrom(clazz)) { - decoder = createStreamDecoder(type); - } else if (Map.class.isAssignableFrom(clazz)) { - decoder = createMapDecoder(type); - } else if (Optional.class == clazz) { - decoder = new OptionalCoder(this, type); - } else if (clazz == Object.class) { - od = createObjectDecoder(type); - decoder = od; - } else if (!clazz.getName().startsWith("java.") - || java.net.HttpCookie.class == clazz - || java.util.AbstractMap.SimpleEntry.class == clazz - || clazz.getName().startsWith("java.awt.geom.Point2D")) { - Decodeable simpleCoder = null; - for (final Method method : clazz.getDeclaredMethods()) { - if (!Modifier.isStatic(method.getModifiers())) continue; - Class[] paramTypes = method.getParameterTypes(); - if (paramTypes.length != 1) continue; - if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue; - if (!Decodeable.class.isAssignableFrom(method.getReturnType())) continue; - try { - method.setAccessible(true); - simpleCoder = (Decodeable) method.invoke(null, this); - break; - } catch (Exception e) { - } - } - if (simpleCoder == null) { - od = createObjectDecoder(type); - decoder = od; - } else { - decoder = simpleCoder; - } - } - if (decoder == null) throw new ConvertException("not support the type (" + type + ")"); - register(type, decoder); - if (od != null) od.init(this); - return decoder; - } - - public final Encodeable loadEncoder(final Type type) { - Encodeable encoder = findEncoder(type); - if (encoder != null) return encoder; - if (type instanceof GenericArrayType) return createArrayEncoder(type); - Class clazz; - if (type instanceof ParameterizedType) { - final ParameterizedType pts = (ParameterizedType) type; - clazz = (Class) (pts).getRawType(); - } else if (type instanceof TypeVariable) { - TypeVariable tv = (TypeVariable) type; - Type t = Object.class; - if (tv.getBounds().length == 1) { - t = tv.getBounds()[0]; - } - if (!(t instanceof Class)) t = Object.class; - clazz = (Class) t; - } else if (type instanceof Class) { - clazz = (Class) type; - } else { - throw new ConvertException("not support the type (" + type + ")"); - } - //此处不能再findEncoder,否则type与class不一致, 如: RetResult 和 RetResult - return createEncoder(type, clazz); - } - - public final Encodeable createEncoder(final Type type) { - Class clazz; - if (type instanceof ParameterizedType) { - final ParameterizedType pts = (ParameterizedType) type; - clazz = (Class) (pts).getRawType(); - } else if (type instanceof Class) { - clazz = (Class) type; - } else { - throw new ConvertException("not support the type (" + type + ")"); - } - return createEncoder(type, clazz); - } - - private Encodeable createEncoder(final Type type, final Class clazz) { - Encodeable encoder = null; - ObjectEncoder oe = null; - if (clazz.isEnum()) { - encoder = createEnumSimpledCoder(clazz); - } else if (clazz.isArray()) { - encoder = createArrayEncoder(type); - } else if (Collection.class.isAssignableFrom(clazz)) { - encoder = createCollectionEncoder(type); - } else if (Stream.class.isAssignableFrom(clazz)) { - encoder = createStreamEncoder(type); - } else if (Map.class.isAssignableFrom(clazz)) { - encoder = createMapEncoder(type); - } else if (Optional.class == clazz) { - encoder = new OptionalCoder(this, type); - } else if (clazz == Object.class) { - return (Encodeable) this.anyEncoder; - } else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz - || java.util.Map.Entry.class == clazz || java.util.AbstractMap.SimpleEntry.class == clazz) { - Encodeable simpleCoder = null; - for (final Method method : clazz.getDeclaredMethods()) { - if (!Modifier.isStatic(method.getModifiers())) continue; - Class[] paramTypes = method.getParameterTypes(); - if (paramTypes.length != 1) continue; - if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue; - if (!Encodeable.class.isAssignableFrom(method.getReturnType())) continue; - try { - method.setAccessible(true); - simpleCoder = (Encodeable) method.invoke(null, this); - break; - } catch (Exception e) { - } - } - if (simpleCoder == null) { - encoder = createDyncEncoder(type); - if (encoder == null) { - oe = createObjectEncoder(type); - encoder = oe; - } - } else { - encoder = simpleCoder; - } - } - if (encoder == null) throw new ConvertException("not support the type (" + type + ")"); - register(type, encoder); - if (oe != null) oe.init(this); - return encoder; - - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.io.File; +import java.lang.reflect.*; +import java.math.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.CompletionHandler; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.*; +import java.util.regex.Pattern; +import java.util.stream.*; +import org.redkale.convert.bson.BsonConvert; +import org.redkale.convert.ext.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.*; + +/** + * 序列化模块的工厂类,用于注册自定义的序列化类型,获取Convert + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param Writer输出的子类 + */ +@SuppressWarnings("unchecked") +public abstract class ConvertFactory { + + private static final AtomicBoolean loaderInited = new AtomicBoolean(); + + private static Convert defProtobufConvert; + + private final ConvertFactory parent; + + protected Convert convert; + + protected boolean tiny; //String类型值为"",Boolean类型值为false时是否需要输出, 默认为true + + private final Encodeable anyEncoder = new AnyEncoder(this); + + //----------------------------------------------------------------------------------- + private final ConcurrentHashMap creators = new ConcurrentHashMap(); + + private final ConcurrentHashMap entitys = new ConcurrentHashMap(); + + private final ConcurrentHashMap>> fieldCoders = new ConcurrentHashMap(); + + private final ConcurrentHashMap> decoders = new ConcurrentHashMap(); + + private final ConcurrentHashMap> encoders = new ConcurrentHashMap(); + + private final ConcurrentHashMap columnEntrys = new ConcurrentHashMap(); + + private final Set skipIgnores = new HashSet(); + + //key:需要屏蔽的字段;value:排除的字段名 + private final ConcurrentHashMap> ignoreAlls = new ConcurrentHashMap(); + + private boolean skipAllIgnore = false; + + protected ConvertFactory(ConvertFactory parent, boolean tiny) { + this.tiny = tiny; + this.parent = parent; + if (parent == null) { + //--------------------------------------------------------- + + this.register(boolean.class, BoolSimpledCoder.instance); + this.register(Boolean.class, BoolSimpledCoder.instance); + + this.register(byte.class, ByteSimpledCoder.instance); + this.register(Byte.class, ByteSimpledCoder.instance); + + this.register(short.class, ShortSimpledCoder.instance); + this.register(Short.class, ShortSimpledCoder.instance); + + this.register(char.class, CharSimpledCoder.instance); + this.register(Character.class, CharSimpledCoder.instance); + + this.register(int.class, IntSimpledCoder.instance); + this.register(Integer.class, IntSimpledCoder.instance); + + this.register(long.class, LongSimpledCoder.instance); + this.register(Long.class, LongSimpledCoder.instance); + + this.register(float.class, FloatSimpledCoder.instance); + this.register(Float.class, FloatSimpledCoder.instance); + + this.register(double.class, DoubleSimpledCoder.instance); + this.register(Double.class, DoubleSimpledCoder.instance); + + this.register(Number.class, NumberSimpledCoder.instance); + this.register(String.class, StringSimpledCoder.instance); + this.register(StringWrapper.class, StringWrapperSimpledCoder.instance); + this.register(CharSequence.class, CharSequenceSimpledCoder.instance); + this.register(StringBuilder.class, CharSequenceSimpledCoder.StringBuilderSimpledCoder.instance); + this.register(java.util.Date.class, DateSimpledCoder.instance); + this.register(java.time.Instant.class, InstantSimpledCoder.instance); + this.register(java.time.LocalDate.class, LocalDateSimpledCoder.instance); + this.register(java.time.LocalTime.class, LocalTimeSimpledCoder.instance); + this.register(java.time.LocalDateTime.class, LocalDateTimeSimpledCoder.instance); + this.register(java.time.Duration.class, DurationSimpledCoder.instance); + this.register(AtomicInteger.class, AtomicIntegerSimpledCoder.instance); + this.register(AtomicLong.class, AtomicLongSimpledCoder.instance); + this.register(BigInteger.class, BigIntegerSimpledCoder.instance); + this.register(BigDecimal.class, BigDecimalSimpledCoder.instance); + this.register(InetAddress.class, InetAddressSimpledCoder.instance); + this.register(LongAdder.class, LongAdderSimpledCoder.instance); + this.register(DLong.class, DLongSimpledCoder.instance); + this.register(Class.class, TypeSimpledCoder.instance); + this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressSimpledCoder.instance); + this.register(Pattern.class, PatternSimpledCoder.instance); + this.register(File.class, FileSimpledCoder.instance); + this.register(Throwable.class, ThrowableSimpledCoder.instance); + this.register(CompletionHandler.class, CompletionHandlerSimpledCoder.instance); + this.register(URL.class, URLSimpledCoder.instance); + this.register(URI.class, URISimpledCoder.instance); + //--------------------------------------------------------- + this.register(ByteBuffer.class, ByteBufferSimpledCoder.instance); + this.register(boolean[].class, BoolArraySimpledCoder.instance); + this.register(byte[].class, ByteArraySimpledCoder.instance); + this.register(short[].class, ShortArraySimpledCoder.instance); + this.register(char[].class, CharArraySimpledCoder.instance); + this.register(int[].class, IntArraySimpledCoder.instance); + this.register(IntStream.class, IntArraySimpledCoder.IntStreamSimpledCoder.instance); + this.register(long[].class, LongArraySimpledCoder.instance); + this.register(LongStream.class, LongArraySimpledCoder.LongStreamSimpledCoder.instance); + this.register(float[].class, FloatArraySimpledCoder.instance); + this.register(double[].class, DoubleArraySimpledCoder.instance); + this.register(DoubleStream.class, DoubleArraySimpledCoder.DoubleStreamSimpledCoder.instance); + this.register(String[].class, StringArraySimpledCoder.instance); + //--------------------------------------------------------- + this.register(AnyValue.class, Creator.create(AnyValue.DefaultAnyValue.class)); + this.register(HttpCookie.class, new Creator() { + @Override + @ConstructorParameters({"name", "value"}) + public HttpCookie create(Object... params) { + return new HttpCookie((String) params[0], (String) params[1]); + } + + }); + try { + Class sqldateClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Date"); + Invoker sqldateInvoker = Invoker.create(sqldateClass, "valueOf", String.class); + this.register(sqldateClass, new SimpledCoder() { + + @Override + public void convertTo(W out, Object value) { + out.writeSmallString(value == null ? null : value.toString()); + } + + @Override + public Object convertFrom(R in) { + String t = in.readSmallString(); + return t == null ? null : sqldateInvoker.invoke(null, t); + } + + }); + Class sqltimeClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Time"); + Invoker sqltimeInvoker = Invoker.create(sqltimeClass, "valueOf", String.class); + this.register(sqltimeClass, new SimpledCoder() { + + @Override + public void convertTo(W out, Object value) { + out.writeSmallString(value == null ? null : value.toString()); + } + + @Override + public Object convertFrom(R in) { + String t = in.readSmallString(); + return t == null ? null : sqltimeInvoker.invoke(null, t); + } + + }); + Class timestampClass = Thread.currentThread().getContextClassLoader().loadClass("java.sql.Timestamp"); + Invoker timestampInvoker = Invoker.create(timestampClass, "valueOf", String.class); + this.register(timestampClass, new SimpledCoder() { + + @Override + public void convertTo(W out, Object value) { + out.writeSmallString(value == null ? null : value.toString()); + } + + @Override + public Object convertFrom(R in) { + String t = in.readSmallString(); + return t == null ? null : timestampInvoker.invoke(null, t); + } + + }); + } catch (Throwable t) { + } + } + } + + public ConvertFactory parent() { + return this.parent; + } + + public static Convert findConvert(ConvertType type) { + if (type == null) return null; + if (type == ConvertType.JSON) return JsonConvert.root(); + if (type == ConvertType.BSON) return BsonConvert.root(); + if (loaderInited.get()) { + if (type == ConvertType.PROTOBUF) return defProtobufConvert; + } + synchronized (loaderInited) { + if (!loaderInited.get()) { + Iterator it = ServiceLoader.load(ConvertProvider.class).iterator(); + RedkaleClassLoader.putServiceLoader(ConvertProvider.class); + while (it.hasNext()) { + ConvertProvider cl = it.next(); + RedkaleClassLoader.putReflectionPublicConstructors(cl.getClass(), cl.getClass().getName()); + if (cl.type() == ConvertType.PROTOBUF) defProtobufConvert = cl.convert(); + } + loaderInited.set(true); + } + } + return type == ConvertType.PROTOBUF ? defProtobufConvert : null; + } + + protected static boolean getSystemPropertyBoolean(String key, String parentkey, boolean defvalue) { + return Boolean.parseBoolean(System.getProperty(key, System.getProperty(parentkey, String.valueOf(defvalue)))); + } + + public abstract ConvertType getConvertType(); + + public abstract boolean isReversible(); //是否可逆的 + + public abstract boolean isFieldSort(); //当ConvertColumn.index相同时是否按字段名称排序 + + public abstract ConvertFactory createChild(); + + public abstract ConvertFactory createChild(boolean tiny); + + protected SimpledCoder createEnumSimpledCoder(Class enumClass) { + return new EnumSimpledCoder(enumClass); + } + + protected Type formatObjectType(Type type) { + if (type instanceof Class) { + Class clazz = (Class) type; + ConvertImpl ci = clazz.getAnnotation(ConvertImpl.class); + if (ci != null) { + if (!Modifier.isAbstract(clazz.getModifiers()) && !Modifier.isInterface(clazz.getModifiers())) { + throw new ConvertException("@" + ConvertImpl.class.getSimpleName() + " must at interface or abstract class, but " + clazz + " not"); + } + Class impl = ci.value(); + if (Modifier.isAbstract(impl.getModifiers()) || Modifier.isInterface(impl.getModifiers())) { + throw new ConvertException("@" + ConvertImpl.class.getSimpleName() + " at class " + impl + " cannot be interface or abstract class"); + } + return impl; + } + } + return type; + } + + protected Encodeable createDyncEncoder(Type type) { + return null; + } + + protected ObjectDecoder createObjectDecoder(Type type) { + return new ObjectDecoder(type); + } + + protected ObjectEncoder createObjectEncoder(Type type) { + return new ObjectEncoder(type); + } + + protected Decodeable createMapDecoder(Type type) { + return new MapDecoder(this, type); + } + + protected Encodeable createMapEncoder(Type type) { + return new MapEncoder(this, type); + } + + protected Decodeable createArrayDecoder(Type type) { + return new ArrayDecoder(this, type); + } + + protected Encodeable createArrayEncoder(Type type) { + return new ArrayEncoder(this, type); + } + + protected Decodeable createCollectionDecoder(Type type) { + return new CollectionDecoder(this, type); + } + + protected Encodeable createCollectionEncoder(Type type) { + return new CollectionEncoder(this, type); + } + + protected Decodeable createStreamDecoder(Type type) { + return new StreamDecoder(this, type); + } + + protected Encodeable createStreamEncoder(Type type) { + return new StreamEncoder(this, type); + } + + public Convert getConvert() { + return convert; + } + + public ConvertFactory tiny(boolean tiny) { + this.tiny = tiny; + return this; + } + + public boolean isConvertDisabled(AccessibleObject element) { + ConvertDisabled[] ccs = element.getAnnotationsByType(ConvertDisabled.class); + if (ccs.length == 0 && element instanceof Method) { + final Method method = (Method) element; + String fieldName = readGetSetFieldName(method); + if (fieldName != null) { + try { + ccs = method.getDeclaringClass().getDeclaredField(fieldName).getAnnotationsByType(ConvertDisabled.class); + } catch (Exception e) { //说明没有该字段,忽略异常 + } + } + } + final ConvertType ct = this.getConvertType(); + for (ConvertDisabled ref : ccs) { + if (ref.type().contains(ct)) return true; + } + return false; + } + + public ConvertColumnEntry findRef(Class clazz, AccessibleObject element) { + if (element == null) return null; + ConvertColumnEntry en = this.columnEntrys.get(element); + Set onlyColumns = ignoreAlls.get(clazz); + if (en != null && onlyColumns == null) return en; + final ConvertType ct = this.getConvertType(); + ConvertColumn[] ccs = element.getAnnotationsByType(ConvertColumn.class); + String fieldName = null; + if (ccs.length == 0 && element instanceof Method) { + final Method method = (Method) element; + fieldName = readGetSetFieldName(method); + if (fieldName != null) { + Class mclz = method.getDeclaringClass(); + do { + try { + ccs = mclz.getDeclaredField(fieldName).getAnnotationsByType(ConvertColumn.class); + break; + } catch (Exception e) { //说明没有该字段,忽略异常 + } + } while (mclz != Object.class && (mclz = mclz.getSuperclass()) != Object.class); + } + } + if (onlyColumns != null && fieldName == null) { + if (element instanceof Method) { + fieldName = readGetSetFieldName((Method) element); + } else if (element instanceof Field) { + fieldName = ((Field) element).getName(); + } + } + if (ccs.length == 0 && onlyColumns != null && fieldName != null) { + if (!onlyColumns.contains(fieldName)) return new ConvertColumnEntry(fieldName, true); + } + for (ConvertColumn ref : ccs) { + if (ref.type().contains(ct)) { + String realName = ref.name().isEmpty() ? fieldName : ref.name(); + if (onlyColumns != null && fieldName != null) { + if (!onlyColumns.contains(realName)) return new ConvertColumnEntry(realName, true); + } + ConvertColumnEntry entry = new ConvertColumnEntry(ref); + if (skipAllIgnore) { + entry.setIgnore(false); + return entry; + } + if (skipIgnores.isEmpty()) { + if (onlyColumns != null && realName != null && onlyColumns.contains(realName)) entry.setIgnore(false); + return entry; + } + if (skipIgnores.contains(((Member) element).getDeclaringClass())) entry.setIgnore(false); + return entry; + } + } + return null; + } + + static Field readGetSetField(Method method) { + String name = readGetSetFieldName(method); + if (name == null) return null; + try { + return method.getDeclaringClass().getDeclaredField(name); + } catch (Exception e) { + return null; + } + } + + static String readGetSetFieldName(Method method) { + if (method == null) return null; + String fname = method.getName(); + if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; //record类会直接用field名作为method名 + fname = fname.substring(fname.startsWith("is") ? 2 : 3); + if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) { + fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1); + } else if (fname.length() == 1) { + fname = "" + Character.toLowerCase(fname.charAt(0)); + } + return fname; + } + + final String getEntityAlias(Class clazz) { + if (clazz == String.class) return "A"; + if (clazz == int.class) return "I"; + if (clazz == Integer.class) return "i"; + if (clazz == long.class) return "J"; + if (clazz == Long.class) return "j"; + if (clazz == byte.class) return "B"; + if (clazz == Byte.class) return "b"; + if (clazz == boolean.class) return "Z"; + if (clazz == Boolean.class) return "z"; + if (clazz == short.class) return "S"; + if (clazz == Short.class) return "s"; + if (clazz == char.class) return "C"; + if (clazz == Character.class) return "c"; + if (clazz == float.class) return "F"; + if (clazz == Float.class) return "f"; + if (clazz == double.class) return "D"; + if (clazz == Double.class) return "d"; + + if (clazz == String[].class) return "[A"; + if (clazz == int[].class) return "[I"; + if (clazz == long[].class) return "[J"; + if (clazz == byte[].class) return "[B"; + if (clazz == boolean[].class) return "[Z"; + if (clazz == short[].class) return "[S"; + if (clazz == char[].class) return "[C"; + if (clazz == float[].class) return "[F"; + if (clazz == double[].class) return "[D"; + + ConvertEntity ce = (ConvertEntity) clazz.getAnnotation(ConvertEntity.class); + if (ce != null && findEntityAlias(ce.value()) == null) entitys.put(ce.value(), clazz); + return ce == null ? clazz.getName() : ce.value(); + } + + final Class getEntityAlias(String name) { + if ("A".equals(name)) return String.class; + if ("I".equals(name)) return int.class; + if ("i".equals(name)) return Integer.class; + if ("J".equals(name)) return long.class; + if ("j".equals(name)) return Long.class; + if ("B".equals(name)) return byte.class; + if ("b".equals(name)) return Byte.class; + if ("Z".equals(name)) return boolean.class; + if ("z".equals(name)) return Boolean.class; + if ("S".equals(name)) return short.class; + if ("s".equals(name)) return Short.class; + if ("C".equals(name)) return char.class; + if ("c".equals(name)) return Character.class; + if ("F".equals(name)) return float.class; + if ("f".equals(name)) return Float.class; + if ("D".equals(name)) return double.class; + if ("d".equals(name)) return Double.class; + + if ("[A".equals(name)) return String[].class; + if ("[I".equals(name)) return int[].class; + if ("[J".equals(name)) return long[].class; + if ("[B".equals(name)) return byte[].class; + if ("[Z".equals(name)) return boolean[].class; + if ("[S".equals(name)) return short[].class; + if ("[C".equals(name)) return char[].class; + if ("[F".equals(name)) return float[].class; + if ("[D".equals(name)) return double[].class; + + Class clazz = findEntityAlias(name); + try { + return clazz == null ? Thread.currentThread().getContextClassLoader().loadClass(name) : clazz; + } catch (Exception ex) { + throw new ConvertException("convert entity is " + name, ex); + } + } + + private Class findEntityAlias(String name) { + Class clazz = entitys.get(name); + return parent == null ? clazz : parent.findEntityAlias(name); + } + + /** + * 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false + * + * @param skipIgnore 是否忽略Ignore注解 + */ + public final void registerSkipAllIgnore(final boolean skipIgnore) { + this.skipAllIgnore = skipIgnore; + } + + /** + * 使所有类的所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false + * + * @param skipIgnore 忽略ignore + * + * @return 自身 + */ + public ConvertFactory skipAllIgnore(final boolean skipIgnore) { + this.skipAllIgnore = skipIgnore; + return this; + } + + /** + * 使该类所有被声明为ConvertColumn.ignore = true 的字段或方法变为ConvertColumn.ignore = false + * + * @param type 指定的类 + */ + public final void registerSkipIgnore(final Class type) { + skipIgnores.add(type); + } + + /** + * 屏蔽指定类所有字段,仅仅保留指定字段
    + * 注意: 该配置优先级高于skipAllIgnore和ConvertColumnEntry配置 + * + * @param type 指定的类 + * @param excludeColumns 需要排除的字段名 + */ + public final void registerIgnoreAll(final Class type, String... excludeColumns) { + Set set = ignoreAlls.get(type); + if (set == null) { + ignoreAlls.put(type, new HashSet<>(Arrays.asList(excludeColumns))); + } else { + set.addAll(Arrays.asList(excludeColumns)); + } + } + + public final void registerIgnoreAll(final Class type, Collection excludeColumns) { + Set set = ignoreAlls.get(type); + if (set == null) { + ignoreAlls.put(type, new HashSet<>(excludeColumns)); + } else { + set.addAll(new ArrayList(excludeColumns)); + } + } + + public final void register(final Class type, boolean ignore, String... columns) { + for (String column : columns) { + register(type, column, new ConvertColumnEntry(column, ignore)); + } + } + + public final void register(final Class type, boolean ignore, Collection columns) { + for (String column : columns) { + register(type, column, new ConvertColumnEntry(column, ignore)); + } + } + + public final boolean register(final Class type, String column, String alias) { + return register(type, column, new ConvertColumnEntry(alias)); + } + + public final boolean register(final Class type, String column, ConvertColumnEntry entry) { + if (type == null || column == null || entry == null) return false; + Field field = null; + try { + field = type.getDeclaredField(column); + } catch (Exception e) { + } + String get = "get"; + if (field != null && (field.getType() == boolean.class || field.getType() == Boolean.class)) get = "is"; + char[] cols = column.toCharArray(); + cols[0] = Character.toUpperCase(cols[0]); + final String bigColumn = new String(cols); + try { + register(type.getMethod(get + bigColumn), entry); + } catch (NoSuchMethodException mex) { + if (get.length() >= 3) { //get + try { + register(type.getMethod("is" + bigColumn), entry); + } catch (Exception ex) { + } + } + } catch (Exception ex) { + } + try { + register(type.getMethod("set" + bigColumn, field.getType()), entry); + } catch (Exception ex) { + } + return field == null ? true : register(field, entry); + } + + public final boolean register(final AccessibleObject field, final ConvertColumnEntry entry) { + if (field == null || entry == null) return false; + this.columnEntrys.put(field, entry); + return true; + } + + public final void reloadCoder(final Type type) { + this.register(type, this.createDecoder(type)); + this.register(type, this.createEncoder(type)); + } + + public final void reloadCoder(final Type type, final Class clazz) { + this.register(type, this.createDecoder(type, clazz)); + this.register(type, this.createEncoder(type, clazz)); + } + + public final void register(final Class clazz, final Creator creator) { + creators.put(clazz, creator); + } + + public final Creator findCreator(Class type) { + Creator creator = creators.get(type); + if (creator != null) return creator; + return this.parent == null ? null : this.parent.findCreator(type); + } + + public final Creator loadCreator(Class type) { + Creator result = findCreator(type); + if (result == null) { + result = Creator.create(type); + if (result != null) creators.put(type, result); + } + return result; + } + + //---------------------------------------------------------------------- + public final Encodeable getAnyEncoder() { + return (Encodeable) anyEncoder; + } + + public final void register(final Type clazz, final SimpledCoder coder) { + decoders.put(clazz, coder); + encoders.put(clazz, coder); + } + + public final void register(final Type clazz, final Decodeable decoder, final Encodeable encoder) { + decoders.put(clazz, decoder); + encoders.put(clazz, encoder); + } + + public final void register(final Type clazz, final Decodeable decoder) { + decoders.put(clazz, decoder); + } + + public final void register(final Type clazz, final Encodeable encoder) { + encoders.put(clazz, encoder); + } + + //coder = null表示删除该字段的指定SimpledCoder + public final void register(final Class clazz, final String field, final SimpledCoder coder) { + if (field == null || clazz == null) return; + try { + clazz.getDeclaredField(field); + } catch (Exception e) { + throw new RuntimeException(clazz + " not found field(" + field + ")"); + } + if (coder == null) { + Map map = this.fieldCoders.get(clazz); + if (map != null) map.remove(field); + } else { + this.fieldCoders.computeIfAbsent(clazz, c -> new ConcurrentHashMap<>()).put(field, coder); + } + } + + public final SimpledCoder findFieldCoder(final Type clazz, final String field) { + if (field == null) return null; + Map> map = this.fieldCoders.get(clazz); + if (map == null) return parent == null ? null : parent.findFieldCoder(clazz, field); + return (SimpledCoder) map.get(field); + } + + public final Decodeable findDecoder(final Type type) { + Decodeable rs = (Decodeable) decoders.get(type); + if (rs != null) return rs; + return this.parent == null ? null : this.parent.findDecoder(type); + } + + public final Encodeable findEncoder(final Type type) { + Encodeable rs = (Encodeable) encoders.get(type); + if (rs != null) return rs; + return this.parent == null ? null : this.parent.findEncoder(type); + } + + public final Decodeable loadDecoder(final Type type) { + Decodeable decoder = findDecoder(type); + if (decoder != null) return decoder; + if (type instanceof GenericArrayType) return createArrayDecoder(type); + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof TypeVariable) { // e.g. + final TypeVariable tv = (TypeVariable) type; + Class cz = tv.getBounds().length == 0 ? Object.class : null; + for (Type f : tv.getBounds()) { + if (f instanceof Class) { + cz = (Class) f; + break; + } + } + clazz = cz; + if (cz == null) throw new ConvertException("not support the type (" + type + ")"); + } else if (type instanceof WildcardType) { // e.g. + final WildcardType wt = (WildcardType) type; + Class cz = null; + for (Type f : wt.getUpperBounds()) { + if (f instanceof Class) { + cz = (Class) f; + break; + } + } + clazz = cz; + if (cz == null) throw new ConvertException("not support the type (" + type + ")"); + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + //此处不能再findDecoder,否则type与class不一致, 如: RetResult 和 RetResult + return createDecoder(type, clazz); + } + + public final Decodeable createDecoder(final Type type) { + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + return createDecoder(type, clazz); + } + + private Decodeable createDecoder(final Type type, final Class clazz) { + Decodeable decoder = null; + ObjectDecoder od = null; + if (clazz.isEnum()) { + decoder = createEnumSimpledCoder(clazz); + } else if (clazz.isArray()) { + decoder = createArrayDecoder(type); + } else if (Collection.class.isAssignableFrom(clazz)) { + decoder = createCollectionDecoder(type); + } else if (Stream.class.isAssignableFrom(clazz)) { + decoder = createStreamDecoder(type); + } else if (Map.class.isAssignableFrom(clazz)) { + decoder = createMapDecoder(type); + } else if (Optional.class == clazz) { + decoder = new OptionalCoder(this, type); + } else if (clazz == Object.class) { + od = createObjectDecoder(type); + decoder = od; + } else if (!clazz.getName().startsWith("java.") + || java.net.HttpCookie.class == clazz + || java.util.AbstractMap.SimpleEntry.class == clazz + || clazz.getName().startsWith("java.awt.geom.Point2D")) { + Decodeable simpleCoder = null; + for (final Method method : clazz.getDeclaredMethods()) { + if (!Modifier.isStatic(method.getModifiers())) continue; + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 1) continue; + if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue; + if (!Decodeable.class.isAssignableFrom(method.getReturnType())) continue; + try { + method.setAccessible(true); + simpleCoder = (Decodeable) method.invoke(null, this); + RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName()); + RedkaleClassLoader.putReflectionMethod(clazz.getName(), method); + break; + } catch (Exception e) { + } + } + if (simpleCoder == null) { + Type impl = formatObjectType(type); + od = createObjectDecoder(impl); + decoder = od; + } else { + decoder = simpleCoder; + } + } + if (decoder == null) throw new ConvertException("not support the type (" + type + ")"); + register(type, decoder); + if (od != null) od.init(this); + return decoder; + } + + public final Encodeable loadEncoder(final Type type) { + Encodeable encoder = findEncoder(type); + if (encoder != null) return encoder; + if (type instanceof GenericArrayType) return createArrayEncoder(type); + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof TypeVariable) { + TypeVariable tv = (TypeVariable) type; + Type t = Object.class; + if (tv.getBounds().length == 1) { + t = tv.getBounds()[0]; + } + if (!(t instanceof Class)) t = Object.class; + clazz = (Class) t; + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + //此处不能再findEncoder,否则type与class不一致, 如: RetResult 和 RetResult + return createEncoder(type, clazz); + } + + public final Encodeable createEncoder(final Type type) { + Class clazz; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof Class) { + clazz = (Class) type; + } else { + throw new ConvertException("not support the type (" + type + ")"); + } + return createEncoder(type, clazz); + } + + private Encodeable createEncoder(final Type type, final Class clazz) { + Encodeable encoder = null; + ObjectEncoder oe = null; + if (clazz.isEnum()) { + encoder = createEnumSimpledCoder(clazz); + } else if (clazz.isArray()) { + encoder = createArrayEncoder(type); + } else if (Collection.class.isAssignableFrom(clazz)) { + encoder = createCollectionEncoder(type); + } else if (Stream.class.isAssignableFrom(clazz)) { + encoder = createStreamEncoder(type); + } else if (Map.class.isAssignableFrom(clazz)) { + encoder = createMapEncoder(type); + } else if (Optional.class == clazz) { + encoder = new OptionalCoder(this, type); + } else if (clazz == Object.class) { + return (Encodeable) this.anyEncoder; + } else if (!clazz.getName().startsWith("java.") || java.net.HttpCookie.class == clazz + || java.util.Map.Entry.class == clazz || java.util.AbstractMap.SimpleEntry.class == clazz) { + Encodeable simpleCoder = null; + for (final Method method : clazz.getDeclaredMethods()) { + if (!Modifier.isStatic(method.getModifiers())) continue; + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 1) continue; + if (paramTypes[0] != ConvertFactory.class && paramTypes[0] != this.getClass()) continue; + if (!Encodeable.class.isAssignableFrom(method.getReturnType())) continue; + try { + method.setAccessible(true); + simpleCoder = (Encodeable) method.invoke(null, this); + RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName()); + RedkaleClassLoader.putReflectionMethod(clazz.getName(), method); + break; + } catch (Exception e) { + } + } + if (simpleCoder == null) { + Type impl = formatObjectType(type); + encoder = createDyncEncoder(impl); + if (encoder == null) { + oe = createObjectEncoder(impl); + encoder = oe; + } + } else { + encoder = simpleCoder; + } + } + if (encoder == null) throw new ConvertException("not support the type (" + type + ")"); + register(type, encoder); + if (oe != null) oe.init(this); + return encoder; + + } + +} diff --git a/src/org/redkale/convert/ConvertField.java b/src/main/java/org/redkale/convert/ConvertField.java similarity index 95% rename from src/org/redkale/convert/ConvertField.java rename to src/main/java/org/redkale/convert/ConvertField.java index 16dc869e6..176eb3cad 100644 --- a/src/org/redkale/convert/ConvertField.java +++ b/src/main/java/org/redkale/convert/ConvertField.java @@ -1,102 +1,102 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.io.Serializable; -import java.lang.reflect.Type; -import org.redkale.convert.json.JsonConvert; - -/** - * newConvert参数中的Function返回结果的数据类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class ConvertField implements Serializable { - - protected String name; - - protected Type type; - - protected int position; - - protected Object value; - - public ConvertField() { - } - - public ConvertField(String name, Object value) { - this.name = name; - this.value = value; - } - - public ConvertField(String name, int position, Object value) { - this.name = name; - this.position = position; - this.value = value; - } - - public ConvertField(String name, Type type, Object value) { - this.name = name; - this.type = type; - this.value = value; - } - - public ConvertField(String name, Type type, int position, Object value) { - this.name = name; - this.type = type; - this.position = position; - this.value = value; - } - - public static ConvertField[] ofArray(Object... items) { - int len = items.length / 2; - ConvertField[] rs = new ConvertField[len]; - for (int i = 0; i < len; i++) { - rs[i] = new ConvertField(items[i * 2].toString(), items[i * 2 + 1]); - } - return rs; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public Type getType() { - return type; - } - - public void setType(Type type) { - this.type = type; - } - - public int getPosition() { - return position; - } - - public void setPosition(int position) { - this.position = position; - } - - public Object getValue() { - return value; - } - - public void setValue(Object value) { - this.value = value; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.io.Serializable; +import java.lang.reflect.Type; +import org.redkale.convert.json.JsonConvert; + +/** + * newConvert参数中的Function返回结果的数据类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class ConvertField implements Serializable { + + protected String name; + + protected Type type; + + protected int position; + + protected Object value; + + public ConvertField() { + } + + public ConvertField(String name, Object value) { + this.name = name; + this.value = value; + } + + public ConvertField(String name, int position, Object value) { + this.name = name; + this.position = position; + this.value = value; + } + + public ConvertField(String name, Type type, Object value) { + this.name = name; + this.type = type; + this.value = value; + } + + public ConvertField(String name, Type type, int position, Object value) { + this.name = name; + this.type = type; + this.position = position; + this.value = value; + } + + public static ConvertField[] ofArray(Object... items) { + int len = items.length / 2; + ConvertField[] rs = new ConvertField[len]; + for (int i = 0; i < len; i++) { + rs[i] = new ConvertField(items[i * 2].toString(), items[i * 2 + 1]); + } + return rs; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Type getType() { + return type; + } + + public void setType(Type type) { + this.type = type; + } + + public int getPosition() { + return position; + } + + public void setPosition(int position) { + this.position = position; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/main/java/org/redkale/convert/ConvertImpl.java b/src/main/java/org/redkale/convert/ConvertImpl.java new file mode 100644 index 000000000..e6a216cee --- /dev/null +++ b/src/main/java/org/redkale/convert/ConvertImpl.java @@ -0,0 +1,53 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 用于序列化时接口或抽象类的默认实现类, 被标记的类必须是接口或抽象类
    + * 使用场景:
    + * + *

    + * @ConvertImpl(OneImpl.class)
    + * public interface OneEntity {
    + *     public String getName();
    + * }
    + *
    + * 
    + * public class OneImpl implements OneEntity {
    + *     private String name;
    + *     public String getName(){return name;}
    + *     public void setName(String name){this.name=name;}
    + * }
    + * 
    + *
    + * String json = "{'name':'hello'}";
    + * OneEntity one = JsonConvert.root.convertFrom(OneEntity.class, json);
    + * //one instanceof OneImpl
    + * 
    + * 
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ConvertImpl { + + /** + * 默认的实现类 + * + * @return String + */ + Class value(); +} diff --git a/src/org/redkale/convert/ConvertMask.java b/src/main/java/org/redkale/convert/ConvertMask.java similarity index 94% rename from src/org/redkale/convert/ConvertMask.java rename to src/main/java/org/redkale/convert/ConvertMask.java index 11829f57f..49633c86d 100644 --- a/src/org/redkale/convert/ConvertMask.java +++ b/src/main/java/org/redkale/convert/ConvertMask.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -/** - * Mask接口 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface ConvertMask { - - default byte mask(byte value) { - return value; - } - - default byte unmask(byte value) { - return value; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +/** + * Mask接口 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface ConvertMask { + + default byte mask(byte value) { + return value; + } + + default byte unmask(byte value) { + return value; + } +} diff --git a/src/org/redkale/convert/ConvertLoader.java b/src/main/java/org/redkale/convert/ConvertProvider.java similarity index 84% rename from src/org/redkale/convert/ConvertLoader.java rename to src/main/java/org/redkale/convert/ConvertProvider.java index 8b9a3f5a5..160de4a1c 100644 --- a/src/org/redkale/convert/ConvertLoader.java +++ b/src/main/java/org/redkale/convert/ConvertProvider.java @@ -1,23 +1,23 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -/** - * Convert的扩展实现类加载器 - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.2.0 - */ -public interface ConvertLoader { - - public ConvertType type(); - - public Convert convert(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +/** + * Convert的扩展实现类加载器 + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.5.0 + */ +public interface ConvertProvider { + + public ConvertType type(); + + public Convert convert(); +} diff --git a/src/org/redkale/convert/ConvertSmallString.java b/src/main/java/org/redkale/convert/ConvertSmallString.java similarity index 95% rename from src/org/redkale/convert/ConvertSmallString.java rename to src/main/java/org/redkale/convert/ConvertSmallString.java index 4fcdd637f..210f8c384 100644 --- a/src/org/redkale/convert/ConvertSmallString.java +++ b/src/main/java/org/redkale/convert/ConvertSmallString.java @@ -1,27 +1,27 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 序列化时标记String字段的值是否为无转义字符且长度不超过255的字符串 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.3.0 - * - */ -@Target({METHOD, FIELD}) -@Retention(RUNTIME) -public @interface ConvertSmallString { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 序列化时标记String字段的值是否为无转义字符且长度不超过255的字符串 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + * + */ +@Target({METHOD, FIELD}) +@Retention(RUNTIME) +public @interface ConvertSmallString { + +} diff --git a/src/org/redkale/convert/ConvertType.java b/src/main/java/org/redkale/convert/ConvertType.java similarity index 95% rename from src/org/redkale/convert/ConvertType.java rename to src/main/java/org/redkale/convert/ConvertType.java index 19f816e3e..61bdedec2 100644 --- a/src/org/redkale/convert/ConvertType.java +++ b/src/main/java/org/redkale/convert/ConvertType.java @@ -1,46 +1,46 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -/** - * 序列化类型枚举,结合@ConvertColumn使用 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public enum ConvertType { - - JSON(1), - BSON(2), - PROTOBUF(64), - PROTOBUF_JSON(64 + 1), - DIY(256), - ALL(1023); - - private final int value; - - private ConvertType(int v) { - this.value = v; - } - - public int getValue() { - return value; - } - - public boolean contains(ConvertType type) { - if (type == null) return false; - return this.value >= type.value && (this.value & type.value) > 0; - } - - public static ConvertType find(int value) { - for (ConvertType t : ConvertType.values()) { - if (value == t.value) return t; - } - return null; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +/** + * 序列化类型枚举,结合@ConvertColumn使用 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public enum ConvertType { + + JSON(1), + BSON(2), + PROTOBUF(64), + PROTOBUF_JSON(64 + 1), + DIY(256), + ALL(1023); + + private final int value; + + private ConvertType(int v) { + this.value = v; + } + + public int getValue() { + return value; + } + + public boolean contains(ConvertType type) { + if (type == null) return false; + return this.value >= type.value && (this.value & type.value) > 0; + } + + public static ConvertType find(int value) { + for (ConvertType t : ConvertType.values()) { + if (value == t.value) return t; + } + return null; + } +} diff --git a/src/org/redkale/convert/DeMember.java b/src/main/java/org/redkale/convert/DeMember.java similarity index 80% rename from src/org/redkale/convert/DeMember.java rename to src/main/java/org/redkale/convert/DeMember.java index 35fc45d6f..44fc8f30a 100644 --- a/src/org/redkale/convert/DeMember.java +++ b/src/main/java/org/redkale/convert/DeMember.java @@ -1,118 +1,137 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import org.redkale.util.Attribute; - -/** - * 字段的反序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param 字段依附的类 - * @param 字段的数据类型 - */ -@SuppressWarnings("unchecked") -public final class DeMember { - - protected int index; - - protected int position; //从1开始 - - protected int tag; //主要给protobuf使用 - - protected final Attribute attribute; - - protected Decodeable decoder; - - public DeMember(final Attribute attribute) { - this.attribute = attribute; - } - - public DeMember(Attribute attribute, Decodeable decoder) { - this(attribute); - this.decoder = decoder; - } - - public static DeMember create(final ConvertFactory factory, final Class clazz, final String fieldname) { - try { - Field field = clazz.getDeclaredField(fieldname); - return new DeMember<>(Attribute.create(field), factory.loadDecoder(field.getGenericType())); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static DeMember create(final ConvertFactory factory, final Class clazz, final String fieldname, final Class fieldtype) { - return new DeMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadDecoder(fieldtype)); - } - - public static DeMember create(final Attribute attribute, final ConvertFactory factory, final Class fieldtype) { - return new DeMember<>(attribute, factory.loadDecoder(fieldtype)); - } - - public final boolean match(String name) { - return attribute.field().equals(name); - } - - public final void read(R in, T obj) { - this.attribute.set(obj, decoder.convertFrom(in)); - } - - public final F read(R in) { - return decoder.convertFrom(in); - } - - public Attribute getAttribute() { - return this.attribute; - } - - public Decodeable getDecoder() { - return decoder; - } - - public int getIndex() { - return this.index; - } - - public int getPosition() { - return this.position; - } - - public int getTag() { - return this.tag; - } - - public int compareTo(boolean fieldSort, DeMember o) { - if (o == null) return -1; - if (this.position != o.position) return (this.position == 0 ? Integer.MAX_VALUE : this.position) - (o.position == 0 ? Integer.MAX_VALUE : o.position); - if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index); - if (this.index != 0) throw new RuntimeException("fields (" + attribute.field() + ", " + o.attribute.field() + ") have same ConvertColumn.index(" + this.index + ") in " + attribute.declaringClass()); - return fieldSort ? this.attribute.field().compareTo(o.attribute.field()) : 0; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof DeMember)) return false; - DeMember other = (DeMember) obj; - return compareTo(true, other) == 0; - } - - @Override - public int hashCode() { - return this.attribute.field().hashCode(); - } - - @Override - public String toString() { - return "DeMember{" + "attribute=" + attribute.field() + ", position=" + position + ", tag=" + tag + ", decoder=" + (decoder == null ? null : decoder.getClass().getName()) + '}'; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import org.redkale.util.Attribute; + +/** + * 字段的反序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param 字段依附的类 + * @param 字段的数据类型 + */ +@SuppressWarnings("unchecked") +public final class DeMember { + + final Field field; //对应类成员的Field, 也可能为null + + final Method method; //对应类成员的Method也可能为null + + protected int index; + + protected int position; //从1开始 + + protected int tag; //主要给protobuf使用 + + protected final Attribute attribute; + + protected Decodeable decoder; + + public DeMember(final Attribute attribute, Field field, Method method) { + this.attribute = attribute; + this.field = field; + this.method = method; + } + + public DeMember(Attribute attribute, Decodeable decoder, Field field, Method method) { + this(attribute, field, method); + this.decoder = decoder; + } + + public static DeMember create(final ConvertFactory factory, final Class clazz, final String fieldname) { + try { + Field field = clazz.getDeclaredField(fieldname); + return new DeMember<>(Attribute.create(field), factory.loadDecoder(field.getGenericType()), field, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static DeMember create(final ConvertFactory factory, final Class clazz, final String fieldname, final Class fieldtype) { + try { + Field field = clazz.getDeclaredField(fieldname); + return new DeMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadDecoder(fieldtype), field, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static DeMember create(final Attribute attribute, final ConvertFactory factory, final Class fieldtype) { + return new DeMember<>(attribute, factory.loadDecoder(fieldtype), null, null); + } + + public final boolean accepts(String name) { + return attribute.field().equals(name); + } + + public final void read(R in, T obj) { + this.attribute.set(obj, decoder.convertFrom(in)); + } + + public final F read(R in) { + return decoder.convertFrom(in); + } + + public Attribute getAttribute() { + return this.attribute; + } + + public Decodeable getDecoder() { + return decoder; + } + + public Field getField() { + return field; + } + + public Method getMethod() { + return method; + } + + public int getIndex() { + return this.index; + } + + public int getPosition() { + return this.position; + } + + public int getTag() { + return this.tag; + } + + public int compareTo(boolean fieldSort, DeMember o) { + if (o == null) return -1; + if (this.position != o.position) return (this.position == 0 ? Integer.MAX_VALUE : this.position) - (o.position == 0 ? Integer.MAX_VALUE : o.position); + if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index); + if (this.index != 0) throw new RuntimeException("fields (" + attribute.field() + ", " + o.attribute.field() + ") have same ConvertColumn.index(" + this.index + ") in " + attribute.declaringClass()); + return fieldSort ? this.attribute.field().compareTo(o.attribute.field()) : 0; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof DeMember)) return false; + DeMember other = (DeMember) obj; + return compareTo(true, other) == 0; + } + + @Override + public int hashCode() { + return this.attribute.field().hashCode(); + } + + @Override + public String toString() { + return "DeMember{" + "attribute=" + attribute.field() + ", position=" + position + ", tag=" + tag + ", decoder=" + (decoder == null ? null : decoder.getClass().getName()) + '}'; + } +} diff --git a/src/org/redkale/convert/Decodeable.java b/src/main/java/org/redkale/convert/Decodeable.java similarity index 95% rename from src/org/redkale/convert/Decodeable.java rename to src/main/java/org/redkale/convert/Decodeable.java index 98e9b933f..f1db855f4 100644 --- a/src/org/redkale/convert/Decodeable.java +++ b/src/main/java/org/redkale/convert/Decodeable.java @@ -1,31 +1,31 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.Type; - -/** - * 反序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param 反解析的数据类型 - */ -public interface Decodeable { - - public T convertFrom(final R in); - - /** - * 泛型映射接口 - * - * @return 反解析的数据类型 - */ - public Type getType(); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; + +/** + * 反序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param 反解析的数据类型 + */ +public interface Decodeable { + + public T convertFrom(final R in); + + /** + * 泛型映射接口 + * + * @return 反解析的数据类型 + */ + public Type getType(); + +} diff --git a/src/org/redkale/convert/EnMember.java b/src/main/java/org/redkale/convert/EnMember.java similarity index 84% rename from src/org/redkale/convert/EnMember.java rename to src/main/java/org/redkale/convert/EnMember.java index 12edd905f..f6da37cfc 100644 --- a/src/org/redkale/convert/EnMember.java +++ b/src/main/java/org/redkale/convert/EnMember.java @@ -1,137 +1,156 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import org.redkale.util.Attribute; - -/** - * 字段的序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Writer输出的子类 - * @param 字段依附的类 - * @param 字段的数据类型 - */ -@SuppressWarnings("unchecked") -public final class EnMember { - - final Attribute attribute; - - final Encodeable encoder; - - final boolean string; - - //final boolean isnumber; - final boolean bool; - - final char[] jsonFieldNameChars; - - final byte[] jsonFieldNameBytes; - - protected int index; - - protected int position; //从1开始 - - protected int tag; //主要给protobuf使用 - - public EnMember(Attribute attribute, Encodeable encoder) { - this.attribute = attribute; - this.encoder = encoder; - Class t = attribute.type(); - this.string = CharSequence.class.isAssignableFrom(t); - this.bool = t == Boolean.class || t == boolean.class; - this.jsonFieldNameChars = ('"' + attribute.field() + "\":").toCharArray(); - this.jsonFieldNameBytes = ('"' + attribute.field() + "\":").getBytes(); - //this.isnumber = Number.class.isAssignableFrom(t) || (!this.isbool && t.isPrimitive()); - } - - public static EnMember create(final ConvertFactory factory, final Class clazz, final String fieldname) { - try { - Field field = clazz.getDeclaredField(fieldname); - return new EnMember<>(Attribute.create(field), factory.loadEncoder(field.getGenericType())); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static EnMember create(final ConvertFactory factory, final Class clazz, final String fieldname, final Class fieldtype) { - return new EnMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadEncoder(fieldtype)); - } - - public static EnMember create(final Attribute attribute, final ConvertFactory factory, final Class fieldtype) { - return new EnMember<>(attribute, factory.loadEncoder(fieldtype)); - } - - public final boolean match(String name) { - return attribute.field().equals(name); - } - - public Attribute getAttribute() { - return attribute; - } - - public char[] getJsonFieldNameChars() { - return jsonFieldNameChars; - } - - public byte[] getJsonFieldNameBytes() { - return jsonFieldNameBytes; - } - - public Encodeable getEncoder() { - return encoder; - } - - public boolean isStringType() { - return string; - } - - public boolean isBoolType() { - return bool; - } - - public int getIndex() { - return this.index; - } - - public int getPosition() { - return this.position; - } - - public int getTag() { - return this.tag; - } - - public int compareTo(boolean fieldSort, EnMember o) { - if (o == null) return -1; - if (this.position != o.position) return (this.position == 0 ? Integer.MAX_VALUE : this.position) - (o.position == 0 ? Integer.MAX_VALUE : o.position); - if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index); - if (this.index != 0) throw new RuntimeException("fields (" + attribute.field() + ", " + o.attribute.field() + ") have same ConvertColumn.index(" + this.index + ") in " + attribute.declaringClass()); - return fieldSort ? this.attribute.field().compareTo(o.attribute.field()) : 0; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof EnMember)) return false; - EnMember other = (EnMember) obj; - return compareTo(true, other) == 0; - } - - @Override - public int hashCode() { - return this.attribute.field().hashCode(); - } - - @Override - public String toString() { - return "EnMember{" + "attribute=" + attribute.field() + ", position=" + position + ", encoder=" + (encoder == null ? null : encoder.getClass().getName()) + '}'; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import org.redkale.util.Attribute; + +/** + * 字段的序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Writer输出的子类 + * @param 字段依附的类 + * @param 字段的数据类型 + */ +@SuppressWarnings("unchecked") +public final class EnMember { + + final Attribute attribute; + + final Encodeable encoder; + + final boolean string; + + //final boolean isnumber; + final boolean bool; + + final char[] jsonFieldNameChars; + + final byte[] jsonFieldNameBytes; + + final Field field; //对应类成员的Field也可能为null + + final Method method; //对应类成员的Method也可能为null + + protected int index; + + protected int position; //从1开始 + + protected int tag; //主要给protobuf使用 + + public EnMember(Attribute attribute, Encodeable encoder, Field field, Method method) { + this.attribute = attribute; + this.encoder = encoder; + this.field = field; + this.method = method; + Class t = attribute.type(); + this.string = CharSequence.class.isAssignableFrom(t); + this.bool = t == Boolean.class || t == boolean.class; + this.jsonFieldNameChars = ('"' + attribute.field() + "\":").toCharArray(); + this.jsonFieldNameBytes = ('"' + attribute.field() + "\":").getBytes(); + //this.isnumber = Number.class.isAssignableFrom(t) || (!this.isbool && t.isPrimitive()); + } + + public static EnMember create(final ConvertFactory factory, final Class clazz, final String fieldname) { + try { + Field field = clazz.getDeclaredField(fieldname); + return new EnMember<>(Attribute.create(field), factory.loadEncoder(field.getGenericType()), field, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static EnMember create(final ConvertFactory factory, final Class clazz, final String fieldname, final Class fieldtype) { + try { + Field field = clazz.getDeclaredField(fieldname); + return new EnMember<>(Attribute.create(clazz, fieldname, fieldtype), factory.loadEncoder(fieldtype), field, null); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static EnMember create(final Attribute attribute, final ConvertFactory factory, final Class fieldtype) { + return new EnMember<>(attribute, factory.loadEncoder(fieldtype), null, null); + } + + public final boolean accepts(String name) { + return attribute.field().equals(name); + } + + public Attribute getAttribute() { + return attribute; + } + + public char[] getJsonFieldNameChars() { + return jsonFieldNameChars; + } + + public byte[] getJsonFieldNameBytes() { + return jsonFieldNameBytes; + } + + public Encodeable getEncoder() { + return encoder; + } + + public boolean isStringType() { + return string; + } + + public Field getField() { + return field; + } + + public Method getMethod() { + return method; + } + + public boolean isBoolType() { + return bool; + } + + public int getIndex() { + return this.index; + } + + public int getPosition() { + return this.position; + } + + public int getTag() { + return this.tag; + } + + public int compareTo(boolean fieldSort, EnMember o) { + if (o == null) return -1; + if (this.position != o.position) return (this.position == 0 ? Integer.MAX_VALUE : this.position) - (o.position == 0 ? Integer.MAX_VALUE : o.position); + if (this.index != o.index) return (this.index == 0 ? Integer.MAX_VALUE : this.index) - (o.index == 0 ? Integer.MAX_VALUE : o.index); + if (this.index != 0) throw new RuntimeException("fields (" + attribute.field() + ", " + o.attribute.field() + ") have same ConvertColumn.index(" + this.index + ") in " + attribute.declaringClass()); + return fieldSort ? this.attribute.field().compareTo(o.attribute.field()) : 0; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (!(obj instanceof EnMember)) return false; + EnMember other = (EnMember) obj; + return compareTo(true, other) == 0; + } + + @Override + public int hashCode() { + return this.attribute.field().hashCode(); + } + + @Override + public String toString() { + return "EnMember{" + "attribute=" + attribute.field() + ", position=" + position + ", encoder=" + (encoder == null ? null : encoder.getClass().getName()) + '}'; + } +} diff --git a/src/org/redkale/convert/Encodeable.java b/src/main/java/org/redkale/convert/Encodeable.java similarity index 95% rename from src/org/redkale/convert/Encodeable.java rename to src/main/java/org/redkale/convert/Encodeable.java index 136e1458c..14a5de81f 100644 --- a/src/org/redkale/convert/Encodeable.java +++ b/src/main/java/org/redkale/convert/Encodeable.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.Type; - -/** - * 序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Writer输出的子类 - * @param 序列化的数据类型 - */ -public interface Encodeable { - - public void convertTo(final W out, T value); - - /** - * 泛型映射接口 - * - * @return 返回序列化对象类的数据类型 - */ - public Type getType(); - - //是否需要检查Writer.specify - default boolean specifyable() { - return true; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; + +/** + * 序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Writer输出的子类 + * @param 序列化的数据类型 + */ +public interface Encodeable { + + public void convertTo(final W out, T value); + + /** + * 泛型映射接口 + * + * @return 返回序列化对象类的数据类型 + */ + public Type getType(); + + //是否需要检查Writer.specify + default boolean specifyable() { + return true; + } + +} diff --git a/src/org/redkale/convert/MapDecoder.java b/src/main/java/org/redkale/convert/MapDecoder.java similarity index 97% rename from src/org/redkale/convert/MapDecoder.java rename to src/main/java/org/redkale/convert/MapDecoder.java index bef9269f8..3baa14a3b 100644 --- a/src/org/redkale/convert/MapDecoder.java +++ b/src/main/java/org/redkale/convert/MapDecoder.java @@ -1,189 +1,189 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import org.redkale.util.Creator; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.*; - -/** - * Map的反序列化操作类
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Map key的数据类型 - * @param Map value的数据类型 - */ -@SuppressWarnings("unchecked") -public class MapDecoder implements Decodeable> { - - protected final Type type; - - protected final Type keyType; - - protected final Type valueType; - - protected Creator> creator; - - protected final Decodeable keyDecoder; - - protected final Decodeable valueDecoder; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public MapDecoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type == java.util.Properties.class) { - this.keyType = String.class; - this.valueType = String.class; - this.creator = factory.loadCreator(java.util.Properties.class); - factory.register(type, this); - this.keyDecoder = factory.loadDecoder(String.class); - this.valueDecoder = factory.loadDecoder(String.class); - } else if (type instanceof ParameterizedType) { - final ParameterizedType pt = (ParameterizedType) type; - this.keyType = pt.getActualTypeArguments()[0]; - this.valueType = pt.getActualTypeArguments()[1]; - this.creator = factory.loadCreator((Class) pt.getRawType()); - factory.register(type, this); - this.keyDecoder = factory.loadDecoder(this.keyType); - this.valueDecoder = factory.loadDecoder(this.valueType); - } else if (factory.isReversible()) { - this.keyType = Object.class; - this.valueType = Object.class; - this.creator = factory.loadCreator((Class) type); - this.keyDecoder = factory.loadDecoder(this.keyType); - this.valueDecoder = factory.loadDecoder(this.valueType); - } else { - throw new ConvertException("mapdecoder not support the type (" + type + ")"); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - //仅供类似JsonAnyDecoder这种动态创建使用, 不得调用 factory.register - public MapDecoder(final ConvertFactory factory, Type type, Type keyType, Type valueType, - Creator> creator, final Decodeable keyDecoder, Decodeable valueDecoder) { - Objects.requireNonNull(keyDecoder); - Objects.requireNonNull(valueDecoder); - this.type = type; - this.keyType = keyType; - this.valueType = valueType; - this.creator = creator; - this.keyDecoder = keyDecoder; - this.valueDecoder = valueDecoder; - this.inited = true; - } - - @Override - public Map convertFrom(Reader in) { - return convertFrom(in, null); - } - - public Map convertFrom(Reader in, DeMember member) { - if (this.keyDecoder == null || this.valueDecoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - byte[] typevals = new byte[2]; - int len = in.readMapB(member, typevals, this.keyDecoder, this.valueDecoder); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(member, null); - len = Reader.SIGN_NOLENGTH; - } - final Map result = this.creator.create(); - boolean first = true; - Decodeable kdecoder = getKeyDecoder(this.keyDecoder, typevals); - Decodeable vdecoder = getValueDecoder(this.valueDecoder, typevals); - if (len == Reader.SIGN_NOLENGTH) { - int startPosition = in.position(); - while (hasNext(in, member, startPosition, contentLength, first)) { - Reader entryReader = getEntryReader(in, member, first); - if (entryReader == null) break; - K key = readKeyMember(entryReader, member, kdecoder, first); - entryReader.readBlank(); - V value = readValueMember(entryReader, member, vdecoder, first); - result.put(key, value); - first = false; - } - } else { - for (int i = 0; i < len; i++) { - K key = readKeyMember(in, member, kdecoder, first); - in.readBlank(); - V value = readValueMember(in, member, vdecoder, first); - result.put(key, value); - first = false; - } - } - in.readMapE(); - return result; - } - - protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { - return in.hasNext(startPosition, contentLength); - } - - protected Decodeable getKeyDecoder(Decodeable decoder, byte[] typevals) { - return decoder; - } - - protected Decodeable getValueDecoder(Decodeable decoder, byte[] typevals) { - return decoder; - } - - protected Reader getEntryReader(Reader in, DeMember member, boolean first) { - return in; - } - - protected K readKeyMember(Reader in, DeMember member, Decodeable decoder, boolean first) { - return decoder.convertFrom(in); - } - - protected V readValueMember(Reader in, DeMember member, Decodeable decoder, boolean first) { - return decoder.convertFrom(in); - } - - @Override - public Type getType() { - return this.type; - } - - public Type getKeyType() { - return keyType; - } - - public Type getValueType() { - return valueType; - } - - public Decodeable getKeyDecoder() { - return keyDecoder; - } - - public Decodeable getValueDecoder() { - return valueDecoder; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import org.redkale.util.Creator; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; + +/** + * Map的反序列化操作类
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Map key的数据类型 + * @param Map value的数据类型 + */ +@SuppressWarnings("unchecked") +public class MapDecoder implements Decodeable> { + + protected final Type type; + + protected final Type keyType; + + protected final Type valueType; + + protected Creator> creator; + + protected final Decodeable keyDecoder; + + protected final Decodeable valueDecoder; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public MapDecoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type == java.util.Properties.class) { + this.keyType = String.class; + this.valueType = String.class; + this.creator = factory.loadCreator(java.util.Properties.class); + factory.register(type, this); + this.keyDecoder = factory.loadDecoder(String.class); + this.valueDecoder = factory.loadDecoder(String.class); + } else if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.keyType = pt.getActualTypeArguments()[0]; + this.valueType = pt.getActualTypeArguments()[1]; + this.creator = factory.loadCreator((Class) pt.getRawType()); + factory.register(type, this); + this.keyDecoder = factory.loadDecoder(this.keyType); + this.valueDecoder = factory.loadDecoder(this.valueType); + } else if (factory.isReversible()) { + this.keyType = Object.class; + this.valueType = Object.class; + this.creator = factory.loadCreator((Class) type); + this.keyDecoder = factory.loadDecoder(this.keyType); + this.valueDecoder = factory.loadDecoder(this.valueType); + } else { + throw new ConvertException("mapdecoder not support the type (" + type + ")"); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + //仅供类似JsonAnyDecoder这种动态创建使用, 不得调用 factory.register + public MapDecoder(final ConvertFactory factory, Type type, Type keyType, Type valueType, + Creator> creator, final Decodeable keyDecoder, Decodeable valueDecoder) { + Objects.requireNonNull(keyDecoder); + Objects.requireNonNull(valueDecoder); + this.type = type; + this.keyType = keyType; + this.valueType = valueType; + this.creator = creator; + this.keyDecoder = keyDecoder; + this.valueDecoder = valueDecoder; + this.inited = true; + } + + @Override + public Map convertFrom(Reader in) { + return convertFrom(in, null); + } + + public Map convertFrom(Reader in, DeMember member) { + if (this.keyDecoder == null || this.valueDecoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + byte[] typevals = new byte[2]; + int len = in.readMapB(member, typevals, this.keyDecoder, this.valueDecoder); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(member, null); + len = Reader.SIGN_NOLENGTH; + } + final Map result = this.creator.create(); + boolean first = true; + Decodeable kdecoder = getKeyDecoder(this.keyDecoder, typevals); + Decodeable vdecoder = getValueDecoder(this.valueDecoder, typevals); + if (len == Reader.SIGN_NOLENGTH) { + int startPosition = in.position(); + while (hasNext(in, member, startPosition, contentLength, first)) { + Reader entryReader = getEntryReader(in, member, first); + if (entryReader == null) break; + K key = readKeyMember(entryReader, member, kdecoder, first); + entryReader.readBlank(); + V value = readValueMember(entryReader, member, vdecoder, first); + result.put(key, value); + first = false; + } + } else { + for (int i = 0; i < len; i++) { + K key = readKeyMember(in, member, kdecoder, first); + in.readBlank(); + V value = readValueMember(in, member, vdecoder, first); + result.put(key, value); + first = false; + } + } + in.readMapE(); + return result; + } + + protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { + return in.hasNext(startPosition, contentLength); + } + + protected Decodeable getKeyDecoder(Decodeable decoder, byte[] typevals) { + return decoder; + } + + protected Decodeable getValueDecoder(Decodeable decoder, byte[] typevals) { + return decoder; + } + + protected Reader getEntryReader(Reader in, DeMember member, boolean first) { + return in; + } + + protected K readKeyMember(Reader in, DeMember member, Decodeable decoder, boolean first) { + return decoder.convertFrom(in); + } + + protected V readValueMember(Reader in, DeMember member, Decodeable decoder, boolean first) { + return decoder.convertFrom(in); + } + + @Override + public Type getType() { + return this.type; + } + + public Type getKeyType() { + return keyType; + } + + public Type getValueType() { + return valueType; + } + + public Decodeable getKeyDecoder() { + return keyDecoder; + } + + public Decodeable getValueDecoder() { + return valueDecoder; + } + +} diff --git a/src/org/redkale/convert/MapEncoder.java b/src/main/java/org/redkale/convert/MapEncoder.java similarity index 96% rename from src/org/redkale/convert/MapEncoder.java rename to src/main/java/org/redkale/convert/MapEncoder.java index 53f5b0f40..299aff86d 100644 --- a/src/org/redkale/convert/MapEncoder.java +++ b/src/main/java/org/redkale/convert/MapEncoder.java @@ -1,115 +1,115 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.Map; - -/** - * Map的序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Map key的数据类型 - * @param Map value的数据类型 - */ -@SuppressWarnings("unchecked") -public class MapEncoder implements Encodeable> { - - protected final Type type; - - protected final Encodeable keyEncoder; - - protected final Encodeable valueEncoder; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public MapEncoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof ParameterizedType) { - final Type[] pt = ((ParameterizedType) type).getActualTypeArguments(); - this.keyEncoder = factory.loadEncoder(pt[0]); - this.valueEncoder = factory.loadEncoder(pt[1]); - } else { - this.keyEncoder = factory.getAnyEncoder(); - this.valueEncoder = factory.getAnyEncoder(); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - @Override - public void convertTo(Writer out, Map value) { - convertTo(out, null, value); - } - - public void convertTo(Writer out, EnMember member, Map value) { - final Map values = value; - if (values == null) { - out.writeNull(); - return; - } - - if (this.keyEncoder == null || this.valueEncoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - if (out.writeMapB(values.size(), (Encodeable) keyEncoder, (Encodeable) valueEncoder, value) < 0) { - boolean first = true; - for (Map.Entry en : values.entrySet()) { - if (!first) out.writeArrayMark(); - writeMemberValue(out, member, en.getKey(), en.getValue(), first); - if (first) first = false; - } - } - out.writeMapE(); - } - - protected void writeMemberValue(Writer out, EnMember member, K key, V value, boolean first) { - keyEncoder.convertTo(out, key); - out.writeMapMark(); - valueEncoder.convertTo(out, value); - } - - @Override - public Type getType() { - return type; - } - - public Type getKeyType() { - return keyEncoder == null ? null : keyEncoder.getType(); - } - - public Type getValueType() { - return valueEncoder == null ? null : valueEncoder.getType(); - } - - public Encodeable getKeyEncoder() { - return keyEncoder; - } - - public Encodeable getValueEncoder() { - return valueEncoder; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * Map的序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Map key的数据类型 + * @param Map value的数据类型 + */ +@SuppressWarnings("unchecked") +public class MapEncoder implements Encodeable> { + + protected final Type type; + + protected final Encodeable keyEncoder; + + protected final Encodeable valueEncoder; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public MapEncoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof ParameterizedType) { + final Type[] pt = ((ParameterizedType) type).getActualTypeArguments(); + this.keyEncoder = factory.loadEncoder(pt[0]); + this.valueEncoder = factory.loadEncoder(pt[1]); + } else { + this.keyEncoder = factory.getAnyEncoder(); + this.valueEncoder = factory.getAnyEncoder(); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + @Override + public void convertTo(Writer out, Map value) { + convertTo(out, null, value); + } + + public void convertTo(Writer out, EnMember member, Map value) { + final Map values = value; + if (values == null) { + out.writeNull(); + return; + } + + if (this.keyEncoder == null || this.valueEncoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + if (out.writeMapB(values.size(), (Encodeable) keyEncoder, (Encodeable) valueEncoder, value) < 0) { + boolean first = true; + for (Map.Entry en : values.entrySet()) { + if (!first) out.writeArrayMark(); + writeMemberValue(out, member, en.getKey(), en.getValue(), first); + if (first) first = false; + } + } + out.writeMapE(); + } + + protected void writeMemberValue(Writer out, EnMember member, K key, V value, boolean first) { + keyEncoder.convertTo(out, key); + out.writeMapMark(); + valueEncoder.convertTo(out, value); + } + + @Override + public Type getType() { + return type; + } + + public Type getKeyType() { + return keyEncoder == null ? null : keyEncoder.getType(); + } + + public Type getValueType() { + return valueEncoder == null ? null : valueEncoder.getType(); + } + + public Encodeable getKeyEncoder() { + return keyEncoder; + } + + public Encodeable getValueEncoder() { + return valueEncoder; + } + +} diff --git a/src/org/redkale/convert/ObjectDecoder.java b/src/main/java/org/redkale/convert/ObjectDecoder.java similarity index 88% rename from src/org/redkale/convert/ObjectDecoder.java rename to src/main/java/org/redkale/convert/ObjectDecoder.java index c64e25bae..f0076a69c 100644 --- a/src/org/redkale/convert/ObjectDecoder.java +++ b/src/main/java/org/redkale/convert/ObjectDecoder.java @@ -1,338 +1,359 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import org.redkale.util.Creator; -import java.lang.reflect.*; -import java.util.*; -import org.redkale.convert.ext.StringSimpledCoder; -import org.redkale.util.*; - -/** - * 自定义对象的反序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param 反解析的数据类型 - */ -@SuppressWarnings("unchecked") -public class ObjectDecoder implements Decodeable { - - protected final Type type; - - protected final Class typeClass; - - protected Creator creator; - - protected DeMember[] creatorConstructorMembers = null; - - protected DeMember[] members; - - protected ConvertFactory factory; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - protected ObjectDecoder(Type type) { - this.type = ((type instanceof Class) && ((Class) type).isInterface()) ? Object.class : type; - if (type instanceof ParameterizedType) { - final ParameterizedType pt = (ParameterizedType) type; - this.typeClass = (Class) pt.getRawType(); - } else if (type instanceof TypeVariable) { - TypeVariable tv = (TypeVariable) type; - Type[] ts = tv.getBounds(); - if (ts.length == 1 && ts[0] instanceof Class) { - this.typeClass = (Class) ts[0]; - } else { - throw new ConvertException("[" + type + "] is no a class or ParameterizedType"); - } - } else { - this.typeClass = (Class) type; - } - this.members = new DeMember[0]; - } - - public void init(final ConvertFactory factory) { - this.factory = factory; - try { - if (type == Object.class) { - this.creatorConstructorMembers = null; - return; - } - - Class clazz = null; - if (type instanceof ParameterizedType) { - final ParameterizedType pts = (ParameterizedType) type; - clazz = (Class) (pts).getRawType(); - } else if (type instanceof TypeVariable) { - TypeVariable tv = (TypeVariable) type; - Type[] ts = tv.getBounds(); - if (ts.length == 1 && ts[0] instanceof Class) { - clazz = (Class) ts[0]; - } else { - throw new ConvertException("[" + type + "] is no a class or TypeVariable"); - } - } else if (!(type instanceof Class)) { - throw new ConvertException("[" + type + "] is no a class"); - } else { - clazz = (Class) type; - } - if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) { - this.creator = factory.loadCreator(clazz); - if (this.creator == null) throw new ConvertException("Cannot create a creator for " + clazz); - } - final Set list = new LinkedHashSet(); - final String[] cps = ObjectEncoder.findConstructorProperties(this.creator); - try { - ConvertColumnEntry ref; - for (final Field field : clazz.getFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (factory.isConvertDisabled(field)) continue; - ref = factory.findRef(clazz, field); - if (ref != null && ref.ignore()) continue; - ConvertSmallString small = field.getAnnotation(ConvertSmallString.class); - Decodeable fieldCoder; - if (small != null && field.getType() == String.class) { - fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; - } else { - fieldCoder = factory.findFieldCoder(clazz, field.getName()); - } - if (fieldCoder == null) { - Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type); - fieldCoder = factory.loadDecoder(t); - } - DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, field, null, null), fieldCoder); - if (ref != null) member.index = ref.getIndex(); - list.add(member); - } - final boolean reversible = factory.isReversible(); - for (final Method method : clazz.getMethods()) { - if (Modifier.isStatic(method.getModifiers())) continue; - if (Modifier.isAbstract(method.getModifiers())) continue; - if (method.isSynthetic()) continue; - if (method.getName().length() < 4) continue; - if (!method.getName().startsWith("set")) continue; - if (factory.isConvertDisabled(method)) continue; - if (method.getParameterTypes().length != 1) continue; - if (method.getReturnType() != void.class) continue; - if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) { - boolean is = method.getParameterTypes()[0] == boolean.class || method.getParameterTypes()[0] == Boolean.class; - try { - clazz.getMethod(method.getName().replaceFirst("set", is ? "is" : "get")); - } catch (Exception e) { - continue; - } - } - ref = factory.findRef(clazz, method); - if (ref != null && ref.ignore()) continue; - - ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); - Decodeable fieldCoder; - if (small != null && method.getReturnType() == String.class) { - fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; - } else { - fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method)); - } - if (fieldCoder == null) { - Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type); - fieldCoder = factory.loadDecoder(t); - } - DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, null, method), fieldCoder); - if (ref != null) member.index = ref.getIndex(); - list.add(member); - } - if (cps != null) { //可能存在某些构造函数中的字段名不存在setter方法 - for (final String constructorField : cps) { - boolean flag = false; - for (DeMember m : list) { - if (m.attribute.field().equals(constructorField)) { - flag = true; - break; - } - } - if (flag) continue; - //不存在setter方法 - try { - Field f = clazz.getDeclaredField(constructorField); - Type t = TypeToken.createClassType(f.getGenericType(), this.type); - list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, f, null, null), factory.loadDecoder(t))); - } catch (NoSuchFieldException nsfe) { //不存在field, 可能存在getter方法 - char[] fs = constructorField.toCharArray(); - fs[0] = Character.toUpperCase(fs[0]); - String mn = new String(fs); - Method getter; - try { - getter = clazz.getMethod("get" + mn); - } catch (NoSuchMethodException ex) { - getter = clazz.getMethod("is" + mn); - } - Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type); - list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, getter, null), factory.loadDecoder(t))); - } - } - } - this.members = list.toArray(new DeMember[list.size()]); - //先排序一次 - Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); - Set pos = new HashSet<>(); - for (int i = 0; i < this.members.length; i++) { - if (this.members[i].index > 0) pos.add(this.members[i].index); - } - int pidx = 0; - for (DeMember member : this.members) { - if (member.index > 0) { - member.position = member.index; - } else { - while (pos.contains(++pidx)); - member.position = pidx; - } - initForEachDeMember(factory, member); - } - Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); - - if (cps != null) { - final String[] fields = cps; - final DeMember[] ms = new DeMember[fields.length]; - for (int i = 0; i < fields.length; i++) { - for (DeMember m : this.members) { - if (m.attribute.field().equals(fields[i])) { - ms[i] = m; - break; - } - } - } - this.creatorConstructorMembers = ms; - } - } catch (Exception ex) { - throw new ConvertException(ex); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - protected void initForEachDeMember(ConvertFactory factory, DeMember member) { - } - - protected void setTag(DeMember member, int tag) { - member.tag = tag; - } - - /** - * 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2] - * - * @param in 输入流 - * - * @return 反解析后的对象结果 - */ - @Override - public T convertFrom(final R in) { - R objin = objectReader(in); - final String clazz = objin.readObjectB(typeClass); - if (clazz == null) return null; - if (!clazz.isEmpty()) return (T) factory.loadDecoder(factory.getEntityAlias(clazz)).convertFrom(objin); - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - if (this.creator == null) { - if (typeClass.isInterface() || Modifier.isAbstract(typeClass.getModifiers())) { - throw new ConvertException("[" + typeClass + "] is a interface or abstract class, cannot create it's Creator."); - } - } - if (this.creatorConstructorMembers == null) { //空构造函数 - final T result = this.creator == null ? null : this.creator.create(); - boolean first = true; - while (hasNext(objin, first)) { - DeMember member = objin.readFieldName(members); - objin.readBlank(); - if (member == null) { - objin.skipValue(); //跳过不存在的属性的值 - } else { - readMemberValue(objin, member, result, first); - } - first = false; - } - objin.readObjectE(typeClass); - return result; - } else { //带参数的构造函数 - final DeMember[] fields = this.creatorConstructorMembers; - final Object[] constructorParams = new Object[fields.length]; - final Object[][] otherParams = new Object[this.members.length][2]; - int oc = 0; - boolean first = true; - while (hasNext(objin, first)) { - DeMember member = objin.readFieldName(members); - objin.readBlank(); - if (member == null) { - objin.skipValue(); //跳过不存在的属性的值 - } else { - Object val = readMemberValue(objin, member, first); - boolean flag = true; - for (int i = 0; i < fields.length; i++) { - if (member == fields[i]) { - constructorParams[i] = val; - flag = false; - break; - } - } - if (flag) otherParams[oc++] = new Object[]{member.attribute, val}; - - } - first = false; - } - objin.readObjectE(typeClass); - if (this.creator == null) return null; - final T result = this.creator.create(constructorParams); - for (int i = 0; i < oc; i++) { - ((Attribute) otherParams[i][0]).set(result, otherParams[i][1]); - } - return result; - } - } - - protected R objectReader(R in) { - return in; - } - - protected boolean hasNext(R in, boolean first) { - return in.hasNext(); - } - - protected Object readMemberValue(R in, DeMember member, boolean first) { - return member.read(in); - } - - protected void readMemberValue(R in, DeMember member, T result, boolean first) { - member.read(in, result); - } - - @Override - public Type getType() { - return this.type; - } - - public DeMember[] getMembers() { - return Arrays.copyOf(members, members.length); - } - - @Override - public String toString() { - return "ObjectDecoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}'; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import org.redkale.util.Creator; +import java.lang.reflect.*; +import java.util.*; +import org.redkale.convert.ext.StringSimpledCoder; +import org.redkale.util.*; + +/** + * 自定义对象的反序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param 反解析的数据类型 + */ +@SuppressWarnings("unchecked") +public class ObjectDecoder implements Decodeable { + + protected final Type type; + + protected final Class typeClass; + + protected Creator creator; + + protected DeMember[] creatorConstructorMembers = null; + + protected DeMember[] members; + + protected ConvertFactory factory; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + protected ObjectDecoder(Type type) { + this.type = ((type instanceof Class) && ((Class) type).isInterface()) ? Object.class : type; + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.typeClass = (Class) pt.getRawType(); + } else if (type instanceof TypeVariable) { + TypeVariable tv = (TypeVariable) type; + Type[] ts = tv.getBounds(); + if (ts.length == 1 && ts[0] instanceof Class) { + this.typeClass = (Class) ts[0]; + } else { + throw new ConvertException("[" + type + "] is no a class or ParameterizedType"); + } + } else { + this.typeClass = (Class) type; + } + this.members = new DeMember[0]; + } + + public void init(final ConvertFactory factory) { + this.factory = factory; + try { + if (type == Object.class) { + this.creatorConstructorMembers = null; + return; + } + + Class clazz = null; + if (type instanceof ParameterizedType) { + final ParameterizedType pts = (ParameterizedType) type; + clazz = (Class) (pts).getRawType(); + } else if (type instanceof TypeVariable) { + TypeVariable tv = (TypeVariable) type; + Type[] ts = tv.getBounds(); + if (ts.length == 1 && ts[0] instanceof Class) { + clazz = (Class) ts[0]; + } else { + throw new ConvertException("[" + type + "] is no a class or TypeVariable"); + } + } else if (!(type instanceof Class)) { + throw new ConvertException("[" + type + "] is no a class"); + } else { + clazz = (Class) type; + } + if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) { + this.creator = factory.loadCreator(clazz); + if (this.creator == null) throw new ConvertException("Cannot create a creator for " + clazz); + } + final Set list = new LinkedHashSet(); + final String[] cps = ObjectEncoder.findConstructorProperties(this.creator); + try { + ConvertColumnEntry ref; + RedkaleClassLoader.putReflectionPublicFields(clazz.getName()); + for (final Field field : clazz.getFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (factory.isConvertDisabled(field)) continue; + ref = factory.findRef(clazz, field); + if (ref != null && ref.ignore()) continue; + ConvertSmallString small = field.getAnnotation(ConvertSmallString.class); + Decodeable fieldCoder; + if (small != null && field.getType() == String.class) { + fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; + } else { + fieldCoder = factory.findFieldCoder(clazz, field.getName()); + } + if (fieldCoder == null) { + Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type); + fieldCoder = factory.loadDecoder(t); + } + DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, field, null, null), fieldCoder, field, null); + if (ref != null) member.index = ref.getIndex(); + list.add(member); + } + final boolean reversible = factory.isReversible(); + RedkaleClassLoader.putReflectionPublicMethods(clazz.getName()); + for (final Method method : clazz.getMethods()) { + if (Modifier.isStatic(method.getModifiers())) continue; + if (Modifier.isAbstract(method.getModifiers())) continue; + if (method.isSynthetic()) continue; + if (method.getReturnType() != void.class) continue; + if (method.getParameterCount() != 1) continue; + if (method.getName().length() < 4) continue; + if (!method.getName().startsWith("set")) continue; + if (factory.isConvertDisabled(method)) continue; + if (reversible && (cps == null || !ObjectEncoder.contains(cps, ConvertFactory.readGetSetFieldName(method)))) { + boolean is = method.getParameterTypes()[0] == boolean.class || method.getParameterTypes()[0] == Boolean.class; + try { + Method getter = clazz.getMethod(method.getName().replaceFirst("set", is ? "is" : "get")); + if (getter.getReturnType() != method.getParameterTypes()[0]) continue; + } catch (Exception e) { + continue; + } + } else { + String fieldname = ConvertFactory.readGetSetFieldName(method); + Field f = null; + try { + f = clazz.getDeclaredField(fieldname); + if (f.getType() != method.getParameterTypes()[0]) continue; + } catch (Exception e) { + } + if (f == null) { + boolean is = method.getParameterTypes()[0] == boolean.class || method.getParameterTypes()[0] == Boolean.class; + try { + Method getter = clazz.getMethod(method.getName().replaceFirst("set", is ? "is" : "get")); + if (getter.getReturnType() != method.getParameterTypes()[0]) continue; + } catch (Exception e) { + } + } + } + ref = factory.findRef(clazz, method); + if (ref != null && ref.ignore()) continue; + + ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); + Decodeable fieldCoder; + if (small != null && method.getReturnType() == String.class) { + fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; + } else { + fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method)); + } + if (fieldCoder == null) { + Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericParameterTypes()[0], this.type), this.type); + fieldCoder = factory.loadDecoder(t); + } + DeMember member = new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, null, method), fieldCoder, ConvertFactory.readGetSetField(method), method); + if (ref != null) member.index = ref.getIndex(); + list.add(member); + } + if (cps != null) { //可能存在某些构造函数中的字段名不存在setter方法 + for (final String constructorField : cps) { + boolean flag = false; + for (DeMember m : list) { + if (m.attribute.field().equals(constructorField)) { + flag = true; + break; + } + } + if (flag) continue; + //不存在setter方法 + try { + Field f = clazz.getDeclaredField(constructorField); + Type t = TypeToken.createClassType(f.getGenericType(), this.type); + list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, f, null, null), factory.loadDecoder(t), f, null)); + } catch (NoSuchFieldException nsfe) { //不存在field, 可能存在getter方法 + char[] fs = constructorField.toCharArray(); + fs[0] = Character.toUpperCase(fs[0]); + String mn = new String(fs); + Method getter; + try { + getter = clazz.getMethod("get" + mn); + } catch (NoSuchMethodException ex) { + getter = clazz.getMethod("is" + mn); + } + Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type); + list.add(new DeMember(ObjectEncoder.createAttribute(factory, type, clazz, null, getter, null), factory.loadDecoder(t), ConvertFactory.readGetSetField(getter), getter)); + } + } + } + + List sorts = new ArrayList<>(list); + Collections.sort(sorts, (a, b) -> a.compareTo(factory.isFieldSort(), b)); + Set pos = new HashSet<>(); + for (DeMember member : sorts) { + if (member.index > 0) pos.add(member.index); + } + int pidx = 0; + for (DeMember member : sorts) { + if (member.index > 0) { + member.position = member.index; + } else { + while (pos.contains(++pidx)); + member.position = pidx; + } + initForEachDeMember(factory, member); + } + + this.members = list.toArray(new DeMember[list.size()]); + Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); + + if (cps != null) { + final String[] fields = cps; + final DeMember[] ms = new DeMember[fields.length]; + for (int i = 0; i < fields.length; i++) { + for (DeMember m : this.members) { + if (m.attribute.field().equals(fields[i])) { + ms[i] = m; + break; + } + } + } + this.creatorConstructorMembers = ms; + } + } catch (Exception ex) { + throw new ConvertException(ex); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + protected void initForEachDeMember(ConvertFactory factory, DeMember member) { + } + + protected void setTag(DeMember member, int tag) { + member.tag = tag; + } + + /** + * 对象格式: [0x1][short字段个数][字段名][字段值]...[0x2] + * + * @param in 输入流 + * + * @return 反解析后的对象结果 + */ + @Override + public T convertFrom(final R in) { + R objin = objectReader(in); + final String clazz = objin.readObjectB(typeClass); + if (clazz == null) return null; + if (!clazz.isEmpty()) return (T) factory.loadDecoder(factory.getEntityAlias(clazz)).convertFrom(objin); + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + if (this.creator == null) { + if (typeClass.isInterface() || Modifier.isAbstract(typeClass.getModifiers())) { + throw new ConvertException("[" + typeClass + "] is a interface or abstract class, cannot create it's Creator."); + } + } + if (this.creatorConstructorMembers == null) { //空构造函数 + final T result = this.creator == null ? null : this.creator.create(); + boolean first = true; + while (hasNext(objin, first)) { + DeMember member = objin.readFieldName(members); + objin.readBlank(); + if (member == null) { + objin.skipValue(); //跳过不存在的属性的值 + } else { + readMemberValue(objin, member, result, first); + } + first = false; + } + objin.readObjectE(typeClass); + return result; + } else { //带参数的构造函数 + final DeMember[] fields = this.creatorConstructorMembers; + final Object[] constructorParams = new Object[fields.length]; + final Object[][] otherParams = new Object[this.members.length][2]; + int oc = 0; + boolean first = true; + while (hasNext(objin, first)) { + DeMember member = objin.readFieldName(members); + objin.readBlank(); + if (member == null) { + objin.skipValue(); //跳过不存在的属性的值 + } else { + Object val = readMemberValue(objin, member, first); + boolean flag = true; + for (int i = 0; i < fields.length; i++) { + if (member == fields[i]) { + constructorParams[i] = val; + flag = false; + break; + } + } + if (flag) otherParams[oc++] = new Object[]{member.attribute, val}; + + } + first = false; + } + objin.readObjectE(typeClass); + if (this.creator == null) return null; + final T result = this.creator.create(constructorParams); + for (int i = 0; i < oc; i++) { + ((Attribute) otherParams[i][0]).set(result, otherParams[i][1]); + } + return result; + } + } + + protected R objectReader(R in) { + return in; + } + + protected boolean hasNext(R in, boolean first) { + return in.hasNext(); + } + + protected Object readMemberValue(R in, DeMember member, boolean first) { + return member.read(in); + } + + protected void readMemberValue(R in, DeMember member, T result, boolean first) { + member.read(in, result); + } + + @Override + public Type getType() { + return this.type; + } + + public DeMember[] getMembers() { + return Arrays.copyOf(members, members.length); + } + + @Override + public String toString() { + return "ObjectDecoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}'; + } +} diff --git a/src/org/redkale/convert/ObjectEncoder.java b/src/main/java/org/redkale/convert/ObjectEncoder.java similarity index 79% rename from src/org/redkale/convert/ObjectEncoder.java rename to src/main/java/org/redkale/convert/ObjectEncoder.java index 45e56774e..1aa21cd4c 100644 --- a/src/org/redkale/convert/ObjectEncoder.java +++ b/src/main/java/org/redkale/convert/ObjectEncoder.java @@ -1,331 +1,378 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import java.util.*; -import org.redkale.convert.ext.StringSimpledCoder; -import org.redkale.util.*; - -/** - * 自定义对象的序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Writer输出的子类 - * @param 序列化的数据类型 - */ -@SuppressWarnings("unchecked") -public class ObjectEncoder implements Encodeable { - - static final Type[] TYPEZERO = new Type[0]; - - protected final Type type; - - protected final Class typeClass; - - protected EnMember[] members; - - protected ConvertFactory factory; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - protected ObjectEncoder(Type type) { - this.type = type; - if (type instanceof ParameterizedType) { - final ParameterizedType pt = (ParameterizedType) type; - this.typeClass = (Class) pt.getRawType(); - } else if (type instanceof TypeVariable) { - TypeVariable tv = (TypeVariable) type; - Type[] ts = tv.getBounds(); - if (ts.length == 1 && ts[0] instanceof Class) { - this.typeClass = (Class) ts[0]; - } else { - throw new ConvertException("[" + type + "] is no a class or ParameterizedType"); - } - } else { - this.typeClass = (Class) type; - } - this.members = new EnMember[0]; - } - - public void init(final ConvertFactory factory) { - this.factory = factory; - try { - if (type == Object.class) return; - //if (!(type instanceof Class)) throw new ConvertException("[" + type + "] is no a class"); - final Class clazz = this.typeClass; - final Set list = new LinkedHashSet(); - final boolean reversible = factory.isReversible(); - Creator creator = null; - try { - creator = factory.loadCreator(this.typeClass); - } catch (RuntimeException e) { - if (reversible) throw e; - } - final String[] cps = creator == null ? null : ObjectEncoder.findConstructorProperties(creator); - try { - ConvertColumnEntry ref; - for (final Field field : clazz.getFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (factory.isConvertDisabled(field)) continue; - ref = factory.findRef(clazz, field); - if (ref != null && ref.ignore()) continue; - ConvertSmallString small = field.getAnnotation(ConvertSmallString.class); - Encodeable fieldCoder; - if (small != null && field.getType() == String.class) { - fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; - } else { - fieldCoder = factory.findFieldCoder(clazz, field.getName()); - } - if (fieldCoder == null) { - Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type); - fieldCoder = factory.loadEncoder(t); - } - EnMember member = new EnMember(createAttribute(factory, type, clazz, field, null, null), fieldCoder); - if (ref != null) member.index = ref.getIndex(); - list.add(member); - } - for (final Method method : clazz.getMethods()) { - if (Modifier.isStatic(method.getModifiers())) continue; - if (Modifier.isAbstract(method.getModifiers())) continue; - if (method.isSynthetic()) continue; - if (method.getName().length() < 3) continue; - if (method.getName().equals("getClass")) continue; - if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue; - if (factory.isConvertDisabled(method)) continue; - if (method.getParameterTypes().length != 0) continue; - if (method.getReturnType() == void.class) continue; - String convertname = ConvertFactory.readGetSetFieldName(method); - if (reversible && (cps == null || !contains(cps, convertname))) { - boolean is = method.getName().startsWith("is"); - try { - clazz.getMethod(method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType()); - } catch (Exception e) { - continue; - } - } - ref = factory.findRef(clazz, method); - if (ref != null && ref.ignore()) continue; - ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); - if (small == null) { - try { - Field f = clazz.getDeclaredField(convertname); - if (f != null) small = f.getAnnotation(ConvertSmallString.class); - } catch (Exception e) { - } - } - Encodeable fieldCoder; - if (small != null && method.getReturnType() == String.class) { - fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; - } else { - fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method)); - } - if (fieldCoder == null) { - Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type); - fieldCoder = factory.loadEncoder(t); - } - EnMember member = new EnMember(createAttribute(factory, type, clazz, null, method, null), fieldCoder); - if (ref != null) member.index = ref.getIndex(); - list.add(member); - } - this.members = list.toArray(new EnMember[list.size()]); - //先排序一次 - Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); - Set pos = new HashSet<>(); - for (int i = 0; i < this.members.length; i++) { - if (this.members[i].index > 0) pos.add(this.members[i].index); - } - int pidx = 0; - for (EnMember member : this.members) { - if (member.index > 0) { - member.position = member.index; - } else { - while (pos.contains(++pidx)); - member.position = pidx; - } - initForEachEnMember(factory, member); - } - Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); - - } catch (Exception ex) { - throw new ConvertException("ObjectEncoder init type=" + this.type + " error", ex); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - protected void initForEachEnMember(ConvertFactory factory, EnMember member) { - } - - protected void setTag(EnMember member, int tag) { - member.tag = tag; - } - - @Override - public void convertTo(W out, T value) { - if (value == null) { - out.writeObjectNull(null); - return; - } - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - if (value.getClass() != this.typeClass && !this.type.equals(out.specify())) { - final Class clz = value.getClass(); - if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clz)); - factory.loadEncoder(clz).convertTo(out, value); - return; - } - W objout = objectWriter(out, value); - if (objout.writeObjectB(value) < 0) { - int maxPosition = 0; - for (EnMember member : members) { - maxPosition = member.getPosition(); - objout.writeObjectField(member, value); - } - if (objout.objExtFunc != null) { - ConvertField[] extFields = objout.objExtFunc.apply(value); - if (extFields != null) { - Encodeable anyEncoder = factory.getAnyEncoder(); - for (ConvertField en : extFields) { - if (en == null) continue; - maxPosition++; - objout.writeObjectField(en.getName(), en.getType(), Math.max(en.getPosition(), maxPosition), anyEncoder, en.getValue()); - } - } - } - } - objout.writeObjectE(value); - } - - protected W objectWriter(W out, T value) { - return out; - } - - @Override - public Type getType() { - return this.type; - } - - public EnMember[] getMembers() { - return Arrays.copyOf(members, members.length); - } - - @Override - public String toString() { - return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}'; - } - -// -// static Type makeGenericType(final Type type, final Type[] virGenericTypes, final Type[] realGenericTypes) { -// if (type instanceof Class) { //e.g. String -// return type; -// } else if (type instanceof ParameterizedType) { //e.g. Map -// final ParameterizedType pt = (ParameterizedType) type; -// Type[] paramTypes = pt.getActualTypeArguments(); -// final Type[] newTypes = new Type[paramTypes.length]; -// int count = 0; -// for (int i = 0; i < newTypes.length; i++) { -// newTypes[i] = makeGenericType(paramTypes[i], virGenericTypes, realGenericTypes); -// if (paramTypes[i] == newTypes[i]) count++; -// } -// if (count == paramTypes.length) return pt; -// return new ParameterizedType() { -// -// @Override -// public Type[] getActualTypeArguments() { -// return newTypes; -// } -// -// @Override -// public Type getRawType() { -// return pt.getRawType(); -// } -// -// @Override -// public Type getOwnerType() { -// return pt.getOwnerType(); -// } -// -// }; -// } -// if (realGenericTypes == null) return type; -// if (type instanceof WildcardType) { // e.g. -// final WildcardType wt = (WildcardType) type; -// for (Type f : wt.getUpperBounds()) { -// for (int i = 0; i < virGenericTypes.length; i++) { -// if (virGenericTypes[i] == f) return realGenericTypes.length == 0 ? Object.class : realGenericTypes[i]; -// } -// } -// } else if (type instanceof TypeVariable) { // e.g. -// for (int i = 0; i < virGenericTypes.length; i++) { -// if (virGenericTypes[i] == type) return i >= realGenericTypes.length ? Object.class : realGenericTypes[i]; -// } -// } -// return type; -// } - static boolean contains(String[] values, String value) { - for (String str : values) { - if (str.equals(value)) return true; - } - return false; - } - - static String[] findConstructorProperties(Creator creator) { - if (creator == null) return null; - try { - ConstructorParameters cps = creator.getClass().getMethod("create", Object[].class).getAnnotation(ConstructorParameters.class); - return cps == null ? null : cps.value(); - } catch (Exception e) { - return null; - } - } - - static Attribute createAttribute(final ConvertFactory factory, Type realType, Class clazz, final Field field, final Method getter, final Method setter) { - String fieldalias; - if (field != null) { // public field - ConvertColumnEntry ref = factory.findRef(clazz, field); - fieldalias = ref == null || ref.name().isEmpty() ? field.getName() : ref.name(); - } else if (getter != null) { - ConvertColumnEntry ref = factory.findRef(clazz, getter); - String mfieldname = ConvertFactory.readGetSetFieldName(getter); - if (ref == null) { - try { - ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname)); - } catch (Exception e) { - } - } - fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); - } else { // setter != null - ConvertColumnEntry ref = factory.findRef(clazz, setter); - String mfieldname = ConvertFactory.readGetSetFieldName(setter); - if (ref == null) { - try { - ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname)); - } catch (Exception e) { - } - } - fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); - } - return Attribute.create(realType, clazz, fieldalias, null, field, getter, setter, null); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import java.util.*; +import org.redkale.convert.ext.StringSimpledCoder; +import org.redkale.util.*; + +/** + * 自定义对象的序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Writer输出的子类 + * @param 序列化的数据类型 + */ +@SuppressWarnings("unchecked") +public class ObjectEncoder implements Encodeable { + + static final Type[] TYPEZERO = new Type[0]; + + protected final Type type; + + protected final Class typeClass; + + protected EnMember[] members; + + protected ConvertFactory factory; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + protected ObjectEncoder(Type type) { + this.type = type; + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.typeClass = (Class) pt.getRawType(); + } else if (type instanceof TypeVariable) { + TypeVariable tv = (TypeVariable) type; + Type[] ts = tv.getBounds(); + if (ts.length == 1 && ts[0] instanceof Class) { + this.typeClass = (Class) ts[0]; + } else { + throw new ConvertException("[" + type + "] is no a class or ParameterizedType"); + } + } else { + this.typeClass = (Class) type; + } + this.members = new EnMember[0]; + } + + public void init(final ConvertFactory factory) { + this.factory = factory; + try { + if (type == Object.class) return; + //if (!(type instanceof Class)) throw new ConvertException("[" + type + "] is no a class"); + final Class clazz = this.typeClass; + final Set list = new LinkedHashSet(); + final boolean reversible = factory.isReversible(); + Creator creator = null; + try { + creator = factory.loadCreator(this.typeClass); + } catch (RuntimeException e) { + if (reversible && !Modifier.isAbstract(this.typeClass.getModifiers())) throw e; + } + final String[] cps = creator == null ? null : ObjectEncoder.findConstructorProperties(creator); + try { + ConvertColumnEntry ref; + + RedkaleClassLoader.putReflectionPublicFields(clazz.getName()); + for (final Field field : clazz.getFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (factory.isConvertDisabled(field)) continue; + ref = factory.findRef(clazz, field); + if (ref != null && ref.ignore()) continue; + ConvertSmallString small = field.getAnnotation(ConvertSmallString.class); + Encodeable fieldCoder; + if (small != null && field.getType() == String.class) { + fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; + } else { + fieldCoder = factory.findFieldCoder(clazz, field.getName()); + } + if (fieldCoder == null) { + Type t = TypeToken.createClassType(TypeToken.getGenericType(field.getGenericType(), this.type), this.type); + fieldCoder = factory.loadEncoder(t); + } + EnMember member = new EnMember(createAttribute(factory, type, clazz, field, null, null), fieldCoder, field, null); + if (ref != null) member.index = ref.getIndex(); + list.add(member); + } + + RedkaleClassLoader.putReflectionPublicMethods(clazz.getName()); + for (final Method method : clazz.getMethods()) { + if (Modifier.isStatic(method.getModifiers())) continue; + if (Modifier.isAbstract(method.getModifiers())) continue; + if (method.isSynthetic()) continue; + if (method.getName().equals("getClass")) continue; + if (method.getReturnType() == void.class) continue; + if (method.getParameterCount() != 0) continue; + if (!method.getName().startsWith("is") && !method.getName().startsWith("get") && !Utility.isRecordGetter(clazz, method)) continue; + if (factory.isConvertDisabled(method)) continue; + String convertname = ConvertFactory.readGetSetFieldName(method); + if (reversible && (cps == null || !contains(cps, convertname))) { + boolean is = method.getName().startsWith("is"); + try { + clazz.getMethod(method.getName().replaceFirst(is ? "is" : "get", "set"), method.getReturnType()); + } catch (Exception e) { + continue; + } + } + ref = factory.findRef(clazz, method); + if (ref != null && ref.ignore()) continue; + ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); + if (small == null) { + try { + Field f = clazz.getDeclaredField(convertname); + if (f != null) small = f.getAnnotation(ConvertSmallString.class); + } catch (Exception e) { + } + } + Encodeable fieldCoder; + if (small != null && method.getReturnType() == String.class) { + fieldCoder = StringSimpledCoder.SmallStringSimpledCoder.instance; + } else { + fieldCoder = factory.findFieldCoder(clazz, ConvertFactory.readGetSetFieldName(method)); + } + if (fieldCoder == null) { + Type t = TypeToken.createClassType(TypeToken.getGenericType(method.getGenericReturnType(), this.type), this.type); + fieldCoder = factory.loadEncoder(t); + } + EnMember member = new EnMember(createAttribute(factory, type, clazz, null, method, null), fieldCoder, ConvertFactory.readGetSetField(method), method); + if (ref != null) member.index = ref.getIndex(); + list.add(member); + } + List sorts = new ArrayList<>(list); + if (cps != null) { + Set dissorts = new LinkedHashSet<>(list); + for (final String constructorField : cps) { //reversible模式下需要确保DeMember与EnMember的个数和顺序保持一致,不然postition会不一致导致反序列化对应的字段顺序不同 + boolean flag = false; + for (EnMember m : dissorts) { + if (m.attribute.field().equals(constructorField)) { + flag = true; + break; + } + } + if (flag) continue; + //不存在setter方法 + try { + Field f = clazz.getDeclaredField(constructorField); + Type t = TypeToken.createClassType(f.getGenericType(), this.type); + try { + dissorts.add(new EnMember(createAttribute(factory, type, clazz, f, null, null), null, f, null)); //虚构 + } catch (RuntimeException e) { + } + } catch (NoSuchFieldException nsfe) { //不存在field, 可能存在getter方法 + char[] fs = constructorField.toCharArray(); + fs[0] = Character.toUpperCase(fs[0]); + String mn = new String(fs); + Method getter; + try { + getter = clazz.getMethod("get" + mn); + } catch (NoSuchMethodException ex) { + getter = clazz.getMethod("is" + mn); + } + Type t = TypeToken.createClassType(TypeToken.getGenericType(getter.getGenericParameterTypes()[0], this.type), this.type); + try { + dissorts.add(new EnMember(createAttribute(factory, type, clazz, null, getter, null), null, null, null)); //虚构 + } catch (RuntimeException e) { + } + } + } + if (dissorts.size() != list.size()) sorts = new ArrayList<>(dissorts); + } + Collections.sort(sorts, (a, b) -> a.compareTo(factory.isFieldSort(), b)); + Set pos = new HashSet<>(); + for (EnMember member : sorts) { + if (member.index > 0) pos.add(member.index); + } + int pidx = 0; + for (EnMember member : sorts) { + if (member.index > 0) { + member.position = member.index; + } else { + while (pos.contains(++pidx)); + member.position = pidx; + } + initForEachEnMember(factory, member); + } + + this.members = list.toArray(new EnMember[list.size()]); + Arrays.sort(this.members, (a, b) -> a.compareTo(factory.isFieldSort(), b)); + + } catch (Exception ex) { + throw new ConvertException("ObjectEncoder init type=" + this.type + " error", ex); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + protected void initForEachEnMember(ConvertFactory factory, EnMember member) { + } + + protected void setTag(EnMember member, int tag) { + member.tag = tag; + } + + @Override + public void convertTo(W out, T value) { + if (value == null) { + out.writeObjectNull(null); + return; + } + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + if (value.getClass() != this.typeClass && !this.type.equals(out.specify())) { + final Class clz = value.getClass(); + if (out.needWriteClassName()) out.writeClassName(factory.getEntityAlias(clz)); + factory.loadEncoder(clz).convertTo(out, value); + return; + } + W objout = objectWriter(out, value); + if (objout.writeObjectB(value) < 0) { + int maxPosition = 0; + for (EnMember member : members) { + maxPosition = member.getPosition(); + objout.writeObjectField(member, value); + } + if (objout.objExtFunc != null) { + ConvertField[] extFields = objout.objExtFunc.apply(value); + if (extFields != null) { + Encodeable anyEncoder = factory.getAnyEncoder(); + for (ConvertField en : extFields) { + if (en == null) continue; + maxPosition++; + objout.writeObjectField(en.getName(), en.getType(), Math.max(en.getPosition(), maxPosition), anyEncoder, en.getValue()); + } + } + } + } + objout.writeObjectE(value); + } + + protected W objectWriter(W out, T value) { + return out; + } + + @Override + public Type getType() { + return this.type; + } + + public Class getTypeClass() { + return this.typeClass; + } + + public EnMember[] getMembers() { + return Arrays.copyOf(members, members.length); + } + + @Override + public String toString() { + return "ObjectEncoder{" + "type=" + type + ", members=" + Arrays.toString(members) + '}'; + } + +// +// static Type makeGenericType(final Type type, final Type[] virGenericTypes, final Type[] realGenericTypes) { +// if (type instanceof Class) { //e.g. String +// return type; +// } else if (type instanceof ParameterizedType) { //e.g. Map +// final ParameterizedType pt = (ParameterizedType) type; +// Type[] paramTypes = pt.getActualTypeArguments(); +// final Type[] newTypes = new Type[paramTypes.length]; +// int count = 0; +// for (int i = 0; i < newTypes.length; i++) { +// newTypes[i] = makeGenericType(paramTypes[i], virGenericTypes, realGenericTypes); +// if (paramTypes[i] == newTypes[i]) count++; +// } +// if (count == paramTypes.length) return pt; +// return new ParameterizedType() { +// +// @Override +// public Type[] getActualTypeArguments() { +// return newTypes; +// } +// +// @Override +// public Type getRawType() { +// return pt.getRawType(); +// } +// +// @Override +// public Type getOwnerType() { +// return pt.getOwnerType(); +// } +// +// }; +// } +// if (realGenericTypes == null) return type; +// if (type instanceof WildcardType) { // e.g. +// final WildcardType wt = (WildcardType) type; +// for (Type f : wt.getUpperBounds()) { +// for (int i = 0; i < virGenericTypes.length; i++) { +// if (virGenericTypes[i] == f) return realGenericTypes.length == 0 ? Object.class : realGenericTypes[i]; +// } +// } +// } else if (type instanceof TypeVariable) { // e.g. +// for (int i = 0; i < virGenericTypes.length; i++) { +// if (virGenericTypes[i] == type) return i >= realGenericTypes.length ? Object.class : realGenericTypes[i]; +// } +// } +// return type; +// } + static boolean contains(String[] values, String value) { + for (String str : values) { + if (str.equals(value)) return true; + } + return false; + } + + static String[] findConstructorProperties(Creator creator) { + if (creator == null) return null; + try { + ConstructorParameters cps = creator.getClass().getMethod("create", Object[].class).getAnnotation(ConstructorParameters.class); + return cps == null ? null : cps.value(); + } catch (Exception e) { + return null; + } + } + + static Attribute createAttribute(final ConvertFactory factory, final Type realType, Class clazz, final Field field, final Method getter, final Method setter) { + String fieldalias; + if (field != null) { // public field + ConvertColumnEntry ref = factory.findRef(clazz, field); + fieldalias = ref == null || ref.name().isEmpty() ? field.getName() : ref.name(); + } else if (getter != null) { + ConvertColumnEntry ref = factory.findRef(clazz, getter); + String mfieldname = ConvertFactory.readGetSetFieldName(getter); + if (ref == null) { + try { + ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname)); + } catch (Exception e) { + } + } + fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); + } else { // setter != null + ConvertColumnEntry ref = factory.findRef(clazz, setter); + String mfieldname = ConvertFactory.readGetSetFieldName(setter); + if (ref == null) { + try { + ref = factory.findRef(clazz, clazz.getDeclaredField(mfieldname)); + } catch (Exception e) { + } + } + fieldalias = ref == null || ref.name().isEmpty() ? mfieldname : ref.name(); + } + + return Attribute.create(realType, clazz, fieldalias, null, field, getter, setter, null); + } + +} diff --git a/src/org/redkale/convert/OptionalCoder.java b/src/main/java/org/redkale/convert/OptionalCoder.java similarity index 96% rename from src/org/redkale/convert/OptionalCoder.java rename to src/main/java/org/redkale/convert/OptionalCoder.java index 13f5d51f0..170e69da8 100644 --- a/src/org/redkale/convert/OptionalCoder.java +++ b/src/main/java/org/redkale/convert/OptionalCoder.java @@ -1,106 +1,106 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import java.util.*; - -/** - * Optional 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class OptionalCoder extends SimpledCoder> { - - protected final Type componentType; - - protected final Class componentClass; - - protected final Decodeable decoder; - - protected final Encodeable encoder; - - protected volatile boolean inited = false; - - private final Object lock = new Object(); - - @SuppressWarnings("unchecked") - public OptionalCoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof ParameterizedType) { - final ParameterizedType pt = (ParameterizedType) type; - this.componentType = pt.getActualTypeArguments()[0]; - factory.register(type, this); - this.decoder = factory.loadDecoder(this.componentType); - if (this.componentType instanceof TypeVariable) { - this.encoder = factory.getAnyEncoder(); - this.componentClass = Object.class; - } else { - if (componentType instanceof ParameterizedType) { - final ParameterizedType pt2 = (ParameterizedType) componentType; - this.componentClass = (Class) pt2.getRawType(); - } else { - this.componentClass = (Class) componentType; - } - this.encoder = factory.loadEncoder(this.componentType); - } - } else { - this.componentType = Object.class; - this.componentClass = Object.class; - this.decoder = factory.loadDecoder(this.componentType); - this.encoder = factory.getAnyEncoder(); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - @Override - public void convertTo(W out, Optional value) { - if (value == null || !value.isPresent()) { - out.writeObjectNull(null); - return; - } - if (this.encoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - this.encoder.convertTo(out, value.get()); - } - - @Override - public Optional convertFrom(R in) { - if (this.decoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - return Optional.ofNullable(this.decoder.convertFrom(in)); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import java.util.*; + +/** + * Optional 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class OptionalCoder extends SimpledCoder> { + + protected final Type componentType; + + protected final Class componentClass; + + protected final Decodeable decoder; + + protected final Encodeable encoder; + + protected volatile boolean inited = false; + + private final Object lock = new Object(); + + @SuppressWarnings("unchecked") + public OptionalCoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.componentType = pt.getActualTypeArguments()[0]; + factory.register(type, this); + this.decoder = factory.loadDecoder(this.componentType); + if (this.componentType instanceof TypeVariable) { + this.encoder = factory.getAnyEncoder(); + this.componentClass = Object.class; + } else { + if (componentType instanceof ParameterizedType) { + final ParameterizedType pt2 = (ParameterizedType) componentType; + this.componentClass = (Class) pt2.getRawType(); + } else { + this.componentClass = (Class) componentType; + } + this.encoder = factory.loadEncoder(this.componentType); + } + } else { + this.componentType = Object.class; + this.componentClass = Object.class; + this.decoder = factory.loadDecoder(this.componentType); + this.encoder = factory.getAnyEncoder(); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + @Override + public void convertTo(W out, Optional value) { + if (value == null || !value.isPresent()) { + out.writeObjectNull(null); + return; + } + if (this.encoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + this.encoder.convertTo(out, value.get()); + } + + @Override + public Optional convertFrom(R in) { + if (this.decoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + return Optional.ofNullable(this.decoder.convertFrom(in)); + } + +} diff --git a/src/org/redkale/convert/Reader.java b/src/main/java/org/redkale/convert/Reader.java similarity index 95% rename from src/org/redkale/convert/Reader.java rename to src/main/java/org/redkale/convert/Reader.java index c9aa9a102..df1a4da14 100644 --- a/src/org/redkale/convert/Reader.java +++ b/src/main/java/org/redkale/convert/Reader.java @@ -1,234 +1,234 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -/** - * 反序列化的数据读取流 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class Reader { - - public static enum ValueType { - STRING, ARRAY, MAP; - } - - //当前对象字段名的游标 - protected int fieldIndex; - - public static final short SIGN_NULL = -1; - - public static final short SIGN_NOLENGTH = -2; - - public static final short SIGN_NOLENBUTBYTES = -3; //目前只适合于protobuf的boolean[]...double[]类型 - - /** - * 是否还存在下个元素或字段
    - * 注意: 主要用于Array、Collection、Stream或Map等集合对象 - * - * @param startPosition 起始位置 - * @param contentLength 内容大小, 不确定的传-1 - * - * @return 是否还存在下个元素或字段 - */ - public abstract boolean hasNext(int startPosition, int contentLength); - - /** - * 是否还存在下个元素或字段 - * - * - * @return 是否还存在下个元素或字段 - */ - public boolean hasNext() { - return hasNext(-1, -1); - } - - /** - * 获取当前位置 - * - * @return 当前位置 - */ - public abstract int position(); - - /** - * 读取字段值内容的字节数
    - * 只有在readXXXB方法返回SIGN_NOLENBUTBYTES值才会调用此方法 - * - * @param member DeMember - * @param decoder Decodeable - * - * @return 内容大小, 不确定返回-1 - */ - public abstract int readMemberContentLength(DeMember member, Decodeable decoder); - - /** - * 跳过值(不包含值前面的字段) - */ - public abstract void skipValue(); - - /** - * /跳过字段与值之间的多余内容, json就是跳过:符, map跳过: - */ - public abstract void readBlank(); - - /** - * 读取下个值的类型 - * - * @return ValueType - */ - public abstract ValueType readType(); - - /** - * 读取对象的类名, 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。 - * - * @param clazz 类名 - * - * @return 返回字段数 - */ - public String readObjectB(final Class clazz) { - this.fieldIndex = 0; - return null; - } - - /** - * 读取对象的尾端 - * - * @param clazz 类名 - */ - public abstract void readObjectE(final Class clazz); - - /** - * 读取数组的开头并返回数组的长度 - * - * @param member DeMember - * @param typevals byte[] - * @param componentDecoder Decodeable - * - * @return 返回数组的长度 - */ - public abstract int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder); - - /** - * 读取数组的尾端 - * - */ - public abstract void readArrayE(); - - /** - * 读取map的开头并返回map的size - * - * @param member DeMember - * @param typevals byte[] - * @param keyDecoder Decodeable - * @param valueDecoder Decodeable - * - * @return 返回map的size - */ - public abstract int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder); - - /** - * 读取数组的尾端 - * - */ - public abstract void readMapE(); - - /** - * 根据字段读取字段对应的DeMember - * - * @param members DeMember的全量集合 - * - * @return 匹配的DeMember - */ - public abstract DeMember readFieldName(final DeMember[] members); - - /** - * 读取一个boolean值 - * - * @return boolean值 - */ - public abstract boolean readBoolean(); - - /** - * 读取一个byte值 - * - * @return byte值 - */ - public abstract byte readByte(); - - /** - * 读取byte[] - * - * @return byte[] - */ - public abstract byte[] readByteArray(); - - /** - * 读取一个char值 - * - * @return char值 - */ - public abstract char readChar(); - - /** - * 读取一个short值 - * - * @return short值 - */ - public abstract short readShort(); - - /** - * 读取一个int值 - * - * @return int值 - */ - public abstract int readInt(); - - /** - * 读取一个long值 - * - * @return long值 - */ - public abstract long readLong(); - - /** - * 读取一个float值 - * - * @return float值 - */ - public abstract float readFloat(); - - /** - * 读取一个double值 - * - * @return double值 - */ - public abstract double readDouble(); - - /** - * 读取无转义字符长度不超过255的字符串, 例如枚举值、字段名、类名字符串等 - * - * @return String值 - */ - public abstract String readSmallString(); - - /** - * 读取反解析对象的类名 - * - * @return 类名 - */ - public abstract String readClassName(); - - /** - * 读取一个String值 - * - * @return String值 - */ - public abstract String readString(); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +/** + * 反序列化的数据读取流 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class Reader { + + public static enum ValueType { + STRING, ARRAY, MAP; + } + + //当前对象字段名的游标 + protected int fieldIndex; + + public static final short SIGN_NULL = -1; + + public static final short SIGN_NOLENGTH = -2; + + public static final short SIGN_NOLENBUTBYTES = -3; //目前只适合于protobuf的boolean[]...double[]类型 + + /** + * 是否还存在下个元素或字段
    + * 注意: 主要用于Array、Collection、Stream或Map等集合对象 + * + * @param startPosition 起始位置 + * @param contentLength 内容大小, 不确定的传-1 + * + * @return 是否还存在下个元素或字段 + */ + public abstract boolean hasNext(int startPosition, int contentLength); + + /** + * 是否还存在下个元素或字段 + * + * + * @return 是否还存在下个元素或字段 + */ + public boolean hasNext() { + return hasNext(-1, -1); + } + + /** + * 获取当前位置 + * + * @return 当前位置 + */ + public abstract int position(); + + /** + * 读取字段值内容的字节数
    + * 只有在readXXXB方法返回SIGN_NOLENBUTBYTES值才会调用此方法 + * + * @param member DeMember + * @param decoder Decodeable + * + * @return 内容大小, 不确定返回-1 + */ + public abstract int readMemberContentLength(DeMember member, Decodeable decoder); + + /** + * 跳过值(不包含值前面的字段) + */ + public abstract void skipValue(); + + /** + * /跳过字段与值之间的多余内容, json就是跳过:符, map跳过: + */ + public abstract void readBlank(); + + /** + * 读取下个值的类型 + * + * @return ValueType + */ + public abstract ValueType readType(); + + /** + * 读取对象的类名, 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。 + * + * @param clazz 类名 + * + * @return 返回字段数 + */ + public String readObjectB(final Class clazz) { + this.fieldIndex = 0; + return null; + } + + /** + * 读取对象的尾端 + * + * @param clazz 类名 + */ + public abstract void readObjectE(final Class clazz); + + /** + * 读取数组的开头并返回数组的长度 + * + * @param member DeMember + * @param typevals byte[] + * @param componentDecoder Decodeable + * + * @return 返回数组的长度 + */ + public abstract int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder); + + /** + * 读取数组的尾端 + * + */ + public abstract void readArrayE(); + + /** + * 读取map的开头并返回map的size + * + * @param member DeMember + * @param typevals byte[] + * @param keyDecoder Decodeable + * @param valueDecoder Decodeable + * + * @return 返回map的size + */ + public abstract int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder); + + /** + * 读取数组的尾端 + * + */ + public abstract void readMapE(); + + /** + * 根据字段读取字段对应的DeMember + * + * @param members DeMember的全量集合 + * + * @return 匹配的DeMember + */ + public abstract DeMember readFieldName(final DeMember[] members); + + /** + * 读取一个boolean值 + * + * @return boolean值 + */ + public abstract boolean readBoolean(); + + /** + * 读取一个byte值 + * + * @return byte值 + */ + public abstract byte readByte(); + + /** + * 读取byte[] + * + * @return byte[] + */ + public abstract byte[] readByteArray(); + + /** + * 读取一个char值 + * + * @return char值 + */ + public abstract char readChar(); + + /** + * 读取一个short值 + * + * @return short值 + */ + public abstract short readShort(); + + /** + * 读取一个int值 + * + * @return int值 + */ + public abstract int readInt(); + + /** + * 读取一个long值 + * + * @return long值 + */ + public abstract long readLong(); + + /** + * 读取一个float值 + * + * @return float值 + */ + public abstract float readFloat(); + + /** + * 读取一个double值 + * + * @return double值 + */ + public abstract double readDouble(); + + /** + * 读取无转义字符长度不超过255的字符串, 例如枚举值、字段名、类名字符串等 + * + * @return String值 + */ + public abstract String readSmallString(); + + /** + * 读取反解析对象的类名 + * + * @return 类名 + */ + public abstract String readClassName(); + + /** + * 读取一个String值 + * + * @return String值 + */ + public abstract String readString(); + +} diff --git a/src/org/redkale/convert/SimpledCoder.java b/src/main/java/org/redkale/convert/SimpledCoder.java similarity index 96% rename from src/org/redkale/convert/SimpledCoder.java rename to src/main/java/org/redkale/convert/SimpledCoder.java index dd1f3bec3..0a1dd8da1 100644 --- a/src/org/redkale/convert/SimpledCoder.java +++ b/src/main/java/org/redkale/convert/SimpledCoder.java @@ -1,47 +1,47 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; - -/** - * 简易类的序列化和反序列化操作类
    - * 能序列化为Boolean、Number或者字符串的类视为简易类
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param Writer输出的子类 - * @param 序列化/反解析的数据类型 - */ -public abstract class SimpledCoder implements Decodeable, Encodeable { - - protected Type type; - - @Override - public abstract void convertTo(final W out, final T value); - - @Override - public abstract T convertFrom(final R in); - - @Override - @SuppressWarnings("unchecked") - public Class getType() { - if (type == null) { - Type[] ts = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments(); - type = ts[ts.length - 1]; - } - return (Class) type; - } - - @Override - public String toString() { - return this.getClass().getSimpleName(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * 简易类的序列化和反序列化操作类
    + * 能序列化为Boolean、Number或者字符串的类视为简易类
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param Writer输出的子类 + * @param 序列化/反解析的数据类型 + */ +public abstract class SimpledCoder implements Decodeable, Encodeable { + + protected Type type; + + @Override + public abstract void convertTo(final W out, final T value); + + @Override + public abstract T convertFrom(final R in); + + @Override + @SuppressWarnings("unchecked") + public Class getType() { + if (type == null) { + Type[] ts = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments(); + type = ts[ts.length - 1]; + } + return (Class) type; + } + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } +} diff --git a/src/org/redkale/convert/StreamDecoder.java b/src/main/java/org/redkale/convert/StreamDecoder.java similarity index 94% rename from src/org/redkale/convert/StreamDecoder.java rename to src/main/java/org/redkale/convert/StreamDecoder.java index 4828657c5..bc1629883 100644 --- a/src/org/redkale/convert/StreamDecoder.java +++ b/src/main/java/org/redkale/convert/StreamDecoder.java @@ -1,133 +1,130 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import org.redkale.util.Creator; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.util.*; -import java.util.stream.Stream; - -/** - * Stream的反序列化操作类
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 反解析的集合元素类型 - */ -@SuppressWarnings("unchecked") -public class StreamDecoder implements Decodeable> { - - protected final Type type; - - protected final Type componentType; - - protected Creator> creator; - - protected final Decodeable componentDecoder; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public StreamDecoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof ParameterizedType) { - final ParameterizedType pt = (ParameterizedType) type; - this.componentType = pt.getActualTypeArguments()[0]; - this.creator = factory.loadCreator((Class) pt.getRawType()); - factory.register(type, this); - this.componentDecoder = factory.loadDecoder(this.componentType); - } else { - throw new ConvertException("StreamDecoder not support the type (" + type + ")"); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - @Override - public Stream convertFrom(Reader in) { - return convertFrom(in, null); - } - - public Stream convertFrom(Reader in, DeMember member) { - byte[] typevals = new byte[1]; - int len = in.readArrayB(member, typevals, this.componentDecoder); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(member, this.componentDecoder); - len = Reader.SIGN_NOLENGTH; - } - if (this.componentDecoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - final Decodeable localdecoder = getComponentDecoder(this.componentDecoder, typevals); - final List result = new ArrayList(); - boolean first = true; - if (len == Reader.SIGN_NOLENGTH) { - int startPosition = in.position(); - while (hasNext(in, member, startPosition, contentLength, first)) { - Reader itemReader = getItemReader(in, member, first); - if (itemReader == null) break; - result.add(readMemberValue(itemReader, member, localdecoder, first)); - first = false; - } - } else { - for (int i = 0; i < len; i++) { - result.add(localdecoder.convertFrom(in)); - } - } - in.readArrayE(); - return result.stream(); - } - - protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { - return in.hasNext(startPosition, contentLength); - } - - protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { - return decoder; - } - - protected Reader getItemReader(Reader in, DeMember member, boolean first) { - return in; - } - - protected T readMemberValue(Reader in, DeMember member, Decodeable decoder, boolean first) { - return decoder.convertFrom(in); - } - - @Override - public Type getType() { - return type; - } - - public Type getComponentType() { - return componentType; - } - - public Decodeable getComponentDecoder() { - return componentDecoder; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import org.redkale.util.Creator; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; +import java.util.stream.Stream; + +/** + * Stream的反序列化操作类
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 反解析的集合元素类型 + */ +@SuppressWarnings("unchecked") +public class StreamDecoder implements Decodeable> { + + protected final Type type; + + protected final Type componentType; + + protected final Decodeable componentDecoder; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public StreamDecoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof ParameterizedType) { + final ParameterizedType pt = (ParameterizedType) type; + this.componentType = pt.getActualTypeArguments()[0]; + factory.register(type, this); + this.componentDecoder = factory.loadDecoder(this.componentType); + } else { + throw new ConvertException("StreamDecoder not support the type (" + type + ")"); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + @Override + public Stream convertFrom(Reader in) { + return convertFrom(in, null); + } + + public Stream convertFrom(Reader in, DeMember member) { + byte[] typevals = new byte[1]; + int len = in.readArrayB(member, typevals, this.componentDecoder); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(member, this.componentDecoder); + len = Reader.SIGN_NOLENGTH; + } + if (this.componentDecoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + final Decodeable localdecoder = getComponentDecoder(this.componentDecoder, typevals); + final List result = new ArrayList(); + boolean first = true; + if (len == Reader.SIGN_NOLENGTH) { + int startPosition = in.position(); + while (hasNext(in, member, startPosition, contentLength, first)) { + Reader itemReader = getItemReader(in, member, first); + if (itemReader == null) break; + result.add(readMemberValue(itemReader, member, localdecoder, first)); + first = false; + } + } else { + for (int i = 0; i < len; i++) { + result.add(localdecoder.convertFrom(in)); + } + } + in.readArrayE(); + return result.stream(); + } + + protected boolean hasNext(Reader in, DeMember member, int startPosition, int contentLength, boolean first) { + return in.hasNext(startPosition, contentLength); + } + + protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { + return decoder; + } + + protected Reader getItemReader(Reader in, DeMember member, boolean first) { + return in; + } + + protected T readMemberValue(Reader in, DeMember member, Decodeable decoder, boolean first) { + return decoder.convertFrom(in); + } + + @Override + public Type getType() { + return type; + } + + public Type getComponentType() { + return componentType; + } + + public Decodeable getComponentDecoder() { + return componentDecoder; + } + +} diff --git a/src/org/redkale/convert/StreamEncoder.java b/src/main/java/org/redkale/convert/StreamEncoder.java similarity index 96% rename from src/org/redkale/convert/StreamEncoder.java rename to src/main/java/org/redkale/convert/StreamEncoder.java index e09339dbe..abb22be9d 100644 --- a/src/org/redkale/convert/StreamEncoder.java +++ b/src/main/java/org/redkale/convert/StreamEncoder.java @@ -1,107 +1,107 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import java.util.stream.Stream; - -/** - * Stream的序列化操作类
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 序列化的集合元素类型 - */ -@SuppressWarnings("unchecked") -public class StreamEncoder implements Encodeable> { - - protected final Type type; - - protected final Encodeable componentEncoder; - - protected volatile boolean inited = false; - - protected final Object lock = new Object(); - - public StreamEncoder(final ConvertFactory factory, final Type type) { - this.type = type; - try { - if (type instanceof ParameterizedType) { - Type t = ((ParameterizedType) type).getActualTypeArguments()[0]; - if (t instanceof TypeVariable) { - this.componentEncoder = factory.getAnyEncoder(); - } else { - this.componentEncoder = factory.loadEncoder(t); - } - } else { - this.componentEncoder = factory.getAnyEncoder(); - } - } finally { - inited = true; - synchronized (lock) { - lock.notifyAll(); - } - } - } - - @Override - public void convertTo(Writer out, Stream value) { - convertTo(out, null, value); - } - - public void convertTo(Writer out, EnMember member, Stream value) { - if (value == null) { - out.writeNull(); - return; - } - Object[] array = value.toArray(); - if (array.length == 0) { - out.writeArrayB(0, this, componentEncoder, array); - out.writeArrayE(); - return; - } - if (this.componentEncoder == null) { - if (!this.inited) { - synchronized (lock) { - try { - lock.wait(); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - if (out.writeArrayB(array.length, this, componentEncoder, array) < 0) { - boolean first = true; - for (Object v : array) { - if (!first) out.writeArrayMark(); - writeMemberValue(out, member, v, first); - if (first) first = false; - } - } - out.writeArrayE(); - } - - protected void writeMemberValue(Writer out, EnMember member, Object value, boolean first) { - componentEncoder.convertTo(out, value); - } - - @Override - public Type getType() { - return type; - } - - public Encodeable getComponentEncoder() { - return componentEncoder; - } - - public Type getComponentType() { - return componentEncoder == null ? null : componentEncoder.getType(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import java.util.stream.Stream; + +/** + * Stream的序列化操作类
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 序列化的集合元素类型 + */ +@SuppressWarnings("unchecked") +public class StreamEncoder implements Encodeable> { + + protected final Type type; + + protected final Encodeable componentEncoder; + + protected volatile boolean inited = false; + + protected final Object lock = new Object(); + + public StreamEncoder(final ConvertFactory factory, final Type type) { + this.type = type; + try { + if (type instanceof ParameterizedType) { + Type t = ((ParameterizedType) type).getActualTypeArguments()[0]; + if (t instanceof TypeVariable) { + this.componentEncoder = factory.getAnyEncoder(); + } else { + this.componentEncoder = factory.loadEncoder(t); + } + } else { + this.componentEncoder = factory.getAnyEncoder(); + } + } finally { + inited = true; + synchronized (lock) { + lock.notifyAll(); + } + } + } + + @Override + public void convertTo(Writer out, Stream value) { + convertTo(out, null, value); + } + + public void convertTo(Writer out, EnMember member, Stream value) { + if (value == null) { + out.writeNull(); + return; + } + Object[] array = value.toArray(); + if (array.length == 0) { + out.writeArrayB(0, this, componentEncoder, array); + out.writeArrayE(); + return; + } + if (this.componentEncoder == null) { + if (!this.inited) { + synchronized (lock) { + try { + lock.wait(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + if (out.writeArrayB(array.length, this, componentEncoder, array) < 0) { + boolean first = true; + for (Object v : array) { + if (!first) out.writeArrayMark(); + writeMemberValue(out, member, v, first); + if (first) first = false; + } + } + out.writeArrayE(); + } + + protected void writeMemberValue(Writer out, EnMember member, Object value, boolean first) { + componentEncoder.convertTo(out, value); + } + + @Override + public Type getType() { + return type; + } + + public Encodeable getComponentEncoder() { + return componentEncoder; + } + + public Type getComponentType() { + return componentEncoder == null ? null : componentEncoder.getType(); + } +} diff --git a/src/org/redkale/convert/TextConvert.java b/src/main/java/org/redkale/convert/TextConvert.java similarity index 95% rename from src/org/redkale/convert/TextConvert.java rename to src/main/java/org/redkale/convert/TextConvert.java index c6f3b095a..c6e2e34ef 100644 --- a/src/org/redkale/convert/TextConvert.java +++ b/src/main/java/org/redkale/convert/TextConvert.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.Type; - -/** - * 文本序列化/反序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类 - * @param Writer输出的子类 - */ -public abstract class TextConvert extends Convert { - - protected TextConvert(ConvertFactory factory) { - super(factory); - } - - @Override - public final boolean isBinary() { - return false; - } - - public abstract String convertTo(final Object value); - - public abstract String convertTo(final Type type, final Object value); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.Type; + +/** + * 文本序列化/反序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类 + * @param Writer输出的子类 + */ +public abstract class TextConvert extends Convert { + + protected TextConvert(ConvertFactory factory) { + super(factory); + } + + @Override + public final boolean isBinary() { + return false; + } + + public abstract String convertTo(final Object value); + + public abstract String convertTo(final Type type, final Object value); + +} diff --git a/src/org/redkale/convert/Writer.java b/src/main/java/org/redkale/convert/Writer.java similarity index 96% rename from src/org/redkale/convert/Writer.java rename to src/main/java/org/redkale/convert/Writer.java index 052321df8..435a2b378 100644 --- a/src/org/redkale/convert/Writer.java +++ b/src/main/java/org/redkale/convert/Writer.java @@ -1,326 +1,326 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert; - -import java.lang.reflect.*; -import java.util.function.*; -import org.redkale.util.*; - -/** - * 序列化的数据输出流 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class Writer { - - //当前对象输出字段名之前是否需要分隔符, JSON字段间的分隔符为,逗号 - protected boolean comma; - - //convertTo时是否以指定Type的ObjectEncoder进行处理 - protected Type specify; - - //对某个字段值进行动态处理 - protected BiFunction objFieldFunc; - - //对某个对象进行动态扩展字段值处理 - protected Function objExtFunc; - - /** - * 设置specify - * - * @param value Type - */ - public void specify(Type value) { - if (value instanceof GenericArrayType) { - this.specify = ((GenericArrayType) value).getGenericComponentType(); - } else if (value instanceof Class && ((Class) value).isArray()) { - this.specify = ((Class) value).getComponentType(); - } else { - this.specify = value; - } - } - - protected boolean recycle() { - this.objFieldFunc = null; - return true; - } - - /** - * 返回specify - * - * @return int - */ - public Type specify() { - return this.specify; - } - - /** - * 当tiny=true时, 字符串为空、boolean为false的字段值都会被跳过, 不会输出。 - * - * @return 是否简化 - */ - public abstract boolean tiny(); - - /** - * 输出null值 - */ - public abstract void writeNull(); - - /** - * 是否需要写入类名, BSON需要, JSON不需要 - * - * @return boolean - */ - public abstract boolean needWriteClassName(); - - /** - * 写入类名 - * - * @param clazz 类名 - */ - public abstract void writeClassName(String clazz); - - /** - * 输出一个对象前的操作 - * 注: 覆盖此方法必须要先调用父方法 super.writeObjectB(obj); - * - * @param obj 写入的对象 - * - * @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小 - */ - public int writeObjectB(Object obj) { - this.comma = false; - return -1; - } - - /** - * 输出一个为null的对象 - * - * @param clazz 对象的类名 - */ - public final void writeObjectNull(final Class clazz) { - writeClassName(null); - writeNull(); - } - - /** - * 输出一个对象的某个字段 - * - * @param member 字段 - * - * @param obj 写入的对象 - */ - @SuppressWarnings("unchecked") - public void writeObjectField(final EnMember member, Object obj) { - Object value; - if (objFieldFunc == null) { - value = member.attribute.get(obj); - } else { - value = objFieldFunc.apply(member.attribute, obj); - } - if (value == null) return; - if (tiny()) { - if (member.string) { - if (((CharSequence) value).length() == 0) return; - } else if (member.bool) { - if (!((Boolean) value)) return; - } - } - Attribute attr = member.getAttribute(); - this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition()); - member.encoder.convertTo(this, value); - this.comma = true; - } - - /** - * 输出一个对象的某个扩展字段 - * - * - * @param fieldName 字段名称 - * @param fieldType 字段类型 - * @param fieldPos 字段顺序 - * @param anyEncoder Encoder - * @param value 写入的字段对象 - */ - @SuppressWarnings("unchecked") - public void writeObjectField(final String fieldName, Type fieldType, int fieldPos, Encodeable anyEncoder, Object value) { - if (value == null) return; - if (fieldType == null) fieldType = value.getClass(); - if (tiny() && fieldType instanceof Class) { - Class clazz = (Class) fieldType; - if (CharSequence.class.isAssignableFrom(clazz)) { - if (((CharSequence) value).length() == 0) return; - } else if (clazz == boolean.class || clazz == Boolean.class) { - if (!((Boolean) value)) return; - } - } - this.writeFieldName(null, fieldName, fieldType, fieldPos); - anyEncoder.convertTo(this, value); - this.comma = true; - } - - /** - * 输出一个字段名 - * - * @param member 字段 - */ - public final void writeFieldName(final EnMember member) { - Attribute attr = member.getAttribute(); - this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition()); - } - - /** - * 输出一个对象后的操作 - * - * @param obj 写入的对象 - */ - public abstract void writeObjectE(Object obj); - - /** - * 输出一个数组前的操作 - * - * @param size 数组长度 - * @param arrayEncoder Encodeable 可能是ArrayEncoder、CollectionEncoder或StreamEncoder - * @param componentEncoder Encodeable - * @param obj 对象, 不一定是数组、Collection对象,也可能是伪Collection对象 - * - * @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小 - */ - public abstract int writeArrayB(int size, Encodeable arrayEncoder, Encodeable componentEncoder, Object obj); - - /** - * 输出数组元素间的间隔符 - * - */ - public abstract void writeArrayMark(); - - /** - * 输出一个数组后的操作 - * - */ - public abstract void writeArrayE(); - - /** - * 输出一个Map前的操作 - * - * @param size map大小 - * @param keyEncoder Encodeable - * @param valueEncoder Encodeable - * @param obj 对象, 不一定是Map对象,也可能是伪Map对象 - * - * @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小 - */ - public abstract int writeMapB(int size, Encodeable keyEncoder, Encodeable valueEncoder, Object obj); - - /** - * 输出一个Map中key与value间的间隔符 - * - */ - public abstract void writeMapMark(); - - /** - * 输出一个Map后的操作 - * - */ - public abstract void writeMapE(); - - /** - * 输出一个字段名 - * - * @param member EnMember - * @param fieldName 字段名称 - * @param fieldType 字段类型 - * @param fieldPos 字段顺序 - */ - public abstract void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos); - - /** - * 写入一个boolean值 - * - * @param value boolean值 - */ - public abstract void writeBoolean(boolean value); - - /** - * 写入一个byte值 - * - * @param value byte值 - */ - public abstract void writeByte(byte value); - - /** - * 写入byte[] - * - * @param values byte[] - */ - public abstract void writeByteArray(byte[] values); - - /** - * 写入一个char值 - * - * @param value char值 - */ - public abstract void writeChar(char value); - - /** - * 写入一个short值 - * - * @param value short值 - */ - public abstract void writeShort(short value); - - /** - * 写入一个int值 - * - * @param value int值 - */ - public abstract void writeInt(int value); - - /** - * 写入一个long值 - * - * @param value long值 - */ - public abstract void writeLong(long value); - - /** - * 写入一个float值 - * - * @param value float值 - */ - public abstract void writeFloat(float value); - - /** - * 写入一个double值 - * - * @param value double值 - */ - public abstract void writeDouble(double value); - - /** - * 写入无转义字符长度不超过255的字符串, 例如枚举值、字段名、类名字符串等 * - * - * @param value 非空且不含需要转义的字符的String值 - */ - public abstract void writeSmallString(String value); - - /** - * 写入一个String值 - * - * @param value String值 - */ - public abstract void writeString(String value); - - /** - * 写入一个StringConvertWrapper值 - * - * @param value StringConvertWrapper值 - */ - public abstract void writeWrapper(StringWrapper value); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert; + +import java.lang.reflect.*; +import java.util.function.*; +import org.redkale.util.*; + +/** + * 序列化的数据输出流 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class Writer { + + //当前对象输出字段名之前是否需要分隔符, JSON字段间的分隔符为,逗号 + protected boolean comma; + + //convertTo时是否以指定Type的ObjectEncoder进行处理 + protected Type specify; + + //对某个字段值进行动态处理 + protected BiFunction objFieldFunc; + + //对某个对象进行动态扩展字段值处理 + protected Function objExtFunc; + + /** + * 设置specify + * + * @param value Type + */ + public void specify(Type value) { + if (value instanceof GenericArrayType) { + this.specify = ((GenericArrayType) value).getGenericComponentType(); + } else if (value instanceof Class && ((Class) value).isArray()) { + this.specify = ((Class) value).getComponentType(); + } else { + this.specify = value; + } + } + + protected boolean recycle() { + this.objFieldFunc = null; + return true; + } + + /** + * 返回specify + * + * @return int + */ + public Type specify() { + return this.specify; + } + + /** + * 当tiny=true时, 字符串为空、boolean为false的字段值都会被跳过, 不会输出。 + * + * @return 是否简化 + */ + public abstract boolean tiny(); + + /** + * 输出null值 + */ + public abstract void writeNull(); + + /** + * 是否需要写入类名, BSON需要, JSON不需要 + * + * @return boolean + */ + public abstract boolean needWriteClassName(); + + /** + * 写入类名 + * + * @param clazz 类名 + */ + public abstract void writeClassName(String clazz); + + /** + * 输出一个对象前的操作 + * 注: 覆盖此方法必须要先调用父方法 super.writeObjectB(obj); + * + * @param obj 写入的对象 + * + * @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小 + */ + public int writeObjectB(Object obj) { + this.comma = false; + return -1; + } + + /** + * 输出一个为null的对象 + * + * @param clazz 对象的类名 + */ + public final void writeObjectNull(final Class clazz) { + writeClassName(null); + writeNull(); + } + + /** + * 输出一个对象的某个字段 + * + * @param member 字段 + * + * @param obj 写入的对象 + */ + @SuppressWarnings("unchecked") + public void writeObjectField(final EnMember member, Object obj) { + Object value; + if (objFieldFunc == null) { + value = member.attribute.get(obj); + } else { + value = objFieldFunc.apply(member.attribute, obj); + } + if (value == null) return; + if (tiny()) { + if (member.string) { + if (((CharSequence) value).length() == 0) return; + } else if (member.bool) { + if (!((Boolean) value)) return; + } + } + Attribute attr = member.getAttribute(); + this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition()); + member.encoder.convertTo(this, value); + this.comma = true; + } + + /** + * 输出一个对象的某个扩展字段 + * + * + * @param fieldName 字段名称 + * @param fieldType 字段类型 + * @param fieldPos 字段顺序 + * @param anyEncoder Encoder + * @param value 写入的字段对象 + */ + @SuppressWarnings("unchecked") + public void writeObjectField(final String fieldName, Type fieldType, int fieldPos, Encodeable anyEncoder, Object value) { + if (value == null) return; + if (fieldType == null) fieldType = value.getClass(); + if (tiny() && fieldType instanceof Class) { + Class clazz = (Class) fieldType; + if (CharSequence.class.isAssignableFrom(clazz)) { + if (((CharSequence) value).length() == 0) return; + } else if (clazz == boolean.class || clazz == Boolean.class) { + if (!((Boolean) value)) return; + } + } + this.writeFieldName(null, fieldName, fieldType, fieldPos); + anyEncoder.convertTo(this, value); + this.comma = true; + } + + /** + * 输出一个字段名 + * + * @param member 字段 + */ + public final void writeFieldName(final EnMember member) { + Attribute attr = member.getAttribute(); + this.writeFieldName(member, attr.field(), attr.genericType(), member.getPosition()); + } + + /** + * 输出一个对象后的操作 + * + * @param obj 写入的对象 + */ + public abstract void writeObjectE(Object obj); + + /** + * 输出一个数组前的操作 + * + * @param size 数组长度 + * @param arrayEncoder Encodeable 可能是ArrayEncoder、CollectionEncoder或StreamEncoder + * @param componentEncoder Encodeable + * @param obj 对象, 不一定是数组、Collection对象,也可能是伪Collection对象 + * + * @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小 + */ + public abstract int writeArrayB(int size, Encodeable arrayEncoder, Encodeable componentEncoder, Object obj); + + /** + * 输出数组元素间的间隔符 + * + */ + public abstract void writeArrayMark(); + + /** + * 输出一个数组后的操作 + * + */ + public abstract void writeArrayE(); + + /** + * 输出一个Map前的操作 + * + * @param size map大小 + * @param keyEncoder Encodeable + * @param valueEncoder Encodeable + * @param obj 对象, 不一定是Map对象,也可能是伪Map对象 + * + * @return 返回-1表示还没有写入对象内容,大于-1表示已写入对象内容,返回对象内容大小 + */ + public abstract int writeMapB(int size, Encodeable keyEncoder, Encodeable valueEncoder, Object obj); + + /** + * 输出一个Map中key与value间的间隔符 + * + */ + public abstract void writeMapMark(); + + /** + * 输出一个Map后的操作 + * + */ + public abstract void writeMapE(); + + /** + * 输出一个字段名 + * + * @param member EnMember + * @param fieldName 字段名称 + * @param fieldType 字段类型 + * @param fieldPos 字段顺序 + */ + public abstract void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos); + + /** + * 写入一个boolean值 + * + * @param value boolean值 + */ + public abstract void writeBoolean(boolean value); + + /** + * 写入一个byte值 + * + * @param value byte值 + */ + public abstract void writeByte(byte value); + + /** + * 写入byte[] + * + * @param values byte[] + */ + public abstract void writeByteArray(byte[] values); + + /** + * 写入一个char值 + * + * @param value char值 + */ + public abstract void writeChar(char value); + + /** + * 写入一个short值 + * + * @param value short值 + */ + public abstract void writeShort(short value); + + /** + * 写入一个int值 + * + * @param value int值 + */ + public abstract void writeInt(int value); + + /** + * 写入一个long值 + * + * @param value long值 + */ + public abstract void writeLong(long value); + + /** + * 写入一个float值 + * + * @param value float值 + */ + public abstract void writeFloat(float value); + + /** + * 写入一个double值 + * + * @param value double值 + */ + public abstract void writeDouble(double value); + + /** + * 写入无转义字符长度不超过255的字符串, 例如枚举值、字段名、类名字符串等 * + * + * @param value 非空且不含需要转义的字符的String值 + */ + public abstract void writeSmallString(String value); + + /** + * 写入一个String值 + * + * @param value String值 + */ + public abstract void writeString(String value); + + /** + * 写入一个StringConvertWrapper值 + * + * @param value StringConvertWrapper值 + */ + public abstract void writeWrapper(StringWrapper value); +} diff --git a/src/org/redkale/convert/bson/BsonByteBufferReader.java b/src/main/java/org/redkale/convert/bson/BsonByteBufferReader.java similarity index 97% rename from src/org/redkale/convert/bson/BsonByteBufferReader.java rename to src/main/java/org/redkale/convert/bson/BsonByteBufferReader.java index 77bdfe3b9..ef60e8456 100644 --- a/src/org/redkale/convert/bson/BsonByteBufferReader.java +++ b/src/main/java/org/redkale/convert/bson/BsonByteBufferReader.java @@ -1,238 +1,238 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.nio.*; -import java.nio.charset.StandardCharsets; -import org.redkale.convert.*; -import static org.redkale.convert.Reader.SIGN_NULL; -import org.redkale.convert.ext.ByteSimpledCoder; - -/** - * 以ByteBuffer为数据载体的BsonReader - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class BsonByteBufferReader extends BsonReader { - - private ByteBuffer[] buffers; - - private int currentIndex = 0; - - private ByteBuffer currentBuffer; - - protected ConvertMask mask; - - protected BsonByteBufferReader(ConvertMask mask, ByteBuffer... buffers) { - this.mask = mask; - this.buffers = buffers; - if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex]; - } - - @Override - protected boolean recycle() { - super.recycle(); // this.position 初始化值为-1 - this.currentIndex = 0; - this.currentBuffer = null; - this.buffers = null; - this.mask = null; - return false; - } - - @Override - protected byte currentByte() { - return mask == null ? currentBuffer.get(currentBuffer.position()) : mask.unmask(currentBuffer.get(currentBuffer.position())); - } - - @Override - public int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder) { - short bt = readShort(); - if (bt == Reader.SIGN_NULL) return bt; - short lt = readShort(); - byte kt = readByte(); - byte vt = readByte(); - if (typevals != null) { - typevals[0] = kt; - typevals[1] = vt; - } - return (bt & 0xffff) << 16 | (lt & 0xffff); - } - - /** - * 判断下一个非空白字节是否为[ - * - * @param member DeMember - * @param typevals byte[] - * @param componentDecoder Decodeable - * - * @return 数组长度或 SIGN_NULL - */ - @Override - public final int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) { - short bt = readShort(); - if (bt == Reader.SIGN_NULL) return bt; - short lt = readShort(); - if (componentDecoder != null && componentDecoder != ByteSimpledCoder.instance) { - byte comval = readByte(); - if (typevals != null) typevals[0] = comval; - } - return (bt & 0xffff) << 16 | (lt & 0xffff); - } -//------------------------------------------------------------ - - @Override - public final boolean readBoolean() { - return readByte() == 1; - } - - @Override - public byte readByte() { - if (this.currentBuffer.hasRemaining()) { - this.position++; - return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); - } - for (;;) { - this.currentBuffer = this.buffers[++this.currentIndex]; - if (this.currentBuffer.hasRemaining()) { - this.position++; - return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); - } - } - } - - @Override - public final char readChar() { - if (this.currentBuffer != null) { - int remain = this.currentBuffer.remaining(); - if (remain >= 2) { - this.position += 2; - if (mask == null) { - return this.currentBuffer.getChar(); - } else { - return (char) ((0xff00 & (mask.unmask(this.currentBuffer.get()) << 8)) | (0xff & mask.unmask(this.currentBuffer.get()))); - } - } - } - return (char) ((0xff00 & (readByte() << 8)) | (0xff & readByte())); - } - - @Override - public final short readShort() { - if (this.currentBuffer != null) { - int remain = this.currentBuffer.remaining(); - if (remain >= 2) { - this.position += 2; - if (mask == null) { - return this.currentBuffer.getShort(); - } else { - return (short) ((0xff00 & (mask.unmask(this.currentBuffer.get()) << 8)) | (0xff & mask.unmask(this.currentBuffer.get()))); - } - } - } - return (short) ((0xff00 & (readByte() << 8)) | (0xff & readByte())); - } - - @Override - public final int readInt() { - if (this.currentBuffer != null) { - int remain = this.currentBuffer.remaining(); - if (remain >= 4) { - this.position += 4; - if (mask == null) { - return this.currentBuffer.getInt(); - } else { - return ((mask.unmask(this.currentBuffer.get()) & 0xff) << 24) - | ((mask.unmask(this.currentBuffer.get()) & 0xff) << 16) - | ((mask.unmask(this.currentBuffer.get()) & 0xff) << 8) - | (mask.unmask(this.currentBuffer.get()) & 0xff); - } - } - } - return ((readByte() & 0xff) << 24) | ((readByte() & 0xff) << 16) | ((readByte() & 0xff) << 8) | (readByte() & 0xff); - } - - @Override - public final long readLong() { - if (this.currentBuffer != null) { - int remain = this.currentBuffer.remaining(); - if (remain >= 8) { - this.position += 8; - if (mask == null) { - return this.currentBuffer.getLong(); - } else { - return ((((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 56) - | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 48) - | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 40) - | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 32) - | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 24) - | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 16) - | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 8) - | (((long) mask.unmask(this.currentBuffer.get()) & 0xff))); - } - } - } - return ((((long) readByte() & 0xff) << 56) - | (((long) readByte() & 0xff) << 48) - | (((long) readByte() & 0xff) << 40) - | (((long) readByte() & 0xff) << 32) - | (((long) readByte() & 0xff) << 24) - | (((long) readByte() & 0xff) << 16) - | (((long) readByte() & 0xff) << 8) - | (((long) readByte() & 0xff))); - } - - protected byte[] read(final int len) { - byte[] bs = new byte[len]; - read(bs, 0); - return bs; - } - - private void read(final byte[] bs, final int pos) { - int remain = this.currentBuffer.remaining(); - if (remain < 1) { - this.currentBuffer = this.buffers[++this.currentIndex]; - read(bs, pos); - return; - } - int len = bs.length - pos; - if (remain >= len) { - this.position += len; - this.currentBuffer.get(bs, pos, len); - if (mask != null) { - for (int i = pos, end = pos + len; i < end; i++) { - bs[i] = mask.unmask(bs[i]); - } - } - return; - } - this.currentBuffer.get(bs, pos, remain); - if (mask != null) { - for (int i = pos, end = pos + remain; i < end; i++) { - bs[i] = mask.unmask(bs[i]); - } - } - this.position += remain; - this.currentBuffer = this.buffers[++this.currentIndex]; - read(bs, pos + remain); - } - - @Override - public final String readSmallString() { - int len = 0xff & readByte(); - if (len == 0) return ""; - return new String(read(len)); - } - - @Override - public final String readString() { - int len = readInt(); - if (len == SIGN_NULL) return null; - if (len == 0) return ""; - return new String(read(len), StandardCharsets.UTF_8); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.nio.*; +import java.nio.charset.StandardCharsets; +import org.redkale.convert.*; +import static org.redkale.convert.Reader.SIGN_NULL; +import org.redkale.convert.ext.ByteSimpledCoder; + +/** + * 以ByteBuffer为数据载体的BsonReader + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class BsonByteBufferReader extends BsonReader { + + private ByteBuffer[] buffers; + + private int currentIndex = 0; + + private ByteBuffer currentBuffer; + + protected ConvertMask mask; + + protected BsonByteBufferReader(ConvertMask mask, ByteBuffer... buffers) { + this.mask = mask; + this.buffers = buffers; + if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex]; + } + + @Override + protected boolean recycle() { + super.recycle(); // this.position 初始化值为-1 + this.currentIndex = 0; + this.currentBuffer = null; + this.buffers = null; + this.mask = null; + return false; + } + + @Override + protected byte currentByte() { + return mask == null ? currentBuffer.get(currentBuffer.position()) : mask.unmask(currentBuffer.get(currentBuffer.position())); + } + + @Override + public int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder) { + short bt = readShort(); + if (bt == Reader.SIGN_NULL) return bt; + short lt = readShort(); + byte kt = readByte(); + byte vt = readByte(); + if (typevals != null) { + typevals[0] = kt; + typevals[1] = vt; + } + return (bt & 0xffff) << 16 | (lt & 0xffff); + } + + /** + * 判断下一个非空白字节是否为[ + * + * @param member DeMember + * @param typevals byte[] + * @param componentDecoder Decodeable + * + * @return 数组长度或 SIGN_NULL + */ + @Override + public final int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) { + short bt = readShort(); + if (bt == Reader.SIGN_NULL) return bt; + short lt = readShort(); + if (componentDecoder != null && componentDecoder != ByteSimpledCoder.instance) { + byte comval = readByte(); + if (typevals != null) typevals[0] = comval; + } + return (bt & 0xffff) << 16 | (lt & 0xffff); + } +//------------------------------------------------------------ + + @Override + public final boolean readBoolean() { + return readByte() == 1; + } + + @Override + public byte readByte() { + if (this.currentBuffer.hasRemaining()) { + this.position++; + return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); + } + for (;;) { + this.currentBuffer = this.buffers[++this.currentIndex]; + if (this.currentBuffer.hasRemaining()) { + this.position++; + return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); + } + } + } + + @Override + public final char readChar() { + if (this.currentBuffer != null) { + int remain = this.currentBuffer.remaining(); + if (remain >= 2) { + this.position += 2; + if (mask == null) { + return this.currentBuffer.getChar(); + } else { + return (char) ((0xff00 & (mask.unmask(this.currentBuffer.get()) << 8)) | (0xff & mask.unmask(this.currentBuffer.get()))); + } + } + } + return (char) ((0xff00 & (readByte() << 8)) | (0xff & readByte())); + } + + @Override + public final short readShort() { + if (this.currentBuffer != null) { + int remain = this.currentBuffer.remaining(); + if (remain >= 2) { + this.position += 2; + if (mask == null) { + return this.currentBuffer.getShort(); + } else { + return (short) ((0xff00 & (mask.unmask(this.currentBuffer.get()) << 8)) | (0xff & mask.unmask(this.currentBuffer.get()))); + } + } + } + return (short) ((0xff00 & (readByte() << 8)) | (0xff & readByte())); + } + + @Override + public final int readInt() { + if (this.currentBuffer != null) { + int remain = this.currentBuffer.remaining(); + if (remain >= 4) { + this.position += 4; + if (mask == null) { + return this.currentBuffer.getInt(); + } else { + return ((mask.unmask(this.currentBuffer.get()) & 0xff) << 24) + | ((mask.unmask(this.currentBuffer.get()) & 0xff) << 16) + | ((mask.unmask(this.currentBuffer.get()) & 0xff) << 8) + | (mask.unmask(this.currentBuffer.get()) & 0xff); + } + } + } + return ((readByte() & 0xff) << 24) | ((readByte() & 0xff) << 16) | ((readByte() & 0xff) << 8) | (readByte() & 0xff); + } + + @Override + public final long readLong() { + if (this.currentBuffer != null) { + int remain = this.currentBuffer.remaining(); + if (remain >= 8) { + this.position += 8; + if (mask == null) { + return this.currentBuffer.getLong(); + } else { + return ((((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 56) + | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 48) + | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 40) + | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 32) + | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 24) + | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 16) + | (((long) mask.unmask(this.currentBuffer.get()) & 0xff) << 8) + | (((long) mask.unmask(this.currentBuffer.get()) & 0xff))); + } + } + } + return ((((long) readByte() & 0xff) << 56) + | (((long) readByte() & 0xff) << 48) + | (((long) readByte() & 0xff) << 40) + | (((long) readByte() & 0xff) << 32) + | (((long) readByte() & 0xff) << 24) + | (((long) readByte() & 0xff) << 16) + | (((long) readByte() & 0xff) << 8) + | (((long) readByte() & 0xff))); + } + + protected byte[] read(final int len) { + byte[] bs = new byte[len]; + read(bs, 0); + return bs; + } + + private void read(final byte[] bs, final int pos) { + int remain = this.currentBuffer.remaining(); + if (remain < 1) { + this.currentBuffer = this.buffers[++this.currentIndex]; + read(bs, pos); + return; + } + int len = bs.length - pos; + if (remain >= len) { + this.position += len; + this.currentBuffer.get(bs, pos, len); + if (mask != null) { + for (int i = pos, end = pos + len; i < end; i++) { + bs[i] = mask.unmask(bs[i]); + } + } + return; + } + this.currentBuffer.get(bs, pos, remain); + if (mask != null) { + for (int i = pos, end = pos + remain; i < end; i++) { + bs[i] = mask.unmask(bs[i]); + } + } + this.position += remain; + this.currentBuffer = this.buffers[++this.currentIndex]; + read(bs, pos + remain); + } + + @Override + public final String readSmallString() { + int len = 0xff & readByte(); + if (len == 0) return ""; + return new String(read(len)); + } + + @Override + public final String readString() { + int len = readInt(); + if (len == SIGN_NULL) return null; + if (len == 0) return ""; + return new String(read(len), StandardCharsets.UTF_8); + } +} diff --git a/src/org/redkale/convert/bson/BsonByteBufferWriter.java b/src/main/java/org/redkale/convert/bson/BsonByteBufferWriter.java similarity index 96% rename from src/org/redkale/convert/bson/BsonByteBufferWriter.java rename to src/main/java/org/redkale/convert/bson/BsonByteBufferWriter.java index 2058e92ed..c7a44a213 100644 --- a/src/org/redkale/convert/bson/BsonByteBufferWriter.java +++ b/src/main/java/org/redkale/convert/bson/BsonByteBufferWriter.java @@ -1,155 +1,155 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.nio.*; -import java.util.function.*; -import org.redkale.util.Utility; - -/** - * 以ByteBuffer为数据载体的BsonWriter - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class BsonByteBufferWriter extends BsonWriter { - - private final Supplier supplier; - - private ByteBuffer[] buffers; - - private int index; - - public BsonByteBufferWriter(Supplier supplier) { - this(false, supplier); - } - - protected BsonByteBufferWriter(boolean tiny, Supplier supplier) { - super((byte[]) null); - this.tiny = tiny; - this.supplier = supplier; - } - - @Override - public ByteBuffer[] toBuffers() { - if (buffers == null) return new ByteBuffer[0]; - for (int i = index; i < this.buffers.length; i++) { - ByteBuffer buf = this.buffers[i]; - if (buf.position() != 0) buf.flip(); - } - return this.buffers; - } - - @Override - public byte[] toArray() { - if (buffers == null) return new byte[0]; - int pos = 0; - byte[] bytes = new byte[this.count]; - for (ByteBuffer buf : toBuffers()) { - int r = buf.remaining(); - buf.get(bytes, pos, r); - buf.flip(); - pos += r; - } - return bytes; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "[count=" + this.count + "]"; - } - - @Override - public BsonByteBufferWriter tiny(boolean tiny) { - this.tiny = tiny; - return this; - } - - @Override - protected int expand(final int byteLength) { - if (this.buffers == null) { - this.index = 0; - this.buffers = new ByteBuffer[]{supplier.get()}; - } - ByteBuffer buffer = this.buffers[index]; - if (!buffer.hasRemaining()) { - buffer.flip(); - buffer = supplier.get(); - this.buffers = Utility.append(this.buffers, buffer); - this.index++; - } - int len = buffer.remaining(); - int size = 0; - while (len < byteLength) { - buffer = supplier.get(); - this.buffers = Utility.append(this.buffers, buffer); - len += buffer.remaining(); - size++; - } - return size; - } - - @Override - public void writeTo(final byte[] chs, final int start, final int len) { - if (expand(len) == 0) { - this.buffers[index].put(chs, start, len); - } else { - ByteBuffer buffer = this.buffers[index]; - final int end = start + len; - int remain = len; //还剩多少没有写 - while (remain > 0) { - final int br = buffer.remaining(); - if (remain > br) { //一个buffer写不完 - buffer.put(chs, end - remain, br); - buffer = nextByteBuffer(); - remain -= br; - } else { - buffer.put(chs, end - remain, remain); - remain = 0; - } - } - } - this.count += len; - } - - private ByteBuffer nextByteBuffer() { - this.buffers[this.index].flip(); - return this.buffers[++this.index]; - } - - @Override - public void writeTo(final byte ch) { - expand(1); - this.buffers[index].put(ch); - count++; - } - - @Override - protected boolean recycle() { - super.recycle(); - this.index = 0; - this.specify = null; - this.buffers = null; - return false; - } - - @Override - public byte[] content() { - throw new UnsupportedOperationException("Not supported yet."); //无需实现 - } - - @Override - public int offset() { - throw new UnsupportedOperationException("Not supported yet.");//无需实现 - } - - @Override - public int length() { - throw new UnsupportedOperationException("Not supported yet."); //无需实现 - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.nio.*; +import java.util.function.*; +import org.redkale.util.Utility; + +/** + * 以ByteBuffer为数据载体的BsonWriter + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class BsonByteBufferWriter extends BsonWriter { + + private final Supplier supplier; + + private ByteBuffer[] buffers; + + private int index; + + public BsonByteBufferWriter(Supplier supplier) { + this(false, supplier); + } + + protected BsonByteBufferWriter(boolean tiny, Supplier supplier) { + super((byte[]) null); + this.tiny = tiny; + this.supplier = supplier; + } + + @Override + public ByteBuffer[] toBuffers() { + if (buffers == null) return new ByteBuffer[0]; + for (int i = index; i < this.buffers.length; i++) { + ByteBuffer buf = this.buffers[i]; + if (buf.position() != 0) buf.flip(); + } + return this.buffers; + } + + @Override + public byte[] toArray() { + if (buffers == null) return new byte[0]; + int pos = 0; + byte[] bytes = new byte[this.count]; + for (ByteBuffer buf : toBuffers()) { + int r = buf.remaining(); + buf.get(bytes, pos, r); + buf.flip(); + pos += r; + } + return bytes; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "[count=" + this.count + "]"; + } + + @Override + public BsonByteBufferWriter tiny(boolean tiny) { + this.tiny = tiny; + return this; + } + + @Override + protected int expand(final int byteLength) { + if (this.buffers == null) { + this.index = 0; + this.buffers = new ByteBuffer[]{supplier.get()}; + } + ByteBuffer buffer = this.buffers[index]; + if (!buffer.hasRemaining()) { + buffer.flip(); + buffer = supplier.get(); + this.buffers = Utility.append(this.buffers, buffer); + this.index++; + } + int len = buffer.remaining(); + int size = 0; + while (len < byteLength) { + buffer = supplier.get(); + this.buffers = Utility.append(this.buffers, buffer); + len += buffer.remaining(); + size++; + } + return size; + } + + @Override + public void writeTo(final byte[] chs, final int start, final int len) { + if (expand(len) == 0) { + this.buffers[index].put(chs, start, len); + } else { + ByteBuffer buffer = this.buffers[index]; + final int end = start + len; + int remain = len; //还剩多少没有写 + while (remain > 0) { + final int br = buffer.remaining(); + if (remain > br) { //一个buffer写不完 + buffer.put(chs, end - remain, br); + buffer = nextByteBuffer(); + remain -= br; + } else { + buffer.put(chs, end - remain, remain); + remain = 0; + } + } + } + this.count += len; + } + + private ByteBuffer nextByteBuffer() { + this.buffers[this.index].flip(); + return this.buffers[++this.index]; + } + + @Override + public void writeTo(final byte ch) { + expand(1); + this.buffers[index].put(ch); + count++; + } + + @Override + protected boolean recycle() { + super.recycle(); + this.index = 0; + this.specify = null; + this.buffers = null; + return false; + } + + @Override + public byte[] content() { + throw new UnsupportedOperationException("Not supported yet."); //无需实现 + } + + @Override + public int offset() { + throw new UnsupportedOperationException("Not supported yet.");//无需实现 + } + + @Override + public int length() { + throw new UnsupportedOperationException("Not supported yet."); //无需实现 + } +} diff --git a/src/org/redkale/convert/bson/BsonConvert.java b/src/main/java/org/redkale/convert/bson/BsonConvert.java similarity index 97% rename from src/org/redkale/convert/bson/BsonConvert.java rename to src/main/java/org/redkale/convert/bson/BsonConvert.java index 662ba81af..2eda622a9 100644 --- a/src/org/redkale/convert/bson/BsonConvert.java +++ b/src/main/java/org/redkale/convert/bson/BsonConvert.java @@ -1,295 +1,297 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.io.*; -import java.lang.reflect.*; -import java.nio.*; -import java.util.function.*; -import org.redkale.convert.*; -import org.redkale.util.*; - -/** - *

    - * BSON协议格式:
    - *  1) 基本数据类型: 直接转换成byte[]
    - *  2) SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
    - *  3) String: length(4 bytes) + byte[](utf8);
    - *  4) 数组: length(4 bytes) + byte[]...
    - *  5) Object:
    - *      1、 realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
    - *      2、 空字符串(SmallString)
    - *      3、 SIGN_OBJECTB 标记位,值固定为0xBB (short)
    - *      4、 循环字段值:
    - *          4.1 SIGN_HASNEXT 标记位,值固定为1 (byte)
    - *          4.2 字段类型; 1-9为基本类型和字符串; 101-109为基本类型和字符串的数组; 127为Object
    - *          4.3 字段名 (SmallString)
    - *          4.4 字段的值Object
    - *      5、 SIGN_NONEXT 标记位,值固定为0 (byte)
    - *      6、 SIGN_OBJECTE 标记位,值固定为0xEE (short)
    - *
    - * 
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class BsonConvert extends BinaryConvert { - - private final ThreadLocal writerPool = ThreadLocal.withInitial(BsonWriter::new); - - private final Consumer offerConsumer = w -> offerBsonWriter(w); - - private final boolean tiny; - - protected BsonConvert(ConvertFactory factory, boolean tiny) { - super(factory); - this.tiny = tiny; - } - - @Override - public BsonFactory getFactory() { - return (BsonFactory) factory; - } - - public static BsonConvert root() { - return BsonFactory.root().getConvert(); - } - - @Override - public BsonConvert newConvert(final BiFunction fieldFunc) { - return newConvert(fieldFunc, null); - } - - @Override - public BsonConvert newConvert(final BiFunction fieldFunc, Function objExtFunc) { - return new BsonConvert(getFactory(), tiny) { - @Override - protected S configWrite(S writer) { - return fieldFunc(writer, fieldFunc, objExtFunc); - } - }; - } - - //------------------------------ reader ----------------------------------------------------------- - public BsonReader pollBsonReader(final ByteBuffer... buffers) { - return new BsonByteBufferReader((ConvertMask) null, buffers); - } - - public BsonReader pollBsonReader(final InputStream in) { - return new BsonStreamReader(in); - } - - public BsonReader pollBsonReader() { - return new BsonReader(); - } - - public void offerBsonReader(final BsonReader in) { - //无需回收 - } - - //------------------------------ writer ----------------------------------------------------------- - public BsonByteBufferWriter pollBsonWriter(final Supplier supplier) { - return configWrite(new BsonByteBufferWriter(tiny, supplier)); - } - - protected BsonWriter pollBsonWriter(final OutputStream out) { - return configWrite(new BsonStreamWriter(tiny, out)); - } - - public BsonWriter pollBsonWriter() { - BsonWriter writer = writerPool.get(); - if (writer == null) { - writer = new BsonWriter(); - } else { - writerPool.set(null); - } - return configWrite(writer.tiny(tiny)); - } - - public void offerBsonWriter(final BsonWriter out) { - if (out != null) { - out.recycle(); - writerPool.set(out); - } - } - - //------------------------------ convertFrom ----------------------------------------------------------- - @Override - public T convertFrom(final Type type, final byte[] bytes) { - if (bytes == null) return null; - return convertFrom(type, bytes, 0, bytes.length); - } - - @Override - @SuppressWarnings("unchecked") - public T convertFrom(final Type type, final byte[] bytes, final int offset, final int len) { - if (type == null) return null; - final BsonReader in = new BsonReader(bytes, offset, len); - @SuppressWarnings("unchecked") - T rs = (T) factory.loadDecoder(type).convertFrom(in); - return rs; - } - - @SuppressWarnings("unchecked") - public T convertFrom(final Type type, final InputStream in) { - if (type == null || in == null) return null; - return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in)); - } - - @Override - @SuppressWarnings("unchecked") - public T convertFrom(final Type type, final ByteBuffer... buffers) { - if (type == null || buffers.length < 1) return null; - return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader((ConvertMask) null, buffers)); - } - - @Override - @SuppressWarnings("unchecked") - public T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) { - if (type == null || buffers.length < 1) return null; - return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader(mask, buffers)); - } - - @SuppressWarnings("unchecked") - public T convertFrom(final Type type, final BsonReader reader) { - if (type == null) return null; - @SuppressWarnings("unchecked") - T rs = (T) factory.loadDecoder(type).convertFrom(reader); - return rs; - } - - //------------------------------ convertTo ----------------------------------------------------------- - @Override - public byte[] convertTo(final Object value) { - if (value == null) { - final BsonWriter out = pollBsonWriter(); - out.writeNull(); - byte[] result = out.toArray(); - offerBsonWriter(out); - return result; - } - return convertTo(value.getClass(), value); - } - - @Override - public byte[] convertTo(final Type type, final Object value) { - if (type == null) return null; - final BsonWriter writer = pollBsonWriter(); - factory.loadEncoder(type).convertTo(writer, value); - byte[] result = writer.toArray(); - offerBsonWriter(writer); - return result; - } - - @Override - public byte[] convertToBytes(final Object value) { - return convertTo(value); - } - - @Override - public byte[] convertToBytes(final Type type, final Object value) { - return convertTo(type, value); - } - - @Override - public void convertToBytes(final Object value, final ConvertBytesHandler handler) { - convertToBytes(value == null ? null : value.getClass(), value, handler); - } - - @Override - public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) { - final BsonWriter writer = pollBsonWriter(); - if (type == null) { - writer.writeNull(); - } else { - factory.loadEncoder(type).convertTo(writer, value); - } - writer.completed(handler, offerConsumer); - } - - @Override - public void convertToBytes(final ByteArray array, final Object value) { - convertToBytes(array, value == null ? null : value.getClass(), value); - } - - @Override - public void convertToBytes(final ByteArray array, final Type type, final Object value) { - final BsonWriter writer = configWrite(new BsonWriter(array).tiny(tiny)); - if (type == null) { - writer.writeNull(); - } else { - factory.loadEncoder(type).convertTo(writer, value); - } - writer.directTo(array); - } - - public void convertTo(final OutputStream out, final Object value) { - if (value == null) { - pollBsonWriter(out).writeNull(); - } else { - factory.loadEncoder(value.getClass()).convertTo(pollBsonWriter(out), value); - } - } - - public void convertTo(final OutputStream out, final Type type, final Object value) { - if (type == null) return; - if (value == null) { - pollBsonWriter(out).writeNull(); - } else { - factory.loadEncoder(type).convertTo(pollBsonWriter(out), value); - } - } - - @Override - public ByteBuffer[] convertTo(final Supplier supplier, final Object value) { - if (supplier == null) return null; - BsonByteBufferWriter out = pollBsonWriter(supplier); - if (value == null) { - out.writeNull(); - } else { - factory.loadEncoder(value.getClass()).convertTo(out, value); - } - return out.toBuffers(); - } - - @Override - public ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value) { - if (supplier == null || type == null) return null; - BsonByteBufferWriter writer = pollBsonWriter(supplier); - if (value == null) { - writer.writeNull(); - } else { - factory.loadEncoder(type).convertTo(writer, value); - } - return writer.toBuffers(); - } - - public void convertTo(final BsonWriter writer, final Object value) { - if (value == null) { - writer.writeNull(); - } else { - factory.loadEncoder(value.getClass()).convertTo(writer, value); - } - } - - public void convertTo(final BsonWriter writer, final Type type, final Object value) { - if (type == null) return; - factory.loadEncoder(type).convertTo(writer, value); - } - - public BsonWriter convertToWriter(final Object value) { - if (value == null) return null; - return convertToWriter(value.getClass(), value); - } - - public BsonWriter convertToWriter(final Type type, final Object value) { - if (type == null) return null; - final BsonWriter writer = writerPool.get().tiny(tiny); - factory.loadEncoder(type).convertTo(writer, value); - return writer; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.io.*; +import java.lang.reflect.*; +import java.nio.*; +import java.util.function.*; +import org.redkale.convert.*; +import org.redkale.util.*; + +/** + *

    + * BSON协议格式:
    + *  1) 基本数据类型: 直接转换成byte[]
    + *  2) SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
    + *  3) String: length(4 bytes) + byte[](utf8);
    + *  4) 数组: length(4 bytes) + byte[]...
    + *  5) Object:
    + *      1、 realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
    + *      2、 空字符串(SmallString)
    + *      3、 SIGN_OBJECTB 标记位,值固定为0xBB (short)
    + *      4、 循环字段值:
    + *          4.1 SIGN_HASNEXT 标记位,值固定为1 (byte)
    + *          4.2 字段类型; 1-9为基本类型和字符串; 101-109为基本类型和字符串的数组; 127为Object
    + *          4.3 字段名 (SmallString)
    + *          4.4 字段的值Object
    + *      5、 SIGN_NONEXT 标记位,值固定为0 (byte)
    + *      6、 SIGN_OBJECTE 标记位,值固定为0xEE (short)
    + *
    + * 
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class BsonConvert extends BinaryConvert { + + private final ThreadLocal writerPool = ThreadLocal.withInitial(BsonWriter::new); + + private final Consumer offerConsumer = w -> offerBsonWriter(w); + + private final boolean tiny; + + protected BsonConvert(ConvertFactory factory, boolean tiny) { + super(factory); + this.tiny = tiny; + } + + @Override + public BsonFactory getFactory() { + return (BsonFactory) factory; + } + + public static BsonConvert root() { + return BsonFactory.root().getConvert(); + } + + @Override + public BsonConvert newConvert(final BiFunction fieldFunc) { + return newConvert(fieldFunc, null); + } + + @Override + public BsonConvert newConvert(final BiFunction fieldFunc, Function objExtFunc) { + return new BsonConvert(getFactory(), tiny) { + @Override + protected S configWrite(S writer) { + return fieldFunc(writer, fieldFunc, objExtFunc); + } + }; + } + + //------------------------------ reader ----------------------------------------------------------- + public BsonReader pollBsonReader(final ByteBuffer... buffers) { + return new BsonByteBufferReader((ConvertMask) null, buffers); + } + + public BsonReader pollBsonReader(final InputStream in) { + return new BsonStreamReader(in); + } + + public BsonReader pollBsonReader() { + return new BsonReader(); + } + + public void offerBsonReader(final BsonReader in) { + //无需回收 + } + + //------------------------------ writer ----------------------------------------------------------- + public BsonByteBufferWriter pollBsonWriter(final Supplier supplier) { + return configWrite(new BsonByteBufferWriter(tiny, supplier)); + } + + protected BsonWriter pollBsonWriter(final OutputStream out) { + return configWrite(new BsonStreamWriter(tiny, out)); + } + + public BsonWriter pollBsonWriter() { + BsonWriter writer = writerPool.get(); + if (writer == null) { + writer = new BsonWriter(); + } else { + writerPool.set(null); + } + return configWrite(writer.tiny(tiny)); + } + + public void offerBsonWriter(final BsonWriter out) { + if (out != null) { + out.recycle(); + writerPool.set(out); + } + } + + //------------------------------ convertFrom ----------------------------------------------------------- + @Override + public T convertFrom(final Type type, final byte[] bytes) { + if (bytes == null) return null; + return convertFrom(type, bytes, 0, bytes.length); + } + + @Override + @SuppressWarnings("unchecked") + public T convertFrom(final Type type, final byte[] bytes, final int offset, final int len) { + if (type == null) return null; + final BsonReader in = new BsonReader(bytes, offset, len); + @SuppressWarnings("unchecked") + T rs = (T) factory.loadDecoder(type).convertFrom(in); + return rs; + } + + @SuppressWarnings("unchecked") + public T convertFrom(final Type type, final InputStream in) { + if (type == null || in == null) return null; + return (T) factory.loadDecoder(type).convertFrom(new BsonStreamReader(in)); + } + + @Override + @SuppressWarnings("unchecked") + public T convertFrom(final Type type, final ByteBuffer... buffers) { + if (type == null || buffers.length < 1) return null; + return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader((ConvertMask) null, buffers)); + } + + @Override + @SuppressWarnings("unchecked") + public T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) { + if (type == null || buffers.length < 1) return null; + return (T) factory.loadDecoder(type).convertFrom(new BsonByteBufferReader(mask, buffers)); + } + + @SuppressWarnings("unchecked") + public T convertFrom(final Type type, final BsonReader reader) { + if (type == null) return null; + @SuppressWarnings("unchecked") + T rs = (T) factory.loadDecoder(type).convertFrom(reader); + return rs; + } + + //------------------------------ convertTo ----------------------------------------------------------- + @Override + public byte[] convertTo(final Object value) { + if (value == null) { + final BsonWriter out = pollBsonWriter(); + out.writeNull(); + byte[] result = out.toArray(); + offerBsonWriter(out); + return result; + } + return convertTo(value.getClass(), value); + } + + @Override + public byte[] convertTo(final Type type, final Object value) { + if (type == null) return null; + final BsonWriter writer = pollBsonWriter(); + factory.loadEncoder(type).convertTo(writer, value); + byte[] result = writer.toArray(); + offerBsonWriter(writer); + return result; + } + + @Override + public byte[] convertToBytes(final Object value) { + return convertTo(value); + } + + @Override + public byte[] convertToBytes(final Type type, final Object value) { + return convertTo(type, value); + } + + @Override + public void convertToBytes(final Object value, final ConvertBytesHandler handler) { + convertToBytes(value == null ? null : value.getClass(), value, handler); + } + + @Override + public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) { + final BsonWriter writer = pollBsonWriter(); + if (type == null) { + writer.writeNull(); + } else { + factory.loadEncoder(type).convertTo(writer, value); + } + writer.completed(handler, offerConsumer); + } + + @Override + public void convertToBytes(final ByteArray array, final Object value) { + convertToBytes(array, value == null ? null : value.getClass(), value); + } + + @Override + public void convertToBytes(final ByteArray array, final Type type, final Object value) { + final BsonWriter writer = configWrite(new BsonWriter(array).tiny(tiny)); + if (type == null) { + writer.writeNull(); + } else { + factory.loadEncoder(type).convertTo(writer, value); + } + writer.directTo(array); + } + + public void convertTo(final OutputStream out, final Object value) { + if (value == null) { + pollBsonWriter(out).writeNull(); + } else { + factory.loadEncoder(value.getClass()).convertTo(pollBsonWriter(out), value); + } + } + + public void convertTo(final OutputStream out, final Type type, final Object value) { + if (type == null) return; + if (value == null) { + pollBsonWriter(out).writeNull(); + } else { + factory.loadEncoder(type).convertTo(pollBsonWriter(out), value); + } + } + + @Override + public ByteBuffer[] convertTo(final Supplier supplier, final Object value) { + if (supplier == null) return null; + BsonByteBufferWriter out = pollBsonWriter(supplier); + if (value == null) { + out.writeNull(); + } else { + factory.loadEncoder(value.getClass()).convertTo(out, value); + } + return out.toBuffers(); + } + + @Override + public ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value) { + if (supplier == null || type == null) return null; + BsonByteBufferWriter writer = pollBsonWriter(supplier); + if (value == null) { + writer.writeNull(); + } else { + factory.loadEncoder(type).convertTo(writer, value); + } + return writer.toBuffers(); + } + + @Override + public void convertTo(final BsonWriter writer, final Object value) { + if (value == null) { + writer.writeNull(); + } else { + factory.loadEncoder(value.getClass()).convertTo(writer, value); + } + } + + @Override + public void convertTo(final BsonWriter writer, final Type type, final Object value) { + if (type == null) return; + factory.loadEncoder(type).convertTo(writer, value); + } + + public BsonWriter convertToWriter(final Object value) { + if (value == null) return null; + return convertToWriter(value.getClass(), value); + } + + public BsonWriter convertToWriter(final Type type, final Object value) { + if (type == null) return null; + final BsonWriter writer = writerPool.get().tiny(tiny); + factory.loadEncoder(type).convertTo(writer, value); + return writer; + } +} diff --git a/src/org/redkale/convert/bson/BsonFactory.java b/src/main/java/org/redkale/convert/bson/BsonFactory.java similarity index 91% rename from src/org/redkale/convert/bson/BsonFactory.java rename to src/main/java/org/redkale/convert/bson/BsonFactory.java index e435a9da1..6fa59aca3 100644 --- a/src/org/redkale/convert/bson/BsonFactory.java +++ b/src/main/java/org/redkale/convert/bson/BsonFactory.java @@ -1,212 +1,212 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.io.Serializable; -import java.lang.reflect.Type; -import java.util.*; -import java.util.stream.Stream; -import org.redkale.convert.*; -import org.redkale.convert.ext.*; -import org.redkale.util.*; - -/** - * BSON的ConvertFactory - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public final class BsonFactory extends ConvertFactory { - - private static final BsonFactory instance = new BsonFactory(null, getSystemPropertyBoolean("convert.bson.tiny", "convert.tiny", true)); - - static final Decodeable objectDecoder = instance.loadDecoder(Object.class); - - static final Encodeable objectEncoder = instance.loadEncoder(Object.class); - - static final Decodeable skipArrayDecoder = new SkipArrayDecoder(instance, Object[].class); - - static final Decodeable skipCollectionDecoder = new SkipCollectionDecoder(instance, new TypeToken>() { - }.getType()); - - static final Decodeable skipStreamDecoder = new SkipStreamDecoder(instance, new TypeToken>() { - }.getType()); - - static final Decodeable skipMapDecoder = new SkipMapDecoder(instance, Map.class); - - static { - instance.register(Serializable.class, objectDecoder); - instance.register(Serializable.class, objectEncoder); - - instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class)); - instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class)); - } - - private BsonFactory(BsonFactory parent, boolean tiny) { - super(parent, tiny); - } - - @Override - public BsonFactory tiny(boolean tiny) { - this.tiny = tiny; - return this; - } - - @Override - public BsonFactory skipAllIgnore(final boolean skipIgnore) { - this.registerSkipAllIgnore(skipIgnore); - return this; - } - - public static BsonFactory root() { - return instance; - } - - public static BsonFactory create() { - return new BsonFactory(null, getSystemPropertyBoolean("convert.bson.tiny", "convert.tiny", true)); - } - - @Override - public final BsonConvert getConvert() { - if (convert == null) convert = new BsonConvert(this, tiny); - return (BsonConvert) convert; - } - - @Override - public BsonFactory createChild() { - return new BsonFactory(this, this.tiny); - } - - @Override - public BsonFactory createChild(boolean tiny) { - return new BsonFactory(this, tiny); - } - - @Override - public ConvertType getConvertType() { - return ConvertType.BSON; - } - - @Override - public boolean isReversible() { - return true; - } - - @Override - public boolean isFieldSort() { - return true; - } - - protected static byte typeEnum(final Type type) { - return typeEnum(TypeToken.typeToClass(type)); - } - - protected static byte typeEnum(final Class type) { - Objects.requireNonNull(type); - byte typeval = 127; //字段的类型值 - if (type == boolean.class || type == Boolean.class) { - typeval = 11; - } else if (type == byte.class || type == Byte.class) { - typeval = 12; - } else if (type == short.class || type == Short.class) { - typeval = 13; - } else if (type == char.class || type == Character.class) { - typeval = 14; - } else if (type == int.class || type == Integer.class) { - typeval = 15; - } else if (type == long.class || type == Long.class) { - typeval = 16; - } else if (type == float.class || type == Float.class) { - typeval = 17; - } else if (type == double.class || type == Double.class) { - typeval = 18; - } else if (type == String.class) { - typeval = 19; - } else if (type == boolean[].class || type == Boolean[].class) { - typeval = 21; - } else if (type == byte[].class || type == Byte[].class) { - typeval = 22; - } else if (type == short[].class || type == Short[].class) { - typeval = 23; - } else if (type == char[].class || type == Character[].class) { - typeval = 24; - } else if (type == int[].class || type == Integer[].class) { - typeval = 25; - } else if (type == long[].class || type == Long[].class) { - typeval = 26; - } else if (type == float[].class || type == Float[].class) { - typeval = 27; - } else if (type == double[].class || type == Double[].class) { - typeval = 28; - } else if (type == String[].class) { - typeval = 29; - } else if (type.isArray()) { - typeval = 81; - } else if (Collection.class.isAssignableFrom(type)) { - typeval = 82; - } else if (Stream.class.isAssignableFrom(type)) { - typeval = 83; - } else if (Map.class.isAssignableFrom(type)) { - typeval = 84; - } - return typeval; - } - - protected static Decodeable typeEnum(final byte typeval) { - switch (typeval) { - case 11: - return BoolSimpledCoder.instance; - case 12: - return ByteSimpledCoder.instance; - case 13: - return ShortSimpledCoder.instance; - case 14: - return CharSimpledCoder.instance; - case 15: - return IntSimpledCoder.instance; - case 16: - return LongSimpledCoder.instance; - case 17: - return FloatSimpledCoder.instance; - case 18: - return DoubleSimpledCoder.instance; - case 19: - return StringSimpledCoder.instance; - case 21: - return BoolArraySimpledCoder.instance; - case 22: - return ByteArraySimpledCoder.instance; - case 23: - return ShortArraySimpledCoder.instance; - case 24: - return CharArraySimpledCoder.instance; - case 25: - return IntArraySimpledCoder.instance; - case 26: - return LongArraySimpledCoder.instance; - case 27: - return FloatArraySimpledCoder.instance; - case 28: - return DoubleArraySimpledCoder.instance; - case 29: - return StringArraySimpledCoder.instance; - case 81: - return skipArrayDecoder; - case 82: - return skipCollectionDecoder; - case 83: - return skipStreamDecoder; - case 84: - return skipMapDecoder; - case 127: - return BsonFactory.objectDecoder; - } - return null; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.*; +import java.util.stream.Stream; +import org.redkale.convert.*; +import org.redkale.convert.ext.*; +import org.redkale.util.*; + +/** + * BSON的ConvertFactory + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public final class BsonFactory extends ConvertFactory { + + private static final BsonFactory instance = new BsonFactory(null, getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", true)); + + static final Decodeable objectDecoder = instance.loadDecoder(Object.class); + + static final Encodeable objectEncoder = instance.loadEncoder(Object.class); + + static final Decodeable skipArrayDecoder = new SkipArrayDecoder(instance, Object[].class); + + static final Decodeable skipCollectionDecoder = new SkipCollectionDecoder(instance, new TypeToken>() { + }.getType()); + + static final Decodeable skipStreamDecoder = new SkipStreamDecoder(instance, new TypeToken>() { + }.getType()); + + static final Decodeable skipMapDecoder = new SkipMapDecoder(instance, Map.class); + + static { + instance.register(Serializable.class, objectDecoder); + instance.register(Serializable.class, objectEncoder); + + //instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class)); + //instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class)); + } + + private BsonFactory(BsonFactory parent, boolean tiny) { + super(parent, tiny); + } + + @Override + public BsonFactory tiny(boolean tiny) { + this.tiny = tiny; + return this; + } + + @Override + public BsonFactory skipAllIgnore(final boolean skipIgnore) { + this.registerSkipAllIgnore(skipIgnore); + return this; + } + + public static BsonFactory root() { + return instance; + } + + public static BsonFactory create() { + return new BsonFactory(null, getSystemPropertyBoolean("redkale.convert.bson.tiny", "redkale.convert.tiny", true)); + } + + @Override + public final BsonConvert getConvert() { + if (convert == null) convert = new BsonConvert(this, tiny); + return (BsonConvert) convert; + } + + @Override + public BsonFactory createChild() { + return new BsonFactory(this, this.tiny); + } + + @Override + public BsonFactory createChild(boolean tiny) { + return new BsonFactory(this, tiny); + } + + @Override + public ConvertType getConvertType() { + return ConvertType.BSON; + } + + @Override + public boolean isReversible() { + return true; + } + + @Override + public boolean isFieldSort() { + return true; + } + + protected static byte typeEnum(final Type type) { + return typeEnum(TypeToken.typeToClass(type)); + } + + protected static byte typeEnum(final Class type) { + Objects.requireNonNull(type); + byte typeval = 127; //字段的类型值 + if (type == boolean.class || type == Boolean.class) { + typeval = 11; + } else if (type == byte.class || type == Byte.class) { + typeval = 12; + } else if (type == short.class || type == Short.class) { + typeval = 13; + } else if (type == char.class || type == Character.class) { + typeval = 14; + } else if (type == int.class || type == Integer.class) { + typeval = 15; + } else if (type == long.class || type == Long.class) { + typeval = 16; + } else if (type == float.class || type == Float.class) { + typeval = 17; + } else if (type == double.class || type == Double.class) { + typeval = 18; + } else if (type == String.class) { + typeval = 19; + } else if (type == boolean[].class || type == Boolean[].class) { + typeval = 21; + } else if (type == byte[].class || type == Byte[].class) { + typeval = 22; + } else if (type == short[].class || type == Short[].class) { + typeval = 23; + } else if (type == char[].class || type == Character[].class) { + typeval = 24; + } else if (type == int[].class || type == Integer[].class) { + typeval = 25; + } else if (type == long[].class || type == Long[].class) { + typeval = 26; + } else if (type == float[].class || type == Float[].class) { + typeval = 27; + } else if (type == double[].class || type == Double[].class) { + typeval = 28; + } else if (type == String[].class) { + typeval = 29; + } else if (type.isArray()) { + typeval = 81; + } else if (Collection.class.isAssignableFrom(type)) { + typeval = 82; + } else if (Stream.class.isAssignableFrom(type)) { + typeval = 83; + } else if (Map.class.isAssignableFrom(type)) { + typeval = 84; + } + return typeval; + } + + protected static Decodeable typeEnum(final byte typeval) { + switch (typeval) { + case 11: + return BoolSimpledCoder.instance; + case 12: + return ByteSimpledCoder.instance; + case 13: + return ShortSimpledCoder.instance; + case 14: + return CharSimpledCoder.instance; + case 15: + return IntSimpledCoder.instance; + case 16: + return LongSimpledCoder.instance; + case 17: + return FloatSimpledCoder.instance; + case 18: + return DoubleSimpledCoder.instance; + case 19: + return StringSimpledCoder.instance; + case 21: + return BoolArraySimpledCoder.instance; + case 22: + return ByteArraySimpledCoder.instance; + case 23: + return ShortArraySimpledCoder.instance; + case 24: + return CharArraySimpledCoder.instance; + case 25: + return IntArraySimpledCoder.instance; + case 26: + return LongArraySimpledCoder.instance; + case 27: + return FloatArraySimpledCoder.instance; + case 28: + return DoubleArraySimpledCoder.instance; + case 29: + return StringArraySimpledCoder.instance; + case 81: + return skipArrayDecoder; + case 82: + return skipCollectionDecoder; + case 83: + return skipStreamDecoder; + case 84: + return skipMapDecoder; + case 127: + return BsonFactory.objectDecoder; + } + return null; + } +} diff --git a/src/org/redkale/convert/bson/BsonReader.java b/src/main/java/org/redkale/convert/bson/BsonReader.java similarity index 96% rename from src/org/redkale/convert/bson/BsonReader.java rename to src/main/java/org/redkale/convert/bson/BsonReader.java index 51c09abfd..6490d51ce 100644 --- a/src/org/redkale/convert/bson/BsonReader.java +++ b/src/main/java/org/redkale/convert/bson/BsonReader.java @@ -1,355 +1,355 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.nio.charset.StandardCharsets; -import org.redkale.convert.*; -import static org.redkale.convert.Reader.SIGN_NULL; -import org.redkale.convert.ext.*; -import org.redkale.util.*; - -/** - * BSON数据源 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class BsonReader extends Reader { - - public static final short SIGN_OBJECTB = (short) 0xBB; - - public static final short SIGN_OBJECTE = (short) 0xEE; - - public static final byte SIGN_HASNEXT = 1; - - public static final byte SIGN_NONEXT = 0; - - public static final byte VERBOSE_NO = 1; - - public static final byte VERBOSE_YES = 2; - - protected byte typeval; //字段的类型值 对应 BsonWriter.writeField - - protected int position = -1; - - private byte[] content; - - public BsonReader() { - } - - public static ObjectPool createPool(int max) { - return ObjectPool.createSafePool(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle()); - } - - public BsonReader(byte[] bytes) { - setBytes(bytes, 0, bytes.length); - } - - public BsonReader(byte[] bytes, int start, int len) { - setBytes(bytes, start, len); - } - - public final void setBytes(byte[] bytes) { - if (bytes == null) { - this.position = 0; - } else { - setBytes(bytes, 0, bytes.length); - } - } - - public final void setBytes(byte[] bytes, int start, int len) { - if (bytes == null) { - this.position = 0; - } else { - this.content = bytes; - this.position = start - 1; - //this.limit = start + len - 1; - } - } - - protected boolean recycle() { - this.position = -1; - this.typeval = 0; - //this.limit = -1; - this.content = null; - return true; - } - - public void close() { - this.recycle(); - } - - /** - * 跳过属性的值 - */ - @Override - @SuppressWarnings("unchecked") - public final void skipValue() { - if (typeval == 0) return; - final byte val = this.typeval; - this.typeval = 0; - switch (val) { - case 11: readBoolean(); - break; - case 12: readByte(); - break; - case 13: readShort(); - break; - case 14: readChar(); - break; - case 15: readInt(); - break; - case 16: readLong(); - break; - case 17: readFloat(); - break; - case 18: readDouble(); - break; - case 19: readString(); - break; - default: - Decodeable decoder = BsonFactory.typeEnum(val); - if (decoder != null) decoder.convertFrom(this); - break; - } - } - - @Override - public final String readObjectB(final Class clazz) { - this.fieldIndex = 0; //必须要重置为0 - final String newcls = readClassName(); - if (newcls != null && !newcls.isEmpty()) return newcls; - short bt = readShort(); - if (bt == Reader.SIGN_NULL) return null; - if (bt != SIGN_OBJECTB) { - throw new ConvertException("a bson object must begin with " + (SIGN_OBJECTB) - + " (position = " + position + ") but '" + currentByte() + "'"); - } - return ""; - } - - @Override - public final void readObjectE(final Class clazz) { - if (readShort() != SIGN_OBJECTE) { - throw new ConvertException("a bson object must end with " + (SIGN_OBJECTE) - + " (position = " + position + ") but '" + currentByte() + "'"); - } - } - - protected byte currentByte() { - return this.content[this.position]; - } - - @Override - public int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder) { - short bt = readShort(); - if (bt == Reader.SIGN_NULL) return bt; - int rs = (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff); - byte kt = readByte(); - byte vt = readByte(); - if (typevals != null) { - typevals[0] = kt; - typevals[1] = vt; - } - return rs; - } - - @Override - public final void readMapE() { - } - - /** - * 判断下一个非空白字节是否为[ - * - * @return 数组长度或SIGN_NULL - */ - @Override - public int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) { //componentDecoder可能为null - short bt = readShort(); - if (bt == Reader.SIGN_NULL) return bt; - int rs = (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff); - if (componentDecoder != null && componentDecoder != ByteSimpledCoder.instance) { - byte comval = readByte(); - if (typevals != null) typevals[0] = comval; - } - return rs; - } - - @Override - public final void readArrayE() { - } - - /** - * 判断下一个非空白字节是否: - */ - @Override - public final void readBlank() { - } - - @Override - public int position() { - return this.position; - } - - @Override - public int readMemberContentLength(DeMember member, Decodeable decoder) { - return -1; - } - - /** - * 判断对象是否存在下一个属性或者数组是否存在下一个元素 - * - * @param startPosition 起始位置 - * @param contentLength 内容大小, 不确定的传-1 - * - * @return 是否存在 - */ - @Override - public boolean hasNext(int startPosition, int contentLength) { - byte b = readByte(); - if (b == SIGN_HASNEXT) return true; - if (b != SIGN_NONEXT) throw new ConvertException("hasNext option must be (" + (SIGN_HASNEXT) - + " or " + (SIGN_NONEXT) + ") but '" + b + "' at position(" + this.position + ")"); - return false; - } - - @Override - public final DeMember readFieldName(final DeMember[] members) { - final String exceptedfield = readSmallString(); - this.typeval = readByte(); - final int len = members.length; - if (this.fieldIndex >= len) this.fieldIndex = 0; - for (int k = this.fieldIndex; k < len; k++) { - if (exceptedfield.equals(members[k].getAttribute().field())) { - this.fieldIndex = k; - return members[k]; - } - } - for (int k = 0; k < this.fieldIndex; k++) { - if (exceptedfield.equals(members[k].getAttribute().field())) { - this.fieldIndex = k; - return members[k]; - } - } - return null; - } - - //------------------------------------------------------------ - @Override - public boolean readBoolean() { - return content[++this.position] == 1; - } - - @Override - public byte readByte() { - return content[++this.position]; - } - - @Override - public final byte[] readByteArray() { - int len = readArrayB(null, null, null); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = readMemberContentLength(null, null); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - byte[] data = new byte[8]; - int startPosition = position(); - while (hasNext(startPosition, contentLength)) { - if (size >= data.length) { - byte[] newdata = new byte[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = readByte(); - } - readArrayE(); - byte[] newdata = new byte[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - byte[] values = new byte[len]; - for (int i = 0; i < values.length; i++) { - values[i] = readByte(); - } - readArrayE(); - return values; - } - } - - @Override - public char readChar() { - return (char) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position])); - } - - @Override - public short readShort() { - return (short) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position])); - } - - @Override - public int readInt() { - return ((content[++this.position] & 0xff) << 24) | ((content[++this.position] & 0xff) << 16) - | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff); - } - - @Override - public long readLong() { - return ((((long) content[++this.position] & 0xff) << 56) - | (((long) content[++this.position] & 0xff) << 48) - | (((long) content[++this.position] & 0xff) << 40) - | (((long) content[++this.position] & 0xff) << 32) - | (((long) content[++this.position] & 0xff) << 24) - | (((long) content[++this.position] & 0xff) << 16) - | (((long) content[++this.position] & 0xff) << 8) - | (((long) content[++this.position] & 0xff))); - } - - @Override - public final float readFloat() { - return Float.intBitsToFloat(readInt()); - } - - @Override - public final double readDouble() { - return Double.longBitsToDouble(readLong()); - } - - @Override - public final String readClassName() { - return readSmallString(); - } - - @Override - public String readSmallString() { - int len = 0xff & readByte(); - if (len == 0) return ""; - String value = new String(content, ++this.position, len); - this.position += len - 1; //上一行已经++this.position,所以此处要-1 - return value; - } - - @Override - public String readString() { - int len = readInt(); - if (len == SIGN_NULL) return null; - if (len == 0) return ""; - String value = new String(content, ++this.position, len, StandardCharsets.UTF_8); - this.position += len - 1;//上一行已经++this.position,所以此处要-1 - return value; - } - - @Override - public ValueType readType() { - throw new UnsupportedOperationException("Not supported yet."); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.nio.charset.StandardCharsets; +import org.redkale.convert.*; +import static org.redkale.convert.Reader.SIGN_NULL; +import org.redkale.convert.ext.*; +import org.redkale.util.*; + +/** + * BSON数据源 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class BsonReader extends Reader { + + public static final short SIGN_OBJECTB = (short) 0xBB; + + public static final short SIGN_OBJECTE = (short) 0xEE; + + public static final byte SIGN_HASNEXT = 1; + + public static final byte SIGN_NONEXT = 0; + + public static final byte VERBOSE_NO = 1; + + public static final byte VERBOSE_YES = 2; + + protected byte typeval; //字段的类型值 对应 BsonWriter.writeField + + protected int position = -1; + + private byte[] content; + + public BsonReader() { + } + + public static ObjectPool createPool(int max) { + return ObjectPool.createSafePool(max, (Object... params) -> new BsonReader(), null, (t) -> t.recycle()); + } + + public BsonReader(byte[] bytes) { + setBytes(bytes, 0, bytes.length); + } + + public BsonReader(byte[] bytes, int start, int len) { + setBytes(bytes, start, len); + } + + public final void setBytes(byte[] bytes) { + if (bytes == null) { + this.position = 0; + } else { + setBytes(bytes, 0, bytes.length); + } + } + + public final void setBytes(byte[] bytes, int start, int len) { + if (bytes == null) { + this.position = 0; + } else { + this.content = bytes; + this.position = start - 1; + //this.limit = start + len - 1; + } + } + + protected boolean recycle() { + this.position = -1; + this.typeval = 0; + //this.limit = -1; + this.content = null; + return true; + } + + public void close() { + this.recycle(); + } + + /** + * 跳过属性的值 + */ + @Override + @SuppressWarnings("unchecked") + public final void skipValue() { + if (typeval == 0) return; + final byte val = this.typeval; + this.typeval = 0; + switch (val) { + case 11: readBoolean(); + break; + case 12: readByte(); + break; + case 13: readShort(); + break; + case 14: readChar(); + break; + case 15: readInt(); + break; + case 16: readLong(); + break; + case 17: readFloat(); + break; + case 18: readDouble(); + break; + case 19: readString(); + break; + default: + Decodeable decoder = BsonFactory.typeEnum(val); + if (decoder != null) decoder.convertFrom(this); + break; + } + } + + @Override + public final String readObjectB(final Class clazz) { + this.fieldIndex = 0; //必须要重置为0 + final String newcls = readClassName(); + if (newcls != null && !newcls.isEmpty()) return newcls; + short bt = readShort(); + if (bt == Reader.SIGN_NULL) return null; + if (bt != SIGN_OBJECTB) { + throw new ConvertException("a bson object must begin with " + (SIGN_OBJECTB) + + " (position = " + position + ") but '" + currentByte() + "'"); + } + return ""; + } + + @Override + public final void readObjectE(final Class clazz) { + if (readShort() != SIGN_OBJECTE) { + throw new ConvertException("a bson object must end with " + (SIGN_OBJECTE) + + " (position = " + position + ") but '" + currentByte() + "'"); + } + } + + protected byte currentByte() { + return this.content[this.position]; + } + + @Override + public int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valueDecoder) { + short bt = readShort(); + if (bt == Reader.SIGN_NULL) return bt; + int rs = (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff); + byte kt = readByte(); + byte vt = readByte(); + if (typevals != null) { + typevals[0] = kt; + typevals[1] = vt; + } + return rs; + } + + @Override + public final void readMapE() { + } + + /** + * 判断下一个非空白字节是否为[ + * + * @return 数组长度或SIGN_NULL + */ + @Override + public int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) { //componentDecoder可能为null + short bt = readShort(); + if (bt == Reader.SIGN_NULL) return bt; + int rs = (bt & 0xffff) << 16 | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff); + if (componentDecoder != null && componentDecoder != ByteSimpledCoder.instance) { + byte comval = readByte(); + if (typevals != null) typevals[0] = comval; + } + return rs; + } + + @Override + public final void readArrayE() { + } + + /** + * 判断下一个非空白字节是否: + */ + @Override + public final void readBlank() { + } + + @Override + public int position() { + return this.position; + } + + @Override + public int readMemberContentLength(DeMember member, Decodeable decoder) { + return -1; + } + + /** + * 判断对象是否存在下一个属性或者数组是否存在下一个元素 + * + * @param startPosition 起始位置 + * @param contentLength 内容大小, 不确定的传-1 + * + * @return 是否存在 + */ + @Override + public boolean hasNext(int startPosition, int contentLength) { + byte b = readByte(); + if (b == SIGN_HASNEXT) return true; + if (b != SIGN_NONEXT) throw new ConvertException("hasNext option must be (" + (SIGN_HASNEXT) + + " or " + (SIGN_NONEXT) + ") but '" + b + "' at position(" + this.position + ")"); + return false; + } + + @Override + public final DeMember readFieldName(final DeMember[] members) { + final String exceptedfield = readSmallString(); + this.typeval = readByte(); + final int len = members.length; + if (this.fieldIndex >= len) this.fieldIndex = 0; + for (int k = this.fieldIndex; k < len; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + this.fieldIndex = k; + return members[k]; + } + } + for (int k = 0; k < this.fieldIndex; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + this.fieldIndex = k; + return members[k]; + } + } + return null; + } + + //------------------------------------------------------------ + @Override + public boolean readBoolean() { + return content[++this.position] == 1; + } + + @Override + public byte readByte() { + return content[++this.position]; + } + + @Override + public final byte[] readByteArray() { + int len = readArrayB(null, null, null); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = readMemberContentLength(null, null); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + byte[] data = new byte[8]; + int startPosition = position(); + while (hasNext(startPosition, contentLength)) { + if (size >= data.length) { + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = readByte(); + } + readArrayE(); + byte[] newdata = new byte[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + byte[] values = new byte[len]; + for (int i = 0; i < values.length; i++) { + values[i] = readByte(); + } + readArrayE(); + return values; + } + } + + @Override + public char readChar() { + return (char) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position])); + } + + @Override + public short readShort() { + return (short) ((0xff00 & (content[++this.position] << 8)) | (0xff & content[++this.position])); + } + + @Override + public int readInt() { + return ((content[++this.position] & 0xff) << 24) | ((content[++this.position] & 0xff) << 16) + | ((content[++this.position] & 0xff) << 8) | (content[++this.position] & 0xff); + } + + @Override + public long readLong() { + return ((((long) content[++this.position] & 0xff) << 56) + | (((long) content[++this.position] & 0xff) << 48) + | (((long) content[++this.position] & 0xff) << 40) + | (((long) content[++this.position] & 0xff) << 32) + | (((long) content[++this.position] & 0xff) << 24) + | (((long) content[++this.position] & 0xff) << 16) + | (((long) content[++this.position] & 0xff) << 8) + | (((long) content[++this.position] & 0xff))); + } + + @Override + public final float readFloat() { + return Float.intBitsToFloat(readInt()); + } + + @Override + public final double readDouble() { + return Double.longBitsToDouble(readLong()); + } + + @Override + public final String readClassName() { + return readSmallString(); + } + + @Override + public String readSmallString() { + int len = 0xff & readByte(); + if (len == 0) return ""; + String value = new String(content, ++this.position, len); + this.position += len - 1; //上一行已经++this.position,所以此处要-1 + return value; + } + + @Override + public String readString() { + int len = readInt(); + if (len == SIGN_NULL) return null; + if (len == 0) return ""; + String value = new String(content, ++this.position, len, StandardCharsets.UTF_8); + this.position += len - 1;//上一行已经++this.position,所以此处要-1 + return value; + } + + @Override + public ValueType readType() { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/src/org/redkale/convert/bson/BsonSimpledCoder.java b/src/main/java/org/redkale/convert/bson/BsonSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/bson/BsonSimpledCoder.java rename to src/main/java/org/redkale/convert/bson/BsonSimpledCoder.java index 140e00c82..3f63b5a83 100644 --- a/src/org/redkale/convert/bson/BsonSimpledCoder.java +++ b/src/main/java/org/redkale/convert/bson/BsonSimpledCoder.java @@ -1,20 +1,20 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import org.redkale.convert.SimpledCoder; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 序列化/反解析的数据类型 - */ -public abstract class BsonSimpledCoder extends SimpledCoder { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import org.redkale.convert.SimpledCoder; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 序列化/反解析的数据类型 + */ +public abstract class BsonSimpledCoder extends SimpledCoder { + +} diff --git a/src/org/redkale/convert/bson/BsonStreamReader.java b/src/main/java/org/redkale/convert/bson/BsonStreamReader.java similarity index 95% rename from src/org/redkale/convert/bson/BsonStreamReader.java rename to src/main/java/org/redkale/convert/bson/BsonStreamReader.java index b717e61a9..7a704b3af 100644 --- a/src/org/redkale/convert/bson/BsonStreamReader.java +++ b/src/main/java/org/redkale/convert/bson/BsonStreamReader.java @@ -1,63 +1,63 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.io.*; -import org.redkale.convert.*; - -/** - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -class BsonStreamReader extends BsonByteBufferReader { - - private InputStream in; - - private byte currByte; - - protected BsonStreamReader(InputStream in) { - super((ConvertMask) null); - this.in = in; - } - - @Override - protected boolean recycle() { - super.recycle(); // this.position 初始化值为-1 - this.in = null; - this.currByte = 0; - return false; - } - - @Override - public byte readByte() { - try { - byte b = (currByte = (byte) in.read()); - this.position++; - return b; - } catch (IOException e) { - throw new ConvertException(e); - } - } - - @Override - protected byte currentByte() { - return currByte; - } - - @Override - protected byte[] read(final int len) { - byte[] bs = new byte[len]; - try { - in.read(bs); - this.position += len; - } catch (IOException e) { - throw new ConvertException(e); - } - return bs; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.io.*; +import org.redkale.convert.*; + +/** + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +class BsonStreamReader extends BsonByteBufferReader { + + private InputStream in; + + private byte currByte; + + protected BsonStreamReader(InputStream in) { + super((ConvertMask) null); + this.in = in; + } + + @Override + protected boolean recycle() { + super.recycle(); // this.position 初始化值为-1 + this.in = null; + this.currByte = 0; + return false; + } + + @Override + public byte readByte() { + try { + byte b = (currByte = (byte) in.read()); + this.position++; + return b; + } catch (IOException e) { + throw new ConvertException(e); + } + } + + @Override + protected byte currentByte() { + return currByte; + } + + @Override + protected byte[] read(final int len) { + byte[] bs = new byte[len]; + try { + in.read(bs); + this.position += len; + } catch (IOException e) { + throw new ConvertException(e); + } + return bs; + } +} diff --git a/src/org/redkale/convert/bson/BsonStreamWriter.java b/src/main/java/org/redkale/convert/bson/BsonStreamWriter.java similarity index 95% rename from src/org/redkale/convert/bson/BsonStreamWriter.java rename to src/main/java/org/redkale/convert/bson/BsonStreamWriter.java index 35b828d13..e3ccf4547 100644 --- a/src/org/redkale/convert/bson/BsonStreamWriter.java +++ b/src/main/java/org/redkale/convert/bson/BsonStreamWriter.java @@ -1,50 +1,50 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.io.*; -import org.redkale.convert.*; - -/** - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -class BsonStreamWriter extends BsonByteBufferWriter { - - private OutputStream out; - - protected BsonStreamWriter(boolean tiny, OutputStream out) { - super(tiny, null); - this.out = out; - } - - @Override - protected boolean recycle() { - super.recycle(); - this.out = null; - return false; - } - - @Override - public void writeTo(final byte[] chs, final int start, final int len) { - try { - out.write(chs, start, len); - } catch (IOException e) { - throw new ConvertException(e); - } - } - - @Override - public void writeTo(final byte ch) { - try { - out.write((byte) ch); - } catch (IOException e) { - throw new ConvertException(e); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.io.*; +import org.redkale.convert.*; + +/** + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +class BsonStreamWriter extends BsonByteBufferWriter { + + private OutputStream out; + + protected BsonStreamWriter(boolean tiny, OutputStream out) { + super(tiny, null); + this.out = out; + } + + @Override + protected boolean recycle() { + super.recycle(); + this.out = null; + return false; + } + + @Override + public void writeTo(final byte[] chs, final int start, final int len) { + try { + out.write(chs, start, len); + } catch (IOException e) { + throw new ConvertException(e); + } + } + + @Override + public void writeTo(final byte ch) { + try { + out.write((byte) ch); + } catch (IOException e) { + throw new ConvertException(e); + } + } +} diff --git a/src/org/redkale/convert/bson/BsonWriter.java b/src/main/java/org/redkale/convert/bson/BsonWriter.java similarity index 94% rename from src/org/redkale/convert/bson/BsonWriter.java rename to src/main/java/org/redkale/convert/bson/BsonWriter.java index 0e7a5a20d..77ae8f6ce 100644 --- a/src/org/redkale/convert/bson/BsonWriter.java +++ b/src/main/java/org/redkale/convert/bson/BsonWriter.java @@ -1,336 +1,336 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.util.function.Consumer; -import org.redkale.convert.*; -import org.redkale.convert.ext.ByteSimpledCoder; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class BsonWriter extends Writer implements ByteTuple { - - private static final int defaultSize = Integer.getInteger("convert.bson.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024)); - - private byte[] content; - - protected int count; - - protected boolean tiny; - - public static ObjectPool createPool(int max) { - return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle()); - } - - @Override - public byte[] content() { - return content; - } - - @Override - public int offset() { - return 0; - } - - @Override - public int length() { - return count; - } - - /** - * 直接获取全部数据, 实际数据需要根据count长度来截取 - * - * @return byte[] - */ - public byte[] directBytes() { - return content; - } - - /** - * 将本对象的内容引用复制给array - * - * @param array ByteArray - */ - public void directTo(ByteArray array) { - array.directFrom(content, count); - } - - public void completed(ConvertBytesHandler handler, Consumer callback) { - handler.completed(content, 0, count, callback, this); - } - - public ByteArray toByteArray() { - return new ByteArray(this); - } - - public byte[] toArray() { - if (count == content.length) return content; - byte[] newdata = new byte[count]; - System.arraycopy(content, 0, newdata, 0, count); - return newdata; - } - - public ByteBuffer[] toBuffers() { - return new ByteBuffer[]{ByteBuffer.wrap(content, 0, count)}; - } - - protected BsonWriter(byte[] bs) { - this.content = bs == null ? new byte[0] : bs; - } - - public BsonWriter() { - this(defaultSize); - } - - public BsonWriter(int size) { - this.content = new byte[size > 128 ? size : 128]; - } - - public BsonWriter(ByteArray array) { - this.content = array.content(); - this.count = array.length(); - } - - @Override - public final boolean tiny() { - return tiny; - } - - public BsonWriter tiny(boolean tiny) { - this.tiny = tiny; - return this; - } - - //----------------------------------------------------------------------- - //----------------------------------------------------------------------- - /** - * 扩充指定长度的缓冲区 - * - * @param len 扩容长度 - * - * @return 固定0 - */ - protected int expand(int len) { - int newcount = count + len; - if (newcount <= content.length) return 0; - byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)]; - System.arraycopy(content, 0, newdata, 0, count); - this.content = newdata; - return 0; - } - - public void writeTo(final byte ch) { - expand(1); - content[count++] = ch; - } - - public final void writeTo(final byte... chs) { - writeTo(chs, 0, chs.length); - } - - public void writeTo(final byte[] chs, final int start, final int len) { - expand(len); - System.arraycopy(chs, start, content, count, len); - count += len; - } - - @Override - protected boolean recycle() { - super.recycle(); - this.count = 0; - this.specify = null; - if (this.content != null && this.content.length > defaultSize) { - this.content = new byte[defaultSize]; - } - return true; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "[count=" + this.count + "]"; - } - - //------------------------------------------------------------------------ - public final int count() { - return this.count; - } - - @Override - public final void writeBoolean(boolean value) { - writeTo(value ? (byte) 1 : (byte) 0); - } - - @Override - public final void writeByte(byte value) { - writeTo(value); - } - - @Override - public final void writeByteArray(byte[] values) { - if (values == null) { - writeNull(); - return; - } - writeArrayB(values.length, null, null, values); - boolean flag = false; - for (byte v : values) { - if (flag) writeArrayMark(); - writeByte(v); - flag = true; - } - writeArrayE(); - } - - @Override - public final void writeChar(final char value) { - writeTo((byte) ((value & 0xFF00) >> 8), (byte) (value & 0xFF)); - } - - @Override - public final void writeShort(short value) { - writeTo((byte) (value >> 8), (byte) value); - } - - @Override - public final void writeInt(int value) { - writeTo((byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value); - } - - @Override - public final void writeLong(long value) { - writeTo((byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32), - (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value); - } - - @Override - public final void writeFloat(float value) { - writeInt(Float.floatToIntBits(value)); - } - - @Override - public final void writeDouble(double value) { - writeLong(Double.doubleToLongBits(value)); - } - - @Override - public final boolean needWriteClassName() { - return true; - } - - @Override - public final void writeClassName(String clazz) { - writeSmallString(clazz == null ? "" : clazz); - } - - @Override - public final int writeObjectB(Object obj) { - super.writeObjectB(obj); - writeSmallString(""); - writeShort(BsonReader.SIGN_OBJECTB); - return -1; - } - - @Override - public final void writeObjectE(Object obj) { - writeByte(BsonReader.SIGN_NONEXT); - writeShort(BsonReader.SIGN_OBJECTE); - } - - @Override - public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) { - writeByte(BsonReader.SIGN_HASNEXT); - writeSmallString(fieldName); - writeByte(BsonFactory.typeEnum(fieldType)); - } - - /** - * 对于类的字段名、枚举值这些长度一般不超过255且不会出现双字节字符的字符串采用writeSmallString处理, readSmallString用于读取 - * - * @param value String值 - */ - @Override - public final void writeSmallString(String value) { - if (value.isEmpty()) { - writeTo((byte) 0); - return; - } - char[] chars = Utility.charArray(value); - if (chars.length > 255) throw new ConvertException("'" + value + "' have very long length"); - byte[] bytes = new byte[chars.length + 1]; - bytes[0] = (byte) chars.length; - for (int i = 0; i < chars.length; i++) { - if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' have double-word"); - bytes[i + 1] = (byte) chars[i]; - } - writeTo(bytes); - } - - @Override - public final void writeString(String value) { - if (value == null) { - writeInt(Reader.SIGN_NULL); - return; - } else if (value.isEmpty()) { - writeInt(0); - return; - } - byte[] bytes = Utility.encodeUTF8(value); - writeInt(bytes.length); - writeTo(bytes); - } - - @Override - public final void writeWrapper(StringWrapper value) { - this.writeString(value == null ? null : value.getValue()); - } - - @Override - public final void writeNull() { - writeShort(Reader.SIGN_NULL); - } - - @Override - public final int writeArrayB(int size, Encodeable arrayEncoder, Encodeable componentEncoder, Object obj) { - writeInt(size); - if (componentEncoder != null && componentEncoder != ByteSimpledCoder.instance) { - writeByte(BsonFactory.typeEnum(componentEncoder.getType())); - } - return -1; - } - - @Override - public final void writeArrayMark() { - } - - @Override - public final void writeArrayE() { - } - - @Override - public int writeMapB(int size, Encodeable keyEncoder, Encodeable valueEncoder, Object obj) { - writeInt(size); - writeByte(BsonFactory.typeEnum(keyEncoder.getType())); - writeByte(BsonFactory.typeEnum(valueEncoder.getType())); - return -1; - } - - @Override - public final void writeMapMark() { - } - - @Override - public final void writeMapE() { - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.lang.reflect.Type; +import java.nio.ByteBuffer; +import java.util.function.Consumer; +import org.redkale.convert.*; +import org.redkale.convert.ext.ByteSimpledCoder; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class BsonWriter extends Writer implements ByteTuple { + + private static final int defaultSize = Integer.getInteger("redkale.convert.bson.writer.buffer.defsize", Integer.getInteger("redkale.convert.writer.buffer.defsize", 1024)); + + private byte[] content; + + protected int count; + + protected boolean tiny; + + public static ObjectPool createPool(int max) { + return ObjectPool.createSafePool(max, (Object... params) -> new BsonWriter(), null, (t) -> t.recycle()); + } + + @Override + public byte[] content() { + return content; + } + + @Override + public int offset() { + return 0; + } + + @Override + public int length() { + return count; + } + + /** + * 直接获取全部数据, 实际数据需要根据count长度来截取 + * + * @return byte[] + */ + public byte[] directBytes() { + return content; + } + + /** + * 将本对象的内容引用复制给array + * + * @param array ByteArray + */ + public void directTo(ByteArray array) { + array.directFrom(content, count); + } + + public void completed(ConvertBytesHandler handler, Consumer callback) { + handler.completed(content, 0, count, callback, this); + } + + public ByteArray toByteArray() { + return new ByteArray(this); + } + + public byte[] toArray() { + if (count == content.length) return content; + byte[] newdata = new byte[count]; + System.arraycopy(content, 0, newdata, 0, count); + return newdata; + } + + public ByteBuffer[] toBuffers() { + return new ByteBuffer[]{ByteBuffer.wrap(content, 0, count)}; + } + + protected BsonWriter(byte[] bs) { + this.content = bs == null ? new byte[0] : bs; + } + + public BsonWriter() { + this(defaultSize); + } + + public BsonWriter(int size) { + this.content = new byte[size > 128 ? size : 128]; + } + + public BsonWriter(ByteArray array) { + this.content = array.content(); + this.count = array.length(); + } + + @Override + public final boolean tiny() { + return tiny; + } + + public BsonWriter tiny(boolean tiny) { + this.tiny = tiny; + return this; + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + /** + * 扩充指定长度的缓冲区 + * + * @param len 扩容长度 + * + * @return 固定0 + */ + protected int expand(int len) { + int newcount = count + len; + if (newcount <= content.length) return 0; + byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)]; + System.arraycopy(content, 0, newdata, 0, count); + this.content = newdata; + return 0; + } + + public void writeTo(final byte ch) { + expand(1); + content[count++] = ch; + } + + public final void writeTo(final byte... chs) { + writeTo(chs, 0, chs.length); + } + + public void writeTo(final byte[] chs, final int start, final int len) { + expand(len); + System.arraycopy(chs, start, content, count, len); + count += len; + } + + @Override + protected boolean recycle() { + super.recycle(); + this.count = 0; + this.specify = null; + if (this.content != null && this.content.length > defaultSize) { + this.content = new byte[defaultSize]; + } + return true; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "[count=" + this.count + "]"; + } + + //------------------------------------------------------------------------ + public final int count() { + return this.count; + } + + @Override + public final void writeBoolean(boolean value) { + writeTo(value ? (byte) 1 : (byte) 0); + } + + @Override + public final void writeByte(byte value) { + writeTo(value); + } + + @Override + public final void writeByteArray(byte[] values) { + if (values == null) { + writeNull(); + return; + } + writeArrayB(values.length, null, null, values); + boolean flag = false; + for (byte v : values) { + if (flag) writeArrayMark(); + writeByte(v); + flag = true; + } + writeArrayE(); + } + + @Override + public final void writeChar(final char value) { + writeTo((byte) ((value & 0xFF00) >> 8), (byte) (value & 0xFF)); + } + + @Override + public final void writeShort(short value) { + writeTo((byte) (value >> 8), (byte) value); + } + + @Override + public final void writeInt(int value) { + writeTo((byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value); + } + + @Override + public final void writeLong(long value) { + writeTo((byte) (value >> 56), (byte) (value >> 48), (byte) (value >> 40), (byte) (value >> 32), + (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value); + } + + @Override + public final void writeFloat(float value) { + writeInt(Float.floatToIntBits(value)); + } + + @Override + public final void writeDouble(double value) { + writeLong(Double.doubleToLongBits(value)); + } + + @Override + public final boolean needWriteClassName() { + return true; + } + + @Override + public final void writeClassName(String clazz) { + writeSmallString(clazz == null ? "" : clazz); + } + + @Override + public final int writeObjectB(Object obj) { + super.writeObjectB(obj); + writeSmallString(""); + writeShort(BsonReader.SIGN_OBJECTB); + return -1; + } + + @Override + public final void writeObjectE(Object obj) { + writeByte(BsonReader.SIGN_NONEXT); + writeShort(BsonReader.SIGN_OBJECTE); + } + + @Override + public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) { + writeByte(BsonReader.SIGN_HASNEXT); + writeSmallString(fieldName); + writeByte(BsonFactory.typeEnum(fieldType)); + } + + /** + * 对于类的字段名、枚举值这些长度一般不超过255且不会出现双字节字符的字符串采用writeSmallString处理, readSmallString用于读取 + * + * @param value String值 + */ + @Override + public final void writeSmallString(String value) { + if (value.isEmpty()) { + writeTo((byte) 0); + return; + } + char[] chars = Utility.charArray(value); + if (chars.length > 255) throw new ConvertException("'" + value + "' have very long length"); + byte[] bytes = new byte[chars.length + 1]; + bytes[0] = (byte) chars.length; + for (int i = 0; i < chars.length; i++) { + if (chars[i] > Byte.MAX_VALUE) throw new ConvertException("'" + value + "' have double-word"); + bytes[i + 1] = (byte) chars[i]; + } + writeTo(bytes); + } + + @Override + public final void writeString(String value) { + if (value == null) { + writeInt(Reader.SIGN_NULL); + return; + } else if (value.isEmpty()) { + writeInt(0); + return; + } + byte[] bytes = Utility.encodeUTF8(value); + writeInt(bytes.length); + writeTo(bytes); + } + + @Override + public final void writeWrapper(StringWrapper value) { + this.writeString(value == null ? null : value.getValue()); + } + + @Override + public final void writeNull() { + writeShort(Reader.SIGN_NULL); + } + + @Override + public final int writeArrayB(int size, Encodeable arrayEncoder, Encodeable componentEncoder, Object obj) { + writeInt(size); + if (componentEncoder != null && componentEncoder != ByteSimpledCoder.instance) { + writeByte(BsonFactory.typeEnum(componentEncoder.getType())); + } + return -1; + } + + @Override + public final void writeArrayMark() { + } + + @Override + public final void writeArrayE() { + } + + @Override + public int writeMapB(int size, Encodeable keyEncoder, Encodeable valueEncoder, Object obj) { + writeInt(size); + writeByte(BsonFactory.typeEnum(keyEncoder.getType())); + writeByte(BsonFactory.typeEnum(valueEncoder.getType())); + return -1; + } + + @Override + public final void writeMapMark() { + } + + @Override + public final void writeMapE() { + } + +} diff --git a/src/org/redkale/convert/bson/SkipArrayDecoder.java b/src/main/java/org/redkale/convert/bson/SkipArrayDecoder.java similarity index 96% rename from src/org/redkale/convert/bson/SkipArrayDecoder.java rename to src/main/java/org/redkale/convert/bson/SkipArrayDecoder.java index 5efe46d80..1c65babaf 100644 --- a/src/org/redkale/convert/bson/SkipArrayDecoder.java +++ b/src/main/java/org/redkale/convert/bson/SkipArrayDecoder.java @@ -1,33 +1,33 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.lang.reflect.Type; -import org.redkale.convert.*; - -/** - * 数组的反序列化操作类
    - * 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组。
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 反解析的数组元素类型 - */ -public class SkipArrayDecoder extends ArrayDecoder { - - public SkipArrayDecoder(final ConvertFactory factory, final Type type) { - super(factory, type); - } - - @Override - protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { - if (typevals != null) return BsonFactory.typeEnum(typevals[0]); - return decoder; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.lang.reflect.Type; +import org.redkale.convert.*; + +/** + * 数组的反序列化操作类
    + * 对象数组的反序列化,不包含int[]、long[]这样的primitive class数组。
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 反解析的数组元素类型 + */ +public class SkipArrayDecoder extends ArrayDecoder { + + public SkipArrayDecoder(final ConvertFactory factory, final Type type) { + super(factory, type); + } + + @Override + protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { + if (typevals != null) return BsonFactory.typeEnum(typevals[0]); + return decoder; + } +} diff --git a/src/org/redkale/convert/bson/SkipCollectionDecoder.java b/src/main/java/org/redkale/convert/bson/SkipCollectionDecoder.java similarity index 96% rename from src/org/redkale/convert/bson/SkipCollectionDecoder.java rename to src/main/java/org/redkale/convert/bson/SkipCollectionDecoder.java index 0b74fe110..c16a562ff 100644 --- a/src/org/redkale/convert/bson/SkipCollectionDecoder.java +++ b/src/main/java/org/redkale/convert/bson/SkipCollectionDecoder.java @@ -1,32 +1,32 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.lang.reflect.Type; -import org.redkale.convert.*; - -/** - * Collection的反序列化操作类
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 反解析的集合元素类型 - */ -public class SkipCollectionDecoder extends CollectionDecoder { - - public SkipCollectionDecoder(final ConvertFactory factory, final Type type) { - super(factory, type); - } - - @Override - protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { - if (typevals != null) return BsonFactory.typeEnum(typevals[0]); - return decoder; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.lang.reflect.Type; +import org.redkale.convert.*; + +/** + * Collection的反序列化操作类
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 反解析的集合元素类型 + */ +public class SkipCollectionDecoder extends CollectionDecoder { + + public SkipCollectionDecoder(final ConvertFactory factory, final Type type) { + super(factory, type); + } + + @Override + protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { + if (typevals != null) return BsonFactory.typeEnum(typevals[0]); + return decoder; + } +} diff --git a/src/org/redkale/convert/bson/SkipMapDecoder.java b/src/main/java/org/redkale/convert/bson/SkipMapDecoder.java similarity index 96% rename from src/org/redkale/convert/bson/SkipMapDecoder.java rename to src/main/java/org/redkale/convert/bson/SkipMapDecoder.java index a0b352849..e92225d1c 100644 --- a/src/org/redkale/convert/bson/SkipMapDecoder.java +++ b/src/main/java/org/redkale/convert/bson/SkipMapDecoder.java @@ -1,38 +1,38 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.lang.reflect.Type; -import org.redkale.convert.*; - -/** - * Map的反序列化操作类
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Map key的数据类型 - * @param Map value的数据类型 - */ -public class SkipMapDecoder extends MapDecoder { - - public SkipMapDecoder(final ConvertFactory factory, final Type type) { - super(factory, type); - } - - @Override - protected Decodeable getKeyDecoder(Decodeable decoder, byte[] typevals) { - if (typevals != null) return BsonFactory.typeEnum(typevals[0]); - return decoder; - } - - @Override - protected Decodeable getValueDecoder(Decodeable decoder, byte[] typevals) { - if (typevals != null) return BsonFactory.typeEnum(typevals[1]); - return decoder; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.lang.reflect.Type; +import org.redkale.convert.*; + +/** + * Map的反序列化操作类
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Map key的数据类型 + * @param Map value的数据类型 + */ +public class SkipMapDecoder extends MapDecoder { + + public SkipMapDecoder(final ConvertFactory factory, final Type type) { + super(factory, type); + } + + @Override + protected Decodeable getKeyDecoder(Decodeable decoder, byte[] typevals) { + if (typevals != null) return BsonFactory.typeEnum(typevals[0]); + return decoder; + } + + @Override + protected Decodeable getValueDecoder(Decodeable decoder, byte[] typevals) { + if (typevals != null) return BsonFactory.typeEnum(typevals[1]); + return decoder; + } +} diff --git a/src/org/redkale/convert/bson/SkipStreamDecoder.java b/src/main/java/org/redkale/convert/bson/SkipStreamDecoder.java similarity index 96% rename from src/org/redkale/convert/bson/SkipStreamDecoder.java rename to src/main/java/org/redkale/convert/bson/SkipStreamDecoder.java index 8a42ace7f..08eb62d21 100644 --- a/src/org/redkale/convert/bson/SkipStreamDecoder.java +++ b/src/main/java/org/redkale/convert/bson/SkipStreamDecoder.java @@ -1,32 +1,32 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.bson; - -import java.lang.reflect.Type; -import org.redkale.convert.*; - -/** - * Stream的反序列化操作类
    - * 支持一定程度的泛型。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 反解析的集合元素类型 - */ -public class SkipStreamDecoder extends StreamDecoder { - - public SkipStreamDecoder(final ConvertFactory factory, final Type type) { - super(factory, type); - } - - @Override - protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { - if (typevals != null) return BsonFactory.typeEnum(typevals[0]); - return decoder; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.bson; + +import java.lang.reflect.Type; +import org.redkale.convert.*; + +/** + * Stream的反序列化操作类
    + * 支持一定程度的泛型。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 反解析的集合元素类型 + */ +public class SkipStreamDecoder extends StreamDecoder { + + public SkipStreamDecoder(final ConvertFactory factory, final Type type) { + super(factory, type); + } + + @Override + protected Decodeable getComponentDecoder(Decodeable decoder, byte[] typevals) { + if (typevals != null) return BsonFactory.typeEnum(typevals[0]); + return decoder; + } +} diff --git a/src/org/redkale/convert/bson/package-info.java b/src/main/java/org/redkale/convert/bson/package-info.java similarity index 95% rename from src/org/redkale/convert/bson/package-info.java rename to src/main/java/org/redkale/convert/bson/package-info.java index 6518406c1..fa92a2f44 100644 --- a/src/org/redkale/convert/bson/package-info.java +++ b/src/main/java/org/redkale/convert/bson/package-info.java @@ -1,4 +1,4 @@ -/** - * 提供BSON的序列化和反解析功能 - */ -package org.redkale.convert.bson; +/** + * 提供BSON的序列化和反解析功能 + */ +package org.redkale.convert.bson; diff --git a/src/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java b/src/main/java/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java index 29b879e6e..2445b41dc 100644 --- a/src/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/AtomicIntegerSimpledCoder.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.util.concurrent.atomic.AtomicInteger; -import org.redkale.convert.*; - -/** - * AtomicInteger 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class AtomicIntegerSimpledCoder extends SimpledCoder { - - public static final AtomicIntegerSimpledCoder instance = new AtomicIntegerSimpledCoder(); - - @Override - public void convertTo(W out, AtomicInteger value) { - out.writeInt(value == null ? 0 : value.get()); - } - - @Override - public AtomicInteger convertFrom(R in) { - return new AtomicInteger(in.readInt()); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.util.concurrent.atomic.AtomicInteger; +import org.redkale.convert.*; + +/** + * AtomicInteger 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class AtomicIntegerSimpledCoder extends SimpledCoder { + + public static final AtomicIntegerSimpledCoder instance = new AtomicIntegerSimpledCoder(); + + @Override + public void convertTo(W out, AtomicInteger value) { + out.writeInt(value == null ? 0 : value.get()); + } + + @Override + public AtomicInteger convertFrom(R in) { + return new AtomicInteger(in.readInt()); + } + +} diff --git a/src/org/redkale/convert/ext/AtomicLongSimpledCoder.java b/src/main/java/org/redkale/convert/ext/AtomicLongSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/AtomicLongSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/AtomicLongSimpledCoder.java index 914b81262..503cdfea0 100644 --- a/src/org/redkale/convert/ext/AtomicLongSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/AtomicLongSimpledCoder.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.util.concurrent.atomic.AtomicLong; -import org.redkale.convert.*; - -/** - * AtomicLong 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class AtomicLongSimpledCoder extends SimpledCoder { - - public static final AtomicLongSimpledCoder instance = new AtomicLongSimpledCoder(); - - @Override - public void convertTo(W out, AtomicLong value) { - out.writeLong(value == null ? 0 : value.get()); - } - - @Override - public AtomicLong convertFrom(R in) { - return new AtomicLong(in.readLong()); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.util.concurrent.atomic.AtomicLong; +import org.redkale.convert.*; + +/** + * AtomicLong 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class AtomicLongSimpledCoder extends SimpledCoder { + + public static final AtomicLongSimpledCoder instance = new AtomicLongSimpledCoder(); + + @Override + public void convertTo(W out, AtomicLong value) { + out.writeLong(value == null ? 0 : value.get()); + } + + @Override + public AtomicLong convertFrom(R in) { + return new AtomicLong(in.readLong()); + } + +} diff --git a/src/org/redkale/convert/ext/BigDecimalSimpledCoder.java b/src/main/java/org/redkale/convert/ext/BigDecimalSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/BigDecimalSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/BigDecimalSimpledCoder.java index 07aa95aa7..32b6aaaf8 100644 --- a/src/org/redkale/convert/ext/BigDecimalSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/BigDecimalSimpledCoder.java @@ -1,44 +1,44 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; -import org.redkale.convert.Reader; -import java.math.BigDecimal; -import org.redkale.util.Utility; - -/** - * BigDecimal 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class BigDecimalSimpledCoder extends SimpledCoder { - - public static final BigDecimalSimpledCoder instance = new BigDecimalSimpledCoder(); - - @Override - public void convertTo(W out, BigDecimal value) { - if (value == null) { - out.writeNull(); - return; - } - out.writeSmallString(value.toString()); - } - - @Override - public BigDecimal convertFrom(R in) { - String value = in.readSmallString(); - if (value == null) return null; - return new BigDecimal(Utility.charArray(value)); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; +import org.redkale.convert.Reader; +import java.math.BigDecimal; +import org.redkale.util.Utility; + +/** + * BigDecimal 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class BigDecimalSimpledCoder extends SimpledCoder { + + public static final BigDecimalSimpledCoder instance = new BigDecimalSimpledCoder(); + + @Override + public void convertTo(W out, BigDecimal value) { + if (value == null) { + out.writeNull(); + return; + } + out.writeSmallString(value.toString()); + } + + @Override + public BigDecimal convertFrom(R in) { + String value = in.readSmallString(); + if (value == null) return null; + return new BigDecimal(Utility.charArray(value)); + } + +} diff --git a/src/org/redkale/convert/ext/BigIntegerSimpledCoder.java b/src/main/java/org/redkale/convert/ext/BigIntegerSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/BigIntegerSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/BigIntegerSimpledCoder.java index a6ea50d1e..7795e3f74 100644 --- a/src/org/redkale/convert/ext/BigIntegerSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/BigIntegerSimpledCoder.java @@ -1,70 +1,70 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; -import org.redkale.convert.Reader; -import java.math.BigInteger; - -/** - * BigInteger 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class BigIntegerSimpledCoder extends SimpledCoder { - - public static final BigIntegerSimpledCoder instance = new BigIntegerSimpledCoder(); - - @Override - @SuppressWarnings("unchecked") - public void convertTo(W out, BigInteger value) { - if (value == null) { - out.writeNull(); - return; - } - ByteArraySimpledCoder.instance.convertTo(out, value.toByteArray()); - } - - @Override - @SuppressWarnings("unchecked") - public BigInteger convertFrom(R in) { - byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); - return bytes == null ? null : new BigInteger(bytes); - } - - /** - * BigInteger 的JsonSimpledCoder实现 - * - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ - public static class BigIntegerJsonSimpledCoder extends SimpledCoder { - - public static final BigIntegerJsonSimpledCoder instance = new BigIntegerJsonSimpledCoder(); - - @Override - public void convertTo(final Writer out, final BigInteger value) { - if (value == null) { - out.writeNull(); - } else { - out.writeString(value.toString()); - } - } - - @Override - public BigInteger convertFrom(Reader in) { - final String str = in.readString(); - if (str == null) return null; - return new BigInteger(str); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; +import org.redkale.convert.Reader; +import java.math.BigInteger; + +/** + * BigInteger 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class BigIntegerSimpledCoder extends SimpledCoder { + + public static final BigIntegerSimpledCoder instance = new BigIntegerSimpledCoder(); + + @Override + @SuppressWarnings("unchecked") + public void convertTo(W out, BigInteger value) { + if (value == null) { + out.writeNull(); + return; + } + ByteArraySimpledCoder.instance.convertTo(out, value.toByteArray()); + } + + @Override + @SuppressWarnings("unchecked") + public BigInteger convertFrom(R in) { + byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); + return bytes == null ? null : new BigInteger(bytes); + } + + /** + * BigInteger 的JsonSimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + public static class BigIntegerJsonSimpledCoder extends SimpledCoder { + + public static final BigIntegerJsonSimpledCoder instance = new BigIntegerJsonSimpledCoder(); + + @Override + public void convertTo(final Writer out, final BigInteger value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(value.toString()); + } + } + + @Override + public BigInteger convertFrom(Reader in) { + final String str = in.readString(); + if (str == null) return null; + return new BigInteger(str); + } + } +} diff --git a/src/org/redkale/convert/ext/BoolArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/BoolArraySimpledCoder.java similarity index 97% rename from src/org/redkale/convert/ext/BoolArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/BoolArraySimpledCoder.java index bf0ad401d..856acc260 100644 --- a/src/org/redkale/convert/ext/BoolArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/BoolArraySimpledCoder.java @@ -1,78 +1,78 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * boolean[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class BoolArraySimpledCoder extends SimpledCoder { - - public static final BoolArraySimpledCoder instance = new BoolArraySimpledCoder(); - - @Override - public void convertTo(W out, boolean[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, BoolSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (boolean v : values) { - if (flag) out.writeArrayMark(); - out.writeBoolean(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public boolean[] convertFrom(R in) { - int len = in.readArrayB(null, null, BoolSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, BoolSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - boolean[] data = new boolean[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - boolean[] newdata = new boolean[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readBoolean(); - } - in.readArrayE(); - boolean[] newdata = new boolean[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - boolean[] values = new boolean[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readBoolean(); - } - in.readArrayE(); - return values; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * boolean[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class BoolArraySimpledCoder extends SimpledCoder { + + public static final BoolArraySimpledCoder instance = new BoolArraySimpledCoder(); + + @Override + public void convertTo(W out, boolean[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, BoolSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (boolean v : values) { + if (flag) out.writeArrayMark(); + out.writeBoolean(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public boolean[] convertFrom(R in) { + int len = in.readArrayB(null, null, BoolSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, BoolSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + boolean[] data = new boolean[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + boolean[] newdata = new boolean[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readBoolean(); + } + in.readArrayE(); + boolean[] newdata = new boolean[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + boolean[] values = new boolean[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readBoolean(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/src/org/redkale/convert/ext/BoolSimpledCoder.java b/src/main/java/org/redkale/convert/ext/BoolSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/BoolSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/BoolSimpledCoder.java index fe61d083f..a675fffa0 100644 --- a/src/org/redkale/convert/ext/BoolSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/BoolSimpledCoder.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * boolean 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class BoolSimpledCoder extends SimpledCoder { - - public static final BoolSimpledCoder instance = new BoolSimpledCoder(); - - @Override - public void convertTo(W out, Boolean value) { - out.writeBoolean(value); - } - - @Override - public Boolean convertFrom(R in) { - return in.readBoolean(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * boolean 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class BoolSimpledCoder extends SimpledCoder { + + public static final BoolSimpledCoder instance = new BoolSimpledCoder(); + + @Override + public void convertTo(W out, Boolean value) { + out.writeBoolean(value); + } + + @Override + public Boolean convertFrom(R in) { + return in.readBoolean(); + } + +} diff --git a/src/org/redkale/convert/ext/ByteArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/ByteArraySimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/ByteArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/ByteArraySimpledCoder.java index 088c0d273..ddcfa9046 100644 --- a/src/org/redkale/convert/ext/ByteArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/ByteArraySimpledCoder.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * byte[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class ByteArraySimpledCoder extends SimpledCoder { - - public static final ByteArraySimpledCoder instance = new ByteArraySimpledCoder(); - - @Override - public void convertTo(W out, byte[] values) { - out.writeByteArray(values); - } - - @Override - public byte[] convertFrom(R in) { - return in.readByteArray(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * byte[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class ByteArraySimpledCoder extends SimpledCoder { + + public static final ByteArraySimpledCoder instance = new ByteArraySimpledCoder(); + + @Override + public void convertTo(W out, byte[] values) { + out.writeByteArray(values); + } + + @Override + public byte[] convertFrom(R in) { + return in.readByteArray(); + } + +} diff --git a/src/org/redkale/convert/ext/ByteBufferSimpledCoder.java b/src/main/java/org/redkale/convert/ext/ByteBufferSimpledCoder.java similarity index 97% rename from src/org/redkale/convert/ext/ByteBufferSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/ByteBufferSimpledCoder.java index 0d7583664..ab3812dd6 100644 --- a/src/org/redkale/convert/ext/ByteBufferSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/ByteBufferSimpledCoder.java @@ -1,77 +1,77 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.nio.ByteBuffer; -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * ByteBuffer 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class ByteBufferSimpledCoder extends SimpledCoder { - - public static final ByteBufferSimpledCoder instance = new ByteBufferSimpledCoder(); - - @Override - public void convertTo(W out, ByteBuffer value) { - if (value == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(value.remaining(), this, ByteSimpledCoder.instance, value) < 0) { - boolean flag = false; - for (byte v : value.array()) { - if (flag) out.writeArrayMark(); - out.writeByte(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public ByteBuffer convertFrom(R in) { - int len = in.readArrayB(null, null, ByteSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, ByteSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - byte[] data = new byte[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - byte[] newdata = new byte[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readByte(); - } - in.readArrayE(); - return ByteBuffer.wrap(data, 0, size); - } else { - byte[] values = new byte[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readByte(); - } - in.readArrayE(); - return ByteBuffer.wrap(values); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.nio.ByteBuffer; +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * ByteBuffer 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class ByteBufferSimpledCoder extends SimpledCoder { + + public static final ByteBufferSimpledCoder instance = new ByteBufferSimpledCoder(); + + @Override + public void convertTo(W out, ByteBuffer value) { + if (value == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(value.remaining(), this, ByteSimpledCoder.instance, value) < 0) { + boolean flag = false; + for (byte v : value.array()) { + if (flag) out.writeArrayMark(); + out.writeByte(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public ByteBuffer convertFrom(R in) { + int len = in.readArrayB(null, null, ByteSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, ByteSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + byte[] data = new byte[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readByte(); + } + in.readArrayE(); + return ByteBuffer.wrap(data, 0, size); + } else { + byte[] values = new byte[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readByte(); + } + in.readArrayE(); + return ByteBuffer.wrap(values); + } + } + +} diff --git a/src/org/redkale/convert/ext/ByteSimpledCoder.java b/src/main/java/org/redkale/convert/ext/ByteSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/ByteSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/ByteSimpledCoder.java index ff630f072..bee075834 100644 --- a/src/org/redkale/convert/ext/ByteSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/ByteSimpledCoder.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * byte 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class ByteSimpledCoder extends SimpledCoder { - - public static final ByteSimpledCoder instance = new ByteSimpledCoder(); - - @Override - public void convertTo(W out, Byte value) { - out.writeByte(value); - } - - @Override - public Byte convertFrom(R in) { - return in.readByte(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * byte 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class ByteSimpledCoder extends SimpledCoder { + + public static final ByteSimpledCoder instance = new ByteSimpledCoder(); + + @Override + public void convertTo(W out, Byte value) { + out.writeByte(value); + } + + @Override + public Byte convertFrom(R in) { + return in.readByte(); + } + +} diff --git a/src/org/redkale/convert/ext/CharArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/CharArraySimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/CharArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/CharArraySimpledCoder.java index 1199ccde8..c60039d16 100644 --- a/src/org/redkale/convert/ext/CharArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/CharArraySimpledCoder.java @@ -1,78 +1,78 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * char[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class CharArraySimpledCoder extends SimpledCoder { - - public static final CharArraySimpledCoder instance = new CharArraySimpledCoder(); - - @Override - public void convertTo(W out, char[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, CharSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (char v : values) { - if (flag) out.writeArrayMark(); - out.writeChar(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public char[] convertFrom(R in) { - int len = in.readArrayB(null, null, CharSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, CharSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - char[] data = new char[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - char[] newdata = new char[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readChar(); - } - in.readArrayE(); - char[] newdata = new char[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - char[] values = new char[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readChar(); - } - in.readArrayE(); - return values; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * char[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class CharArraySimpledCoder extends SimpledCoder { + + public static final CharArraySimpledCoder instance = new CharArraySimpledCoder(); + + @Override + public void convertTo(W out, char[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, CharSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (char v : values) { + if (flag) out.writeArrayMark(); + out.writeChar(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public char[] convertFrom(R in) { + int len = in.readArrayB(null, null, CharSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, CharSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + char[] data = new char[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + char[] newdata = new char[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readChar(); + } + in.readArrayE(); + char[] newdata = new char[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + char[] values = new char[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readChar(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/src/org/redkale/convert/ext/CharSequenceSimpledCoder.java b/src/main/java/org/redkale/convert/ext/CharSequenceSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/CharSequenceSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/CharSequenceSimpledCoder.java index 2250ee0fa..a74a6f7bd 100644 --- a/src/org/redkale/convert/ext/CharSequenceSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/CharSequenceSimpledCoder.java @@ -1,49 +1,49 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.*; - -/** - * CharSequence 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class CharSequenceSimpledCoder extends SimpledCoder { - - public static final CharSequenceSimpledCoder instance = new CharSequenceSimpledCoder(); - - @Override - public void convertTo(W out, CharSequence value) { - out.writeString(value == null ? null : value.toString()); - } - - @Override - public CharSequence convertFrom(R in) { - return in.readString(); - } - - public static class StringBuilderSimpledCoder extends SimpledCoder { - - public static final StringBuilderSimpledCoder instance = new StringBuilderSimpledCoder(); - - @Override - public void convertTo(W out, StringBuilder value) { - out.writeString(value == null ? null : value.toString()); - } - - @Override - public StringBuilder convertFrom(R in) { - String rs = in.readString(); - return rs == null ? null : new StringBuilder(rs); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.*; + +/** + * CharSequence 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class CharSequenceSimpledCoder extends SimpledCoder { + + public static final CharSequenceSimpledCoder instance = new CharSequenceSimpledCoder(); + + @Override + public void convertTo(W out, CharSequence value) { + out.writeString(value == null ? null : value.toString()); + } + + @Override + public CharSequence convertFrom(R in) { + return in.readString(); + } + + public static class StringBuilderSimpledCoder extends SimpledCoder { + + public static final StringBuilderSimpledCoder instance = new StringBuilderSimpledCoder(); + + @Override + public void convertTo(W out, StringBuilder value) { + out.writeString(value == null ? null : value.toString()); + } + + @Override + public StringBuilder convertFrom(R in) { + String rs = in.readString(); + return rs == null ? null : new StringBuilder(rs); + } + } +} diff --git a/src/org/redkale/convert/ext/CharSimpledCoder.java b/src/main/java/org/redkale/convert/ext/CharSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/CharSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/CharSimpledCoder.java index 8c25c5a35..78badf4d8 100644 --- a/src/org/redkale/convert/ext/CharSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/CharSimpledCoder.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * char 的SimpledCoder实现 - * - *

    详情见: https://redkale.org - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class CharSimpledCoder extends SimpledCoder { - - public static final CharSimpledCoder instance = new CharSimpledCoder(); - - @Override - public void convertTo(W out, Character value) { - out.writeChar(value); - } - - @Override - public Character convertFrom(R in) { - return in.readChar(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * char 的SimpledCoder实现 + * + *

    详情见: https://redkale.org + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class CharSimpledCoder extends SimpledCoder { + + public static final CharSimpledCoder instance = new CharSimpledCoder(); + + @Override + public void convertTo(W out, Character value) { + out.writeChar(value); + } + + @Override + public Character convertFrom(R in) { + return in.readChar(); + } + +} diff --git a/src/org/redkale/convert/ext/CompletionHandlerSimpledCoder.java b/src/main/java/org/redkale/convert/ext/CompletionHandlerSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/CompletionHandlerSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/CompletionHandlerSimpledCoder.java index 5ea8116fc..a72831989 100644 --- a/src/org/redkale/convert/ext/CompletionHandlerSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/CompletionHandlerSimpledCoder.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.nio.channels.*; -import org.redkale.convert.*; - -/** - * java.nio.channels.CompletionHandler 的SimpledCoder实现, 只输出null - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class CompletionHandlerSimpledCoder extends SimpledCoder { - - public static final CompletionHandlerSimpledCoder instance = new CompletionHandlerSimpledCoder(); - - @Override - public void convertTo(W out, CompletionHandler value) { - out.writeObjectNull(CompletionHandler.class); - } - - @Override - public CompletionHandler convertFrom(R in) { - in.readObjectB(CompletionHandler.class); - return null; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.nio.channels.*; +import org.redkale.convert.*; + +/** + * java.nio.channels.CompletionHandler 的SimpledCoder实现, 只输出null + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class CompletionHandlerSimpledCoder extends SimpledCoder { + + public static final CompletionHandlerSimpledCoder instance = new CompletionHandlerSimpledCoder(); + + @Override + public void convertTo(W out, CompletionHandler value) { + out.writeObjectNull(CompletionHandler.class); + } + + @Override + public CompletionHandler convertFrom(R in) { + in.readObjectB(CompletionHandler.class); + return null; + } + +} diff --git a/src/org/redkale/convert/ext/DLongSimpledCoder.java b/src/main/java/org/redkale/convert/ext/DLongSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/DLongSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/DLongSimpledCoder.java index cbf9a8eb3..0daf717a4 100644 --- a/src/org/redkale/convert/ext/DLongSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/DLongSimpledCoder.java @@ -1,73 +1,73 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.Writer; -import org.redkale.convert.SimpledCoder; -import org.redkale.util.*; - -/** - * Dlong 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class DLongSimpledCoder extends SimpledCoder { - - private static final ByteArraySimpledCoder bsSimpledCoder = ByteArraySimpledCoder.instance; - - public static final DLongSimpledCoder instance = new DLongSimpledCoder(); - - @Override - @SuppressWarnings("unchecked") - public void convertTo(final W out, final DLong value) { - if (value == null) { - out.writeNull(); - } else { - bsSimpledCoder.convertTo(out, value.directBytes()); - } - } - - @Override - @SuppressWarnings("unchecked") - public DLong convertFrom(R in) { - byte[] bs = bsSimpledCoder.convertFrom(in); - if (bs == null) return null; - return DLong.create(bs); - } - - /** - * DLong 的JsonSimpledCoder实现 - * - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ - public static class DLongJsonSimpledCoder extends SimpledCoder { - - public static final DLongJsonSimpledCoder instance = new DLongJsonSimpledCoder(); - - @Override - public void convertTo(final Writer out, final DLong value) { - if (value == null) { - out.writeNull(); - } else { - out.writeSmallString(value.toString()); - } - } - - @Override - public DLong convertFrom(Reader in) { - final String str = in.readSmallString(); - if (str == null) return null; - return DLong.create(Utility.hexToBin(str)); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.Writer; +import org.redkale.convert.SimpledCoder; +import org.redkale.util.*; + +/** + * Dlong 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class DLongSimpledCoder extends SimpledCoder { + + private static final ByteArraySimpledCoder bsSimpledCoder = ByteArraySimpledCoder.instance; + + public static final DLongSimpledCoder instance = new DLongSimpledCoder(); + + @Override + @SuppressWarnings("unchecked") + public void convertTo(final W out, final DLong value) { + if (value == null) { + out.writeNull(); + } else { + bsSimpledCoder.convertTo(out, value.directBytes()); + } + } + + @Override + @SuppressWarnings("unchecked") + public DLong convertFrom(R in) { + byte[] bs = bsSimpledCoder.convertFrom(in); + if (bs == null) return null; + return DLong.create(bs); + } + + /** + * DLong 的JsonSimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + public static class DLongJsonSimpledCoder extends SimpledCoder { + + public static final DLongJsonSimpledCoder instance = new DLongJsonSimpledCoder(); + + @Override + public void convertTo(final Writer out, final DLong value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.toString()); + } + } + + @Override + public DLong convertFrom(Reader in) { + final String str = in.readSmallString(); + if (str == null) return null; + return DLong.create(Utility.hexToBin(str)); + } + } +} diff --git a/src/org/redkale/convert/ext/DateSimpledCoder.java b/src/main/java/org/redkale/convert/ext/DateSimpledCoder.java similarity index 93% rename from src/org/redkale/convert/ext/DateSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/DateSimpledCoder.java index 182c0b2fd..68bc48167 100644 --- a/src/org/redkale/convert/ext/DateSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/DateSimpledCoder.java @@ -1,38 +1,38 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; -import java.util.Date; - -/** - * Date 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class DateSimpledCoder extends SimpledCoder { - - public static final DateSimpledCoder instance = new DateSimpledCoder(); - - @Override - public void convertTo(W out, Date value) { - out.writeLong(value == null ? 0L : value.getTime()); - } - - @Override - public Date convertFrom(R in) { - long t = in.readLong(); - return t == 0 ? null : new Date(t); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; +import java.util.Date; + +/** + * java.util.Date 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class DateSimpledCoder extends SimpledCoder { + + public static final DateSimpledCoder instance = new DateSimpledCoder(); + + @Override + public void convertTo(W out, Date value) { + out.writeLong(value == null ? 0L : value.getTime()); + } + + @Override + public Date convertFrom(R in) { + long t = in.readLong(); + return t == 0 ? null : new Date(t); + } + +} diff --git a/src/org/redkale/convert/ext/DoubleArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/DoubleArraySimpledCoder.java similarity index 97% rename from src/org/redkale/convert/ext/DoubleArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/DoubleArraySimpledCoder.java index c5db2c821..a9aa2b083 100644 --- a/src/org/redkale/convert/ext/DoubleArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/DoubleArraySimpledCoder.java @@ -1,101 +1,101 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.util.stream.DoubleStream; -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * double[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class DoubleArraySimpledCoder extends SimpledCoder { - - public static final DoubleArraySimpledCoder instance = new DoubleArraySimpledCoder(); - - @Override - public void convertTo(W out, double[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, DoubleSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (double v : values) { - if (flag) out.writeArrayMark(); - out.writeDouble(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public double[] convertFrom(R in) { - int len = in.readArrayB(null, null, DoubleSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, DoubleSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - double[] data = new double[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - double[] newdata = new double[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readDouble(); - } - in.readArrayE(); - double[] newdata = new double[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - double[] values = new double[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readDouble(); - } - in.readArrayE(); - return values; - } - } - - public final static class DoubleStreamSimpledCoder extends SimpledCoder { - - public static final DoubleStreamSimpledCoder instance = new DoubleStreamSimpledCoder(); - - @Override - @SuppressWarnings("unchecked") - public void convertTo(W out, DoubleStream values) { - if (values == null) { - out.writeNull(); - return; - } - DoubleArraySimpledCoder.instance.convertTo(out, values.toArray()); - } - - @Override - @SuppressWarnings("unchecked") - public DoubleStream convertFrom(R in) { - double[] value = DoubleArraySimpledCoder.instance.convertFrom(in); - return value == null ? null : DoubleStream.of(value); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.util.stream.DoubleStream; +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * double[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class DoubleArraySimpledCoder extends SimpledCoder { + + public static final DoubleArraySimpledCoder instance = new DoubleArraySimpledCoder(); + + @Override + public void convertTo(W out, double[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, DoubleSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (double v : values) { + if (flag) out.writeArrayMark(); + out.writeDouble(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public double[] convertFrom(R in) { + int len = in.readArrayB(null, null, DoubleSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, DoubleSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + double[] data = new double[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + double[] newdata = new double[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readDouble(); + } + in.readArrayE(); + double[] newdata = new double[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + double[] values = new double[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readDouble(); + } + in.readArrayE(); + return values; + } + } + + public final static class DoubleStreamSimpledCoder extends SimpledCoder { + + public static final DoubleStreamSimpledCoder instance = new DoubleStreamSimpledCoder(); + + @Override + @SuppressWarnings("unchecked") + public void convertTo(W out, DoubleStream values) { + if (values == null) { + out.writeNull(); + return; + } + DoubleArraySimpledCoder.instance.convertTo(out, values.toArray()); + } + + @Override + @SuppressWarnings("unchecked") + public DoubleStream convertFrom(R in) { + double[] value = DoubleArraySimpledCoder.instance.convertFrom(in); + return value == null ? null : DoubleStream.of(value); + } + + } +} diff --git a/src/org/redkale/convert/ext/DoubleSimpledCoder.java b/src/main/java/org/redkale/convert/ext/DoubleSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/DoubleSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/DoubleSimpledCoder.java index 0b0a6fc6a..3a04ef4bf 100644 --- a/src/org/redkale/convert/ext/DoubleSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/DoubleSimpledCoder.java @@ -1,34 +1,34 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * double 的SimpledCoder实现 - * - *

    详情见: https://redkale.org - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class DoubleSimpledCoder extends SimpledCoder { - - public static final DoubleSimpledCoder instance = new DoubleSimpledCoder(); - - @Override - public void convertTo(W out, Double value) { - out.writeDouble(value); - } - - @Override - public Double convertFrom(R in) { - return in.readDouble(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * double 的SimpledCoder实现 + * + *

    详情见: https://redkale.org + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class DoubleSimpledCoder extends SimpledCoder { + + public static final DoubleSimpledCoder instance = new DoubleSimpledCoder(); + + @Override + public void convertTo(W out, Double value) { + out.writeDouble(value); + } + + @Override + public Double convertFrom(R in) { + return in.readDouble(); + } + +} diff --git a/src/org/redkale/convert/ext/DurationSimpledCoder.java b/src/main/java/org/redkale/convert/ext/DurationSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/DurationSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/DurationSimpledCoder.java index 631d41965..d8ed4b2bf 100644 --- a/src/org/redkale/convert/ext/DurationSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/DurationSimpledCoder.java @@ -1,41 +1,41 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.time.Duration; -import org.redkale.convert.*; - -/** - * Duration 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class DurationSimpledCoder extends SimpledCoder { - - public static final DurationSimpledCoder instance = new DurationSimpledCoder(); - - @Override - public void convertTo(W out, Duration value) { - if (value == null) { - out.writeNull(); - } else { - out.writeLong(value.toNanos()); - } - } - - @Override - public Duration convertFrom(R in) { - String value = in.readSmallString(); - if (value == null) return null; - return Duration.ofNanos(Long.parseLong(value)); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.time.Duration; +import org.redkale.convert.*; + +/** + * Duration 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class DurationSimpledCoder extends SimpledCoder { + + public static final DurationSimpledCoder instance = new DurationSimpledCoder(); + + @Override + public void convertTo(W out, Duration value) { + if (value == null) { + out.writeNull(); + } else { + out.writeLong(value.toNanos()); + } + } + + @Override + public Duration convertFrom(R in) { + String value = in.readSmallString(); + if (value == null) return null; + return Duration.ofNanos(Long.parseLong(value)); + } + +} diff --git a/src/org/redkale/convert/ext/EnumSimpledCoder.java b/src/main/java/org/redkale/convert/ext/EnumSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/EnumSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/EnumSimpledCoder.java index 3c8090bb5..a4b8a2200 100644 --- a/src/org/redkale/convert/ext/EnumSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/EnumSimpledCoder.java @@ -1,50 +1,50 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * 枚举 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - * @param Enum的子类 - */ -public final class EnumSimpledCoder extends SimpledCoder { - - public EnumSimpledCoder(Class type) { - this.type = type; - } - - @Override - public void convertTo(final W out, final E value) { - if (value == null) { - out.writeNull(); - } else { - out.writeSmallString(value.toString()); - } - } - - @Override - @SuppressWarnings("unchecked") - public E convertFrom(final R in) { - String value = in.readSmallString(); - if (value == null) return null; - return (E) Enum.valueOf((Class) type, value); - } - - @Override - public Class getType() { - return (Class) type; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * 枚举 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + * @param Enum的子类 + */ +public final class EnumSimpledCoder extends SimpledCoder { + + public EnumSimpledCoder(Class type) { + this.type = type; + } + + @Override + public void convertTo(final W out, final E value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.toString()); + } + } + + @Override + @SuppressWarnings("unchecked") + public E convertFrom(final R in) { + String value = in.readSmallString(); + if (value == null) return null; + return (E) Enum.valueOf((Class) type, value); + } + + @Override + public Class getType() { + return (Class) type; + } +} diff --git a/src/org/redkale/convert/ext/FileSimpledCoder.java b/src/main/java/org/redkale/convert/ext/FileSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/FileSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/FileSimpledCoder.java index b5885b2e7..a7ae3f9b8 100644 --- a/src/org/redkale/convert/ext/FileSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/FileSimpledCoder.java @@ -1,41 +1,41 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.io.File; -import org.redkale.convert.*; - -/** - * 文件 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class FileSimpledCoder extends SimpledCoder { - - public static final FileSimpledCoder instance = new FileSimpledCoder(); - - @Override - public void convertTo(W out, File value) { - if (value == null) { - out.writeNull(); - } else { - out.writeString(value.getPath()); - } - } - - @Override - public File convertFrom(R in) { - String value = in.readString(); - if (value == null) return null; - return new File(value); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.io.File; +import org.redkale.convert.*; + +/** + * 文件 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class FileSimpledCoder extends SimpledCoder { + + public static final FileSimpledCoder instance = new FileSimpledCoder(); + + @Override + public void convertTo(W out, File value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(value.getPath()); + } + } + + @Override + public File convertFrom(R in) { + String value = in.readString(); + if (value == null) return null; + return new File(value); + } + +} diff --git a/src/org/redkale/convert/ext/FloatArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/FloatArraySimpledCoder.java similarity index 97% rename from src/org/redkale/convert/ext/FloatArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/FloatArraySimpledCoder.java index b7892a0ae..f8d1e46ab 100644 --- a/src/org/redkale/convert/ext/FloatArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/FloatArraySimpledCoder.java @@ -1,78 +1,78 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * float[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class FloatArraySimpledCoder extends SimpledCoder { - - public static final FloatArraySimpledCoder instance = new FloatArraySimpledCoder(); - - @Override - public void convertTo(W out, float[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, FloatSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (float v : values) { - if (flag) out.writeArrayMark(); - out.writeFloat(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public float[] convertFrom(R in) { - int len = in.readArrayB(null, null, FloatSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, FloatSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - float[] data = new float[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - float[] newdata = new float[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readFloat(); - } - in.readArrayE(); - float[] newdata = new float[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - float[] values = new float[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readFloat(); - } - in.readArrayE(); - return values; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * float[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class FloatArraySimpledCoder extends SimpledCoder { + + public static final FloatArraySimpledCoder instance = new FloatArraySimpledCoder(); + + @Override + public void convertTo(W out, float[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, FloatSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (float v : values) { + if (flag) out.writeArrayMark(); + out.writeFloat(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public float[] convertFrom(R in) { + int len = in.readArrayB(null, null, FloatSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, FloatSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + float[] data = new float[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + float[] newdata = new float[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readFloat(); + } + in.readArrayE(); + float[] newdata = new float[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + float[] values = new float[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readFloat(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/src/org/redkale/convert/ext/FloatSimpledCoder.java b/src/main/java/org/redkale/convert/ext/FloatSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/FloatSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/FloatSimpledCoder.java index 9169a2258..682e6f816 100644 --- a/src/org/redkale/convert/ext/FloatSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/FloatSimpledCoder.java @@ -1,34 +1,34 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * float 的SimpledCoder实现 - * - *

    详情见: https://redkale.org - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class FloatSimpledCoder extends SimpledCoder { - - public static final FloatSimpledCoder instance = new FloatSimpledCoder(); - - @Override - public void convertTo(W out, Float value) { - out.writeFloat(value); - } - - @Override - public Float convertFrom(R in) { - return in.readFloat(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * float 的SimpledCoder实现 + * + *

    详情见: https://redkale.org + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class FloatSimpledCoder extends SimpledCoder { + + public static final FloatSimpledCoder instance = new FloatSimpledCoder(); + + @Override + public void convertTo(W out, Float value) { + out.writeFloat(value); + } + + @Override + public Float convertFrom(R in) { + return in.readFloat(); + } + +} diff --git a/src/org/redkale/convert/ext/InetAddressSimpledCoder.java b/src/main/java/org/redkale/convert/ext/InetAddressSimpledCoder.java similarity index 93% rename from src/org/redkale/convert/ext/InetAddressSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/InetAddressSimpledCoder.java index fc9cc2e3e..a010d720f 100644 --- a/src/org/redkale/convert/ext/InetAddressSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/InetAddressSimpledCoder.java @@ -1,147 +1,148 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; -import org.redkale.convert.Reader; -import java.net.*; - -/** - * InetAddress 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -@SuppressWarnings("unchecked") -public final class InetAddressSimpledCoder extends SimpledCoder { - - public static final InetAddressSimpledCoder instance = new InetAddressSimpledCoder(); - - @Override - public void convertTo(W out, InetAddress value) { - if (value == null) { - out.writeNull(); - return; - } - ByteArraySimpledCoder.instance.convertTo(out, value.getAddress()); - } - - @Override - public InetAddress convertFrom(R in) { - byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); - if (bytes == null) return null; - try { - return InetAddress.getByAddress(bytes); - } catch (Exception ex) { - return null; - } - } - - /** - * InetSocketAddress 的SimpledCoder实现 - * - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ - @SuppressWarnings("unchecked") - public final static class InetSocketAddressSimpledCoder extends SimpledCoder { - - public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder(); - - @Override - public void convertTo(W out, InetSocketAddress value) { - if (value == null) { - out.writeNull(); - return; - } - ByteArraySimpledCoder.instance.convertTo(out, value.getAddress().getAddress()); - out.writeInt(value.getPort()); - } - - @Override - public InetSocketAddress convertFrom(R in) { - byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); - if (bytes == null) return null; - int port = in.readInt(); - try { - return new InetSocketAddress(InetAddress.getByAddress(bytes), port); - } catch (Exception ex) { - return null; - } - } - - } - - /** - * InetAddress 的JsonSimpledCoder实现 - * - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ - public final static class InetAddressJsonSimpledCoder extends SimpledCoder { - - public static final InetAddressJsonSimpledCoder instance = new InetAddressJsonSimpledCoder(); - - @Override - public void convertTo(W out, InetAddress value) { - if (value == null) { - out.writeNull(); - return; - } - StringSimpledCoder.instance.convertTo(out, value.getHostAddress()); - } - - @Override - public InetAddress convertFrom(R in) { - String str = StringSimpledCoder.instance.convertFrom(in); - if (str == null) return null; - try { - return InetAddress.getByName(str); - } catch (Exception ex) { - return null; - } - } - - } - - /** - * InetSocketAddress 的JsonSimpledCoder实现 - * - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ - public final static class InetSocketAddressJsonSimpledCoder extends SimpledCoder { - - public static final InetSocketAddressJsonSimpledCoder instance = new InetSocketAddressJsonSimpledCoder(); - - @Override - public void convertTo(W out, InetSocketAddress value) { - if (value == null) { - out.writeNull(); - return; - } - StringSimpledCoder.instance.convertTo(out, value.getHostString() + ":" + value.getPort()); - } - - @Override - public InetSocketAddress convertFrom(R in) { - String str = StringSimpledCoder.instance.convertFrom(in); - if (str == null) return null; - try { - int pos = str.indexOf(':'); - return new InetSocketAddress(str.substring(0, pos), Integer.parseInt(str.substring(pos + 1))); - } catch (Exception ex) { - return null; - } - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; +import org.redkale.convert.Reader; +import java.net.*; +import org.redkale.util.StringWrapper; + +/** + * InetAddress 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +@SuppressWarnings("unchecked") +public final class InetAddressSimpledCoder extends SimpledCoder { + + public static final InetAddressSimpledCoder instance = new InetAddressSimpledCoder(); + + @Override + public void convertTo(W out, InetAddress value) { + if (value == null) { + out.writeNull(); + return; + } + ByteArraySimpledCoder.instance.convertTo(out, value.getAddress()); + } + + @Override + public InetAddress convertFrom(R in) { + byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); + if (bytes == null) return null; + try { + return InetAddress.getByAddress(bytes); + } catch (Exception ex) { + return null; + } + } + + /** + * InetSocketAddress 的SimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + @SuppressWarnings("unchecked") + public final static class InetSocketAddressSimpledCoder extends SimpledCoder { + + public static final InetSocketAddressSimpledCoder instance = new InetSocketAddressSimpledCoder(); + + @Override + public void convertTo(W out, InetSocketAddress value) { + if (value == null) { + out.writeNull(); + return; + } + ByteArraySimpledCoder.instance.convertTo(out, value.getAddress().getAddress()); + out.writeInt(value.getPort()); + } + + @Override + public InetSocketAddress convertFrom(R in) { + byte[] bytes = ByteArraySimpledCoder.instance.convertFrom(in); + if (bytes == null) return null; + int port = in.readInt(); + try { + return new InetSocketAddress(InetAddress.getByAddress(bytes), port); + } catch (Exception ex) { + return null; + } + } + + } + + /** + * InetAddress 的JsonSimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + public final static class InetAddressJsonSimpledCoder extends SimpledCoder { + + public static final InetAddressJsonSimpledCoder instance = new InetAddressJsonSimpledCoder(); + + @Override + public void convertTo(W out, InetAddress value) { + if (value == null) { + out.writeNull(); + return; + } + out.writeWrapper(new StringWrapper(value.getHostAddress())); + } + + @Override + public InetAddress convertFrom(R in) { + String str = in.readString(); + if (str == null) return null; + try { + return InetAddress.getByName(str); + } catch (Exception ex) { + return null; + } + } + + } + + /** + * InetSocketAddress 的JsonSimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + public final static class InetSocketAddressJsonSimpledCoder extends SimpledCoder { + + public static final InetSocketAddressJsonSimpledCoder instance = new InetSocketAddressJsonSimpledCoder(); + + @Override + public void convertTo(W out, InetSocketAddress value) { + if (value == null) { + out.writeNull(); + return; + } + StringSimpledCoder.instance.convertTo(out, value.getHostString() + ":" + value.getPort()); + } + + @Override + public InetSocketAddress convertFrom(R in) { + String str = StringSimpledCoder.instance.convertFrom(in); + if (str == null) return null; + try { + int pos = str.indexOf(':'); + return new InetSocketAddress(str.substring(0, pos), Integer.parseInt(str.substring(pos + 1))); + } catch (Exception ex) { + return null; + } + } + + } +} diff --git a/src/main/java/org/redkale/convert/ext/InstantSimpledCoder.java b/src/main/java/org/redkale/convert/ext/InstantSimpledCoder.java new file mode 100644 index 000000000..4084c1b3d --- /dev/null +++ b/src/main/java/org/redkale/convert/ext/InstantSimpledCoder.java @@ -0,0 +1,56 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.time.*; +import org.redkale.convert.*; + +/** + * java.time.Instant 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class InstantSimpledCoder extends SimpledCoder { + + public static final InstantSimpledCoder instance = new InstantSimpledCoder(); + + @Override + public void convertTo(W out, Instant value) { + out.writeLong(value == null ? -1L : value.toEpochMilli()); + } + + @Override + public Instant convertFrom(R in) { + long t = in.readLong(); + return t == -1 ? null : Instant.ofEpochMilli(t); + } + + public final static class InstantJsonSimpledCoder extends SimpledCoder { + + public static final InstantJsonSimpledCoder instance = new InstantJsonSimpledCoder(); + + @Override + public void convertTo(final Writer out, final Instant value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.toString()); + } + } + + @Override + public Instant convertFrom(Reader in) { + final String str = in.readSmallString(); + if (str == null) return null; + return Instant.parse(str); + } + } +} diff --git a/src/org/redkale/convert/ext/IntArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/IntArraySimpledCoder.java similarity index 97% rename from src/org/redkale/convert/ext/IntArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/IntArraySimpledCoder.java index d3820dbc5..5cd7fd4f6 100644 --- a/src/org/redkale/convert/ext/IntArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/IntArraySimpledCoder.java @@ -1,101 +1,101 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.util.stream.IntStream; -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * int[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class IntArraySimpledCoder extends SimpledCoder { - - public static final IntArraySimpledCoder instance = new IntArraySimpledCoder(); - - @Override - public void convertTo(W out, int[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, IntSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (int v : values) { - if (flag) out.writeArrayMark(); - out.writeInt(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public int[] convertFrom(R in) { - int len = in.readArrayB(null, null, IntSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, IntSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - int[] data = new int[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - int[] newdata = new int[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readInt(); - } - in.readArrayE(); - int[] newdata = new int[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - int[] values = new int[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readInt(); - } - in.readArrayE(); - return values; - } - } - - public final static class IntStreamSimpledCoder extends SimpledCoder { - - public static final IntStreamSimpledCoder instance = new IntStreamSimpledCoder(); - - @Override - @SuppressWarnings("unchecked") - public void convertTo(W out, IntStream values) { - if (values == null) { - out.writeNull(); - return; - } - IntArraySimpledCoder.instance.convertTo(out, values.toArray()); - } - - @Override - @SuppressWarnings("unchecked") - public IntStream convertFrom(R in) { - int[] value = IntArraySimpledCoder.instance.convertFrom(in); - return value == null ? null : IntStream.of(value); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.util.stream.IntStream; +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * int[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class IntArraySimpledCoder extends SimpledCoder { + + public static final IntArraySimpledCoder instance = new IntArraySimpledCoder(); + + @Override + public void convertTo(W out, int[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, IntSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (int v : values) { + if (flag) out.writeArrayMark(); + out.writeInt(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public int[] convertFrom(R in) { + int len = in.readArrayB(null, null, IntSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, IntSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + int[] data = new int[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + int[] newdata = new int[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readInt(); + } + in.readArrayE(); + int[] newdata = new int[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + int[] values = new int[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readInt(); + } + in.readArrayE(); + return values; + } + } + + public final static class IntStreamSimpledCoder extends SimpledCoder { + + public static final IntStreamSimpledCoder instance = new IntStreamSimpledCoder(); + + @Override + @SuppressWarnings("unchecked") + public void convertTo(W out, IntStream values) { + if (values == null) { + out.writeNull(); + return; + } + IntArraySimpledCoder.instance.convertTo(out, values.toArray()); + } + + @Override + @SuppressWarnings("unchecked") + public IntStream convertFrom(R in) { + int[] value = IntArraySimpledCoder.instance.convertFrom(in); + return value == null ? null : IntStream.of(value); + } + + } +} diff --git a/src/org/redkale/convert/ext/IntSimpledCoder.java b/src/main/java/org/redkale/convert/ext/IntSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/IntSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/IntSimpledCoder.java index c7889ec27..de9368807 100644 --- a/src/org/redkale/convert/ext/IntSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/IntSimpledCoder.java @@ -1,34 +1,34 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * int 的SimpledCoder实现 - * - *

    详情见: https://redkale.org - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class IntSimpledCoder extends SimpledCoder { - - public static final IntSimpledCoder instance = new IntSimpledCoder(); - - @Override - public void convertTo(W out, Integer value) { - out.writeInt(value); - } - - @Override - public Integer convertFrom(R in) { - return in.readInt(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * int 的SimpledCoder实现 + * + *

    详情见: https://redkale.org + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class IntSimpledCoder extends SimpledCoder { + + public static final IntSimpledCoder instance = new IntSimpledCoder(); + + @Override + public void convertTo(W out, Integer value) { + out.writeInt(value); + } + + @Override + public Integer convertFrom(R in) { + return in.readInt(); + } + +} diff --git a/src/main/java/org/redkale/convert/ext/LocalDateSimpledCoder.java b/src/main/java/org/redkale/convert/ext/LocalDateSimpledCoder.java new file mode 100644 index 000000000..2ab440aad --- /dev/null +++ b/src/main/java/org/redkale/convert/ext/LocalDateSimpledCoder.java @@ -0,0 +1,72 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.time.*; +import org.redkale.convert.*; + +/** + * java.time.LocalDate 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class LocalDateSimpledCoder extends SimpledCoder { + + public static final LocalDateSimpledCoder instance = new LocalDateSimpledCoder(); + + @Override + public void convertTo(W out, LocalDate value) { + out.writeInt(value == null ? -1 : value.getYear() * 100_00 + value.getMonthValue() * 100 + value.getDayOfMonth()); + } + + @Override + public LocalDate convertFrom(R in) { + int t = in.readInt(); + return t == -1 ? null : LocalDate.of(t / 100_00, t % 100_00 / 100, t % 100); + } + +// public static void main(String[] args) throws Throwable { +// LocalDate now = LocalDate.now(); +// System.out.println(now); +// BsonWriter writer = new BsonWriter(); +// LocalDateSimpledCoder.instance.convertTo(writer, now); +// System.out.println(new ByteArray(writer).getInt(0)); +// BsonReader reader = new BsonReader(writer.toArray()); +// System.out.println(LocalDateSimpledCoder.instance.convertFrom(reader)); +// } + + /** + * java.time.LocalDate 的JsonSimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + public final static class LocalDateJsonSimpledCoder extends SimpledCoder { + + public static final LocalDateJsonSimpledCoder instance = new LocalDateJsonSimpledCoder(); + + @Override + public void convertTo(final Writer out, final LocalDate value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.toString()); + } + } + + @Override + public LocalDate convertFrom(Reader in) { + final String str = in.readSmallString(); + if (str == null) return null; + return LocalDate.parse(str); + } + } +} diff --git a/src/main/java/org/redkale/convert/ext/LocalDateTimeSimpledCoder.java b/src/main/java/org/redkale/convert/ext/LocalDateTimeSimpledCoder.java new file mode 100644 index 000000000..bf30a23cd --- /dev/null +++ b/src/main/java/org/redkale/convert/ext/LocalDateTimeSimpledCoder.java @@ -0,0 +1,85 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.time.*; +import org.redkale.convert.*; + +/** + * java.time.LocalDateTime 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class LocalDateTimeSimpledCoder extends SimpledCoder { + + private static final ByteArraySimpledCoder bsSimpledCoder = ByteArraySimpledCoder.instance; + + public static final LocalDateTimeSimpledCoder instance = new LocalDateTimeSimpledCoder(); + + @Override + public void convertTo(W out, LocalDateTime value) { + if (value == null) { + bsSimpledCoder.convertTo(out, null); + } else { + long v1 = value.toEpochSecond(ZoneOffset.UTC); + int v2 = value.getNano(); + byte[] bs = new byte[]{(byte) (v1 >> 56), (byte) (v1 >> 48), (byte) (v1 >> 40), (byte) (v1 >> 32), + (byte) (v1 >> 24), (byte) (v1 >> 16), (byte) (v1 >> 8), (byte) v1, + (byte) (v2 >> 24), (byte) (v2 >> 16), (byte) (v2 >> 8), (byte) v2}; + bsSimpledCoder.convertTo(out, bs); + } + } + + @Override + public LocalDateTime convertFrom(R in) { + byte[] bs = bsSimpledCoder.convertFrom(in); + if (bs == null) return null; + long v1 = (((long) bs[0] & 0xff) << 56) | (((long) bs[1] & 0xff) << 48) | (((long) bs[2] & 0xff) << 40) | (((long) bs[3] & 0xff) << 32) + | (((long) bs[4] & 0xff) << 24) | (((long) bs[5] & 0xff) << 16) | (((long) bs[6] & 0xff) << 8) | ((long) bs[7] & 0xff); + int v2 = ((bs[8] & 0xff) << 24) | ((bs[9] & 0xff) << 16) | ((bs[10] & 0xff) << 8) | (bs[11] & 0xff); + return LocalDateTime.ofEpochSecond(v1, v2, ZoneOffset.UTC); + } + +// public static void main(String[] args) throws Throwable { +// LocalDateTime now = LocalDateTime.now(); +// System.out.println(now); +// BsonWriter writer = new BsonWriter(); +// LocalDateTimeSimpledCoder.instance.convertTo(writer, now); +// BsonReader reader = new BsonReader(writer.toArray()); +// System.out.println(LocalDateTimeSimpledCoder.instance.convertFrom(reader)); +// } + /** + * java.time.LocalDateTime 的JsonSimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + public final static class LocalDateTimeJsonSimpledCoder extends SimpledCoder { + + public static final LocalDateTimeJsonSimpledCoder instance = new LocalDateTimeJsonSimpledCoder(); + + @Override + public void convertTo(final Writer out, final LocalDateTime value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.toString()); + } + } + + @Override + public LocalDateTime convertFrom(Reader in) { + final String str = in.readSmallString(); + if (str == null) return null; + return LocalDateTime.parse(str); + } + } +} diff --git a/src/main/java/org/redkale/convert/ext/LocalTimeSimpledCoder.java b/src/main/java/org/redkale/convert/ext/LocalTimeSimpledCoder.java new file mode 100644 index 000000000..49875cb00 --- /dev/null +++ b/src/main/java/org/redkale/convert/ext/LocalTimeSimpledCoder.java @@ -0,0 +1,70 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.time.*; +import org.redkale.convert.*; + +/** + * java.time.LocalTime 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class LocalTimeSimpledCoder extends SimpledCoder { + + public static final LocalTimeSimpledCoder instance = new LocalTimeSimpledCoder(); + + @Override + public void convertTo(W out, LocalTime value) { + out.writeLong(value == null ? -1L : value.toNanoOfDay()); + } + + @Override + public LocalTime convertFrom(R in) { + long t = in.readLong(); + return t == -1 ? null : LocalTime.ofNanoOfDay(t); + } + +// public static void main(String[] args) throws Throwable { +// LocalTime now = LocalTime.now(); +// System.out.println(now); +// BsonWriter writer = new BsonWriter(); +// LocalTimeSimpledCoder.instance.convertTo(writer, now); +// BsonReader reader = new BsonReader(writer.toArray()); +// System.out.println(LocalTimeSimpledCoder.instance.convertFrom(reader)); +// } + /** + * java.time.LocalTime 的JsonSimpledCoder实现 + * + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ + public final static class LocalTimeJsonSimpledCoder extends SimpledCoder { + + public static final LocalTimeJsonSimpledCoder instance = new LocalTimeJsonSimpledCoder(); + + @Override + public void convertTo(final Writer out, final LocalTime value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.toString()); + } + } + + @Override + public LocalTime convertFrom(Reader in) { + final String str = in.readSmallString(); + if (str == null) return null; + return LocalTime.parse(str); + } + } +} diff --git a/src/main/java/org/redkale/convert/ext/LongAdderSimpledCoder.java b/src/main/java/org/redkale/convert/ext/LongAdderSimpledCoder.java new file mode 100644 index 000000000..bf16284da --- /dev/null +++ b/src/main/java/org/redkale/convert/ext/LongAdderSimpledCoder.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.util.concurrent.atomic.LongAdder; +import org.redkale.convert.*; + +/** + * LongAdder 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class LongAdderSimpledCoder extends SimpledCoder { + + public static final LongAdderSimpledCoder instance = new LongAdderSimpledCoder(); + + @Override + public void convertTo(W out, LongAdder value) { + out.writeLong(value == null ? 0L : value.longValue()); + } + + @Override + public LongAdder convertFrom(R in) { + LongAdder la = new LongAdder(); + la.add(in.readLong()); + return la; + } + +} diff --git a/src/org/redkale/convert/ext/LongArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/LongArraySimpledCoder.java similarity index 97% rename from src/org/redkale/convert/ext/LongArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/LongArraySimpledCoder.java index 33a91961d..4921894c7 100644 --- a/src/org/redkale/convert/ext/LongArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/LongArraySimpledCoder.java @@ -1,101 +1,101 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.util.stream.LongStream; -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * long[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class LongArraySimpledCoder extends SimpledCoder { - - public static final LongArraySimpledCoder instance = new LongArraySimpledCoder(); - - @Override - public void convertTo(W out, long[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, LongSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (long v : values) { - if (flag) out.writeArrayMark(); - out.writeLong(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public long[] convertFrom(R in) { - int len = in.readArrayB(null, null, LongSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, LongSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - long[] data = new long[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - long[] newdata = new long[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readLong(); - } - in.readArrayE(); - long[] newdata = new long[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - long[] values = new long[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readLong(); - } - in.readArrayE(); - return values; - } - } - - public final static class LongStreamSimpledCoder extends SimpledCoder { - - public static final LongStreamSimpledCoder instance = new LongStreamSimpledCoder(); - - @Override - @SuppressWarnings("unchecked") - public void convertTo(W out, LongStream values) { - if (values == null) { - out.writeNull(); - return; - } - LongArraySimpledCoder.instance.convertTo(out, values.toArray()); - } - - @Override - @SuppressWarnings("unchecked") - public LongStream convertFrom(R in) { - long[] value = LongArraySimpledCoder.instance.convertFrom(in); - return value == null ? null : LongStream.of(value); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.util.stream.LongStream; +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * long[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class LongArraySimpledCoder extends SimpledCoder { + + public static final LongArraySimpledCoder instance = new LongArraySimpledCoder(); + + @Override + public void convertTo(W out, long[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, LongSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (long v : values) { + if (flag) out.writeArrayMark(); + out.writeLong(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public long[] convertFrom(R in) { + int len = in.readArrayB(null, null, LongSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, LongSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + long[] data = new long[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + long[] newdata = new long[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readLong(); + } + in.readArrayE(); + long[] newdata = new long[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + long[] values = new long[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readLong(); + } + in.readArrayE(); + return values; + } + } + + public final static class LongStreamSimpledCoder extends SimpledCoder { + + public static final LongStreamSimpledCoder instance = new LongStreamSimpledCoder(); + + @Override + @SuppressWarnings("unchecked") + public void convertTo(W out, LongStream values) { + if (values == null) { + out.writeNull(); + return; + } + LongArraySimpledCoder.instance.convertTo(out, values.toArray()); + } + + @Override + @SuppressWarnings("unchecked") + public LongStream convertFrom(R in) { + long[] value = LongArraySimpledCoder.instance.convertFrom(in); + return value == null ? null : LongStream.of(value); + } + + } +} diff --git a/src/org/redkale/convert/ext/LongSimpledCoder.java b/src/main/java/org/redkale/convert/ext/LongSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/LongSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/LongSimpledCoder.java index b335564da..125f296b6 100644 --- a/src/org/redkale/convert/ext/LongSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/LongSimpledCoder.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * long 的SimpledCoder实现 - * - *

    详情见: https://redkale.org - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class LongSimpledCoder extends SimpledCoder { - - public static final LongSimpledCoder instance = new LongSimpledCoder(); - - @Override - public void convertTo(W out, Long value) { - out.writeLong(value); - } - - @Override - public Long convertFrom(R in) { - return in.readLong(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * long 的SimpledCoder实现 + * + *

    详情见: https://redkale.org + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class LongSimpledCoder extends SimpledCoder { + + public static final LongSimpledCoder instance = new LongSimpledCoder(); + + @Override + public void convertTo(W out, Long value) { + out.writeLong(value); + } + + @Override + public Long convertFrom(R in) { + return in.readLong(); + } + +} diff --git a/src/org/redkale/convert/ext/NumberSimpledCoder.java b/src/main/java/org/redkale/convert/ext/NumberSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/NumberSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/NumberSimpledCoder.java index ae628333d..e3fd4d544 100644 --- a/src/org/redkale/convert/ext/NumberSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/NumberSimpledCoder.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * Number 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class NumberSimpledCoder extends SimpledCoder { - - public static final NumberSimpledCoder instance = new NumberSimpledCoder(); - - @Override - public void convertTo(W out, Number value) { - out.writeLong(value == null ? 0L : value.longValue()); - } - - @Override - public Number convertFrom(R in) { - return in.readLong(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * Number 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class NumberSimpledCoder extends SimpledCoder { + + public static final NumberSimpledCoder instance = new NumberSimpledCoder(); + + @Override + public void convertTo(W out, Number value) { + out.writeLong(value == null ? 0L : value.longValue()); + } + + @Override + public Number convertFrom(R in) { + return in.readLong(); + } + +} diff --git a/src/org/redkale/convert/ext/PatternSimpledCoder.java b/src/main/java/org/redkale/convert/ext/PatternSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/PatternSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/PatternSimpledCoder.java index 9d6daa5e2..70b6459fd 100644 --- a/src/org/redkale/convert/ext/PatternSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/PatternSimpledCoder.java @@ -1,40 +1,40 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.util.regex.*; -import org.redkale.convert.*; - -/** - * Pattern 的SimpledCoder实现 - * - *

    详情见: https://redkale.org - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class PatternSimpledCoder extends SimpledCoder { - - public static final PatternSimpledCoder instance = new PatternSimpledCoder(); - - @Override - public void convertTo(W out, Pattern value) { - if (value == null) { - out.writeNull(); - } else { - out.writeString(value.flags() + "," + value.pattern()); - } - } - - @Override - public Pattern convertFrom(R in) { - String value = in.readString(); - if (value == null) return null; - int pos = value.indexOf(','); - return Pattern.compile(value.substring(pos + 1), Integer.parseInt(value.substring(0, pos))); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.util.regex.*; +import org.redkale.convert.*; + +/** + * Pattern 的SimpledCoder实现 + * + *

    详情见: https://redkale.org + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class PatternSimpledCoder extends SimpledCoder { + + public static final PatternSimpledCoder instance = new PatternSimpledCoder(); + + @Override + public void convertTo(W out, Pattern value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(value.flags() + "," + value.pattern()); + } + } + + @Override + public Pattern convertFrom(R in) { + String value = in.readString(); + if (value == null) return null; + int pos = value.indexOf(','); + return Pattern.compile(value.substring(pos + 1), Integer.parseInt(value.substring(0, pos))); + } + +} diff --git a/src/org/redkale/convert/ext/ShortArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/ShortArraySimpledCoder.java similarity index 97% rename from src/org/redkale/convert/ext/ShortArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/ShortArraySimpledCoder.java index 9f2fda2b4..eddb24ca2 100644 --- a/src/org/redkale/convert/ext/ShortArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/ShortArraySimpledCoder.java @@ -1,78 +1,78 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * short[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class ShortArraySimpledCoder extends SimpledCoder { - - public static final ShortArraySimpledCoder instance = new ShortArraySimpledCoder(); - - @Override - public void convertTo(W out, short[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, ShortSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (short v : values) { - if (flag) out.writeArrayMark(); - out.writeShort(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public short[] convertFrom(R in) { - int len = in.readArrayB(null, null, ShortSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, ShortSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - short[] data = new short[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - short[] newdata = new short[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readShort(); - } - in.readArrayE(); - short[] newdata = new short[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - short[] values = new short[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readShort(); - } - in.readArrayE(); - return values; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * short[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class ShortArraySimpledCoder extends SimpledCoder { + + public static final ShortArraySimpledCoder instance = new ShortArraySimpledCoder(); + + @Override + public void convertTo(W out, short[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, ShortSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (short v : values) { + if (flag) out.writeArrayMark(); + out.writeShort(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public short[] convertFrom(R in) { + int len = in.readArrayB(null, null, ShortSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, ShortSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + short[] data = new short[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + short[] newdata = new short[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readShort(); + } + in.readArrayE(); + short[] newdata = new short[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + short[] values = new short[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readShort(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/src/org/redkale/convert/ext/ShortSimpledCoder.java b/src/main/java/org/redkale/convert/ext/ShortSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/ShortSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/ShortSimpledCoder.java index 7df06aa64..0b1487601 100644 --- a/src/org/redkale/convert/ext/ShortSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/ShortSimpledCoder.java @@ -1,34 +1,34 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * short 的SimpledCoder实现 - * - *

    详情见: https://redkale.org - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class ShortSimpledCoder extends SimpledCoder { - - public static final ShortSimpledCoder instance = new ShortSimpledCoder(); - - @Override - public void convertTo(W out, Short value) { - out.writeShort(value); - } - - @Override - public Short convertFrom(R in) { - return in.readShort(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * short 的SimpledCoder实现 + * + *

    详情见: https://redkale.org + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class ShortSimpledCoder extends SimpledCoder { + + public static final ShortSimpledCoder instance = new ShortSimpledCoder(); + + @Override + public void convertTo(W out, Short value) { + out.writeShort(value); + } + + @Override + public Short convertFrom(R in) { + return in.readShort(); + } + +} diff --git a/src/org/redkale/convert/ext/StringArraySimpledCoder.java b/src/main/java/org/redkale/convert/ext/StringArraySimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/StringArraySimpledCoder.java rename to src/main/java/org/redkale/convert/ext/StringArraySimpledCoder.java index 23ba5382a..04c55af05 100644 --- a/src/org/redkale/convert/ext/StringArraySimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/StringArraySimpledCoder.java @@ -1,80 +1,80 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.*; - -/** - * String[] 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class StringArraySimpledCoder extends SimpledCoder { - - public static final StringArraySimpledCoder instance = new StringArraySimpledCoder(); - - @Override - public void convertTo(W out, String[] values) { - if (values == null) { - out.writeNull(); - return; - } - if (out.writeArrayB(values.length, this, StringSimpledCoder.instance, values) < 0) { - boolean flag = false; - for (String v : values) { - if (flag) out.writeArrayMark(); - out.writeString(v); - flag = true; - } - } - out.writeArrayE(); - } - - @Override - public String[] convertFrom(R in) { - return convertFrom(in, null); - } - - public String[] convertFrom(R in, DeMember member) { - int len = in.readArrayB(member, null, StringSimpledCoder.instance); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = in.readMemberContentLength(null, StringSimpledCoder.instance); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - String[] data = new String[8]; - int startPosition = in.position(); - while (in.hasNext(startPosition, contentLength)) { - if (size >= data.length) { - String[] newdata = new String[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = in.readString(); - } - in.readArrayE(); - String[] newdata = new String[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - String[] values = new String[len]; - for (int i = 0; i < values.length; i++) { - values[i] = in.readString(); - } - in.readArrayE(); - return values; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.*; + +/** + * String[] 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class StringArraySimpledCoder extends SimpledCoder { + + public static final StringArraySimpledCoder instance = new StringArraySimpledCoder(); + + @Override + public void convertTo(W out, String[] values) { + if (values == null) { + out.writeNull(); + return; + } + if (out.writeArrayB(values.length, this, StringSimpledCoder.instance, values) < 0) { + boolean flag = false; + for (String v : values) { + if (flag) out.writeArrayMark(); + out.writeString(v); + flag = true; + } + } + out.writeArrayE(); + } + + @Override + public String[] convertFrom(R in) { + return convertFrom(in, null); + } + + public String[] convertFrom(R in, DeMember member) { + int len = in.readArrayB(member, null, StringSimpledCoder.instance); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = in.readMemberContentLength(null, StringSimpledCoder.instance); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + String[] data = new String[8]; + int startPosition = in.position(); + while (in.hasNext(startPosition, contentLength)) { + if (size >= data.length) { + String[] newdata = new String[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = in.readString(); + } + in.readArrayE(); + String[] newdata = new String[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + String[] values = new String[len]; + for (int i = 0; i < values.length; i++) { + values[i] = in.readString(); + } + in.readArrayE(); + return values; + } + } + +} diff --git a/src/org/redkale/convert/ext/StringSimpledCoder.java b/src/main/java/org/redkale/convert/ext/StringSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/StringSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/StringSimpledCoder.java index 2932d10c1..0e89478f5 100644 --- a/src/org/redkale/convert/ext/StringSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/StringSimpledCoder.java @@ -1,51 +1,51 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.SimpledCoder; -import org.redkale.convert.Writer; - -/** - * String 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class StringSimpledCoder extends SimpledCoder { - - public static final StringSimpledCoder instance = new StringSimpledCoder(); - - @Override - public void convertTo(W out, String value) { - out.writeString(value); - } - - @Override - public String convertFrom(R in) { - return in.readString(); - } - - public final static class SmallStringSimpledCoder extends SimpledCoder { - - public static final SmallStringSimpledCoder instance = new SmallStringSimpledCoder(); - - @Override - public void convertTo(W out, String value) { - out.writeSmallString(value); - } - - @Override - public String convertFrom(R in) { - return in.readSmallString(); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.SimpledCoder; +import org.redkale.convert.Writer; + +/** + * String 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class StringSimpledCoder extends SimpledCoder { + + public static final StringSimpledCoder instance = new StringSimpledCoder(); + + @Override + public void convertTo(W out, String value) { + out.writeString(value); + } + + @Override + public String convertFrom(R in) { + return in.readString(); + } + + public final static class SmallStringSimpledCoder extends SimpledCoder { + + public static final SmallStringSimpledCoder instance = new SmallStringSimpledCoder(); + + @Override + public void convertTo(W out, String value) { + out.writeSmallString(value); + } + + @Override + public String convertFrom(R in) { + return in.readSmallString(); + } + + } +} diff --git a/src/org/redkale/convert/ext/StringWrapperSimpledCoder.java b/src/main/java/org/redkale/convert/ext/StringWrapperSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/StringWrapperSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/StringWrapperSimpledCoder.java index ffe447786..7d1a13522 100644 --- a/src/org/redkale/convert/ext/StringWrapperSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/StringWrapperSimpledCoder.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.*; -import org.redkale.util.StringWrapper; - -/** - * String 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public final class StringWrapperSimpledCoder extends SimpledCoder { - - public static final StringWrapperSimpledCoder instance = new StringWrapperSimpledCoder(); - - @Override - public void convertTo(W out, StringWrapper value) { - out.writeWrapper(value); - } - - @Override - public StringWrapper convertFrom(R in) { - return new StringWrapper(in.readString()); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.*; +import org.redkale.util.StringWrapper; + +/** + * String 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public final class StringWrapperSimpledCoder extends SimpledCoder { + + public static final StringWrapperSimpledCoder instance = new StringWrapperSimpledCoder(); + + @Override + public void convertTo(W out, StringWrapper value) { + out.writeWrapper(value); + } + + @Override + public StringWrapper convertFrom(R in) { + return new StringWrapper(in.readString()); + } + +} diff --git a/src/org/redkale/convert/ext/ThrowableSimpledCoder.java b/src/main/java/org/redkale/convert/ext/ThrowableSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/ThrowableSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/ThrowableSimpledCoder.java index 1c872bcfb..66f98b4d2 100644 --- a/src/org/redkale/convert/ext/ThrowableSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/ThrowableSimpledCoder.java @@ -1,40 +1,40 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.*; - -/** - * 文件 的SimpledCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class ThrowableSimpledCoder extends SimpledCoder { - - public static final ThrowableSimpledCoder instance = new ThrowableSimpledCoder(); - - @Override - public void convertTo(W out, Throwable value) { - if (value == null) { - out.writeNull(); - } else { - out.writeString(value.toString()); - } - } - - @Override - public Throwable convertFrom(R in) { - String value = in.readString(); - if (value == null) return null; - return new Exception(value); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.*; + +/** + * 文件 的SimpledCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class ThrowableSimpledCoder extends SimpledCoder { + + public static final ThrowableSimpledCoder instance = new ThrowableSimpledCoder(); + + @Override + public void convertTo(W out, Throwable value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(value.toString()); + } + } + + @Override + public Throwable convertFrom(R in) { + String value = in.readString(); + if (value == null) return null; + return new Exception(value); + } + +} diff --git a/src/org/redkale/convert/ext/TypeSimpledCoder.java b/src/main/java/org/redkale/convert/ext/TypeSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/TypeSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/TypeSimpledCoder.java index 70257d4ac..530b0299a 100644 --- a/src/org/redkale/convert/ext/TypeSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/TypeSimpledCoder.java @@ -1,46 +1,46 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import org.redkale.convert.Reader; -import org.redkale.convert.Writer; -import org.redkale.convert.SimpledCoder; - -/** - * Type 的SimpledCoder实现 只支持Type的子类Class - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class TypeSimpledCoder extends SimpledCoder { - - public static final TypeSimpledCoder instance = new TypeSimpledCoder(); - - @Override - public void convertTo(final W out, final Class value) { - if (value == null) { - out.writeNull(); - } else { - out.writeSmallString(value.getName()); - } - } - - @Override - public Class convertFrom(R in) { - String str = in.readSmallString(); - if (str == null) return null; - try { - return Thread.currentThread().getContextClassLoader().loadClass(str); - } catch (Throwable e) { - return null; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import org.redkale.convert.Reader; +import org.redkale.convert.Writer; +import org.redkale.convert.SimpledCoder; + +/** + * Type 的SimpledCoder实现 只支持Type的子类Class + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class TypeSimpledCoder extends SimpledCoder { + + public static final TypeSimpledCoder instance = new TypeSimpledCoder(); + + @Override + public void convertTo(final W out, final Class value) { + if (value == null) { + out.writeNull(); + } else { + out.writeSmallString(value.getName()); + } + } + + @Override + public Class convertFrom(R in) { + String str = in.readSmallString(); + if (str == null) return null; + try { + return Thread.currentThread().getContextClassLoader().loadClass(str); + } catch (Throwable e) { + return null; + } + } + +} diff --git a/src/org/redkale/convert/ext/URISimpledCoder.java b/src/main/java/org/redkale/convert/ext/URISimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/URISimpledCoder.java rename to src/main/java/org/redkale/convert/ext/URISimpledCoder.java index 1376d2b5c..38a4a06cf 100644 --- a/src/org/redkale/convert/ext/URISimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/URISimpledCoder.java @@ -1,44 +1,44 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.net.*; -import org.redkale.convert.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class URISimpledCoder extends SimpledCoder { - - public static final URLSimpledCoder instance = new URLSimpledCoder(); - - @Override - public void convertTo(final Writer out, final URI value) { - if (value == null) { - out.writeNull(); - } else { - out.writeString(value.toString()); - } - } - - @Override - public URI convertFrom(Reader in) { - final String str = in.readString(); - if (str == null) return null; - try { - return new URI(str); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.net.*; +import org.redkale.convert.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class URISimpledCoder extends SimpledCoder { + + public static final URLSimpledCoder instance = new URLSimpledCoder(); + + @Override + public void convertTo(final Writer out, final URI value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(value.toString()); + } + } + + @Override + public URI convertFrom(Reader in) { + final String str = in.readString(); + if (str == null) return null; + try { + return new URI(str); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/org/redkale/convert/ext/URLSimpledCoder.java b/src/main/java/org/redkale/convert/ext/URLSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/ext/URLSimpledCoder.java rename to src/main/java/org/redkale/convert/ext/URLSimpledCoder.java index 172f2733c..1408026fa 100644 --- a/src/org/redkale/convert/ext/URLSimpledCoder.java +++ b/src/main/java/org/redkale/convert/ext/URLSimpledCoder.java @@ -1,44 +1,44 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.ext; - -import java.net.*; -import org.redkale.convert.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Reader输入的子类型 - * @param Writer输出的子类型 - */ -public class URLSimpledCoder extends SimpledCoder { - - public static final URLSimpledCoder instance = new URLSimpledCoder(); - - @Override - public void convertTo(final Writer out, final URL value) { - if (value == null) { - out.writeNull(); - } else { - out.writeString(value.toString()); - } - } - - @Override - public URL convertFrom(Reader in) { - final String str = in.readString(); - if (str == null) return null; - try { - return new URL(str); - } catch (Exception e) { - e.printStackTrace(); - return null; - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.ext; + +import java.net.*; +import org.redkale.convert.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Reader输入的子类型 + * @param Writer输出的子类型 + */ +public class URLSimpledCoder extends SimpledCoder { + + public static final URLSimpledCoder instance = new URLSimpledCoder(); + + @Override + public void convertTo(final Writer out, final URL value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(value.toString()); + } + } + + @Override + public URL convertFrom(Reader in) { + final String str = in.readString(); + if (str == null) return null; + try { + return new URL(str); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/src/org/redkale/convert/ext/package-info.java b/src/main/java/org/redkale/convert/ext/package-info.java similarity index 95% rename from src/org/redkale/convert/ext/package-info.java rename to src/main/java/org/redkale/convert/ext/package-info.java index 3d10de53b..19c497bd6 100644 --- a/src/org/redkale/convert/ext/package-info.java +++ b/src/main/java/org/redkale/convert/ext/package-info.java @@ -1,4 +1,4 @@ -/** - * Convert的基本数据的Coder实现 - */ -package org.redkale.convert.ext; +/** + * Convert的基本数据的Coder实现 + */ +package org.redkale.convert.ext; diff --git a/src/org/redkale/convert/json/JsonByteBufferReader.java b/src/main/java/org/redkale/convert/json/JsonByteBufferReader.java similarity index 97% rename from src/org/redkale/convert/json/JsonByteBufferReader.java rename to src/main/java/org/redkale/convert/json/JsonByteBufferReader.java index 698e786f9..35f7ed015 100644 --- a/src/org/redkale/convert/json/JsonByteBufferReader.java +++ b/src/main/java/org/redkale/convert/json/JsonByteBufferReader.java @@ -1,387 +1,387 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.nio.*; -import java.nio.charset.*; -import org.redkale.convert.*; -import static org.redkale.convert.Reader.*; - -/** - * 以ByteBuffer为数据载体的JsonReader
    - * - * 只支持UTF-8格式 - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class JsonByteBufferReader extends JsonReader { - - private char currentChar; - - private ByteBuffer[] buffers; - - private int currentIndex = 0; - - private ByteBuffer currentBuffer; - - protected ConvertMask mask; - - protected JsonByteBufferReader(ConvertMask mask, ByteBuffer... buffers) { - this.mask = mask; - this.buffers = buffers; - if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex]; - } - - @Override - protected boolean recycle() { - super.recycle(); // this.position 初始化值为-1 - this.currentIndex = 0; - this.currentChar = 0; - this.currentBuffer = null; - this.buffers = null; - this.mask = null; - return false; - } - - protected byte nextByte() { - if (this.currentBuffer.hasRemaining()) { - this.position++; - return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); - } - for (;;) { - this.currentBuffer = this.buffers[++this.currentIndex]; - if (this.currentBuffer.hasRemaining()) { - this.position++; - return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); - } - } - } - - /** - * 读取下一个字符, 不跳过空白字符 - * - * @return 有效字符或空白字符 - */ - @Override - protected final char nextChar() { - return nextChar(null); - } - - protected final char nextChar(StringBuilder sb) { - if (currentChar != 0) { - char ch = currentChar; - this.currentChar = 0; - return ch; - } - if (this.currentBuffer != null) { - int remain = this.currentBuffer.remaining(); - if (remain == 0 && this.currentIndex + 1 >= this.buffers.length) return 0; - } - byte b = nextByte(); - if (b >= 0) {// 1 byte, 7 bits: 0xxxxxxx - return (char) b; - } else if ((b >> 5) == -2 && (b & 0x1e) != 0) { // 2 bytes, 11 bits: 110xxxxx 10xxxxxx - return (char) (((b << 6) ^ nextByte()) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80))); - } else if ((b >> 4) == -2) { // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx - return (char) ((b << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); - } else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - int uc = ((b << 18) ^ (nextByte() << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xF0 << 18) ^ ((byte) 0x80 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); - if (sb != null) sb.append(Character.highSurrogate(uc)); - return Character.lowSurrogate(uc); - } else { - throw new RuntimeException(new UnmappableCharacterException(4)); - } - } - - /** - * 读取下一个有效字符 - * - * @return 有效字符 - */ - @Override - protected final char nextGoodChar() { - char c = nextChar(); - if (c > ' ' || c == 0) return c; // 0 表示buffer结尾了 - for (;;) { - c = nextChar(); - if (c > ' ' || c == 0) return c; - } - } - - /** - * 回退最后读取的字符 - * - * @param ch 回退的字符 - */ - @Override - protected final void backChar(char ch) { - this.currentChar = ch; - } - - /** - * 判断下一个非空白字符是否为{ - * - * @return SIGN_NOLENGTH 或 SIGN_NULL - */ - @Override - public final String readObjectB(final Class clazz) { - char ch = nextGoodChar(); - if (ch == '{') return ""; - if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return null; - if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return null; - StringBuilder sb = new StringBuilder(); - sb.append(ch); - char one; - try { - while ((one = nextChar()) != 0) sb.append(one); - } catch (Exception e) { - } - throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "' in (" + sb + ")"); - } - - /** - * 判断下一个非空白字符是否为[ - * - * @param member DeMember - * @param typevals byte[] - * @param decoder Decodeable - * - * @return SIGN_NOLENGTH 或 SIGN_NULL - */ - @Override - public final int readArrayB(DeMember member, byte[] typevals, Decodeable decoder) { - char ch = nextGoodChar(); - if (ch == '[' || ch == '{') return SIGN_NOLENGTH; - if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return SIGN_NULL; - if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return SIGN_NULL; - StringBuilder sb = new StringBuilder(); - sb.append(ch); - char one; - try { - while ((one = nextChar()) != 0) sb.append(one); - } catch (Exception e) { - } - throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "' in (" + sb + ")"); - } - - /** - * 判断下一个非空白字符是否: - */ - @Override - public final void readBlank() { - char ch = nextGoodChar(); - if (ch == ':') return; - StringBuilder sb = new StringBuilder(); - sb.append(ch); - char one; - try { - while ((one = nextChar()) != 0) sb.append(one); - } catch (Exception e) { - } - throw new ConvertException("expected a ':' but '" + ch + "'(position = " + position + ") in (" + sb + ")"); - } - - /** - * 判断对象是否存在下一个属性或者数组是否存在下一个元素 - * - * @param startPosition 起始位置 - * @param contentLength 内容大小, 不确定的传-1 - * - * @return 是否存在 - */ - @Override - public boolean hasNext(int startPosition, int contentLength) { - char ch = nextGoodChar(); - if (ch == ',') return true; - if (ch == '}' || ch == ']' || ch == 0) return false; - backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取 - return true; - } - - /** - * 读取小字符串 - * - * @return String值 - */ - @Override - public final String readSmallString() { - char ch = nextGoodChar(); - if (ch == 0) return null; - final StringBuilder sb = new StringBuilder(); - if (ch == '"' || ch == '\'') { - final char quote = ch; - for (;;) { - ch = nextChar(sb); - if (ch == '\\') { - char c = nextChar(sb); - switch (c) { - case '"': - case '\'': - case '\\': - case '/': - sb.append(c); - break; - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - sb.append((char) Integer.parseInt(new String(new char[]{nextChar(), nextChar(), nextChar(), nextChar()}), 16)); - break; - case 't': - sb.append('\t'); - break; - case 'b': - sb.append('\b'); - break; - case 'f': - sb.append('\f'); - break; - default: - throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")"); - } - } else if (ch == quote || ch == 0) { - break; - } else { - sb.append(ch); - } - } - return sb.toString(); - } else { - sb.append(ch); - for (;;) { - ch = nextChar(sb); - if (ch == '\\') { - char c = nextChar(sb); - switch (c) { - case '"': - case '\'': - case '\\': - case '/': - sb.append(c); - break; - case 'n': - sb.append('\n'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - sb.append((char) Integer.parseInt(new String(new char[]{nextChar(), nextChar(), nextChar(), nextChar()}), 16)); - break; - case 't': - sb.append('\t'); - break; - case 'b': - sb.append('\b'); - break; - case 'f': - sb.append('\f'); - break; - default: - throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")"); - } - } else if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') { // ch <= ' ' 包含 0 - backChar(ch); - break; - } else { - sb.append(ch); - } - } - String rs = sb.toString(); - return "null".equalsIgnoreCase(rs) ? null : rs; - } - } - - /** - * 读取一个int值 - * - * @return int值 - */ - @Override - public final int readInt() { - char firstchar = nextGoodChar(); - boolean quote = false; - if (firstchar == '"' || firstchar == '\'') { - quote = true; - firstchar = nextGoodChar(); - if (firstchar == '"' || firstchar == '\'') return 0; - } - int value = 0; - final boolean negative = firstchar == '-'; - if (!negative) { - if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")"); - value = firstchar - '0'; - } - for (;;) { - char ch = nextChar(); - if (ch == 0) break; - if (ch >= '0' && ch <= '9') { - value = (value << 3) + (value << 1) + (ch - '0'); - } else if (ch == '"' || ch == '\'') { - } else if (quote && ch <= ' ') { - } else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') { - backChar(ch); - break; - } else { - throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")"); - } - } - return negative ? -value : value; - } - - /** - * 读取一个long值 - * - * @return long值 - */ - @Override - public final long readLong() { - char firstchar = nextGoodChar(); - boolean quote = false; - if (firstchar == '"' || firstchar == '\'') { - quote = true; - firstchar = nextGoodChar(); - if (firstchar == '"' || firstchar == '\'') return 0L; - } - long value = 0; - final boolean negative = firstchar == '-'; - if (!negative) { - if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")"); - value = firstchar - '0'; - } - for (;;) { - char ch = nextChar(); - if (ch == 0) break; - if (ch >= '0' && ch <= '9') { - value = (value << 3) + (value << 1) + (ch - '0'); - } else if (ch == '"' || ch == '\'') { - } else if (quote && ch <= ' ') { - } else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') { - backChar(ch); - break; - } else { - throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")"); - } - } - return negative ? -value : value; - } - - /** - * 读取字符串, 必须是"或者'包围的字符串值 - * - * @return String值 - */ - @Override - public final String readString() { - return readSmallString(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.nio.*; +import java.nio.charset.*; +import org.redkale.convert.*; +import static org.redkale.convert.Reader.*; + +/** + * 以ByteBuffer为数据载体的JsonReader
    + * + * 只支持UTF-8格式 + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class JsonByteBufferReader extends JsonReader { + + private char currentChar; + + private ByteBuffer[] buffers; + + private int currentIndex = 0; + + private ByteBuffer currentBuffer; + + protected ConvertMask mask; + + protected JsonByteBufferReader(ConvertMask mask, ByteBuffer... buffers) { + this.mask = mask; + this.buffers = buffers; + if (buffers != null && buffers.length > 0) this.currentBuffer = buffers[currentIndex]; + } + + @Override + protected boolean recycle() { + super.recycle(); // this.position 初始化值为-1 + this.currentIndex = 0; + this.currentChar = 0; + this.currentBuffer = null; + this.buffers = null; + this.mask = null; + return false; + } + + protected byte nextByte() { + if (this.currentBuffer.hasRemaining()) { + this.position++; + return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); + } + for (;;) { + this.currentBuffer = this.buffers[++this.currentIndex]; + if (this.currentBuffer.hasRemaining()) { + this.position++; + return mask == null ? this.currentBuffer.get() : mask.unmask(this.currentBuffer.get()); + } + } + } + + /** + * 读取下一个字符, 不跳过空白字符 + * + * @return 有效字符或空白字符 + */ + @Override + protected final char nextChar() { + return nextChar(null); + } + + protected final char nextChar(StringBuilder sb) { + if (currentChar != 0) { + char ch = currentChar; + this.currentChar = 0; + return ch; + } + if (this.currentBuffer != null) { + int remain = this.currentBuffer.remaining(); + if (remain == 0 && this.currentIndex + 1 >= this.buffers.length) return 0; + } + byte b = nextByte(); + if (b >= 0) {// 1 byte, 7 bits: 0xxxxxxx + return (char) b; + } else if ((b >> 5) == -2 && (b & 0x1e) != 0) { // 2 bytes, 11 bits: 110xxxxx 10xxxxxx + return (char) (((b << 6) ^ nextByte()) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80))); + } else if ((b >> 4) == -2) { // 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + return (char) ((b << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); + } else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int uc = ((b << 18) ^ (nextByte() << 12) ^ (nextByte() << 6) ^ (nextByte() ^ (((byte) 0xF0 << 18) ^ ((byte) 0x80 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); + if (sb != null) sb.append(Character.highSurrogate(uc)); + return Character.lowSurrogate(uc); + } else { + throw new RuntimeException(new UnmappableCharacterException(4)); + } + } + + /** + * 读取下一个有效字符 + * + * @return 有效字符 + */ + @Override + protected final char nextGoodChar() { + char c = nextChar(); + if (c > ' ' || c == 0) return c; // 0 表示buffer结尾了 + for (;;) { + c = nextChar(); + if (c > ' ' || c == 0) return c; + } + } + + /** + * 回退最后读取的字符 + * + * @param ch 回退的字符 + */ + @Override + protected final void backChar(char ch) { + this.currentChar = ch; + } + + /** + * 判断下一个非空白字符是否为{ + * + * @return SIGN_NOLENGTH 或 SIGN_NULL + */ + @Override + public final String readObjectB(final Class clazz) { + char ch = nextGoodChar(); + if (ch == '{') return ""; + if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return null; + if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return null; + StringBuilder sb = new StringBuilder(); + sb.append(ch); + char one; + try { + while ((one = nextChar()) != 0) sb.append(one); + } catch (Exception e) { + } + throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "' in (" + sb + ")"); + } + + /** + * 判断下一个非空白字符是否为[ + * + * @param member DeMember + * @param typevals byte[] + * @param decoder Decodeable + * + * @return SIGN_NOLENGTH 或 SIGN_NULL + */ + @Override + public final int readArrayB(DeMember member, byte[] typevals, Decodeable decoder) { + char ch = nextGoodChar(); + if (ch == '[' || ch == '{') return SIGN_NOLENGTH; + if (ch == 'n' && nextChar() == 'u' && nextChar() == 'l' && nextChar() == 'l') return SIGN_NULL; + if (ch == 'N' && nextChar() == 'U' && nextChar() == 'L' && nextChar() == 'L') return SIGN_NULL; + StringBuilder sb = new StringBuilder(); + sb.append(ch); + char one; + try { + while ((one = nextChar()) != 0) sb.append(one); + } catch (Exception e) { + } + throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "' in (" + sb + ")"); + } + + /** + * 判断下一个非空白字符是否: + */ + @Override + public final void readBlank() { + char ch = nextGoodChar(); + if (ch == ':') return; + StringBuilder sb = new StringBuilder(); + sb.append(ch); + char one; + try { + while ((one = nextChar()) != 0) sb.append(one); + } catch (Exception e) { + } + throw new ConvertException("expected a ':' but '" + ch + "'(position = " + position + ") in (" + sb + ")"); + } + + /** + * 判断对象是否存在下一个属性或者数组是否存在下一个元素 + * + * @param startPosition 起始位置 + * @param contentLength 内容大小, 不确定的传-1 + * + * @return 是否存在 + */ + @Override + public boolean hasNext(int startPosition, int contentLength) { + char ch = nextGoodChar(); + if (ch == ',') return true; + if (ch == '}' || ch == ']' || ch == 0) return false; + backChar(ch); // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取 + return true; + } + + /** + * 读取小字符串 + * + * @return String值 + */ + @Override + public final String readSmallString() { + char ch = nextGoodChar(); + if (ch == 0) return null; + final StringBuilder sb = new StringBuilder(); + if (ch == '"' || ch == '\'') { + final char quote = ch; + for (;;) { + ch = nextChar(sb); + if (ch == '\\') { + char c = nextChar(sb); + switch (c) { + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + case 'n': + sb.append('\n'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + sb.append((char) Integer.parseInt(new String(new char[]{nextChar(), nextChar(), nextChar(), nextChar()}), 16)); + break; + case 't': + sb.append('\t'); + break; + case 'b': + sb.append('\b'); + break; + case 'f': + sb.append('\f'); + break; + default: + throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")"); + } + } else if (ch == quote || ch == 0) { + break; + } else { + sb.append(ch); + } + } + return sb.toString(); + } else { + sb.append(ch); + for (;;) { + ch = nextChar(sb); + if (ch == '\\') { + char c = nextChar(sb); + switch (c) { + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + case 'n': + sb.append('\n'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + sb.append((char) Integer.parseInt(new String(new char[]{nextChar(), nextChar(), nextChar(), nextChar()}), 16)); + break; + case 't': + sb.append('\t'); + break; + case 'b': + sb.append('\b'); + break; + case 'f': + sb.append('\f'); + break; + default: + throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ")"); + } + } else if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') { // ch <= ' ' 包含 0 + backChar(ch); + break; + } else { + sb.append(ch); + } + } + String rs = sb.toString(); + return "null".equalsIgnoreCase(rs) ? null : rs; + } + } + + /** + * 读取一个int值 + * + * @return int值 + */ + @Override + public final int readInt() { + char firstchar = nextGoodChar(); + boolean quote = false; + if (firstchar == '"' || firstchar == '\'') { + quote = true; + firstchar = nextGoodChar(); + if (firstchar == '"' || firstchar == '\'') return 0; + } + int value = 0; + final boolean negative = firstchar == '-'; + if (!negative) { + if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")"); + value = firstchar - '0'; + } + for (;;) { + char ch = nextChar(); + if (ch == 0) break; + if (ch >= '0' && ch <= '9') { + value = (value << 3) + (value << 1) + (ch - '0'); + } else if (ch == '"' || ch == '\'') { + } else if (quote && ch <= ' ') { + } else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') { + backChar(ch); + break; + } else { + throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")"); + } + } + return negative ? -value : value; + } + + /** + * 读取一个long值 + * + * @return long值 + */ + @Override + public final long readLong() { + char firstchar = nextGoodChar(); + boolean quote = false; + if (firstchar == '"' || firstchar == '\'') { + quote = true; + firstchar = nextGoodChar(); + if (firstchar == '"' || firstchar == '\'') return 0L; + } + long value = 0; + final boolean negative = firstchar == '-'; + if (!negative) { + if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + position + ")"); + value = firstchar - '0'; + } + for (;;) { + char ch = nextChar(); + if (ch == 0) break; + if (ch >= '0' && ch <= '9') { + value = (value << 3) + (value << 1) + (ch - '0'); + } else if (ch == '"' || ch == '\'') { + } else if (quote && ch <= ' ') { + } else if (ch == ',' || ch == '}' || ch == ']' || ch <= ' ' || ch == ':') { + backChar(ch); + break; + } else { + throw new ConvertException("illegal escape(" + ch + ") (position = " + position + ")"); + } + } + return negative ? -value : value; + } + + /** + * 读取字符串, 必须是"或者'包围的字符串值 + * + * @return String值 + */ + @Override + public final String readString() { + return readSmallString(); + } + +} diff --git a/src/org/redkale/convert/json/JsonByteBufferWriter.java b/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java similarity index 54% rename from src/org/redkale/convert/json/JsonByteBufferWriter.java rename to src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java index 52b674512..3a3bcfc57 100644 --- a/src/org/redkale/convert/json/JsonByteBufferWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonByteBufferWriter.java @@ -1,410 +1,721 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.nio.*; -import java.nio.charset.*; -import java.util.*; -import java.util.function.*; -import org.redkale.convert.*; -import org.redkale.util.*; - -/** - * 以ByteBuffer为数据载体的JsonWriter - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class JsonByteBufferWriter extends JsonWriter { - - private static final char[] CHARS_TUREVALUE = "true".toCharArray(); - - private static final char[] CHARS_FALSEVALUE = "false".toCharArray(); - - protected Charset charset; - - private final Supplier supplier; - - private ByteBuffer[] buffers; - - private int index; - - protected JsonByteBufferWriter(boolean tiny, Supplier supplier) { - this(tiny, null, supplier); - } - - protected JsonByteBufferWriter(boolean tiny, Charset charset, Supplier supplier) { - this.tiny = tiny; - this.charset = charset; - this.supplier = supplier; - } - - @Override - public JsonByteBufferWriter tiny(boolean tiny) { - this.tiny = tiny; - return this; - } - - @Override - protected boolean recycle() { - super.recycle(); - this.index = 0; - this.specify = null; - this.charset = null; - this.buffers = null; - return false; - } - - public ByteBuffer[] toBuffers() { - if (buffers == null) return new ByteBuffer[0]; - for (int i = index; i < this.buffers.length; i++) { - ByteBuffer buf = this.buffers[i]; - if (buf.position() != 0) buf.flip(); - } - return this.buffers; - } - - public int count() { - if (this.buffers == null) return 0; - int len = 0; - for (ByteBuffer buffer : buffers) { - len += buffer.remaining(); - } - return len; - } - - private int expand(final int byteLength) { - if (this.buffers == null) { - this.index = 0; - this.buffers = new ByteBuffer[]{supplier.get()}; - } - ByteBuffer buffer = this.buffers[index]; - if (!buffer.hasRemaining()) { - buffer.flip(); - buffer = supplier.get(); - this.buffers = Utility.append(this.buffers, buffer); - this.index++; - } - int len = buffer.remaining(); - int size = 0; - while (len < byteLength) { - buffer = supplier.get(); - this.buffers = Utility.append(this.buffers, buffer); - len += buffer.remaining(); - size++; - } - return size; - } - - @Override - public void writeTo(final char ch) { - if (ch > Byte.MAX_VALUE) throw new ConvertException("writeTo char(int.value = " + (int) ch + ") must be less 127"); - expand(1); - this.buffers[index].put((byte) ch); - } - - @Override - public void writeTo(final char[] chs, final int start, final int len) { - writeTo(-1, false, chs, start, len); - } - - @Override - public void writeTo(final byte ch) { //只能是 0 - 127 的字符 - expand(1); - this.buffers[index].put(ch); - } - - @Override - public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符 - int expandsize = expand(len); - if (expandsize == 0) { // 只需要一个buffer - this.buffers[index].put(chs, start, len); - } else { - ByteBuffer buffer = this.buffers[index]; - int remain = len; - int offset = start; - while (remain > 0) { - int bsize = Math.min(buffer.remaining(), remain); - buffer.put(chs, offset, bsize); - offset += bsize; - remain -= bsize; - if (remain < 1) break; - buffer = nextByteBuffer(); - } - } - } - - private void writeTo(int expandsize, final boolean quote, final char[] chs, final int start, final int len) { - int byteLength = quote ? 2 : 0; - ByteBuffer bb = null; - if (charset == null) { - byteLength += Utility.encodeUTF8Length(chs, start, len); - } else { - bb = charset.encode(CharBuffer.wrap(chs, start, len)); - byteLength += bb.remaining(); - } - if (expandsize < 0) expandsize = expand(byteLength); - if (expandsize == 0) { // 只需要一个buffer - final ByteBuffer buffer = this.buffers[index]; - if (quote) buffer.put((byte) '"'); - - if (charset == null) { //UTF-8 - final int limit = start + len; - for (int i = start; i < limit; i++) { - char c = chs[i]; - if (c < 0x80) { - buffer.put((byte) c); - } else if (c < 0x800) { - buffer.put((byte) (0xc0 | (c >> 6))); - buffer.put((byte) (0x80 | (c & 0x3f))); - } else if (Character.isSurrogate(c)) { //连取两个 - int uc = Character.toCodePoint(c, chs[i + 1]); - buffer.put((byte) (0xf0 | ((uc >> 18)))); - buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f))); - buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f))); - buffer.put((byte) (0x80 | (uc & 0x3f))); - i++; - } else { - buffer.put((byte) (0xe0 | ((c >> 12)))); - buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); - buffer.put((byte) (0x80 | (c & 0x3f))); - } - } - } else { - buffer.put(bb); - } - - if (quote) buffer.put((byte) '"'); - return; - } - ByteBuffer buffer = this.buffers[index]; - if (quote) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) '"'); - } - if (charset == null) { //UTF-8 - final int limit = start + len; - for (int i = start; i < limit; i++) { - char c = chs[i]; - if (c < 0x80) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) c); - } else if (c < 0x800) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0xc0 | (c >> 6))); - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0x80 | (c & 0x3f))); - } else if (Character.isSurrogate(c)) { //连取两个 - int uc = Character.toCodePoint(c, chs[i + 1]); - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0xf0 | ((uc >> 18)))); - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f))); - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f))); - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0x80 | (uc & 0x3f))); - i++; - } else { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0xe0 | ((c >> 12)))); - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) (0x80 | (c & 0x3f))); - } - } - } else { - while (bb.hasRemaining()) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put(bb.get()); - } - } - if (quote) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) '"'); - } - } - - private ByteBuffer nextByteBuffer() { - this.buffers[this.index].flip(); - return this.buffers[++this.index]; - } - - protected static int encodeEscapeUTF8Length(final char[] text, final int start, final int len) { - char c; - int size = 0; - final char[] chs = text; - final int limit = start + len; - for (int i = start; i < limit; i++) { - c = chs[i]; - switch (c) { - case '\n': size += 2; - break; - case '\r': size += 2; - break; - case '\t': size += 2; - break; - case '\\': size += 2; - break; - case '"': size += 2; - break; - default: - size += (c < 0x80 ? 1 : (c < 0x800 || Character.isSurrogate(c) ? 2 : 3)); - break; - } - } - return size; - } - - /** - * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String - * - * @param quote 是否写入双引号 - * @param value String值 - */ - @Override - public void writeLatin1To(final boolean quote, final String value) { - byte[] bs = Utility.byteArray(value); - int expandsize = expand(bs.length + (quote ? 2 : 0)); - if (expandsize == 0) {// 只需要一个buffer - final ByteBuffer buffer = this.buffers[index]; - if (quote) buffer.put((byte) '"'); - buffer.put(bs); - if (quote) buffer.put((byte) '"'); - } else { - ByteBuffer buffer = this.buffers[index]; - if (quote) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) '"'); - } - for (byte b : bs) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put(b); - } - if (quote) { - if (!buffer.hasRemaining()) buffer = nextByteBuffer(); - buffer.put((byte) '"'); - } - } - } - - @Override - public void writeBoolean(boolean value) { - writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE); - } - - @Override - public void writeInt(int value) { - writeLatin1To(false, String.valueOf(value)); - } - - @Override - public void writeLong(long value) { - writeLatin1To(false, String.valueOf(value)); - } - - @Override - public void writeString(String value) { - if (value == null) { - writeNull(); - return; - } - final char[] chs = Utility.charArray(value); - int len = 0; - for (char ch : chs) { - switch (ch) { - case '\n': len += 2; - break; - case '\r': len += 2; - break; - case '\t': len += 2; - break; - case '\\': len += 2; - break; - case '"': len += 2; - break; - default: len++; - break; - } - } - if (len == chs.length) { - writeTo(-1, true, chs, 0, len); - return; - } - int expandsize = -1; - if (this.charset == null) { //UTF-8 - final int byteLength = 2 + encodeEscapeUTF8Length(chs, 0, chs.length); - expandsize = expand(byteLength); - if (expandsize == 0) { // 只需要一个buffer - final ByteBuffer buffer = this.buffers[index]; - buffer.put((byte) '"'); - for (int i = 0; i < chs.length; i++) { - char c = chs[i]; - switch (c) { - case '\n': buffer.put((byte) '\\').put((byte) 'n'); - break; - case '\r': buffer.put((byte) '\\').put((byte) 'r'); - break; - case '\t': buffer.put((byte) '\\').put((byte) 't'); - break; - case '\\': buffer.put((byte) '\\').put((byte) '\\'); - break; - case '"': buffer.put((byte) '\\').put((byte) '"'); - break; - default: - if (c < 0x80) { - buffer.put((byte) c); - } else if (c < 0x800) { - buffer.put((byte) (0xc0 | (c >> 6))); - buffer.put((byte) (0x80 | (c & 0x3f))); - } else if (Character.isSurrogate(c)) { //连取两个 - int uc = Character.toCodePoint(c, chs[i + 1]); - buffer.put((byte) (0xf0 | ((uc >> 18)))); - buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f))); - buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f))); - buffer.put((byte) (0x80 | (uc & 0x3f))); - i++; - } else { - buffer.put((byte) (0xe0 | ((c >> 12)))); - buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); - buffer.put((byte) (0x80 | (c & 0x3f))); - } - break; - } - } - buffer.put((byte) '"'); - return; - } - } - StringBuilder sb = new StringBuilder(len); - for (char ch : chs) { - switch (ch) { - case '\n': sb.append("\\n"); - break; - case '\r': sb.append("\\r"); - break; - case '\t': sb.append("\\t"); - break; - case '\\': sb.append("\\\\"); - break; - case '"': sb.append("\\\""); - break; - default: sb.append(ch); - break; - } - } - char[] cs = Utility.charArray(sb); - writeTo(expandsize, true, cs, 0, sb.length()); - } - - @Override - public String toString() { - return Objects.toString(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.nio.*; +import java.nio.charset.*; +import java.util.*; +import java.util.function.*; +import org.redkale.convert.*; +import org.redkale.util.*; + +/** + * 以ByteBuffer为数据载体的JsonWriter + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class JsonByteBufferWriter extends JsonWriter { + + private static final char[] CHARS_TUREVALUE = "true".toCharArray(); + + private static final char[] CHARS_FALSEVALUE = "false".toCharArray(); + + protected Charset charset; + + private final Supplier supplier; + + private ByteBuffer[] buffers; + + private int index; + + public JsonByteBufferWriter(boolean tiny, Supplier supplier) { + this(tiny, null, supplier); + } + + public JsonByteBufferWriter(boolean tiny, Charset charset, Supplier supplier) { + this.tiny = tiny; + this.charset = charset; + this.supplier = supplier; + } + + @Override + public JsonByteBufferWriter tiny(boolean tiny) { + this.tiny = tiny; + return this; + } + + @Override + protected boolean recycle() { + super.recycle(); + this.index = 0; + this.specify = null; + this.charset = null; + this.buffers = null; + return false; + } + + public ByteBuffer[] toBuffers() { + if (buffers == null) return new ByteBuffer[0]; + for (int i = index; i < this.buffers.length; i++) { + ByteBuffer buf = this.buffers[i]; + if (buf.position() != 0) buf.flip(); + } + return this.buffers; + } + + public int count() { + if (this.buffers == null) return 0; + int len = 0; + for (ByteBuffer buffer : buffers) { + len += buffer.remaining(); + } + return len; + } + + private int expand(final int byteLength) { + if (this.buffers == null) { + this.index = 0; + this.buffers = new ByteBuffer[]{supplier.get()}; + } + ByteBuffer buffer = this.buffers[index]; + if (!buffer.hasRemaining()) { + buffer.flip(); + buffer = supplier.get(); + this.buffers = Utility.append(this.buffers, buffer); + this.index++; + } + int len = buffer.remaining(); + int size = 0; + while (len < byteLength) { + buffer = supplier.get(); + this.buffers = Utility.append(this.buffers, buffer); + len += buffer.remaining(); + size++; + } + return size; + } + + @Override + public void writeTo(final char ch) { + if (ch > Byte.MAX_VALUE) throw new ConvertException("writeTo char(int.value = " + (int) ch + ") must be less 127"); + expand(1); + this.buffers[index].put((byte) ch); + } + + @Override + public void writeTo(final char[] chs, final int start, final int len) { + writeTo(-1, false, chs, start, len); + } + + @Override + public void writeTo(final byte ch) { //只能是 0 - 127 的字符 + expand(1); + this.buffers[index].put(ch); + } + + @Override + public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符 + int expandsize = expand(len); + if (expandsize == 0) { // 只需要一个buffer + this.buffers[index].put(chs, start, len); + } else { + ByteBuffer buffer = this.buffers[index]; + int remain = len; + int offset = start; + while (remain > 0) { + int bsize = Math.min(buffer.remaining(), remain); + buffer.put(chs, offset, bsize); + offset += bsize; + remain -= bsize; + if (remain < 1) break; + buffer = nextByteBuffer(); + } + } + } + + private void writeTo(int expandsize, final boolean quote, final char[] chs, final int start, final int len) { + int byteLength = quote ? 2 : 0; + ByteBuffer bb = null; + if (charset == null) { + byteLength += Utility.encodeUTF8Length(chs, start, len); + } else { + bb = charset.encode(CharBuffer.wrap(chs, start, len)); + byteLength += bb.remaining(); + } + if (expandsize < 0) expandsize = expand(byteLength); + if (expandsize == 0) { // 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + if (quote) buffer.put((byte) '"'); + + if (charset == null) { //UTF-8 + final int limit = start + len; + for (int i = start; i < limit; i++) { + char c = chs[i]; + if (c < 0x80) { + buffer.put((byte) c); + } else if (c < 0x800) { + buffer.put((byte) (0xc0 | (c >> 6))); + buffer.put((byte) (0x80 | (c & 0x3f))); + } else if (Character.isSurrogate(c)) { //连取两个 + int uc = Character.toCodePoint(c, chs[i + 1]); + buffer.put((byte) (0xf0 | ((uc >> 18)))); + buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f))); + buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f))); + buffer.put((byte) (0x80 | (uc & 0x3f))); + i++; + } else { + buffer.put((byte) (0xe0 | ((c >> 12)))); + buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); + buffer.put((byte) (0x80 | (c & 0x3f))); + } + } + } else { + buffer.put(bb); + } + + if (quote) buffer.put((byte) '"'); + return; + } + ByteBuffer buffer = this.buffers[index]; + if (quote) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + if (charset == null) { //UTF-8 + final int limit = start + len; + for (int i = start; i < limit; i++) { + char c = chs[i]; + if (c < 0x80) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) c); + } else if (c < 0x800) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0xc0 | (c >> 6))); + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0x80 | (c & 0x3f))); + } else if (Character.isSurrogate(c)) { //连取两个 + int uc = Character.toCodePoint(c, chs[i + 1]); + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0xf0 | ((uc >> 18)))); + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f))); + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f))); + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0x80 | (uc & 0x3f))); + i++; + } else { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0xe0 | ((c >> 12)))); + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) (0x80 | (c & 0x3f))); + } + } + } else { + while (bb.hasRemaining()) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(bb.get()); + } + } + if (quote) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + } + + private ByteBuffer nextByteBuffer() { + this.buffers[this.index].flip(); + return this.buffers[++this.index]; + } + + protected static int encodeEscapeUTF8Length(final char[] text, final int start, final int len) { + char c; + int size = 0; + final char[] chs = text; + final int limit = start + len; + for (int i = start; i < limit; i++) { + c = chs[i]; + switch (c) { + case '\n': size += 2; + break; + case '\r': size += 2; + break; + case '\t': size += 2; + break; + case '\\': size += 2; + break; + case '"': size += 2; + break; + default: + size += (c < 0x80 ? 1 : (c < 0x800 || Character.isSurrogate(c) ? 2 : 3)); + break; + } + } + return size; + } + + /** + * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String + * + * @param quote 是否写入双引号 + * @param value String值 + */ + @Override + public void writeLatin1To(final boolean quote, final String value) { + byte[] bs = Utility.latin1ByteArray(value); + int expandsize = expand(bs.length + (quote ? 2 : 0)); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + if (quote) buffer.put((byte) '"'); + buffer.put(bs); + if (quote) buffer.put((byte) '"'); + } else { + ByteBuffer buffer = this.buffers[index]; + if (quote) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + for (byte b : bs) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + if (quote) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + } + } + + @Override + public void writeFieldShortValue(final byte[] fieldBytes, final short value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); + int expandsize = expand(bs1.length + bs2.length); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put(bs2); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + } + } + + @Override + public void writeFieldIntValue(final byte[] fieldBytes, final int value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); + int expandsize = expand(bs1.length + bs2.length); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put(bs2); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + } + } + + @Override + public void writeFieldLongValue(final byte[] fieldBytes, final long value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); + int expandsize = expand(bs1.length + bs2.length); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put(bs2); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + } + } + + @Override + public void writeFieldLatin1Value(final byte[] fieldBytes, final String value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(value); + int expandsize = expand(bs1.length + bs2.length + 2); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put((byte) '"'); + buffer.put(bs2); + buffer.put((byte) '"'); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + } + } + + @Override + public void writeLastFieldShortValue(final byte[] fieldBytes, final short value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); + int expandsize = expand(bs1.length + bs2.length + 1); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put(bs2); + buffer.put((byte) '}'); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '}'); + } + } + } + + @Override + public void writeLastFieldIntValue(final byte[] fieldBytes, final int value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); + int expandsize = expand(bs1.length + bs2.length + 1); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put(bs2); + buffer.put((byte) '}'); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '}'); + } + } + } + + @Override + public void writeLastFieldLongValue(final byte[] fieldBytes, final long value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value)); + int expandsize = expand(bs1.length + bs2.length + 1); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put(bs2); + buffer.put((byte) '}'); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '}'); + } + } + } + + @Override + public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + if (value == null || (tiny && value.isEmpty())) { + expand(1); + this.buffers[index].put((byte) '}'); + } else { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(value); + int expandsize = expand(bs1.length + bs2.length + 3); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put((byte) '"'); + buffer.put(bs2); + buffer.put((byte) '"'); + buffer.put((byte) '}'); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '}'); + } + } + } + } + + @Override //firstFieldBytes 必须带{开头 + public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + if (value == null || (tiny && value.isEmpty())) { + int expandsize = expand(2); + if (expandsize == 0) { // 只需要一个buffer + ByteBuffer bb = this.buffers[index]; + bb.put((byte) '{'); + bb.put((byte) '}'); + } else { + ByteBuffer bb = this.buffers[index]; + bb.put((byte) '{'); + bb = nextByteBuffer(); + bb.put((byte) '}'); + } + } else { + byte[] bs1 = firstFieldBytes; + byte[] bs2 = Utility.latin1ByteArray(value); + int expandsize = expand(bs1.length + bs2.length + 3); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put((byte) '"'); + buffer.put(bs2); + buffer.put((byte) '"'); + buffer.put((byte) '}'); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '"'); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '}'); + } + } + } + } + + @Override //firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 + public void writeObjectByOnlyTwoIntFieldValue(final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2) { + byte[] bs1 = firstFieldBytes; + byte[] bs2 = Utility.latin1ByteArray(String.valueOf(value1)); + byte[] bs3 = lastFieldBytes; + byte[] bs4 = Utility.latin1ByteArray(String.valueOf(value2)); + int expandsize = expand(bs1.length + bs2.length + bs3.length + bs4.length + 1); + if (expandsize == 0) {// 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put(bs1); + buffer.put(bs2); + buffer.put(bs3); + buffer.put(bs4); + buffer.put((byte) '}'); + } else { + ByteBuffer buffer = this.buffers[index]; + for (byte b : bs1) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs2) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs3) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + for (byte b : bs4) { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put(b); + } + { + if (!buffer.hasRemaining()) buffer = nextByteBuffer(); + buffer.put((byte) '}'); + } + } + } + + @Override + public void writeBoolean(boolean value) { + writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE); + } + + @Override + public void writeInt(int value) { + writeLatin1To(false, String.valueOf(value)); + } + + @Override + public void writeLong(long value) { + writeLatin1To(false, String.valueOf(value)); + } + + @Override + public void writeString(String value) { + if (value == null) { + writeNull(); + return; + } + final char[] chs = Utility.charArray(value); + int len = 0; + for (char ch : chs) { + switch (ch) { + case '\n': len += 2; + break; + case '\r': len += 2; + break; + case '\t': len += 2; + break; + case '\\': len += 2; + break; + case '"': len += 2; + break; + default: len++; + break; + } + } + if (len == chs.length) { + writeTo(-1, true, chs, 0, len); + return; + } + int expandsize = -1; + if (this.charset == null) { //UTF-8 + final int byteLength = 2 + encodeEscapeUTF8Length(chs, 0, chs.length); + expandsize = expand(byteLength); + if (expandsize == 0) { // 只需要一个buffer + final ByteBuffer buffer = this.buffers[index]; + buffer.put((byte) '"'); + for (int i = 0; i < chs.length; i++) { + char c = chs[i]; + switch (c) { + case '\n': buffer.put((byte) '\\').put((byte) 'n'); + break; + case '\r': buffer.put((byte) '\\').put((byte) 'r'); + break; + case '\t': buffer.put((byte) '\\').put((byte) 't'); + break; + case '\\': buffer.put((byte) '\\').put((byte) '\\'); + break; + case '"': buffer.put((byte) '\\').put((byte) '"'); + break; + default: + if (c < 0x80) { + buffer.put((byte) c); + } else if (c < 0x800) { + buffer.put((byte) (0xc0 | (c >> 6))); + buffer.put((byte) (0x80 | (c & 0x3f))); + } else if (Character.isSurrogate(c)) { //连取两个 + int uc = Character.toCodePoint(c, chs[i + 1]); + buffer.put((byte) (0xf0 | ((uc >> 18)))); + buffer.put((byte) (0x80 | ((uc >> 12) & 0x3f))); + buffer.put((byte) (0x80 | ((uc >> 6) & 0x3f))); + buffer.put((byte) (0x80 | (uc & 0x3f))); + i++; + } else { + buffer.put((byte) (0xe0 | ((c >> 12)))); + buffer.put((byte) (0x80 | ((c >> 6) & 0x3f))); + buffer.put((byte) (0x80 | (c & 0x3f))); + } + break; + } + } + buffer.put((byte) '"'); + return; + } + } + StringBuilder sb = new StringBuilder(len); + for (char ch : chs) { + switch (ch) { + case '\n': sb.append("\\n"); + break; + case '\r': sb.append("\\r"); + break; + case '\t': sb.append("\\t"); + break; + case '\\': sb.append("\\\\"); + break; + case '"': sb.append("\\\""); + break; + default: sb.append(ch); + break; + } + } + char[] cs = Utility.charArray(sb); + writeTo(expandsize, true, cs, 0, sb.length()); + } + + @Override + public String toString() { + return Objects.toString(this); + } +} diff --git a/src/org/redkale/convert/json/JsonBytesWriter.java b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java similarity index 65% rename from src/org/redkale/convert/json/JsonBytesWriter.java rename to src/main/java/org/redkale/convert/json/JsonBytesWriter.java index 13dafc66a..c4bc555e2 100644 --- a/src/org/redkale/convert/json/JsonBytesWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonBytesWriter.java @@ -1,419 +1,582 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.lang.reflect.Type; -import java.nio.charset.StandardCharsets; -import java.util.function.Consumer; -import org.redkale.convert.*; -import static org.redkale.convert.json.JsonWriter.*; -import org.redkale.util.*; - -/** - * - * writeTo系列的方法输出的字符不能含特殊字符 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.3.0 - */ -public class JsonBytesWriter extends JsonWriter implements ByteTuple { - - private static final boolean greatejdk8 = Utility.greaterJDK8(); - - private static final byte[] BYTES_TUREVALUE = "true".getBytes(); - - private static final byte[] BYTES_FALSEVALUE = "false".getBytes(); - - private static final int TENTHOUSAND_MAX = 10001; - - private static final byte[][] TENTHOUSAND_BYTES = new byte[TENTHOUSAND_MAX][]; - - static { - for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) { - TENTHOUSAND_BYTES[i] = String.valueOf(i).getBytes(); - } - } - - private int count; - - private byte[] content; - - public JsonBytesWriter() { - this(defaultSize); - } - - public JsonBytesWriter(int size) { - this.content = new byte[size > 1024 ? size : 1024]; - } - - public JsonBytesWriter(ByteArray array) { - this.content = array.content(); - this.count = array.length(); - } - - public JsonBytesWriter(boolean tiny, ByteArray array) { - this.tiny = tiny; - this.content = array.content(); - this.count = array.length(); - } - - protected byte[] expand(int len) { - int newcount = count + len; - if (newcount <= content.length) return content; - byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)]; - System.arraycopy(content, 0, newdata, 0, count); - this.content = newdata; - return newdata; - } - - @Override - public byte[] content() { - return content; - } - - @Override - public int offset() { - return 0; - } - - @Override - public int length() { - return count; - } - - /** - * 将本对象的内容引用复制给array - * - * @param array ByteArray - */ - public void directTo(ByteArray array) { - array.directFrom(content, count); - } - - @Override - public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) { - if (this.comma) writeTo(','); - if (member != null) { - byte[] bs = member.getJsonFieldNameBytes(); - expand(bs.length); - System.arraycopy(bs, 0, content, count, bs.length); - count += bs.length; - } else { - writeLatin1To(true, fieldName); - writeTo(':'); - } - } - - @Override - public void writeTo(final char ch) { //只能是 0 - 127 的字符 - expand(1); - content[count++] = (byte) ch; - } - - @Override - public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符 - expand(len); - for (int i = 0; i < len; i++) { - content[count + i] = (byte) chs[start + i]; - } - count += len; - } - - @Override - public void writeTo(final byte ch) { //只能是 0 - 127 的字符 - expand(1); - content[count++] = ch; - } - - @Override - public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符 - expand(len); - System.arraycopy(chs, start, content, count, len); - count += len; - } - - /** - * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String - * - * @param quote 是否加双引号 - * @param value 非null且不含需要转义的字符的String值 - */ - @Override - public void writeLatin1To(final boolean quote, final String value) { - byte[] bs = Utility.byteArray(value); - int len = bs.length; - if (quote) { - byte[] src = expand(len + 2); - src[count++] = '"'; - System.arraycopy(bs, 0, src, count, bs.length); - count += len; - src[count++] = '"'; - } else { - byte[] src = expand(len); - System.arraycopy(bs, 0, src, count, bs.length); - count += len; - } - } - - public JsonBytesWriter clear() { - this.count = 0; - return this; - } - - @Override - public boolean recycle() { - super.recycle(); - this.count = 0; - this.specify = null; - if (this.content != null && this.content.length > defaultSize * 100) { - this.content = new byte[defaultSize]; - } - return true; - } - - /** - * 直接获取全部数据, 实际数据需要根据count长度来截取 - * - * @return byte[] - */ - public byte[] directBytes() { - return content; - } - - public void completed(ConvertBytesHandler handler, Consumer callback) { - handler.completed(content, 0, count, callback, this); - } - - public byte[] toBytes() { - byte[] copy = new byte[count]; - System.arraycopy(content, 0, copy, 0, count); - return copy; - } - - public int count() { - return this.count; - } - - private void writeEscapeLatinString(byte[] value) { - byte[] bytes = expand(value.length * 2 + 2); - int curr = count; - bytes[curr++] = '"'; - for (byte b : value) { - if (b == '"') { - bytes[curr++] = '\\'; - bytes[curr++] = '"'; - } else if (b == '\\') { - bytes[curr++] = '\\'; - bytes[curr++] = '\\'; - } else if (b < 32) { - if (b == '\n') { - bytes[curr++] = '\\'; - bytes[curr++] = 'n'; - } else if (b == '\r') { - bytes[curr++] = '\\'; - bytes[curr++] = 'r'; - } else if (b == '\t') { - bytes[curr++] = '\\'; - bytes[curr++] = 't'; - } else { - bytes[curr++] = b; - } - } else { - bytes[curr++] = b; - } - } - bytes[curr++] = '"'; - count = curr; - } - - @Override - public void writeString(String value) { - if (value == null) { - writeNull(); - return; - } - if (greatejdk8 && Utility.isLatin1(value)) { - writeEscapeLatinString(Utility.byteArray(value)); - return; - } - byte[] bytes = expand(value.length() * 4 + 2); - int curr = count; - bytes[curr++] = '"'; - int len = value.length(); - for (int i = 0; i < len; i++) { - char ch = value.charAt(i); - switch (ch) { - case '\n': - bytes[curr++] = '\\'; - bytes[curr++] = 'n'; - break; - case '\r': - bytes[curr++] = '\\'; - bytes[curr++] = 'r'; - break; - case '\t': - bytes[curr++] = '\\'; - bytes[curr++] = 't'; - break; - case '\\': - bytes[curr++] = '\\'; - bytes[curr++] = '\\'; - break; - case '"': - bytes[curr++] = '\\'; - bytes[curr++] = '"'; - break; - default: - if (ch < 0x80) { - bytes[curr++] = (byte) ch; - } else if (ch < 0x800) { - bytes[curr++] = (byte) (0xc0 | (ch >> 6)); - bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); - } else if (Character.isSurrogate(ch)) { //连取两个 - int uc = Character.toCodePoint(ch, value.charAt(++i)); - bytes[curr++] = (byte) (0xf0 | ((uc >> 18))); - bytes[curr++] = (byte) (0x80 | ((uc >> 12) & 0x3f)); - bytes[curr++] = (byte) (0x80 | ((uc >> 6) & 0x3f)); - bytes[curr++] = (byte) (0x80 | (uc & 0x3f)); - } else { - bytes[curr++] = (byte) (0xe0 | ((ch >> 12))); - bytes[curr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); - bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); - } - break; - } - } - bytes[curr++] = '"'; - count = curr; - } - - @Override - public String toString() { - return new String(content, 0, count, StandardCharsets.UTF_8); - } - - //---------------------------------------------------------------------------------------------- - @Override - public void writeBoolean(boolean value) { - byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE; - expand(bs.length); - System.arraycopy(bs, 0, content, count, bs.length); - count += bs.length; - } - - @Override - public void writeInt(int value) { - if (value >= 0 && value < TENTHOUSAND_MAX) { - byte[] bs = TENTHOUSAND_BYTES[value]; - expand(bs.length); - System.arraycopy(bs, 0, content, count, bs.length); - count += bs.length; - return; - } - final char sign = value >= 0 ? 0 : '-'; - if (value < 0) value = -value; - int size; - for (int i = 0;; i++) { - if (value <= sizeTable[i]) { - size = i + 1; - break; - } - } - if (sign != 0) size++; //负数 - byte[] bytes = expand(size); - - int q, r; - int charPos = count + size; - - // Generate two digits per iteration - while (value >= 65536) { - q = value / 100; - // really: r = i - (q * 100); - r = value - ((q << 6) + (q << 5) + (q << 2)); - value = q; - bytes[--charPos] = (byte) DigitOnes[r]; - bytes[--charPos] = (byte) DigitTens[r]; - } - - // Fall thru to fast mode for smaller numbers - // assert(i <= 65536, i); - for (;;) { - q = (value * 52429) >>> (16 + 3); - r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ... - bytes[--charPos] = (byte) digits[r]; - value = q; - if (value == 0) break; - } - if (sign != 0) bytes[--charPos] = (byte) sign; - count += size; - } - - @Override - public void writeLong(long value) { - if (value >= 0 && value < TENTHOUSAND_MAX) { - byte[] bs = TENTHOUSAND_BYTES[(int) value]; - expand(bs.length); - System.arraycopy(bs, 0, content, count, bs.length); - count += bs.length; - return; - } - final char sign = value >= 0 ? 0 : '-'; - if (value < 0) value = -value; - int size = 19; - long p = 10; - for (int i = 1; i < 19; i++) { - if (value < p) { - size = i; - break; - } - p = 10 * p; - } - if (sign != 0) size++; //负数 - byte[] bytes = expand(size); - - long q; - int r; - int charPos = count + size; - - // Get 2 digits/iteration using longs until quotient fits into an int - while (value > Integer.MAX_VALUE) { - q = value / 100; - // really: r = i - (q * 100); - r = (int) (value - ((q << 6) + (q << 5) + (q << 2))); - value = q; - content[--charPos] = (byte) DigitOnes[r]; - content[--charPos] = (byte) DigitTens[r]; - } - - // Get 2 digits/iteration using ints - int q2; - int i2 = (int) value; - while (i2 >= 65536) { - q2 = i2 / 100; - // really: r = i2 - (q * 100); - r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); - i2 = q2; - bytes[--charPos] = (byte) DigitOnes[r]; - bytes[--charPos] = (byte) DigitTens[r]; - } - - // Fall thru to fast mode for smaller numbers - // assert(i2 <= 65536, i2); - for (;;) { - q2 = (i2 * 52429) >>> (16 + 3); - r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... - bytes[--charPos] = (byte) digits[r]; - i2 = q2; - if (i2 == 0) break; - } - if (sign != 0) bytes[--charPos] = (byte) sign; - count += size; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.lang.reflect.Type; +import java.nio.charset.StandardCharsets; +import java.util.function.Consumer; +import org.redkale.convert.*; +import static org.redkale.convert.json.JsonWriter.*; +import org.redkale.util.*; + +/** + * + * writeTo系列的方法输出的字符不能含特殊字符 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + */ +public class JsonBytesWriter extends JsonWriter implements ByteTuple { + + private static final byte[] BYTES_TUREVALUE = "true".getBytes(); + + private static final byte[] BYTES_FALSEVALUE = "false".getBytes(); + + private static final int TENTHOUSAND_MAX = 10001; + + private static final byte[][] TENTHOUSAND_BYTES = new byte[TENTHOUSAND_MAX][]; + + static { + for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) { + TENTHOUSAND_BYTES[i] = String.valueOf(i).getBytes(); + } + } + + private int count; + + private byte[] content; + + public JsonBytesWriter() { + this(defaultSize); + } + + public JsonBytesWriter(int size) { + this.content = new byte[size > 1024 ? size : 1024]; + } + + public JsonBytesWriter(ByteArray array) { + this.content = array.content(); + this.count = array.length(); + } + + public JsonBytesWriter(boolean tiny, ByteArray array) { + this.tiny = tiny; + this.content = array.content(); + this.count = array.length(); + } + + protected byte[] expand(int len) { + int newcount = count + len; + if (newcount <= content.length) return content; + byte[] newdata = new byte[Math.max(content.length * 3 / 2, newcount)]; + System.arraycopy(content, 0, newdata, 0, count); + this.content = newdata; + return newdata; + } + + @Override + public byte[] content() { + return content; + } + + @Override + public int offset() { + return 0; + } + + @Override + public int length() { + return count; + } + + /** + * 将本对象的内容引用复制给array + * + * @param array ByteArray + */ + public void directTo(ByteArray array) { + array.directFrom(content, count); + } + + @Override + public final void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) { + if (this.comma) writeTo(','); + if (member != null) { + byte[] bs = member.getJsonFieldNameBytes(); + expand(bs.length); + System.arraycopy(bs, 0, content, count, bs.length); + count += bs.length; + } else { + writeLatin1To(true, fieldName); + writeTo(':'); + } + } + + @Override + public void writeTo(final char ch) { //只能是 0 - 127 的字符 + expand(1); + content[count++] = (byte) ch; + } + + @Override + public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符 + expand(len); + for (int i = 0; i < len; i++) { + content[count + i] = (byte) chs[start + i]; + } + count += len; + } + + @Override + public void writeTo(final byte ch) { //只能是 0 - 127 的字符 + expand(1); + content[count++] = ch; + } + + @Override + public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符 + expand(len); + System.arraycopy(chs, start, content, count, len); + count += len; + } + + /** + * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String + * + * @param quote 是否加双引号 + * @param value 非null且不含需要转义的字符的String值 + */ + @Override + public void writeLatin1To(final boolean quote, final String value) { + byte[] bs = Utility.latin1ByteArray(value); + int len = bs.length; + if (quote) { + byte[] src = expand(len + 2); + src[count++] = '"'; + System.arraycopy(bs, 0, src, count, bs.length); + count += len; + src[count++] = '"'; + } else { + byte[] src = expand(len); + System.arraycopy(bs, 0, src, count, bs.length); + count += len; + } + } + + @Override + public void writeFieldShortValue(final byte[] fieldBytes, final short value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] : Utility.latin1ByteArray(String.valueOf(value)); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + } + + @Override + public void writeFieldIntValue(final byte[] fieldBytes, final int value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] : Utility.latin1ByteArray(String.valueOf(value)); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + } + + @Override + public void writeFieldLongValue(final byte[] fieldBytes, final long value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] : Utility.latin1ByteArray(String.valueOf(value)); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + } + + @Override + public void writeFieldLatin1Value(final byte[] fieldBytes, final String value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(value); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2 + 2); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + src[count++] = '"'; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + src[count++] = '"'; + } + + @Override + public void writeLastFieldShortValue(final byte[] fieldBytes, final short value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] : Utility.latin1ByteArray(String.valueOf(value)); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + src[count++] = '}'; + } + + @Override + public void writeLastFieldIntValue(final byte[] fieldBytes, final int value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value] : Utility.latin1ByteArray(String.valueOf(value)); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + src[count++] = '}'; + } + + @Override + public void writeLastFieldLongValue(final byte[] fieldBytes, final long value) { + byte[] bs1 = fieldBytes; + byte[] bs2 = (value >= 0 && value < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[(int) value] : Utility.latin1ByteArray(String.valueOf(value)); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + src[count++] = '}'; + } + + @Override + public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + if (value == null || (tiny && value.isEmpty())) { + expand(1); + content[count++] = '}'; + } else { + byte[] bs1 = fieldBytes; + byte[] bs2 = Utility.latin1ByteArray(value); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2 + 3); + int c = count; + System.arraycopy(bs1, 0, src, c, len1); + c += len1; + src[c++] = '"'; + System.arraycopy(bs2, 0, src, c, len2); + c += len2; + src[c++] = '"'; + src[c++] = '}'; + count = c; + } + } + + @Override //firstFieldBytes 必须带{开头 + public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + if (value == null || (tiny && value.isEmpty())) { + expand(2); + content[count++] = '{'; + content[count++] = '}'; + } else { + byte[] bs1 = firstFieldBytes; + byte[] bs2 = Utility.latin1ByteArray(value); + int len1 = bs1.length; + int len2 = bs2.length; + byte[] src = expand(len1 + len2 + 3); + int c = count; + System.arraycopy(bs1, 0, src, c, len1); + c += len1; + src[c++] = '"'; + System.arraycopy(bs2, 0, src, c, len2); + c += len2; + src[c++] = '"'; + content[c++] = '}'; + count = c; + } + } + + @Override //firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 + public void writeObjectByOnlyTwoIntFieldValue(final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2) { + byte[] bs1 = firstFieldBytes; + byte[] bs2 = (value1 >= 0 && value1 < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value1] : Utility.latin1ByteArray(String.valueOf(value1)); + byte[] bs3 = lastFieldBytes; + byte[] bs4 = (value2 >= 0 && value2 < TENTHOUSAND_MAX) ? TENTHOUSAND_BYTES[value2] : Utility.latin1ByteArray(String.valueOf(value2)); + int len1 = bs1.length; + int len2 = bs2.length; + int len3 = bs3.length; + int len4 = bs4.length; + byte[] src = expand(len1 + len2 + len3 + len4 + 1); + System.arraycopy(bs1, 0, src, count, len1); + count += len1; + System.arraycopy(bs2, 0, src, count, len2); + count += len2; + System.arraycopy(bs3, 0, src, count, len3); + count += len3; + System.arraycopy(bs4, 0, src, count, len4); + count += len4; + src[count++] = '}'; + } + + public JsonBytesWriter clear() { + this.count = 0; + return this; + } + + @Override + public boolean recycle() { + super.recycle(); + this.count = 0; + this.specify = null; + if (this.content != null && this.content.length > defaultSize * 100) { + this.content = new byte[defaultSize]; + } + return true; + } + + /** + * 直接获取全部数据, 实际数据需要根据count长度来截取 + * + * @return byte[] + */ + public byte[] directBytes() { + return content; + } + + public void completed(ConvertBytesHandler handler, Consumer callback) { + handler.completed(content, 0, count, callback, this); + } + + public byte[] toBytes() { + byte[] copy = new byte[count]; + System.arraycopy(content, 0, copy, 0, count); + return copy; + } + + public int count() { + return this.count; + } + + private void writeEscapeLatinString(byte[] value) { + byte[] bytes = expand(value.length * 2 + 2); + int curr = count; + bytes[curr++] = '"'; + for (byte b : value) { + if (b == '"') { + bytes[curr++] = '\\'; + bytes[curr++] = '"'; + } else if (b == '\\') { + bytes[curr++] = '\\'; + bytes[curr++] = '\\'; + } else if (b < 32) { + if (b == '\n') { + bytes[curr++] = '\\'; + bytes[curr++] = 'n'; + } else if (b == '\r') { + bytes[curr++] = '\\'; + bytes[curr++] = 'r'; + } else if (b == '\t') { + bytes[curr++] = '\\'; + bytes[curr++] = 't'; + } else { + bytes[curr++] = b; + } + } else { + bytes[curr++] = b; + } + } + bytes[curr++] = '"'; + count = curr; + } + + @Override + public void writeString(String value) { + if (value == null) { + writeNull(); + return; + } + if (Utility.isLatin1(value)) { + writeEscapeLatinString(Utility.latin1ByteArray(value)); + return; + } + byte[] bytes = expand(value.length() * 4 + 2); + int curr = count; + bytes[curr++] = '"'; + int len = value.length(); + for (int i = 0; i < len; i++) { + char ch = value.charAt(i); + switch (ch) { + case '\n': + bytes[curr++] = '\\'; + bytes[curr++] = 'n'; + break; + case '\r': + bytes[curr++] = '\\'; + bytes[curr++] = 'r'; + break; + case '\t': + bytes[curr++] = '\\'; + bytes[curr++] = 't'; + break; + case '\\': + bytes[curr++] = '\\'; + bytes[curr++] = '\\'; + break; + case '"': + bytes[curr++] = '\\'; + bytes[curr++] = '"'; + break; + default: + if (ch < 0x80) { + bytes[curr++] = (byte) ch; + } else if (ch < 0x800) { + bytes[curr++] = (byte) (0xc0 | (ch >> 6)); + bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); + } else if (Character.isSurrogate(ch)) { //连取两个 + int uc = Character.toCodePoint(ch, value.charAt(++i)); + bytes[curr++] = (byte) (0xf0 | ((uc >> 18))); + bytes[curr++] = (byte) (0x80 | ((uc >> 12) & 0x3f)); + bytes[curr++] = (byte) (0x80 | ((uc >> 6) & 0x3f)); + bytes[curr++] = (byte) (0x80 | (uc & 0x3f)); + } else { + bytes[curr++] = (byte) (0xe0 | ((ch >> 12))); + bytes[curr++] = (byte) (0x80 | ((ch >> 6) & 0x3f)); + bytes[curr++] = (byte) (0x80 | (ch & 0x3f)); + } + break; + } + } + bytes[curr++] = '"'; + count = curr; + } + + @Override + public String toString() { + return new String(content, 0, count, StandardCharsets.UTF_8); + } + + //---------------------------------------------------------------------------------------------- + @Override + public void writeBoolean(boolean value) { + byte[] bs = value ? BYTES_TUREVALUE : BYTES_FALSEVALUE; + expand(bs.length); + System.arraycopy(bs, 0, content, count, bs.length); + count += bs.length; + } + + @Override + public void writeInt(int value) { + if (value >= 0 && value < TENTHOUSAND_MAX) { + byte[] bs = TENTHOUSAND_BYTES[value]; + expand(bs.length); + System.arraycopy(bs, 0, content, count, bs.length); + count += bs.length; + return; + } + final char sign = value >= 0 ? 0 : '-'; + if (value < 0) value = -value; + int size; + for (int i = 0;; i++) { + if (value <= sizeTable[i]) { + size = i + 1; + break; + } + } + if (sign != 0) size++; //负数 + byte[] bytes = expand(size); + + int q, r; + int charPos = count + size; + + // Generate two digits per iteration + while (value >= 65536) { + q = value / 100; + // really: r = i - (q * 100); + r = value - ((q << 6) + (q << 5) + (q << 2)); + value = q; + bytes[--charPos] = (byte) DigitOnes[r]; + bytes[--charPos] = (byte) DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i <= 65536, i); + for (;;) { + q = (value * 52429) >>> (16 + 3); + r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ... + bytes[--charPos] = (byte) digits[r]; + value = q; + if (value == 0) break; + } + if (sign != 0) bytes[--charPos] = (byte) sign; + count += size; + } + + @Override + public void writeLong(long value) { + if (value >= 0 && value < TENTHOUSAND_MAX) { + byte[] bs = TENTHOUSAND_BYTES[(int) value]; + expand(bs.length); + System.arraycopy(bs, 0, content, count, bs.length); + count += bs.length; + return; + } + final char sign = value >= 0 ? 0 : '-'; + if (value < 0) value = -value; + int size = 19; + long p = 10; + for (int i = 1; i < 19; i++) { + if (value < p) { + size = i; + break; + } + p = 10 * p; + } + if (sign != 0) size++; //负数 + byte[] bytes = expand(size); + + long q; + int r; + int charPos = count + size; + + // Get 2 digits/iteration using longs until quotient fits into an int + while (value > Integer.MAX_VALUE) { + q = value / 100; + // really: r = i - (q * 100); + r = (int) (value - ((q << 6) + (q << 5) + (q << 2))); + value = q; + content[--charPos] = (byte) DigitOnes[r]; + content[--charPos] = (byte) DigitTens[r]; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int) value; + while (i2 >= 65536) { + q2 = i2 / 100; + // really: r = i2 - (q * 100); + r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); + i2 = q2; + bytes[--charPos] = (byte) DigitOnes[r]; + bytes[--charPos] = (byte) DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i2 <= 65536, i2); + for (;;) { + q2 = (i2 * 52429) >>> (16 + 3); + r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... + bytes[--charPos] = (byte) digits[r]; + i2 = q2; + if (i2 == 0) break; + } + if (sign != 0) bytes[--charPos] = (byte) sign; + count += size; + } + +} diff --git a/src/org/redkale/convert/json/JsonCharsWriter.java b/src/main/java/org/redkale/convert/json/JsonCharsWriter.java similarity index 58% rename from src/org/redkale/convert/json/JsonCharsWriter.java rename to src/main/java/org/redkale/convert/json/JsonCharsWriter.java index ef1e6dcff..8f9ca1404 100644 --- a/src/org/redkale/convert/json/JsonCharsWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonCharsWriter.java @@ -1,285 +1,461 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.nio.ByteBuffer; -import org.redkale.util.*; - -/** - * - * writeTo系列的方法输出的字符不能含特殊字符 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.3.0 - */ -public class JsonCharsWriter extends JsonWriter { - - private static final char[] CHARS_TUREVALUE = "true".toCharArray(); - - private static final char[] CHARS_FALSEVALUE = "false".toCharArray(); - - private static final int TENTHOUSAND_MAX = 10001; - - private static final char[][] TENTHOUSAND_BYTES = new char[TENTHOUSAND_MAX][]; - - static { - for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) { - TENTHOUSAND_BYTES[i] = String.valueOf(i).toCharArray(); - } - } - - private int count; - - private char[] content; - - public JsonCharsWriter() { - this(defaultSize); - } - - public JsonCharsWriter(int size) { - this.content = new char[size > 1024 ? size : 1024]; - } - - //----------------------------------------------------------------------- - /** - * 返回指定至少指定长度的缓冲区 - * - * @param len - * - * @return - */ - private char[] expand(int len) { - int newcount = count + len; - if (newcount <= content.length) return content; - char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)]; - System.arraycopy(content, 0, newdata, 0, count); - this.content = newdata; - return newdata; - } - - @Override - public void writeTo(final char ch) { //只能是 0 - 127 的字符 - expand(1); - content[count++] = ch; - } - - @Override - public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符 - expand(len); - System.arraycopy(chs, start, content, count, len); - count += len; - } - - @Override - public void writeTo(final byte ch) { //只能是 0 - 127 的字符 - expand(1); - content[count++] = (char) ch; - } - - @Override - public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符 - expand(len); - for (int i = 0; i < len; i++) { - content[count + i] = (char) chs[start + i]; - } - count += len; - } - - /** - * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String - * - * @param quote 是否加双引号 - * @param value 非null且不含需要转义的字符的String值 - */ - @Override - public void writeLatin1To(final boolean quote, final String value) { - int len = value.length(); - expand(len + (quote ? 2 : 0)); - if (quote) content[count++] = '"'; - value.getChars(0, len, content, count); - count += len; - if (quote) content[count++] = '"'; - } - - @Override - protected boolean recycle() { - super.recycle(); - this.count = 0; - this.specify = null; - if (this.content != null && this.content.length > defaultSize) { - this.content = new char[defaultSize]; - } - return true; - } - - public ByteBuffer[] toBuffers() { - return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))}; - } - - public byte[] toBytes() { - return Utility.encodeUTF8(content, 0, count); - } - - public int count() { - return this.count; - } - - @Override - public void writeString(String value) { - if (value == null) { - writeNull(); - return; - } - expand(value.length() * 2 + 2); - content[count++] = '"'; - for (char ch : Utility.charArray(value)) { - switch (ch) { - case '\n': - content[count++] = '\\'; - content[count++] = 'n'; - break; - case '\r': - content[count++] = '\\'; - content[count++] = 'r'; - break; - case '\t': - content[count++] = '\\'; - content[count++] = 't'; - break; - case '\\': - content[count++] = '\\'; - content[count++] = ch; - break; - case '"': - content[count++] = '\\'; - content[count++] = ch; - break; - default: - content[count++] = ch; - break; - } - } - content[count++] = '"'; - } - - @Override - public String toString() { - return new String(content, 0, count); - } - - //---------------------------------------------------------------------------------------------- - @Override - public void writeBoolean(boolean value) { - writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE); - } - - @Override - public void writeInt(int value) { - if (value >= 0 && value < TENTHOUSAND_MAX) { - writeTo(TENTHOUSAND_BYTES[value]); - return; - } - final char sign = value >= 0 ? 0 : '-'; - if (value < 0) value = -value; - int size; - for (int i = 0;; i++) { - if (value <= sizeTable[i]) { - size = i + 1; - break; - } - } - if (sign != 0) size++; //负数 - expand(size); - - int q, r; - int charPos = count + size; - - // Generate two digits per iteration - while (value >= 65536) { - q = value / 100; - // really: r = i - (q * 100); - r = value - ((q << 6) + (q << 5) + (q << 2)); - value = q; - content[--charPos] = DigitOnes[r]; - content[--charPos] = DigitTens[r]; - } - - // Fall thru to fast mode for smaller numbers - // assert(i <= 65536, i); - for (;;) { - q = (value * 52429) >>> (16 + 3); - r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ... - content[--charPos] = digits[r]; - value = q; - if (value == 0) break; - } - if (sign != 0) content[--charPos] = sign; - count += size; - } - - @Override - public void writeLong(long value) { - if (value >= 0 && value < TENTHOUSAND_MAX) { - writeTo(TENTHOUSAND_BYTES[(int) value]); - return; - } - final char sign = value >= 0 ? 0 : '-'; - if (value < 0) value = -value; - int size = 19; - long p = 10; - for (int i = 1; i < 19; i++) { - if (value < p) { - size = i; - break; - } - p = 10 * p; - } - if (sign != 0) size++; //负数 - expand(size); - - long q; - int r; - int charPos = count + size; - - // Get 2 digits/iteration using longs until quotient fits into an int - while (value > Integer.MAX_VALUE) { - q = value / 100; - // really: r = i - (q * 100); - r = (int) (value - ((q << 6) + (q << 5) + (q << 2))); - value = q; - content[--charPos] = DigitOnes[r]; - content[--charPos] = DigitTens[r]; - } - - // Get 2 digits/iteration using ints - int q2; - int i2 = (int) value; - while (i2 >= 65536) { - q2 = i2 / 100; - // really: r = i2 - (q * 100); - r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); - i2 = q2; - content[--charPos] = DigitOnes[r]; - content[--charPos] = DigitTens[r]; - } - - // Fall thru to fast mode for smaller numbers - // assert(i2 <= 65536, i2); - for (;;) { - q2 = (i2 * 52429) >>> (16 + 3); - r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... - content[--charPos] = digits[r]; - i2 = q2; - if (i2 == 0) break; - } - if (sign != 0) content[--charPos] = sign; - count += size; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.nio.ByteBuffer; +import org.redkale.util.*; + +/** + * + * writeTo系列的方法输出的字符不能含特殊字符 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + * + * @deprecated 2.5.0 JDK9以上用byte[]代替char[]会有更好的性能 + */ +@Deprecated +public class JsonCharsWriter extends JsonWriter { + + private static final char[] CHARS_TUREVALUE = "true".toCharArray(); + + private static final char[] CHARS_FALSEVALUE = "false".toCharArray(); + + private static final int TENTHOUSAND_MAX = 10001; + + private static final char[][] TENTHOUSAND_BYTES = new char[TENTHOUSAND_MAX][]; + + static { + for (int i = 0; i < TENTHOUSAND_BYTES.length; i++) { + TENTHOUSAND_BYTES[i] = String.valueOf(i).toCharArray(); + } + } + + private int count; + + private char[] content; + + public JsonCharsWriter() { + this(defaultSize); + } + + public JsonCharsWriter(int size) { + this.content = new char[size > 1024 ? size : 1024]; + } + + //----------------------------------------------------------------------- + /** + * 返回指定至少指定长度的缓冲区 + * + * @param len + * + * @return + */ + private char[] expand(int len) { + int newcount = count + len; + if (newcount <= content.length) return content; + char[] newdata = new char[Math.max(content.length * 3 / 2, newcount)]; + System.arraycopy(content, 0, newdata, 0, count); + this.content = newdata; + return newdata; + } + + @Override + public void writeTo(final char ch) { //只能是 0 - 127 的字符 + expand(1); + content[count++] = ch; + } + + @Override + public void writeTo(final char[] chs, final int start, final int len) { //只能是 0 - 127 的字符 + expand(len); + System.arraycopy(chs, start, content, count, len); + count += len; + } + + @Override + public void writeTo(final byte ch) { //只能是 0 - 127 的字符 + expand(1); + content[count++] = (char) ch; + } + + @Override + public void writeTo(final byte[] chs, final int start, final int len) { //只能是 0 - 127 的字符 + expand(len); + for (int i = 0; i < len; i++) { + content[count + i] = (char) chs[start + i]; + } + count += len; + } + + /** + * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String + * + * @param quote 是否加双引号 + * @param value 非null且不含需要转义的字符的String值 + */ + @Override + public void writeLatin1To(final boolean quote, final String value) { + int len = value.length(); + expand(len + (quote ? 2 : 0)); + if (quote) content[count++] = '"'; + value.getChars(0, len, content, count); + count += len; + if (quote) content[count++] = '"'; + } + + @Override + public void writeFieldShortValue(final byte[] fieldBytes, final short value) { + String val = String.valueOf(value); + int len1 = fieldBytes.length; + int len2 = val.length(); + expand(len1 + len2); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + val.getChars(0, len2, content, count); + count += len2; + } + + @Override + public void writeFieldIntValue(final byte[] fieldBytes, final int value) { + String val = String.valueOf(value); + int len1 = fieldBytes.length; + int len2 = val.length(); + expand(len1 + len2); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + val.getChars(0, len2, content, count); + count += len2; + } + + @Override + public void writeFieldLongValue(final byte[] fieldBytes, final long value) { + String val = String.valueOf(value); + int len1 = fieldBytes.length; + int len2 = val.length(); + expand(len1 + len2); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + val.getChars(0, len2, content, count); + count += len2; + } + + @Override + public void writeFieldLatin1Value(final byte[] fieldBytes, final String value) { + int len1 = fieldBytes.length; + int len2 = value.length(); + expand(len1 + len2 + 2); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + content[count++] = '"'; + value.getChars(0, len2, content, count); + count += len2; + content[count++] = '"'; + } + + @Override + public void writeLastFieldShortValue(final byte[] fieldBytes, final short value) { + String val = String.valueOf(value); + int len1 = fieldBytes.length; + int len2 = val.length(); + expand(len1 + len2 + 1); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + val.getChars(0, len2, content, count); + count += len2; + content[count++] = '}'; + } + + @Override + public void writeLastFieldIntValue(final byte[] fieldBytes, final int value) { + String val = String.valueOf(value); + int len1 = fieldBytes.length; + int len2 = val.length(); + expand(len1 + len2 + 1); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + val.getChars(0, len2, content, count); + count += len2; + content[count++] = '}'; + } + + @Override + public void writeLastFieldLongValue(final byte[] fieldBytes, final long value) { + String val = String.valueOf(value); + int len1 = fieldBytes.length; + int len2 = val.length(); + expand(len1 + len2 + 1); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + val.getChars(0, len2, content, count); + count += len2; + content[count++] = '}'; + } + + @Override + public void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value) { + if (value == null || (tiny && value.isEmpty())) { + expand(1); + content[count++] = '}'; + } else { + int len1 = fieldBytes.length; + int len2 = value.length(); + expand(len1 + len2 + 3); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + content[count++] = '"'; + value.getChars(0, len2, content, count); + count += len2; + content[count++] = '"'; + content[count++] = '}'; + } + } + + @Override //firstFieldBytes 必须带{开头 + public void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value) { + if (value == null || (tiny && value.isEmpty())) { + expand(2); + content[count++] = '{'; + content[count++] = '}'; + } else { + byte[] fieldBytes = firstFieldBytes; + int len1 = fieldBytes.length; + int len2 = value.length(); + expand(len1 + len2 + 3); + for (int i = 0; i < len1; i++) { + content[count + i] = (char) fieldBytes[i]; + } + count += len1; + content[count++] = '"'; + value.getChars(0, len2, content, count); + count += len2; + content[count++] = '"'; + content[count++] = '}'; + } + } + + @Override //firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 + public void writeObjectByOnlyTwoIntFieldValue(final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2) { + String val1 = String.valueOf(value1); + String val2 = String.valueOf(value2); + int len1 = firstFieldBytes.length; + int len2 = val1.length(); + int len3 = lastFieldBytes.length; + int len4 = val2.length(); + expand(len1 + len2 + len3 + len4 + 1); + + for (int i = 0; i < len1; i++) { + content[count + i] = (char) firstFieldBytes[i]; + } + count += len1; + val1.getChars(0, len2, content, count); + count += len2; + + for (int i = 0; i < len3; i++) { + content[count + i] = (char) lastFieldBytes[i]; + } + count += len3; + val2.getChars(0, len4, content, count); + count += len4; + + content[count++] = '}'; + } + + @Override + protected boolean recycle() { + super.recycle(); + this.count = 0; + this.specify = null; + if (this.content != null && this.content.length > defaultSize) { + this.content = new char[defaultSize]; + } + return true; + } + + public ByteBuffer[] toBuffers() { + return new ByteBuffer[]{ByteBuffer.wrap(Utility.encodeUTF8(content, 0, count))}; + } + + public byte[] toBytes() { + return Utility.encodeUTF8(content, 0, count); + } + + public int count() { + return this.count; + } + + @Override + public void writeString(String value) { + if (value == null) { + writeNull(); + return; + } + expand(value.length() * 2 + 2); + content[count++] = '"'; + for (char ch : Utility.charArray(value)) { + switch (ch) { + case '\n': + content[count++] = '\\'; + content[count++] = 'n'; + break; + case '\r': + content[count++] = '\\'; + content[count++] = 'r'; + break; + case '\t': + content[count++] = '\\'; + content[count++] = 't'; + break; + case '\\': + content[count++] = '\\'; + content[count++] = ch; + break; + case '"': + content[count++] = '\\'; + content[count++] = ch; + break; + default: + content[count++] = ch; + break; + } + } + content[count++] = '"'; + } + + @Override + public String toString() { + return new String(content, 0, count); + } + + //---------------------------------------------------------------------------------------------- + @Override + public void writeBoolean(boolean value) { + writeTo(value ? CHARS_TUREVALUE : CHARS_FALSEVALUE); + } + + @Override + public void writeInt(int value) { + if (value >= 0 && value < TENTHOUSAND_MAX) { + writeTo(TENTHOUSAND_BYTES[value]); + return; + } + final char sign = value >= 0 ? 0 : '-'; + if (value < 0) value = -value; + int size; + for (int i = 0;; i++) { + if (value <= sizeTable[i]) { + size = i + 1; + break; + } + } + if (sign != 0) size++; //负数 + expand(size); + + int q, r; + int charPos = count + size; + + // Generate two digits per iteration + while (value >= 65536) { + q = value / 100; + // really: r = i - (q * 100); + r = value - ((q << 6) + (q << 5) + (q << 2)); + value = q; + content[--charPos] = DigitOnes[r]; + content[--charPos] = DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i <= 65536, i); + for (;;) { + q = (value * 52429) >>> (16 + 3); + r = value - ((q << 3) + (q << 1)); // r = i-(q*10) ... + content[--charPos] = digits[r]; + value = q; + if (value == 0) break; + } + if (sign != 0) content[--charPos] = sign; + count += size; + } + + @Override + public void writeLong(long value) { + if (value >= 0 && value < TENTHOUSAND_MAX) { + writeTo(TENTHOUSAND_BYTES[(int) value]); + return; + } + final char sign = value >= 0 ? 0 : '-'; + if (value < 0) value = -value; + int size = 19; + long p = 10; + for (int i = 1; i < 19; i++) { + if (value < p) { + size = i; + break; + } + p = 10 * p; + } + if (sign != 0) size++; //负数 + expand(size); + + long q; + int r; + int charPos = count + size; + + // Get 2 digits/iteration using longs until quotient fits into an int + while (value > Integer.MAX_VALUE) { + q = value / 100; + // really: r = i - (q * 100); + r = (int) (value - ((q << 6) + (q << 5) + (q << 2))); + value = q; + content[--charPos] = DigitOnes[r]; + content[--charPos] = DigitTens[r]; + } + + // Get 2 digits/iteration using ints + int q2; + int i2 = (int) value; + while (i2 >= 65536) { + q2 = i2 / 100; + // really: r = i2 - (q * 100); + r = i2 - ((q2 << 6) + (q2 << 5) + (q2 << 2)); + i2 = q2; + content[--charPos] = DigitOnes[r]; + content[--charPos] = DigitTens[r]; + } + + // Fall thru to fast mode for smaller numbers + // assert(i2 <= 65536, i2); + for (;;) { + q2 = (i2 * 52429) >>> (16 + 3); + r = i2 - ((q2 << 3) + (q2 << 1)); // r = i2-(q2*10) ... + content[--charPos] = digits[r]; + i2 = q2; + if (i2 == 0) break; + } + if (sign != 0) content[--charPos] = sign; + count += size; + } + +} diff --git a/src/org/redkale/convert/json/JsonConvert.java b/src/main/java/org/redkale/convert/json/JsonConvert.java similarity index 91% rename from src/org/redkale/convert/json/JsonConvert.java rename to src/main/java/org/redkale/convert/json/JsonConvert.java index 23c829007..e4d6a96ff 100644 --- a/src/org/redkale/convert/json/JsonConvert.java +++ b/src/main/java/org/redkale/convert/json/JsonConvert.java @@ -1,393 +1,388 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.io.*; -import java.lang.reflect.*; -import java.nio.*; -import java.nio.charset.*; -import java.util.function.*; -import org.redkale.convert.*; -import org.redkale.service.RetResult; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public class JsonConvert extends TextConvert { - - public static final Type TYPE_MAP_STRING_STRING = new TypeToken>() { - }.getType(); - - public static final Type TYPE_RETRESULT_STRING = new TypeToken>() { - }.getType(); - - private final ThreadLocal charsWriterPool = ThreadLocal.withInitial(JsonCharsWriter::new); - - private final ThreadLocal bytesWriterPool = ThreadLocal.withInitial(JsonBytesWriter::new); - - private final Consumer offerBytesConsumer = w -> offerJsonBytesWriter(w); - - private final boolean tiny; - - private Encodeable lastConvertEncodeable; - - private Decodeable lastConvertDecodeable; - - protected JsonConvert(JsonFactory factory, boolean tiny) { - super(factory); - this.tiny = tiny; - } - - @Override - public JsonFactory getFactory() { - return (JsonFactory) factory; - } - - public static JsonConvert root() { - return JsonFactory.root().getConvert(); - } - - @Override - public JsonConvert newConvert(final BiFunction fieldFunc) { - return newConvert(fieldFunc, null); - } - - @Override - public JsonConvert newConvert(final BiFunction fieldFunc, Function objExtFunc) { - return new JsonConvert(getFactory(), tiny) { - @Override - protected S configWrite(S writer) { - return fieldFunc(writer, fieldFunc, objExtFunc); - } - }; - } - - //------------------------------ writer ----------------------------------------------------------- - private JsonCharsWriter pollJsonCharsWriter() { - JsonCharsWriter writer = charsWriterPool.get(); - if (writer == null) { - writer = new JsonCharsWriter(); - } else { - charsWriterPool.set(null); - } - return configWrite((JsonCharsWriter) writer.tiny(tiny)); - } - - private JsonBytesWriter pollJsonBytesWriter() { - JsonBytesWriter writer = bytesWriterPool.get(); - if (writer == null) { - writer = new JsonBytesWriter(); - } else { - bytesWriterPool.set(null); - } - return configWrite((JsonBytesWriter) writer.tiny(tiny)); - } - - private void offerJsonCharsWriter(final JsonCharsWriter writer) { - if (writer != null) { - writer.recycle(); - charsWriterPool.set(writer); - } - } - - private void offerJsonBytesWriter(final JsonBytesWriter writer) { - if (writer != null) { - writer.recycle(); - bytesWriterPool.set(writer); - } - } - - //------------------------------ convertFrom ----------------------------------------------------------- - @Override - public T convertFrom(final Type type, final byte[] bytes) { - if (bytes == null) return null; - return convertFrom(type, new String(bytes, StandardCharsets.UTF_8)); - } - - @Override - public T convertFrom(final Type type, final byte[] bytes, final int offset, final int length) { - if (bytes == null) return null; - return convertFrom(type, new String(bytes, offset, length, StandardCharsets.UTF_8)); - } - - public T convertFrom(final Type type, final String text) { - if (text == null) return null; - return convertFrom(type, Utility.charArray(text)); - } - - public T convertFrom(final Type type, final char[] text) { - if (text == null) return null; - return convertFrom(type, text, 0, text.length); - } - - public T convertFrom(final Type type, final char[] text, final int offset, final int length) { - if (text == null || type == null) return null; - Decodeable decoder = this.lastConvertDecodeable; - if (decoder == null || decoder.getType() != type) { - decoder = factory.loadDecoder(type); - this.lastConvertDecodeable = decoder; - } - T rs = (T) decoder.convertFrom(new JsonReader(text, offset, length)); - return rs; - } - - public T convertFrom(final Type type, final InputStream in) { - if (type == null || in == null) return null; - Decodeable decoder = this.lastConvertDecodeable; - if (decoder == null || decoder.getType() != type) { - decoder = factory.loadDecoder(type); - this.lastConvertDecodeable = decoder; - } - return (T) decoder.convertFrom(new JsonStreamReader(in)); - } - - @Override - public T convertFrom(final Type type, final ByteBuffer... buffers) { - if (type == null || buffers == null || buffers.length == 0) return null; - Decodeable decoder = this.lastConvertDecodeable; - if (decoder == null || decoder.getType() != type) { - decoder = factory.loadDecoder(type); - this.lastConvertDecodeable = decoder; - } - return (T) decoder.convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers)); - } - - @Override - public T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) { - if (type == null || buffers == null || buffers.length == 0) return null; - Decodeable decoder = this.lastConvertDecodeable; - if (decoder == null || decoder.getType() != type) { - decoder = factory.loadDecoder(type); - this.lastConvertDecodeable = decoder; - } - return (T) decoder.convertFrom(new JsonByteBufferReader(mask, buffers)); - } - - public T convertFrom(final Type type, final JsonReader reader) { - if (type == null) return null; - Decodeable decoder = this.lastConvertDecodeable; - if (decoder == null || decoder.getType() != type) { - decoder = factory.loadDecoder(type); - this.lastConvertDecodeable = decoder; - } - @SuppressWarnings("unchecked") - T rs = (T) decoder.convertFrom(reader); - return rs; - } - - //返回非null的值是由String、ArrayList、HashMap任意组合的对象 - public V convertFrom(final String text) { - if (text == null) return null; - return (V) convertFrom(Utility.charArray(text)); - } - - //返回非null的值是由String、ArrayList、HashMap任意组合的对象 - public V convertFrom(final char[] text) { - if (text == null) return null; - return (V) convertFrom(text, 0, text.length); - } - - //返回非null的值是由String、ArrayList、HashMap任意组合的对象 - public V convertFrom(final char[] text, final int offset, final int length) { - if (text == null) return null; - //final JsonReader in = readerPool.get(); - //in.setText(text, offset, length); - Object rs = new AnyDecoder(factory).convertFrom(new JsonReader(text, offset, length)); - //readerPool.accept(in); - return (V) rs; - } - - //返回非null的值是由String、ArrayList、HashMap任意组合的对象 - public V convertFrom(final InputStream in) { - if (in == null) return null; - return (V) new AnyDecoder(factory).convertFrom(new JsonStreamReader(in)); - } - - //返回非null的值是由String、ArrayList、HashMap任意组合的对象 - public V convertFrom(final ByteBuffer... buffers) { - if (buffers == null || buffers.length == 0) return null; - return (V) new AnyDecoder(factory).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers)); - } - - //返回非null的值是由String、ArrayList、HashMap任意组合的对象 - public V convertFrom(final ConvertMask mask, final ByteBuffer... buffers) { - if (buffers == null || buffers.length == 0) return null; - return (V) new AnyDecoder(factory).convertFrom(new JsonByteBufferReader(mask, buffers)); - } - - //返回非null的值是由String、ArrayList、HashMap任意组合的对象 - public V convertFrom(final JsonReader reader) { - if (reader == null) return null; - return (V) new AnyDecoder(factory).convertFrom(reader); - } - - //------------------------------ convertTo ----------------------------------------------------------- - @Override - public String convertTo(final Object value) { - if (value == null) return "null"; - return convertTo(value.getClass(), value); - } - - @Override - public String convertTo(final Type type, final Object value) { - if (type == null) return null; - if (value == null) return "null"; - JsonCharsWriter writer = pollJsonCharsWriter(); - Encodeable encoder = this.lastConvertEncodeable; - if (encoder == null || encoder.getType() != type) { - encoder = factory.loadEncoder(type); - this.lastConvertEncodeable = encoder; - } - if (encoder.specifyable()) writer.specify(type); - encoder.convertTo(writer, value); - - String result = writer.toString(); - offerJsonCharsWriter(writer); - return result; - } - - @Override - public byte[] convertToBytes(final Object value) { - if (value == null) return null; - return convertToBytes(value.getClass(), value); - } - - @Override - public byte[] convertToBytes(final Type type, final Object value) { - if (type == null) return null; - if (value == null) return null; - JsonBytesWriter writer = pollJsonBytesWriter(); - Encodeable encoder = this.lastConvertEncodeable; - if (encoder == null || encoder.getType() != type) { - encoder = factory.loadEncoder(type); - this.lastConvertEncodeable = encoder; - } - if (encoder.specifyable()) writer.specify(type); - encoder.convertTo(writer, value); - - byte[] result = writer.toBytes(); - offerJsonBytesWriter(writer); - return result; - } - - @Override - public void convertToBytes(final Object value, final ConvertBytesHandler handler) { - convertToBytes(value == null ? null : value.getClass(), value, handler); - } - - @Override - public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) { - JsonBytesWriter writer = pollJsonBytesWriter(); - if (type == null) { - writer.writeNull(); - } else { - Encodeable encoder = this.lastConvertEncodeable; - if (encoder == null || encoder.getType() != type) { - encoder = factory.loadEncoder(type); - this.lastConvertEncodeable = encoder; - } - if (encoder.specifyable()) writer.specify(type); - encoder.convertTo(writer, value); - } - writer.completed(handler, offerBytesConsumer); - } - - @Override - public void convertToBytes(final ByteArray array, final Object value) { - convertToBytes(array, value == null ? null : value.getClass(), value); - } - - @Override - public void convertToBytes(final ByteArray array, final Type type, final Object value) { - JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, array)); - if (type == null) { - writer.writeNull(); - } else { - Encodeable encoder = this.lastConvertEncodeable; - if (encoder == null || encoder.getType() != type) { - encoder = factory.loadEncoder(type); - this.lastConvertEncodeable = encoder; - } - if (encoder.specifyable()) writer.specify(type); - encoder.convertTo(writer, value); - } - writer.directTo(array); - } - - public void convertTo(final OutputStream out, final Object value) { - if (value == null) { - configWrite(new JsonStreamWriter(tiny, out)).writeNull(); - } else { - convertTo(out, value.getClass(), value); - } - } - - public void convertTo(final OutputStream out, final Type type, final Object value) { - if (type == null) return; - if (value == null) { - configWrite(new JsonStreamWriter(tiny, out)).writeNull(); - } else { - JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, out)); - Encodeable encoder = this.lastConvertEncodeable; - if (encoder == null || encoder.getType() != type) { - encoder = factory.loadEncoder(type); - this.lastConvertEncodeable = encoder; - } - if (encoder.specifyable()) writer.specify(type); - encoder.convertTo(writer, value); - } - } - - @Override - public ByteBuffer[] convertTo(final Supplier supplier, final Object value) { - if (supplier == null) return null; - JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier)); - if (value == null) { - out.writeNull(); - } else { - factory.loadEncoder(value.getClass()).convertTo(out, value); - } - return out.toBuffers(); - } - - @Override - public ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value) { - if (supplier == null || type == null) return null; - JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier)); - if (value == null) { - out.writeNull(); - } else { - out.specify(type); - factory.loadEncoder(type).convertTo(out, value); - } - return out.toBuffers(); - } - - public void convertTo(final JsonWriter writer, final Object value) { - if (value == null) { - writer.writeNull(); - } else { - factory.loadEncoder(value.getClass()).convertTo(writer, value); - } - } - - public void convertTo(final JsonWriter writer, final Type type, final Object value) { - if (type == null) return; - if (value == null) { - writer.writeNull(); - } else { - writer.specify(type); - factory.loadEncoder(type).convertTo(writer, value); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.io.*; +import java.lang.reflect.*; +import java.nio.*; +import java.nio.charset.*; +import java.util.function.*; +import org.redkale.convert.*; +import org.redkale.service.RetResult; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public class JsonConvert extends TextConvert { + + public static final Type TYPE_MAP_STRING_STRING = new TypeToken>() { + }.getType(); + + public static final Type TYPE_RETRESULT_STRING = new TypeToken>() { + }.getType(); + + private final ThreadLocal bytesWriterPool = ThreadLocal.withInitial(JsonBytesWriter::new); + + private final Consumer offerBytesConsumer = w -> offerJsonBytesWriter(w); + + private final boolean tiny; + + private Encodeable lastConvertEncodeable; + + private Decodeable lastConvertDecodeable; + + protected JsonConvert(JsonFactory factory, boolean tiny) { + super(factory); + this.tiny = tiny; + } + + @Override + public JsonFactory getFactory() { + return (JsonFactory) factory; + } + + public static JsonConvert root() { + return JsonFactory.root().getConvert(); + } + + @Override + public JsonConvert newConvert(final BiFunction fieldFunc) { + return newConvert(fieldFunc, null); + } + + @Override + public JsonConvert newConvert(final BiFunction fieldFunc, Function objExtFunc) { + return new JsonConvert(getFactory(), tiny) { + @Override + protected S configWrite(S writer) { + return fieldFunc(writer, fieldFunc, objExtFunc); + } + }; + } + + //------------------------------ writer ----------------------------------------------------------- + private JsonBytesWriter pollJsonBytesWriter() { + JsonBytesWriter writer = bytesWriterPool.get(); + if (writer == null) { + writer = new JsonBytesWriter(); + } else { + bytesWriterPool.set(null); + } + return configWrite((JsonBytesWriter) writer.tiny(tiny)); + } + + private void offerJsonBytesWriter(final JsonBytesWriter writer) { + if (writer != null) { + writer.recycle(); + bytesWriterPool.set(writer); + } + } + + //------------------------------ convertFrom ----------------------------------------------------------- + @Override + public T convertFrom(final Type type, final byte[] bytes) { + if (bytes == null) return null; + return convertFrom(type, new String(bytes, StandardCharsets.UTF_8)); + } + + @Override + public T convertFrom(final Type type, final byte[] bytes, final int offset, final int length) { + if (bytes == null) return null; + return convertFrom(type, new String(bytes, offset, length, StandardCharsets.UTF_8)); + } + + public T convertFrom(final Type type, final String text) { + if (text == null) return null; + return convertFrom(type, Utility.charArray(text)); + } + + public T convertFrom(final Type type, final char[] text) { + if (text == null) return null; + return convertFrom(type, text, 0, text.length); + } + + public T convertFrom(final Type type, final char[] text, final int offset, final int length) { + if (text == null || type == null) return null; + Decodeable decoder = this.lastConvertDecodeable; + if (decoder == null || decoder.getType() != type) { + decoder = factory.loadDecoder(type); + this.lastConvertDecodeable = decoder; + } + T rs = (T) decoder.convertFrom(new JsonReader(text, offset, length)); + return rs; + } + + public T convertFrom(final Type type, final InputStream in) { + if (type == null || in == null) return null; + Decodeable decoder = this.lastConvertDecodeable; + if (decoder == null || decoder.getType() != type) { + decoder = factory.loadDecoder(type); + this.lastConvertDecodeable = decoder; + } + return (T) decoder.convertFrom(new JsonStreamReader(in)); + } + + @Override + public T convertFrom(final Type type, final ByteBuffer... buffers) { + if (type == null || buffers == null || buffers.length == 0) return null; + Decodeable decoder = this.lastConvertDecodeable; + if (decoder == null || decoder.getType() != type) { + decoder = factory.loadDecoder(type); + this.lastConvertDecodeable = decoder; + } + return (T) decoder.convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers)); + } + + @Override + public T convertFrom(final Type type, final ConvertMask mask, final ByteBuffer... buffers) { + if (type == null || buffers == null || buffers.length == 0) return null; + Decodeable decoder = this.lastConvertDecodeable; + if (decoder == null || decoder.getType() != type) { + decoder = factory.loadDecoder(type); + this.lastConvertDecodeable = decoder; + } + return (T) decoder.convertFrom(new JsonByteBufferReader(mask, buffers)); + } + + public T convertFrom(final Type type, final JsonReader reader) { + if (type == null) return null; + Decodeable decoder = this.lastConvertDecodeable; + if (decoder == null || decoder.getType() != type) { + decoder = factory.loadDecoder(type); + this.lastConvertDecodeable = decoder; + } + @SuppressWarnings("unchecked") + T rs = (T) decoder.convertFrom(reader); + return rs; + } + + //返回非null的值是由String、ArrayList、HashMap任意组合的对象 + public V convertFrom(final String text) { + if (text == null) return null; + return (V) convertFrom(Utility.charArray(text)); + } + + //返回非null的值是由String、ArrayList、HashMap任意组合的对象 + public V convertFrom(final char[] text) { + if (text == null) return null; + return (V) convertFrom(text, 0, text.length); + } + + //返回非null的值是由String、ArrayList、HashMap任意组合的对象 + public V convertFrom(final char[] text, final int offset, final int length) { + if (text == null) return null; + //final JsonReader in = readerPool.get(); + //in.setText(text, offset, length); + Object rs = new AnyDecoder(factory).convertFrom(new JsonReader(text, offset, length)); + //readerPool.accept(in); + return (V) rs; + } + + //返回非null的值是由String、ArrayList、HashMap任意组合的对象 + public V convertFrom(final InputStream in) { + if (in == null) return null; + return (V) new AnyDecoder(factory).convertFrom(new JsonStreamReader(in)); + } + + //返回非null的值是由String、ArrayList、HashMap任意组合的对象 + public V convertFrom(final ByteBuffer... buffers) { + if (buffers == null || buffers.length == 0) return null; + return (V) new AnyDecoder(factory).convertFrom(new JsonByteBufferReader((ConvertMask) null, buffers)); + } + + //返回非null的值是由String、ArrayList、HashMap任意组合的对象 + public V convertFrom(final ConvertMask mask, final ByteBuffer... buffers) { + if (buffers == null || buffers.length == 0) return null; + return (V) new AnyDecoder(factory).convertFrom(new JsonByteBufferReader(mask, buffers)); + } + + //返回非null的值是由String、ArrayList、HashMap任意组合的对象 + public V convertFrom(final JsonReader reader) { + if (reader == null) return null; + return (V) new AnyDecoder(factory).convertFrom(reader); + } + + //------------------------------ convertTo ----------------------------------------------------------- + @Override + public String convertTo(final Object value) { + if (value == null) return "null"; + return convertTo(value.getClass(), value); + } + + @Override + public String convertTo(final Type type, final Object value) { + if (type == null) return null; + if (value == null) return "null"; + JsonBytesWriter writer = pollJsonBytesWriter(); + Encodeable encoder = this.lastConvertEncodeable; + if (encoder == null || encoder.getType() != type) { + encoder = factory.loadEncoder(type); + this.lastConvertEncodeable = encoder; + } + if (encoder.specifyable()) writer.specify(type); + encoder.convertTo(writer, value); + + String result = writer.toString(); + offerJsonBytesWriter(writer); + return result; + } + + @Override + public byte[] convertToBytes(final Object value) { + if (value == null) return null; + return convertToBytes(value.getClass(), value); + } + + @Override + public byte[] convertToBytes(final Type type, final Object value) { + if (type == null) return null; + if (value == null) return null; + JsonBytesWriter writer = pollJsonBytesWriter(); + Encodeable encoder = this.lastConvertEncodeable; + if (encoder == null || encoder.getType() != type) { + encoder = factory.loadEncoder(type); + this.lastConvertEncodeable = encoder; + } + if (encoder.specifyable()) writer.specify(type); + encoder.convertTo(writer, value); + + byte[] result = writer.toBytes(); + offerJsonBytesWriter(writer); + return result; + } + + @Override + public void convertToBytes(final Object value, final ConvertBytesHandler handler) { + convertToBytes(value == null ? null : value.getClass(), value, handler); + } + + @Override + public void convertToBytes(final Type type, final Object value, final ConvertBytesHandler handler) { + JsonBytesWriter writer = pollJsonBytesWriter(); + if (type == null) { + writer.writeNull(); + } else { + Encodeable encoder = this.lastConvertEncodeable; + if (encoder == null || encoder.getType() != type) { + encoder = factory.loadEncoder(type); + this.lastConvertEncodeable = encoder; + } + if (encoder.specifyable()) writer.specify(type); + encoder.convertTo(writer, value); + } + writer.completed(handler, offerBytesConsumer); + } + + @Override + public void convertToBytes(final ByteArray array, final Object value) { + convertToBytes(array, value == null ? null : value.getClass(), value); + } + + @Override + public void convertToBytes(final ByteArray array, final Type type, final Object value) { + JsonBytesWriter writer = configWrite(new JsonBytesWriter(tiny, array)); + if (type == null) { + writer.writeNull(); + } else { + Encodeable encoder = this.lastConvertEncodeable; + if (encoder == null || encoder.getType() != type) { + encoder = factory.loadEncoder(type); + this.lastConvertEncodeable = encoder; + } + if (encoder.specifyable()) writer.specify(type); + encoder.convertTo(writer, value); + } + writer.directTo(array); + } + + public void convertTo(final OutputStream out, final Object value) { + if (value == null) { + configWrite(new JsonStreamWriter(tiny, out)).writeNull(); + } else { + convertTo(out, value.getClass(), value); + } + } + + public void convertTo(final OutputStream out, final Type type, final Object value) { + if (type == null) return; + if (value == null) { + configWrite(new JsonStreamWriter(tiny, out)).writeNull(); + } else { + JsonStreamWriter writer = configWrite(new JsonStreamWriter(tiny, out)); + Encodeable encoder = this.lastConvertEncodeable; + if (encoder == null || encoder.getType() != type) { + encoder = factory.loadEncoder(type); + this.lastConvertEncodeable = encoder; + } + if (encoder.specifyable()) writer.specify(type); + encoder.convertTo(writer, value); + } + } + + @Override + public ByteBuffer[] convertTo(final Supplier supplier, final Object value) { + if (supplier == null) return null; + JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier)); + if (value == null) { + out.writeNull(); + } else { + factory.loadEncoder(value.getClass()).convertTo(out, value); + } + return out.toBuffers(); + } + + @Override + public ByteBuffer[] convertTo(final Supplier supplier, final Type type, final Object value) { + if (supplier == null || type == null) return null; + JsonByteBufferWriter out = configWrite(new JsonByteBufferWriter(tiny, supplier)); + if (value == null) { + out.writeNull(); + } else { + out.specify(type); + factory.loadEncoder(type).convertTo(out, value); + } + return out.toBuffers(); + } + + @Override + public void convertTo(final JsonWriter writer, final Object value) { + if (value == null) { + writer.writeNull(); + } else { + Class type = value.getClass(); + Encodeable encoder = this.lastConvertEncodeable; + if (encoder == null || encoder.getType() != type) { + encoder = factory.loadEncoder(type); + this.lastConvertEncodeable = encoder; + } + if (encoder.specifyable()) writer.specify(type); + encoder.convertTo(writer, value); + } + } + + @Override + public void convertTo(final JsonWriter writer, final Type type, final Object value) { + if (type == null) return; + if (value == null) { + writer.writeNull(); + } else { + Encodeable encoder = this.lastConvertEncodeable; + if (encoder == null || encoder.getType() != type) { + encoder = factory.loadEncoder(type); + this.lastConvertEncodeable = encoder; + } + if (encoder.specifyable()) writer.specify(type); + encoder.convertTo(writer, value); + } + } + +} diff --git a/src/org/redkale/convert/json/JsonDynEncoder.java b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java similarity index 50% rename from src/org/redkale/convert/json/JsonDynEncoder.java rename to src/main/java/org/redkale/convert/json/JsonDynEncoder.java index d0034ed19..a67c0a295 100644 --- a/src/org/redkale/convert/json/JsonDynEncoder.java +++ b/src/main/java/org/redkale/convert/json/JsonDynEncoder.java @@ -1,564 +1,715 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.lang.reflect.*; -import java.lang.reflect.Type; -import java.util.*; -import org.redkale.asm.*; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; -import static org.redkale.asm.Opcodes.*; -import org.redkale.convert.*; -import org.redkale.convert.ext.*; - -/** - * 简单对象的JSON序列化操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.3.0 - * - * @param 序列化的数据类型 - */ -@SuppressWarnings("unchecked") -public abstract class JsonDynEncoder implements Encodeable { - - protected final Class typeClass; - - protected final ObjectEncoder objectEncoder; - - protected JsonDynEncoder(final JsonFactory factory, Type type) { - this.typeClass = (Class) type; - factory.register(type, this); - this.objectEncoder = factory.createObjectEncoder(type); - } - - @Override - public boolean specifyable() { - return false; - } - - private static boolean checkMemberType(final JsonFactory factory, Type type, Class clazz) { - if (type == String.class) return true; - if (clazz.isPrimitive()) return true; - if (clazz.isEnum()) return true; - if (type == boolean[].class) return true; - if (type == byte[].class) return true; - if (type == short[].class) return true; - if (type == char[].class) return true; - if (type == int[].class) return true; - if (type == float[].class) return true; - if (type == long[].class) return true; - if (type == double[].class) return true; - if (type == Boolean[].class) return true; - if (type == Byte[].class) return true; - if (type == Short[].class) return true; - if (type == Character[].class) return true; - if (type == Integer[].class) return true; - if (type == Float[].class) return true; - if (type == Long[].class) return true; - if (type == Double[].class) return true; - if (type == String[].class) return true; - if (Collection.class.isAssignableFrom(clazz) && type instanceof ParameterizedType) { - Type[] ts = ((ParameterizedType) type).getActualTypeArguments(); - if (ts.length == 1) { - Type t = ts[0]; - if (t == Boolean.class || t == Byte.class || t == Short.class || t == Character.class - || t == Integer.class || t == Float.class || t == Long.class || t == Double.class - || t == String.class || ((t instanceof Class) && ((Class) t).isEnum())) return true; - if (factory.loadEncoder(t) instanceof JsonDynEncoder) return true; - } - } - if (type instanceof TypeVariable) return false; - try { - if (factory.loadEncoder(type) instanceof JsonDynEncoder) return true; - } catch (Exception e) { - return false; - } - return false; - } - - //字段全部是primitive或String类型,且没有泛型的类才能动态生成JsonDynEncoder, 不支持的返回null - public static JsonDynEncoder createDyncEncoder(final JsonFactory factory, final Type type) { - if (!(type instanceof Class)) return null; - //发现有自定义的基础数据类型Encoder就不动态生成JsonDynEncoder了 - if (factory.loadEncoder(boolean.class) != BoolSimpledCoder.instance) return null; - if (factory.loadEncoder(byte.class) != ByteSimpledCoder.instance) return null; - if (factory.loadEncoder(short.class) != ShortSimpledCoder.instance) return null; - if (factory.loadEncoder(char.class) != CharSimpledCoder.instance) return null; - if (factory.loadEncoder(int.class) != IntSimpledCoder.instance) return null; - if (factory.loadEncoder(float.class) != FloatSimpledCoder.instance) return null; - if (factory.loadEncoder(long.class) != LongSimpledCoder.instance) return null; - if (factory.loadEncoder(double.class) != DoubleSimpledCoder.instance) return null; - if (factory.loadEncoder(String.class) != StringSimpledCoder.instance) return null; - //array - if (factory.loadEncoder(boolean[].class) != BoolArraySimpledCoder.instance) return null; - if (factory.loadEncoder(byte[].class) != ByteArraySimpledCoder.instance) return null; - if (factory.loadEncoder(short[].class) != ShortArraySimpledCoder.instance) return null; - if (factory.loadEncoder(char[].class) != CharArraySimpledCoder.instance) return null; - if (factory.loadEncoder(int[].class) != IntArraySimpledCoder.instance) return null; - if (factory.loadEncoder(float[].class) != FloatArraySimpledCoder.instance) return null; - if (factory.loadEncoder(long[].class) != LongArraySimpledCoder.instance) return null; - if (factory.loadEncoder(double[].class) != DoubleArraySimpledCoder.instance) return null; - if (factory.loadEncoder(String[].class) != StringArraySimpledCoder.instance) return null; - - final Class clazz = (Class) type; - List members = null; - Set names = new HashSet<>(); - try { - ConvertColumnEntry ref; - for (final Field field : clazz.getFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (factory.isConvertDisabled(field)) continue; - ref = factory.findRef(clazz, field); - if (ref != null && ref.ignore()) continue; - if (!(checkMemberType(factory, field.getGenericType(), field.getType()))) return null; - String name = convertFieldName(factory, clazz, field); - if (names.contains(name)) continue; - names.add(name); - if (members == null) members = new ArrayList<>(); - members.add(field); - } - for (final Method method : clazz.getMethods()) { - if (Modifier.isStatic(method.getModifiers())) continue; - if (Modifier.isAbstract(method.getModifiers())) continue; - if (method.isSynthetic()) continue; - if (method.getName().length() < 3) continue; - if (method.getName().equals("getClass")) continue; - if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue; - if (factory.isConvertDisabled(method)) continue; - if (method.getParameterTypes().length != 0) continue; - if (method.getReturnType() == void.class) continue; - ref = factory.findRef(clazz, method); - if (ref != null && ref.ignore()) continue; - if (!(checkMemberType(factory, method.getGenericReturnType(), method.getReturnType()))) return null; - String name = convertFieldName(factory, clazz, method); - if (names.contains(name)) continue; - names.add(name); - if (members == null) members = new ArrayList<>(); - members.add(method); - } - if (members == null) return null; - Collections.sort(members, (o1, o2) -> { - ConvertColumnEntry ref1 = factory.findRef(clazz, o1); - ConvertColumnEntry ref2 = factory.findRef(clazz, o2); - if ((ref1 != null && ref1.getIndex() > 0) || (ref2 != null && ref2.getIndex() > 0)) { - int idx1 = ref1 == null ? Integer.MAX_VALUE / 2 : ref1.getIndex(); - int idx2 = ref2 == null ? Integer.MAX_VALUE / 2 : ref2.getIndex(); - if (idx1 != idx2) return idx1 - idx2; - } - String n1 = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(o1) : ref1.name(); - String n2 = ref2 == null || ref2.name().isEmpty() ? readGetSetFieldName(o2) : ref2.name(); - return n1.compareTo(n2); - }); - return generateDyncEncoder(factory, clazz, members); - } catch (Exception ex) { - ex.printStackTrace(); - return null; - } - } - - protected static String convertFieldName(final JsonFactory factory, Class clazz, AccessibleObject element) { - ConvertColumnEntry ref = factory.findRef(clazz, element); - String name = ref == null || ref.name().isEmpty() ? readGetSetFieldName(element) : ref.name(); - return name; - } - - protected static ConvertSmallString readConvertSmallString(AccessibleObject element) { - if (element instanceof Field) return ((Field) element).getAnnotation(ConvertSmallString.class); - Method method = (Method) element; - ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); - if (small == null) { - try { - Field f = method.getDeclaringClass().getDeclaredField(readGetSetFieldName(method)); - if (f != null) small = f.getAnnotation(ConvertSmallString.class); - } catch (Exception e) { - } - } - return small; - } - - protected static Class readGetSetFieldType(AccessibleObject element) { - if (element instanceof Field) return ((Field) element).getType(); - return element == null ? null : ((Method) element).getReturnType(); - } - - protected static String readGetSetFieldName(AccessibleObject element) { - if (element instanceof Field) return ((Field) element).getName(); - Method method = (Method) element; - if (method == null) return null; - String fname = method.getName(); - if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; - fname = fname.substring(fname.startsWith("is") ? 2 : 3); - if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) { - fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1); - } else if (fname.length() == 1) { - fname = "" + Character.toLowerCase(fname.charAt(0)); - } - return fname; - } - - protected static JsonDynEncoder generateDyncEncoder(final JsonFactory factory, final Class clazz, final List members) { - final String supDynName = JsonDynEncoder.class.getName().replace('.', '/'); - final String valtypeName = clazz.getName().replace('.', '/'); - final String writerName = JsonWriter.class.getName().replace('.', '/'); - final String encodeableName = Encodeable.class.getName().replace('.', '/'); - final String objEncoderName = ObjectEncoder.class.getName().replace('.', '/'); - final String typeDesc = org.redkale.asm.Type.getDescriptor(Type.class); - final String jsonfactoryDesc = org.redkale.asm.Type.getDescriptor(JsonFactory.class); - final String jsonwriterDesc = org.redkale.asm.Type.getDescriptor(JsonWriter.class); - final String writerDesc = org.redkale.asm.Type.getDescriptor(Writer.class); - final String encodeableDesc = org.redkale.asm.Type.getDescriptor(Encodeable.class); - final String objEncoderDesc = org.redkale.asm.Type.getDescriptor(ObjectEncoder.class); - final String valtypeDesc = org.redkale.asm.Type.getDescriptor(clazz); - - String newDynName = supDynName + "_Dyn" + clazz.getSimpleName(); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (String.class.getClassLoader() != clazz.getClassLoader()) { - loader = clazz.getClassLoader(); - newDynName = valtypeName + "_" + JsonDynEncoder.class.getSimpleName(); - } - try { - return (JsonDynEncoder) loader.loadClass(newDynName.replace('/', '.')).getDeclaredConstructor().newInstance(); - } catch (Throwable ex) { - } - // ------------------------------------------------------------------------------ - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodVisitor mv; - AnnotationVisitor av0; - - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "L" + supDynName + "<" + valtypeDesc + ">;", supDynName, null); - Map mixedNames = null; - for (AccessibleObject element : members) { - ConvertColumnEntry ref1 = factory.findRef(clazz, element); - final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); - fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FieldBytes", "[B", null, null); - fv.visitEnd(); - fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "CommaFieldBytes", "[B", null, null); - fv.visitEnd(); - final Class fieldtype = readGetSetFieldType(element); - if (fieldtype != String.class && !fieldtype.isPrimitive()) { - if (mixedNames == null) mixedNames = new HashMap<>(); - mixedNames.put(fieldname, element); - fv = cw.visitField(ACC_PROTECTED, fieldname + "Encoder", encodeableDesc, null, null); - fv.visitEnd(); - } - } - - { // 构造函数 - mv = (cw.visitMethod(ACC_PUBLIC, "", "(" + jsonfactoryDesc + typeDesc + ")V", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "(" + jsonfactoryDesc + typeDesc + ")V", false); - - for (AccessibleObject element : members) { - ConvertColumnEntry ref1 = factory.findRef(clazz, element); - final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); - //xxxFieldBytes - mv.visitVarInsn(ALOAD, 0); - mv.visitLdcInsn("\"" + fieldname + "\":"); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false); - mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FieldBytes", "[B"); - //xxxCommaFieldBytes - mv.visitVarInsn(ALOAD, 0); - mv.visitLdcInsn(",\"" + fieldname + "\":"); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false); - mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); - } - mv.visitInsn(RETURN); - mv.visitMaxs(1 + members.size(), 1 + members.size()); - mv.visitEnd(); - } - - { - mv = (cw.visitMethod(ACC_PUBLIC, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", null, null)); - //mv.setDebug(true); - { //if (value == null) { out.writeObjectNull(null); return; } - mv.visitVarInsn(ALOAD, 2); - Label valif = new Label(); - mv.visitJumpInsn(IFNONNULL, valif); - mv.visitVarInsn(ALOAD, 1); - mv.visitInsn(ACONST_NULL); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeObjectNull", "(Ljava/lang/Class;)V", false); - mv.visitInsn(RETURN); - mv.visitLabel(valif); - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - } - { //if (!out.isExtFuncEmpty()) { objectEncoder.convertTo(out, value); return; } - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "isExtFuncEmpty", "()Z", false); - Label extif = new Label(); - mv.visitJumpInsn(IFNE, extif); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "objectEncoder", objEncoderDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEVIRTUAL, objEncoderName, "convertTo", "(" + org.redkale.asm.Type.getDescriptor(Writer.class) + "Ljava/lang/Object;)V", false); - mv.visitInsn(RETURN); - mv.visitLabel(extif); - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - } - { //out.writeTo('{'); - mv.visitVarInsn(ALOAD, 1); - mv.visitIntInsn(BIPUSH, '{'); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false); - } - - int maxLocals = 4; - int elementIndex = -1; - final Class firstType = readGetSetFieldType(members.get(0)); - final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !factory.tiny()); //byte/short/char/int/float/long/double - if (!mustHadComma) { //boolean comma = false; - mv.visitInsn(ICONST_0); - mv.visitVarInsn(ISTORE, 3); - } - for (AccessibleObject element : members) { - elementIndex++; - ConvertColumnEntry ref1 = factory.findRef(clazz, element); - final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); - final Class fieldtype = readGetSetFieldType(element); - int storeid = ASTORE; - int loadid = ALOAD; - { //String message = value.getMessage(); - mv.visitVarInsn(ALOAD, 2); //加载 value - if (element instanceof Field) { - mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element).getName(), org.redkale.asm.Type.getDescriptor(fieldtype)); - } else { - mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype), false); - } - if (fieldtype == boolean.class) { - storeid = ISTORE; - loadid = ILOAD; - mv.visitVarInsn(storeid, maxLocals); - } else if (fieldtype == byte.class) { - storeid = ISTORE; - loadid = ILOAD; - mv.visitVarInsn(storeid, maxLocals); - } else if (fieldtype == short.class) { - storeid = ISTORE; - loadid = ILOAD; - mv.visitVarInsn(storeid, maxLocals); - } else if (fieldtype == char.class) { - storeid = ISTORE; - loadid = ILOAD; - mv.visitVarInsn(storeid, maxLocals); - } else if (fieldtype == int.class) { - storeid = ISTORE; - loadid = ILOAD; - mv.visitVarInsn(storeid, maxLocals); - } else if (fieldtype == float.class) { - storeid = FSTORE; - loadid = FLOAD; - mv.visitVarInsn(storeid, maxLocals); - } else if (fieldtype == long.class) { - storeid = LSTORE; - loadid = LLOAD; - mv.visitVarInsn(storeid, maxLocals); - } else if (fieldtype == double.class) { - storeid = DSTORE; - loadid = DLOAD; - mv.visitVarInsn(storeid, maxLocals); - } else { - //storeid = ASTORE; - //loadid = ALOAD; - mv.visitVarInsn(storeid, maxLocals); - } - } - Label msgnotemptyif = null; - if (!fieldtype.isPrimitive()) { //if (message != null) { start - mv.visitVarInsn(loadid, maxLocals); - msgnotemptyif = new Label(); - mv.visitJumpInsn(IFNULL, msgnotemptyif); - if (factory.tiny() && fieldtype == String.class) { - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z", false); - mv.visitJumpInsn(IFNE, msgnotemptyif); - } - } else if (fieldtype == boolean.class && factory.tiny()) { - mv.visitVarInsn(loadid, maxLocals); - msgnotemptyif = new Label(); - mv.visitJumpInsn(IFEQ, msgnotemptyif); - } - if (mustHadComma) { //第一个字段必然会写入 - if (elementIndex == 0) { //第一个 - //out.writeTo(messageFieldBytes); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); - } else { - //out.writeTo(messageCommaFieldBytes); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); - } - } else { //if(comma) {} else {} 代码块 - //if (comma) { start - mv.visitVarInsn(ILOAD, 3); - Label commaif = new Label(); - mv.visitJumpInsn(IFEQ, commaif); - - //out.writeTo(messageCommaFieldBytes); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); - - Label commaelse = new Label(); - mv.visitJumpInsn(GOTO, commaelse); - mv.visitLabel(commaif); - if (fieldtype == boolean.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); - } else if (fieldtype == byte.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); - } else if (fieldtype == short.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); - } else if (fieldtype == char.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); - } else if (fieldtype == int.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); - } else if (fieldtype == float.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.FLOAT}, 0, null); - } else if (fieldtype == long.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.LONG}, 0, null); - } else if (fieldtype == double.class) { - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.DOUBLE}, 0, null); - } else { - mv.visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.INTEGER, "java/lang/String"}, 0, null); // } else { comma - } - //out.writeTo(messageFieldBytes); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B"); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); - //comma = true; - mv.visitInsn(ICONST_1); - mv.visitVarInsn(ISTORE, 3); - mv.visitLabel(commaelse); - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); //if (comma) } end - } - //out.writeString(message); - if (fieldtype == boolean.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeBoolean", "(Z)V", false); - } else if (fieldtype == byte.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeByte", "(B)V", false); - } else if (fieldtype == short.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeShort", "(S)V", false); - } else if (fieldtype == char.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeChar", "(C)V", false); - } else if (fieldtype == int.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeInt", "(I)V", false); - } else if (fieldtype == float.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeFloat", "(F)V", false); - } else if (fieldtype == long.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLong", "(J)V", false); - } else if (fieldtype == double.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeDouble", "(D)V", false); - } else if (fieldtype == String.class) { - if (readConvertSmallString(element) == null) { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeString", "(Ljava/lang/String;)V", false); - } else { - mv.visitVarInsn(ALOAD, 1); - mv.visitInsn(ICONST_1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLatin1To", "(ZLjava/lang/String;)V", false); - } - } else { //int[],Boolean[],String[] - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "Encoder", encodeableDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(loadid, maxLocals); - mv.visitMethodInsn(INVOKEINTERFACE, encodeableName, "convertTo", "(" + writerDesc + "Ljava/lang/Object;)V", true); - } - if (!fieldtype.isPrimitive()) { //if (message != null) } end - mv.visitLabel(msgnotemptyif); - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - } else if (fieldtype == boolean.class && factory.tiny()) { - mv.visitLabel(msgnotemptyif); - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - } - if (fieldtype == long.class || fieldtype == double.class) { - maxLocals += 2; - } else { - maxLocals++; - } - } - { //out.writeTo('}'); - mv.visitVarInsn(ALOAD, 1); - mv.visitIntInsn(BIPUSH, '}'); - mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false); - } - mv.visitInsn(RETURN); - mv.visitMaxs(maxLocals, maxLocals); - mv.visitEnd(); - } - { - mv = (cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "convertTo", "(" + jsonwriterDesc + "Ljava/lang/Object;)V", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitTypeInsn(CHECKCAST, valtypeName); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - cw.visitEnd(); - // ------------------------------------------------------------------------------ - byte[] bytes = cw.toByteArray(); - Class creatorClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - JsonDynEncoder resultEncoder = (JsonDynEncoder) creatorClazz.getDeclaredConstructor(JsonFactory.class, Type.class).newInstance(factory, clazz); - if (mixedNames != null) { - for (Map.Entry en : mixedNames.entrySet()) { - Field f = creatorClazz.getDeclaredField(en.getKey() + "Encoder"); - f.setAccessible(true); - f.set(resultEncoder, factory.loadEncoder(en.getValue() instanceof Field ? ((Field) en.getValue()).getGenericType() : ((Method) en.getValue()).getGenericReturnType())); - } - } - return resultEncoder; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - @Override - public abstract void convertTo(JsonWriter out, T value); - - @Override - public Type getType() { - return typeClass; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.lang.reflect.*; +import java.lang.reflect.Type; +import java.util.*; +import org.redkale.asm.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import static org.redkale.asm.Opcodes.*; +import org.redkale.convert.*; +import org.redkale.convert.ext.*; +import org.redkale.util.*; + +/** + * 简单对象的JSON序列化操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.3.0 + * + * @param 序列化的数据类型 + */ +@SuppressWarnings("unchecked") +public abstract class JsonDynEncoder implements Encodeable { + + protected final Class typeClass; + + protected final ObjectEncoder objectEncoder; + + protected JsonDynEncoder(final JsonFactory factory, Type type) { + this.typeClass = (Class) type; + factory.register(type, this); + this.objectEncoder = factory.createObjectEncoder(type); + } + + @Override + public boolean specifyable() { + return false; + } + + private static boolean checkMemberType(final JsonFactory factory, Type type, Class clazz) { + if (type == String.class) return true; + if (clazz.isPrimitive()) return true; + if (clazz.isEnum()) return true; + if (type == boolean[].class) return true; + if (type == byte[].class) return true; + if (type == short[].class) return true; + if (type == char[].class) return true; + if (type == int[].class) return true; + if (type == float[].class) return true; + if (type == long[].class) return true; + if (type == double[].class) return true; + if (type == Boolean[].class) return true; + if (type == Byte[].class) return true; + if (type == Short[].class) return true; + if (type == Character[].class) return true; + if (type == Integer[].class) return true; + if (type == Float[].class) return true; + if (type == Long[].class) return true; + if (type == Double[].class) return true; + if (type == String[].class) return true; + if (Collection.class.isAssignableFrom(clazz) && type instanceof ParameterizedType) { + Type[] ts = ((ParameterizedType) type).getActualTypeArguments(); + if (ts.length == 1) { + Type t = ts[0]; + if (t == Boolean.class || t == Byte.class || t == Short.class || t == Character.class + || t == Integer.class || t == Float.class || t == Long.class || t == Double.class + || t == String.class || ((t instanceof Class) && ((Class) t).isEnum())) return true; + if (factory.loadEncoder(t) instanceof JsonDynEncoder) return true; + } + } + if (type instanceof TypeVariable) return false; + try { + if (factory.loadEncoder(type) instanceof JsonDynEncoder) return true; + } catch (Exception e) { + return false; + } + return false; + } + + //字段全部是primitive或String类型,且没有泛型的类才能动态生成JsonDynEncoder, 不支持的返回null + public static JsonDynEncoder createDyncEncoder(final JsonFactory factory, final Type type) { + if (!(type instanceof Class)) return null; + //发现有自定义的基础数据类型Encoder就不动态生成JsonDynEncoder了 + if (factory.loadEncoder(boolean.class) != BoolSimpledCoder.instance) return null; + if (factory.loadEncoder(byte.class) != ByteSimpledCoder.instance) return null; + if (factory.loadEncoder(short.class) != ShortSimpledCoder.instance) return null; + if (factory.loadEncoder(char.class) != CharSimpledCoder.instance) return null; + if (factory.loadEncoder(int.class) != IntSimpledCoder.instance) return null; + if (factory.loadEncoder(float.class) != FloatSimpledCoder.instance) return null; + if (factory.loadEncoder(long.class) != LongSimpledCoder.instance) return null; + if (factory.loadEncoder(double.class) != DoubleSimpledCoder.instance) return null; + if (factory.loadEncoder(String.class) != StringSimpledCoder.instance) return null; + //array + if (factory.loadEncoder(boolean[].class) != BoolArraySimpledCoder.instance) return null; + if (factory.loadEncoder(byte[].class) != ByteArraySimpledCoder.instance) return null; + if (factory.loadEncoder(short[].class) != ShortArraySimpledCoder.instance) return null; + if (factory.loadEncoder(char[].class) != CharArraySimpledCoder.instance) return null; + if (factory.loadEncoder(int[].class) != IntArraySimpledCoder.instance) return null; + if (factory.loadEncoder(float[].class) != FloatArraySimpledCoder.instance) return null; + if (factory.loadEncoder(long[].class) != LongArraySimpledCoder.instance) return null; + if (factory.loadEncoder(double[].class) != DoubleArraySimpledCoder.instance) return null; + if (factory.loadEncoder(String[].class) != StringArraySimpledCoder.instance) return null; + + final Class clazz = (Class) type; + List members = null; + Set names = new HashSet<>(); + try { + ConvertColumnEntry ref; + RedkaleClassLoader.putReflectionPublicFields(clazz.getName()); + for (final Field field : clazz.getFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (factory.isConvertDisabled(field)) continue; + ref = factory.findRef(clazz, field); + if (ref != null && ref.ignore()) continue; + if (!(checkMemberType(factory, field.getGenericType(), field.getType()))) return null; + String name = convertFieldName(factory, clazz, field); + if (names.contains(name)) continue; + names.add(name); + if (members == null) members = new ArrayList<>(); + members.add(field); + } + RedkaleClassLoader.putReflectionPublicMethods(clazz.getName()); + for (final Method method : clazz.getMethods()) { + if (Modifier.isStatic(method.getModifiers())) continue; + if (Modifier.isAbstract(method.getModifiers())) continue; + if (method.isSynthetic()) continue; + if (method.getName().length() < 3) continue; + if (method.getName().equals("getClass")) continue; + if (!method.getName().startsWith("is") && !method.getName().startsWith("get")) continue; + if (factory.isConvertDisabled(method)) continue; + if (method.getParameterTypes().length != 0) continue; + if (method.getReturnType() == void.class) continue; + ref = factory.findRef(clazz, method); + if (ref != null && ref.ignore()) continue; + if (!(checkMemberType(factory, method.getGenericReturnType(), method.getReturnType()))) return null; + String name = convertFieldName(factory, clazz, method); + if (names.contains(name)) continue; + names.add(name); + if (members == null) members = new ArrayList<>(); + members.add(method); + } + if (members == null) return null; + Collections.sort(members, (o1, o2) -> { + ConvertColumnEntry ref1 = factory.findRef(clazz, o1); + ConvertColumnEntry ref2 = factory.findRef(clazz, o2); + if ((ref1 != null && ref1.getIndex() > 0) || (ref2 != null && ref2.getIndex() > 0)) { + int idx1 = ref1 == null ? Integer.MAX_VALUE / 2 : ref1.getIndex(); + int idx2 = ref2 == null ? Integer.MAX_VALUE / 2 : ref2.getIndex(); + if (idx1 != idx2) return idx1 - idx2; + } + String n1 = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(o1) : ref1.name(); + String n2 = ref2 == null || ref2.name().isEmpty() ? readGetSetFieldName(o2) : ref2.name(); + if (n1 == null && n2 == null) return 0; + if (n1 == null) return -1; + if (n2 == null) return 1; + return n1.compareTo(n2); + }); + return generateDyncEncoder(factory, clazz, members); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + } + + protected static String convertFieldName(final JsonFactory factory, Class clazz, AccessibleObject element) { + ConvertColumnEntry ref = factory.findRef(clazz, element); + String name = ref == null || ref.name().isEmpty() ? readGetSetFieldName(element) : ref.name(); + return name; + } + + protected static ConvertSmallString readConvertSmallString(AccessibleObject element) { + if (element instanceof Field) return ((Field) element).getAnnotation(ConvertSmallString.class); + Method method = (Method) element; + ConvertSmallString small = method.getAnnotation(ConvertSmallString.class); + if (small == null) { + try { + Field f = method.getDeclaringClass().getDeclaredField(readGetSetFieldName(method)); + if (f != null) small = f.getAnnotation(ConvertSmallString.class); + } catch (Exception e) { + } + } + return small; + } + + protected static Class readGetSetFieldType(AccessibleObject element) { + if (element instanceof Field) return ((Field) element).getType(); + return element == null ? null : ((Method) element).getReturnType(); + } + + protected static String readGetSetFieldName(AccessibleObject element) { + if (element instanceof Field) return ((Field) element).getName(); + Method method = (Method) element; + if (method == null) return null; + String fname = method.getName(); + if (!fname.startsWith("is") && !fname.startsWith("get") && !fname.startsWith("set")) return fname; + fname = fname.substring(fname.startsWith("is") ? 2 : 3); + if (fname.length() > 1 && !(fname.charAt(1) >= 'A' && fname.charAt(1) <= 'Z')) { + fname = Character.toLowerCase(fname.charAt(0)) + fname.substring(1); + } else if (fname.length() == 1) { + fname = "" + Character.toLowerCase(fname.charAt(0)); + } + return fname; + } + + protected static JsonDynEncoder generateDyncEncoder(final JsonFactory factory, final Class clazz, final List members) { + final String supDynName = JsonDynEncoder.class.getName().replace('.', '/'); + final String valtypeName = clazz.getName().replace('.', '/'); + final String writerName = JsonWriter.class.getName().replace('.', '/'); + final String encodeableName = Encodeable.class.getName().replace('.', '/'); + final String objEncoderName = ObjectEncoder.class.getName().replace('.', '/'); + final String typeDesc = org.redkale.asm.Type.getDescriptor(Type.class); + final String jsonfactoryDesc = org.redkale.asm.Type.getDescriptor(JsonFactory.class); + final String jsonwriterDesc = org.redkale.asm.Type.getDescriptor(JsonWriter.class); + final String writerDesc = org.redkale.asm.Type.getDescriptor(Writer.class); + final String encodeableDesc = org.redkale.asm.Type.getDescriptor(Encodeable.class); + final String objEncoderDesc = org.redkale.asm.Type.getDescriptor(ObjectEncoder.class); + final String valtypeDesc = org.redkale.asm.Type.getDescriptor(clazz); + + Map mixedNames0 = null; + for (AccessibleObject element : members) { + ConvertColumnEntry ref1 = factory.findRef(clazz, element); + final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); + final Class fieldtype = readGetSetFieldType(element); + if (fieldtype != String.class && !fieldtype.isPrimitive()) { + if (mixedNames0 == null) mixedNames0 = new HashMap<>(); + mixedNames0.put(fieldname, element); + } + } + final Map mixedNames = mixedNames0; + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final String newDynName = "org/redkaledyn/json/_Dyn" + JsonDynEncoder.class.getSimpleName() + "__" + clazz.getName().replace('.', '_').replace('$', '_'); + final ObjectEncoder selfObjEncoder = factory.createObjectEncoder(clazz); + selfObjEncoder.init(factory); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + Class newClazz = clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz; + JsonDynEncoder resultEncoder = (JsonDynEncoder) newClazz.getDeclaredConstructor(JsonFactory.class, Type.class).newInstance(factory, clazz); + Field selfField = newClazz.getDeclaredField("objectEncoderSelf"); + selfField.setAccessible(true); + selfField.set(resultEncoder, selfObjEncoder); + if (mixedNames != null) { + for (Map.Entry en : mixedNames.entrySet()) { + Field f = newClazz.getDeclaredField(en.getKey() + "Encoder"); + f.setAccessible(true); + f.set(resultEncoder, factory.loadEncoder(en.getValue() instanceof Field ? ((Field) en.getValue()).getGenericType() : ((Method) en.getValue()).getGenericReturnType())); + } + } + return resultEncoder; + } catch (Throwable ex) { + } + // ------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "L" + supDynName + "<" + valtypeDesc + ">;", supDynName, null); + + fv = cw.visitField(ACC_PROTECTED, "objectEncoderSelf", objEncoderDesc, null, null); + fv.visitEnd(); + final int membersSize = members.size(); + boolean onlyTwoIntFieldObjectFlag = false; //只包含两个int字段 + boolean onlyOneLatin1FieldObjectFlag = false; //只包含一个Latin1 String字段 + boolean onlyShotIntLongLatin1MoreFieldObjectFlag = true; + int intFieldCount = 0; + for (AccessibleObject element : members) { + ConvertColumnEntry ref1 = factory.findRef(clazz, element); + final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); + fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FieldBytes", "[B", null, null); + fv.visitEnd(); + fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "CommaFieldBytes", "[B", null, null); + fv.visitEnd(); + fv = cw.visitField(ACC_PROTECTED + ACC_FINAL, fieldname + "FirstFieldBytes", "[B", null, null); + fv.visitEnd(); + final Class fieldtype = readGetSetFieldType(element); + if (fieldtype != String.class && !fieldtype.isPrimitive()) { + fv = cw.visitField(ACC_PROTECTED, fieldname + "Encoder", encodeableDesc, null, null); + fv.visitEnd(); + } + if (fieldtype == int.class) intFieldCount++; + if (fieldtype == String.class && membersSize == 1 && readConvertSmallString(element) != null) { + onlyOneLatin1FieldObjectFlag = true; + } else if (fieldtype != short.class && fieldtype != int.class && fieldtype != long.class && !(fieldtype == String.class && readConvertSmallString(element) != null)) { + onlyShotIntLongLatin1MoreFieldObjectFlag = false; + } + } + if (intFieldCount == 2 && intFieldCount == membersSize) onlyTwoIntFieldObjectFlag = true; + if (onlyShotIntLongLatin1MoreFieldObjectFlag && membersSize < 2) onlyShotIntLongLatin1MoreFieldObjectFlag = false; //字段个数必须大于1 + + { // 构造函数 + mv = (cw.visitMethod(ACC_PUBLIC, "", "(" + jsonfactoryDesc + typeDesc + ")V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "(" + jsonfactoryDesc + typeDesc + ")V", false); + + for (AccessibleObject element : members) { + ConvertColumnEntry ref1 = factory.findRef(clazz, element); + final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); + //xxxFieldBytes + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn("\"" + fieldname + "\":"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false); + mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FieldBytes", "[B"); + //xxxCommaFieldBytes + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn(",\"" + fieldname + "\":"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false); + mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); + //xxxFirstFieldBytes + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn("{\"" + fieldname + "\":"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "getBytes", "()[B", false); + mv.visitFieldInsn(PUTFIELD, newDynName, fieldname + "FirstFieldBytes", "[B"); + } + mv.visitInsn(RETURN); + mv.visitMaxs(1 + members.size(), 1 + members.size()); + mv.visitEnd(); + } + + { + mv = (cw.visitMethod(ACC_PUBLIC, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", null, null)); + //mv.setDebug(true); + { //if (value == null) { out.writeObjectNull(null); return; } + mv.visitVarInsn(ALOAD, 2); + Label valif = new Label(); + mv.visitJumpInsn(IFNONNULL, valif); + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ACONST_NULL); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeObjectNull", "(Ljava/lang/Class;)V", false); + mv.visitInsn(RETURN); + mv.visitLabel(valif); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } + { //if (!out.isExtFuncEmpty()) { objectEncoder.convertTo(out, value); return; } + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "isExtFuncEmpty", "()Z", false); + Label extif = new Label(); + mv.visitJumpInsn(IFNE, extif); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "objectEncoderSelf", objEncoderDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, objEncoderName, "convertTo", "(" + org.redkale.asm.Type.getDescriptor(Writer.class) + "Ljava/lang/Object;)V", false); + mv.visitInsn(RETURN); + mv.visitLabel(extif); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } + + int maxLocals = 4; + int elementIndex = -1; + final Class firstType = readGetSetFieldType(members.get(0)); + final boolean mustHadComma = firstType.isPrimitive() && (firstType != boolean.class || !factory.tiny()); //byte/short/char/int/float/long/double + + if (onlyOneLatin1FieldObjectFlag) { + //out.writeObjectByOnlyOneLatin1FieldValue(messageFirstFieldBytes, value.getMessage());elementIndex++; + elementIndex++; + AccessibleObject element = members.get(elementIndex); + ConvertColumnEntry ref1 = factory.findRef(clazz, element); + final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); + final Class fieldtype = readGetSetFieldType(element); + + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FirstFieldBytes", "[B"); + + mv.visitVarInsn(ALOAD, 2); //String message = value.getMessage(); 加载 value + if (element instanceof Field) { + mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element).getName(), org.redkale.asm.Type.getDescriptor(fieldtype)); + } else { + mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype), false); + } + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeObjectByOnlyOneLatin1FieldValue", "([BLjava/lang/String;)V", false); + maxLocals++; + } else if (onlyTwoIntFieldObjectFlag) { + elementIndex++; + AccessibleObject element1 = members.get(elementIndex); + ConvertColumnEntry ref1 = factory.findRef(clazz, element1); + final String fieldname1 = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element1) : ref1.name(); + final Class fieldtype1 = readGetSetFieldType(element1); + + elementIndex++; + AccessibleObject element2 = members.get(elementIndex); + ConvertColumnEntry ref2 = factory.findRef(clazz, element2); + final String fieldname2 = ref2 == null || ref2.name().isEmpty() ? readGetSetFieldName(element2) : ref2.name(); + final Class fieldtype2 = readGetSetFieldType(element2); + + mv.visitVarInsn(ALOAD, 1); + + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname1 + "FirstFieldBytes", "[B"); + + mv.visitVarInsn(ALOAD, 2); //String message = value.getMessage(); 加载 value + if (element1 instanceof Field) { + mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element1).getName(), org.redkale.asm.Type.getDescriptor(fieldtype1)); + } else { + mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element1).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype1), false); + } + maxLocals ++; + + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname2 + "CommaFieldBytes", "[B"); + + mv.visitVarInsn(ALOAD, 2); //String message = value.getMessage(); 加载 value + if (element2 instanceof Field) { + mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element2).getName(), org.redkale.asm.Type.getDescriptor(fieldtype2)); + } else { + mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element2).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype2), false); + } + maxLocals ++; + + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeObjectByOnlyTwoIntFieldValue", "([BI[BI)V", false); + + } else if (onlyShotIntLongLatin1MoreFieldObjectFlag && mustHadComma) { + for (AccessibleObject element : members) { + elementIndex++; + ConvertColumnEntry ref1 = factory.findRef(clazz, element); + final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); + final Class fieldtype = readGetSetFieldType(element); + + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + (elementIndex == 0 ? "FirstFieldBytes" : "CommaFieldBytes"), "[B"); + + mv.visitVarInsn(ALOAD, 2); //String message = value.getMessage(); 加载 value + if (element instanceof Field) { + mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element).getName(), org.redkale.asm.Type.getDescriptor(fieldtype)); + } else { + mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype), false); + } + if (fieldtype == short.class) { + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldShortValue" : "writeFieldShortValue", "([BS)V", false); + } else if (fieldtype == int.class) { + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldIntValue" : "writeFieldIntValue", "([BI)V", false); + } else if (fieldtype == long.class) { + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldLongValue" : "writeFieldLongValue", "([BJ)V", false); + } else { + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, elementIndex + 1 == membersSize ? "writeLastFieldLatin1Value" : "writeFieldLatin1Value", "([BLjava/lang/String;)V", false); + } + + if (fieldtype == long.class || fieldtype == double.class) { + maxLocals += 2; + } else { + maxLocals++; + } + } + } else { + { //out.writeTo('{'); + mv.visitVarInsn(ALOAD, 1); + mv.visitIntInsn(BIPUSH, '{'); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false); + } + + if (!mustHadComma) { //boolean comma = false; + mv.visitInsn(ICONST_0); + mv.visitVarInsn(ISTORE, 3); + } + for (AccessibleObject element : members) { + elementIndex++; + ConvertColumnEntry ref1 = factory.findRef(clazz, element); + final String fieldname = ref1 == null || ref1.name().isEmpty() ? readGetSetFieldName(element) : ref1.name(); + final Class fieldtype = readGetSetFieldType(element); + int storeid = ASTORE; + int loadid = ALOAD; + { //String message = value.getMessage(); + mv.visitVarInsn(ALOAD, 2); //加载 value + if (element instanceof Field) { + mv.visitFieldInsn(GETFIELD, valtypeName, ((Field) element).getName(), org.redkale.asm.Type.getDescriptor(fieldtype)); + } else { + mv.visitMethodInsn(INVOKEVIRTUAL, valtypeName, ((Method) element).getName(), "()" + org.redkale.asm.Type.getDescriptor(fieldtype), false); + } + if (fieldtype == boolean.class) { + storeid = ISTORE; + loadid = ILOAD; + mv.visitVarInsn(storeid, maxLocals); + } else if (fieldtype == byte.class) { + storeid = ISTORE; + loadid = ILOAD; + mv.visitVarInsn(storeid, maxLocals); + } else if (fieldtype == short.class) { + storeid = ISTORE; + loadid = ILOAD; + mv.visitVarInsn(storeid, maxLocals); + } else if (fieldtype == char.class) { + storeid = ISTORE; + loadid = ILOAD; + mv.visitVarInsn(storeid, maxLocals); + } else if (fieldtype == int.class) { + storeid = ISTORE; + loadid = ILOAD; + mv.visitVarInsn(storeid, maxLocals); + } else if (fieldtype == float.class) { + storeid = FSTORE; + loadid = FLOAD; + mv.visitVarInsn(storeid, maxLocals); + } else if (fieldtype == long.class) { + storeid = LSTORE; + loadid = LLOAD; + mv.visitVarInsn(storeid, maxLocals); + } else if (fieldtype == double.class) { + storeid = DSTORE; + loadid = DLOAD; + mv.visitVarInsn(storeid, maxLocals); + } else { + //storeid = ASTORE; + //loadid = ALOAD; + mv.visitVarInsn(storeid, maxLocals); + } + } + Label msgnotemptyif = null; + if (!fieldtype.isPrimitive()) { //if (message != null) { start + mv.visitVarInsn(loadid, maxLocals); + msgnotemptyif = new Label(); + mv.visitJumpInsn(IFNULL, msgnotemptyif); + if (factory.tiny() && fieldtype == String.class) { + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "isEmpty", "()Z", false); + mv.visitJumpInsn(IFNE, msgnotemptyif); + } + } else if (fieldtype == boolean.class && factory.tiny()) { + mv.visitVarInsn(loadid, maxLocals); + msgnotemptyif = new Label(); + mv.visitJumpInsn(IFEQ, msgnotemptyif); + } + if (mustHadComma) { //第一个字段必然会写入 + if (elementIndex == 0) { //第一个 + //out.writeTo(messageFieldBytes); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + } else { + //out.writeTo(messageCommaFieldBytes); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + } + } else { //if(comma) {} else {} 代码块 + //if (comma) { start + mv.visitVarInsn(ILOAD, 3); + Label commaif = new Label(); + mv.visitJumpInsn(IFEQ, commaif); + + //out.writeTo(messageCommaFieldBytes); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "CommaFieldBytes", "[B"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + + Label commaelse = new Label(); + mv.visitJumpInsn(GOTO, commaelse); + mv.visitLabel(commaif); + if (fieldtype == boolean.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); + } else if (fieldtype == byte.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); + } else if (fieldtype == short.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); + } else if (fieldtype == char.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); + } else if (fieldtype == int.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.INTEGER}, 0, null); + } else if (fieldtype == float.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.FLOAT}, 0, null); + } else if (fieldtype == long.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.LONG}, 0, null); + } else if (fieldtype == double.class) { + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{Opcodes.DOUBLE}, 0, null); + } else { + mv.visitFrame(Opcodes.F_APPEND, 2, new Object[]{Opcodes.INTEGER, "java/lang/String"}, 0, null); // } else { comma + } + //out.writeTo(messageFieldBytes); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "FieldBytes", "[B"); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "([B)V", false); + //comma = true; + mv.visitInsn(ICONST_1); + mv.visitVarInsn(ISTORE, 3); + mv.visitLabel(commaelse); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); //if (comma) } end + } + //out.writeString(message); + if (fieldtype == boolean.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeBoolean", "(Z)V", false); + } else if (fieldtype == byte.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeByte", "(B)V", false); + } else if (fieldtype == short.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeShort", "(S)V", false); + } else if (fieldtype == char.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeChar", "(C)V", false); + } else if (fieldtype == int.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeInt", "(I)V", false); + } else if (fieldtype == float.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeFloat", "(F)V", false); + } else if (fieldtype == long.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLong", "(J)V", false); + } else if (fieldtype == double.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeDouble", "(D)V", false); + } else if (fieldtype == String.class) { + if (readConvertSmallString(element) == null) { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeString", "(Ljava/lang/String;)V", false); + } else { + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ICONST_1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeLatin1To", "(ZLjava/lang/String;)V", false); + } + } else { //int[],Boolean[],String[] + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, fieldname + "Encoder", encodeableDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(loadid, maxLocals); + mv.visitMethodInsn(INVOKEINTERFACE, encodeableName, "convertTo", "(" + writerDesc + "Ljava/lang/Object;)V", true); + } + if (!fieldtype.isPrimitive()) { //if (message != null) } end + mv.visitLabel(msgnotemptyif); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } else if (fieldtype == boolean.class && factory.tiny()) { + mv.visitLabel(msgnotemptyif); + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + } + if (fieldtype == long.class || fieldtype == double.class) { + maxLocals += 2; + } else { + maxLocals++; + } + } + { //out.writeTo('}'); + mv.visitVarInsn(ALOAD, 1); + mv.visitIntInsn(BIPUSH, '}'); + mv.visitMethodInsn(INVOKEVIRTUAL, writerName, "writeTo", "(B)V", false); + } + } + mv.visitInsn(RETURN); + mv.visitMaxs(maxLocals, maxLocals); + mv.visitEnd(); + } + { + mv = (cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "convertTo", "(" + jsonwriterDesc + "Ljava/lang/Object;)V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, valtypeName); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "convertTo", "(" + jsonwriterDesc + valtypeDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw.visitEnd(); + // ------------------------------------------------------------------------------ + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.'), JsonFactory.class, Type.class); + try { + JsonDynEncoder resultEncoder = (JsonDynEncoder) newClazz.getDeclaredConstructor(JsonFactory.class, Type.class).newInstance(factory, clazz); + Field selfField = newClazz.getDeclaredField("objectEncoderSelf"); + selfField.setAccessible(true); + selfField.set(resultEncoder, selfObjEncoder); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), selfField); + if (mixedNames != null) { + for (Map.Entry en : mixedNames.entrySet()) { + Field f = newClazz.getDeclaredField(en.getKey() + "Encoder"); + f.setAccessible(true); + f.set(resultEncoder, factory.loadEncoder(en.getValue() instanceof Field ? ((Field) en.getValue()).getGenericType() : ((Method) en.getValue()).getGenericReturnType())); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), f); + } + } + return resultEncoder; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + @Override + public abstract void convertTo(JsonWriter out, T value); + + @Override + public Type getType() { + return typeClass; + } +} diff --git a/src/org/redkale/convert/json/JsonFactory.java b/src/main/java/org/redkale/convert/json/JsonFactory.java similarity index 76% rename from src/org/redkale/convert/json/JsonFactory.java rename to src/main/java/org/redkale/convert/json/JsonFactory.java index 9ebecb630..5751a1cbe 100644 --- a/src/org/redkale/convert/json/JsonFactory.java +++ b/src/main/java/org/redkale/convert/json/JsonFactory.java @@ -1,110 +1,114 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.io.Serializable; -import java.lang.reflect.*; -import java.math.BigInteger; -import java.net.*; -import org.redkale.convert.*; -import org.redkale.convert.ext.*; -import org.redkale.util.*; - -/** - * JSON的ConvertFactory - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public final class JsonFactory extends ConvertFactory { - - private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true)); - - static { - instance.register(Serializable.class, instance.loadEncoder(Object.class)); - - instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class)); - instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class)); - } - - private JsonFactory(JsonFactory parent, boolean tiny) { - super(parent, tiny); - if (parent == null) { - this.register(InetAddress.class, InetAddressSimpledCoder.InetAddressJsonSimpledCoder.instance); - this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressJsonSimpledCoder.instance); - this.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance); - this.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance); - } - } - - @Override - public JsonFactory tiny(boolean tiny) { - this.tiny = tiny; - return this; - } - - @Override - public JsonFactory skipAllIgnore(final boolean skipIgnore) { - this.registerSkipAllIgnore(skipIgnore); - return this; - } - - public static JsonFactory root() { - return instance; - } - - public static JsonFactory create() { - return new JsonFactory(null, getSystemPropertyBoolean("convert.json.tiny", "convert.tiny", true)); - } - - @Override - protected Encodeable createDyncEncoder(Type type) { - return JsonDynEncoder.createDyncEncoder(this, type); - } - - @Override - protected ObjectEncoder createObjectEncoder(Type type) { - return super.createObjectEncoder(type); - } - - protected boolean tiny() { - return this.tiny; - } - - @Override - public final JsonConvert getConvert() { - if (convert == null) convert = new JsonConvert(this, tiny); - return (JsonConvert) convert; - } - - @Override - public JsonFactory createChild() { - return new JsonFactory(this, this.tiny); - } - - @Override - public JsonFactory createChild(boolean tiny) { - return new JsonFactory(this, tiny); - } - - @Override - public ConvertType getConvertType() { - return ConvertType.JSON; - } - - @Override - public boolean isReversible() { - return false; - } - - @Override - public boolean isFieldSort() { - return true; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.io.Serializable; +import java.lang.reflect.*; +import java.math.BigInteger; +import java.net.*; +import org.redkale.convert.*; +import org.redkale.convert.ext.*; +import org.redkale.util.*; + +/** + * JSON的ConvertFactory + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public final class JsonFactory extends ConvertFactory { + + private static final JsonFactory instance = new JsonFactory(null, getSystemPropertyBoolean("redkaleconvert.json.tiny", "redkale.convert.tiny", true)); + + static { + instance.register(Serializable.class, instance.loadEncoder(Object.class)); + + //instance.register(AnyValue.class, instance.loadDecoder(AnyValue.DefaultAnyValue.class)); + //instance.register(AnyValue.class, instance.loadEncoder(AnyValue.DefaultAnyValue.class)); + } + + private JsonFactory(JsonFactory parent, boolean tiny) { + super(parent, tiny); + if (parent == null) { + this.register(InetAddress.class, InetAddressSimpledCoder.InetAddressJsonSimpledCoder.instance); + this.register(InetSocketAddress.class, InetAddressSimpledCoder.InetSocketAddressJsonSimpledCoder.instance); + this.register(DLong.class, DLongSimpledCoder.DLongJsonSimpledCoder.instance); + this.register(BigInteger.class, BigIntegerSimpledCoder.BigIntegerJsonSimpledCoder.instance); + this.register(java.time.Instant.class, InstantSimpledCoder.InstantJsonSimpledCoder.instance); + this.register(java.time.LocalDate.class, LocalDateSimpledCoder.LocalDateJsonSimpledCoder.instance); + this.register(java.time.LocalTime.class, LocalTimeSimpledCoder.LocalTimeJsonSimpledCoder.instance); + this.register(java.time.LocalDateTime.class, LocalDateTimeSimpledCoder.LocalDateTimeJsonSimpledCoder.instance); + } + } + + @Override + public JsonFactory tiny(boolean tiny) { + this.tiny = tiny; + return this; + } + + @Override + public JsonFactory skipAllIgnore(final boolean skipIgnore) { + this.registerSkipAllIgnore(skipIgnore); + return this; + } + + public static JsonFactory root() { + return instance; + } + + public static JsonFactory create() { + return new JsonFactory(null, getSystemPropertyBoolean("redkale.convert.json.tiny", "convert.tiny", true)); + } + + @Override + protected Encodeable createDyncEncoder(Type type) { + return JsonDynEncoder.createDyncEncoder(this, type); + } + + @Override + protected ObjectEncoder createObjectEncoder(Type type) { + return super.createObjectEncoder(type); + } + + protected boolean tiny() { + return this.tiny; + } + + @Override + public final JsonConvert getConvert() { + if (convert == null) convert = new JsonConvert(this, tiny); + return (JsonConvert) convert; + } + + @Override + public JsonFactory createChild() { + return new JsonFactory(this, this.tiny); + } + + @Override + public JsonFactory createChild(boolean tiny) { + return new JsonFactory(this, tiny); + } + + @Override + public ConvertType getConvertType() { + return ConvertType.JSON; + } + + @Override + public boolean isReversible() { + return false; + } + + @Override + public boolean isFieldSort() { + return true; + } +} diff --git a/src/org/redkale/convert/json/JsonReader.java b/src/main/java/org/redkale/convert/json/JsonReader.java similarity index 97% rename from src/org/redkale/convert/json/JsonReader.java rename to src/main/java/org/redkale/convert/json/JsonReader.java index 17c4683b7..b3d6da474 100644 --- a/src/org/redkale/convert/json/JsonReader.java +++ b/src/main/java/org/redkale/convert/json/JsonReader.java @@ -1,706 +1,706 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import org.redkale.convert.*; -import static org.redkale.convert.Reader.*; -import org.redkale.util.*; - -/** - * JSON数据源 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class JsonReader extends Reader { - - protected int position = -1; - - private char[] text; - - private int limit; - -// public static ObjectPool createPool(int max) { -// return new ObjectPool<>(max, (Object... params) -> new JsonReader(), null, JsonReader::recycle); -// } - public JsonReader() { - } - - public JsonReader(String json) { - setText(Utility.charArray(json)); - } - - public JsonReader(char[] text) { - setText(text, 0, text.length); - } - - public JsonReader(char[] text, int start, int len) { - setText(text, start, len); - } - - public final void setText(String text) { - setText(Utility.charArray(text)); - } - - public final void setText(char[] text) { - setText(text, 0, text.length); - } - - public final void setText(char[] text, int start, int len) { - this.text = text; - this.position = start - 1; - this.limit = start + len - 1; - } - - protected boolean recycle() { - this.position = -1; - this.limit = -1; - this.text = null; - return true; - } - - public void close() { - this.recycle(); - } - - /** - * 找到指定的属性值 例如: {id : 1, data : { name : 'a', items : [1,2,3]}} seek('data.items') 直接跳转到 [1,2,3]; - * - * @param key 指定的属性名 - */ - public final void seek(String key) { - if (key == null || key.length() < 1) return; - final String[] keys = key.split("\\."); - nextGoodChar(); //读掉 { [ - for (String key1 : keys) { - while (this.hasNext()) { - String field = this.readSmallString(); - readBlank(); - if (key1.equals(field)) break; - skipValue(); - } - } - - } - - /** - * 跳过属性的值 - */ - @Override - public final void skipValue() { - final char ch = nextGoodChar(); - switch (ch) { - case '"': - case '\'': - backChar(ch); - readString(); - break; - case '{': - while (hasNext()) { - this.readSmallString(); //读掉field - this.readBlank(); - this.skipValue(); - } - break; - case '[': - while (hasNext()) { - this.skipValue(); - } - break; - default: - char c; - for (;;) { - c = nextChar(); - if (c <= ' ') return; - if (c == '}' || c == ']' || c == ',' || c == ':') { - backChar(c); - return; - } - } - } - } - - /** - * 读取下一个字符, 不跳过空白字符 - * - * @return 空白字符或有效字符 - */ - protected char nextChar() { - return this.text[++this.position]; - } - - /** - * 跳过空白字符, 返回一个非空白字符 - * - * @return 有效字符 - */ - protected char nextGoodChar() { - char c = nextChar(); - if (c > ' ') return c; - for (;;) { - c = nextChar(); - if (c > ' ') return c; - } - } - - /** - * 回退最后读取的字符 - * - * @param ch 后退的字符 - */ - protected void backChar(char ch) { - this.position--; - } - - @Override - public final ValueType readType() { - char ch = nextGoodChar(); - if (ch == '{') { - backChar(ch); - return ValueType.MAP; - } - if (ch == '[') { - backChar(ch); - return ValueType.ARRAY; - } - backChar(ch); - return ValueType.STRING; - } - - /** - * 判断下一个非空白字符是否为{ - * - * @param clazz 类名 - * - * @return 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。 - */ - @Override - public String readObjectB(final Class clazz) { - this.fieldIndex = 0; //必须要重置为0 - if (this.text.length == 0) return null; - char ch = this.text[++this.position]; - if (ch == '{') return ""; - if (ch <= ' ') { - for (;;) { - ch = this.text[++this.position]; - if (ch > ' ') break; - } - if (ch == '{') return ""; - } - if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return null; - if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return null; - throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")"); - } - - @Override - public final void readObjectE(final Class clazz) { - } - - /** - * 判断下一个非空白字符是否为{ - * - * @param member DeMember - * @param typevals byte[] - * @param keyDecoder Decodeable - * @param valuedecoder Decodeable - * - * @return SIGN_NOLENGTH 或 SIGN_NULL - */ - @Override - public final int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valuedecoder) { - return readArrayB(member, typevals, keyDecoder); - } - - @Override - public final void readMapE() { - } - - /** - * 判断下一个非空白字符是否为[ - * - * @param member DeMember - * @param typevals byte[] - * @param componentDecoder Decodeable - * - * @return SIGN_NOLENGTH 或 SIGN_NULL - */ - @Override - public int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) { - if (this.text.length == 0) return SIGN_NULL; - char ch = this.text[++this.position]; - if (ch == '[') return SIGN_NOLENGTH; - if (ch == '{') return SIGN_NOLENGTH; - if (ch <= ' ') { - for (;;) { - ch = this.text[++this.position]; - if (ch > ' ') break; - } - if (ch == '[') return SIGN_NOLENGTH; - if (ch == '{') return SIGN_NOLENGTH; - } - if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return SIGN_NULL; - if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return SIGN_NULL; - throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")"); - } - - @Override - public final void readArrayE() { - } - - /** - * 判断下一个非空白字符是否: - */ - @Override - public void readBlank() { - char ch = this.text[++this.position]; - if (ch == ':') return; - if (ch <= ' ') { - for (;;) { - ch = this.text[++this.position]; - if (ch > ' ') break; - } - if (ch == ':') return; - } - throw new ConvertException("'" + new String(text) + "'expected a ':' but '" + ch + "'(position = " + position + ") in (" + new String(this.text) + ")"); - } - - @Override - public int position() { - return this.position; - } - - @Override - public int readMemberContentLength(DeMember member, Decodeable decoder) { - return -1; - } - - /** - * 判断对象是否存在下一个属性或者数组是否存在下一个元素 - * - * @param startPosition 起始位置 - * @param contentLength 内容大小, 不确定的传-1 - * - * @return 是否存在 - */ - @Override - public boolean hasNext(int startPosition, int contentLength) { - char ch = this.text[++this.position]; - if (ch == ',') return true; - if (ch == '}' || ch == ']') return false; - if (ch <= ' ') { - for (;;) { - ch = this.text[++this.position]; - if (ch > ' ') break; - } - if (ch == ',') return true; - if (ch == '}' || ch == ']') return false; - } - this.position--; // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取 - return true; - } - - @Override - public final String readClassName() { - return null; - } - - @Override - public String readSmallString() { - final int eof = this.limit; - if (this.position == eof) return null; - final char[] text0 = this.text; - int currpos = this.position; - char ch = text0[++currpos]; - if (ch <= ' ') { - for (;;) { - ch = text0[++currpos]; - if (ch > ' ') break; - } - } - if (ch == '"' || ch == '\'') { - final char quote = ch; - final int start = currpos + 1; - for (;;) { - ch = text0[++currpos]; - if (ch == '\\') { - this.position = currpos - 1; - return readEscapeValue(quote, start); - } else if (ch == quote) { - break; - } - } - this.position = currpos; - char[] chs = new char[currpos - start]; - System.arraycopy(text0, start, chs, 0, chs.length); - return new String(chs); - } else { - int start = currpos; - for (;;) { - if (currpos == eof) break; - ch = text0[++currpos]; - if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') break; - } - int len = currpos - start; - if (len < 1) { - this.position = currpos; - return String.valueOf(ch); - } - this.position = currpos - 1; - if (len == 4 && text0[start] == 'n' && text0[start + 1] == 'u' && text0[start + 2] == 'l' && text0[start + 3] == 'l') return null; - return new String(text0, start, len == eof ? (len + 1) : len); - } - } - - /** - * 读取一个int值 - * - * @return int值 - */ - @Override - public int readInt() { - final char[] text0 = this.text; - final int eof = this.limit; - int currpos = this.position; - char firstchar = text0[++currpos]; - if (firstchar <= ' ') { - for (;;) { - firstchar = text0[++currpos]; - if (firstchar > ' ') break; - } - } - boolean quote = false; - if (firstchar == '"' || firstchar == '\'') { - quote = true; - firstchar = text0[++currpos]; - if (firstchar <= ' ') { - for (;;) { - firstchar = text0[++currpos]; - if (firstchar > ' ') break; - } - } - if (firstchar == '"' || firstchar == '\'') { - this.position = currpos; - return 0; - } - } - int value = 0; - final boolean negative = firstchar == '-'; - if (!negative) { - if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")"); - value = firstchar - '0'; - } - boolean dot = false; - for (;;) { - if (currpos == eof) break; - char ch = text0[++currpos]; - int val = digits[ch]; - if (quote && val == -3) continue; - if (val <= -3) break; - if (dot) continue; - if (val == -1) { - if (ch == '.') { - dot = true; - continue; - } - throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")"); - } - if (val != -2) value = value * 10 + val; - } - this.position = currpos - 1; - return negative ? -value : value; - } - - /** - * 读取一个long值 - * - * @return long值 - */ - @Override - public long readLong() { - final char[] text0 = this.text; - final int eof = this.limit; - int currpos = this.position; - char firstchar = text0[++currpos]; - if (firstchar <= ' ') { - for (;;) { - firstchar = text0[++currpos]; - if (firstchar > ' ') break; - } - } - boolean quote = false; - if (firstchar == '"' || firstchar == '\'') { - quote = true; - firstchar = text0[++currpos]; - if (firstchar <= ' ') { - for (;;) { - firstchar = text0[++currpos]; - if (firstchar > ' ') break; - } - } - if (firstchar == '"' || firstchar == '\'') { - this.position = currpos; - return 0L; - } - } - long value = 0; - final boolean negative = firstchar == '-'; - if (!negative) { - if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")"); - value = firstchar - '0'; - } - boolean dot = false; - for (;;) { - if (currpos == eof) break; - char ch = text0[++currpos]; - int val = digits[ch]; - if (quote && val == -3) continue; - if (val <= -3) break; - if (dot) continue; - if (val == -1) { - if (ch == '.') { - dot = true; - continue; - } - throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")"); - } - if (val != -2) value = value * 10 + val; - } - this.position = currpos - 1; - return negative ? -value : value; - } - - @Override - public final DeMember readFieldName(final DeMember[] members) { - final String exceptedfield = this.readSmallString(); - if (exceptedfield == null) return null; - final int len = members.length; - if (this.fieldIndex >= len) this.fieldIndex = 0; - for (int k = this.fieldIndex; k < len; k++) { - if (exceptedfield.equals(members[k].getAttribute().field())) { - this.fieldIndex = k; - return members[k]; - } - } - for (int k = 0; k < this.fieldIndex; k++) { - if (exceptedfield.equals(members[k].getAttribute().field())) { - this.fieldIndex = k; - return members[k]; - } - } - return null; - //if (result == null && len == 1 && text0[start] == '@') return REFER; - } -//------------------------------------------------------------ - - @Override - public final boolean readBoolean() { - return "true".equalsIgnoreCase(this.readSmallString()); - } - - @Override - public final byte readByte() { - return (byte) readInt(); - } - - @Override - public final byte[] readByteArray() { - int len = readArrayB(null, null, null); - int contentLength = -1; - if (len == Reader.SIGN_NULL) return null; - if (len == Reader.SIGN_NOLENBUTBYTES) { - contentLength = readMemberContentLength(null, null); - len = Reader.SIGN_NOLENGTH; - } - if (len == Reader.SIGN_NOLENGTH) { - int size = 0; - byte[] data = new byte[8]; - int startPosition = position(); - while (hasNext(startPosition, contentLength)) { - if (size >= data.length) { - byte[] newdata = new byte[data.length + 4]; - System.arraycopy(data, 0, newdata, 0, size); - data = newdata; - } - data[size++] = readByte(); - } - readArrayE(); - byte[] newdata = new byte[size]; - System.arraycopy(data, 0, newdata, 0, size); - return newdata; - } else { - byte[] values = new byte[len]; - for (int i = 0; i < values.length; i++) { - values[i] = readByte(); - } - readArrayE(); - return values; - } - } - - @Override - public final char readChar() { - return (char) readInt(); - } - - @Override - public final short readShort() { - return (short) readInt(); - } - - @Override - public final float readFloat() { - String chars = readSmallString(); - if (chars != null) chars = chars.trim(); - if (chars == null || chars.isEmpty()) return 0.f; - return Float.parseFloat(chars); - } - - @Override - public final double readDouble() { - String chars = readSmallString(); - if (chars != null) chars = chars.trim(); - if (chars == null || chars.isEmpty()) return 0.0; - return Double.parseDouble(chars); - } - - /** - * 读取字符串, 必须是"或者'包围的字符串值 - * - * @return String值 - */ - @Override - public String readString() { - final char[] text0 = this.text; - int currpos = this.position; - char expected = text0[++currpos]; - if (expected <= ' ') { - for (;;) { - expected = text0[++currpos]; - if (expected > ' ') break; - } - } - if (expected != '"' && expected != '\'') { - if (expected == 'n' && text0.length > currpos + 3 && (text0[1 + currpos] == 'u' && text0[2 + currpos] == 'l' && text0[3 + currpos] == 'l')) { - if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') { - this.position = currpos; - if (text0.length > currpos + 4) { - char ch = text0[currpos + 1]; - if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') return null; - final int start = currpos - 3; - for (;;) { - if (currpos >= text0.length) break; - ch = text0[currpos]; - if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break; - currpos++; - } - if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); - this.position = currpos - 1; - return new String(text0, start, currpos - start); - } else { - return null; - } - } - } else { - final int start = currpos; - for (;;) { - if (currpos >= text0.length) break; - char ch = text0[currpos]; - if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break; - currpos++; - } - if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); - this.position = currpos - 1; - return new String(text0, start, currpos - start); - } - this.position = currpos; - throw new ConvertException("expected a ':' after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); - } - final int start = ++currpos; - for (;;) { - char ch = text0[currpos]; - if (ch == expected) { - break; - } else if (ch == '\\') { - this.position = currpos - 1; - return readEscapeValue(expected, start); - } - currpos++; - } - this.position = currpos; - return new String(text0, start, currpos - start); - } - - private String readEscapeValue(final char expected, int start) { - StringBuilder array = new StringBuilder(); - final char[] text0 = this.text; - int pos = this.position; - array.append(text0, start, pos + 1 - start); - char c; - for (;;) { - c = text0[++pos]; - if (c == expected) { - this.position = pos; - return array.toString(); - } else if (c == '\\') { - c = text0[++pos]; - switch (c) { - case '"': - case '\'': - case '\\': - case '/': - array.append(c); - break; - case 'n': - array.append('\n'); - break; - case 'r': - array.append('\r'); - break; - case 'u': - array.append((char) Integer.parseInt(new String(new char[]{text0[++pos], text0[++pos], text0[++pos], text0[++pos]}), 16)); - break; - case 't': - array.append('\t'); - break; - case 'b': - array.append('\b'); - break; - case 'f': - array.append('\f'); - break; - default: - this.position = pos; - throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ") in (" + new String(this.text) + ")"); - } - } else { - array.append(c); - } - } - } - - final static int[] digits = new int[255]; - - static { - for (int i = 0; i < digits.length; i++) { - digits[i] = -1; //-1 错误 - } - for (int i = '0'; i <= '9'; i++) { - digits[i] = i - '0'; - } - for (int i = 'a'; i <= 'f'; i++) { - digits[i] = i - 'a' + 10; - } - for (int i = 'A'; i <= 'F'; i++) { - digits[i] = i - 'A' + 10; - } - digits['"'] = digits['\''] = -2; //-2 跳过 - digits[' '] = digits['\t'] = digits['\r'] = digits['\n'] = -3; //-3可能跳过 - digits[','] = digits['}'] = digits[']'] = digits[':'] = -4; //-4退出 - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import org.redkale.convert.*; +import static org.redkale.convert.Reader.*; +import org.redkale.util.*; + +/** + * JSON数据源 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class JsonReader extends Reader { + + protected int position = -1; + + private char[] text; + + private int limit; + +// public static ObjectPool createPool(int max) { +// return new ObjectPool<>(max, (Object... params) -> new JsonReader(), null, JsonReader::recycle); +// } + public JsonReader() { + } + + public JsonReader(String json) { + setText(Utility.charArray(json)); + } + + public JsonReader(char[] text) { + setText(text, 0, text.length); + } + + public JsonReader(char[] text, int start, int len) { + setText(text, start, len); + } + + public final void setText(String text) { + setText(Utility.charArray(text)); + } + + public final void setText(char[] text) { + setText(text, 0, text.length); + } + + public final void setText(char[] text, int start, int len) { + this.text = text; + this.position = start - 1; + this.limit = start + len - 1; + } + + protected boolean recycle() { + this.position = -1; + this.limit = -1; + this.text = null; + return true; + } + + public void close() { + this.recycle(); + } + + /** + * 找到指定的属性值 例如: {id : 1, data : { name : 'a', items : [1,2,3]}} seek('data.items') 直接跳转到 [1,2,3]; + * + * @param key 指定的属性名 + */ + public final void seek(String key) { + if (key == null || key.length() < 1) return; + final String[] keys = key.split("\\."); + nextGoodChar(); //读掉 { [ + for (String key1 : keys) { + while (this.hasNext()) { + String field = this.readSmallString(); + readBlank(); + if (key1.equals(field)) break; + skipValue(); + } + } + + } + + /** + * 跳过属性的值 + */ + @Override + public final void skipValue() { + final char ch = nextGoodChar(); + switch (ch) { + case '"': + case '\'': + backChar(ch); + readString(); + break; + case '{': + while (hasNext()) { + this.readSmallString(); //读掉field + this.readBlank(); + this.skipValue(); + } + break; + case '[': + while (hasNext()) { + this.skipValue(); + } + break; + default: + char c; + for (;;) { + c = nextChar(); + if (c <= ' ') return; + if (c == '}' || c == ']' || c == ',' || c == ':') { + backChar(c); + return; + } + } + } + } + + /** + * 读取下一个字符, 不跳过空白字符 + * + * @return 空白字符或有效字符 + */ + protected char nextChar() { + return this.text[++this.position]; + } + + /** + * 跳过空白字符, 返回一个非空白字符 + * + * @return 有效字符 + */ + protected char nextGoodChar() { + char c = nextChar(); + if (c > ' ') return c; + for (;;) { + c = nextChar(); + if (c > ' ') return c; + } + } + + /** + * 回退最后读取的字符 + * + * @param ch 后退的字符 + */ + protected void backChar(char ch) { + this.position--; + } + + @Override + public final ValueType readType() { + char ch = nextGoodChar(); + if (ch == '{') { + backChar(ch); + return ValueType.MAP; + } + if (ch == '[') { + backChar(ch); + return ValueType.ARRAY; + } + backChar(ch); + return ValueType.STRING; + } + + /** + * 判断下一个非空白字符是否为{ + * + * @param clazz 类名 + * + * @return 返回 null 表示对象为null, 返回空字符串表示当前class与返回的class一致,返回非空字符串表示class是当前class的子类。 + */ + @Override + public String readObjectB(final Class clazz) { + this.fieldIndex = 0; //必须要重置为0 + if (this.text.length == 0) return null; + char ch = this.text[++this.position]; + if (ch == '{') return ""; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == '{') return ""; + } + if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return null; + if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return null; + throw new ConvertException("a json object text must begin with '{' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")"); + } + + @Override + public final void readObjectE(final Class clazz) { + } + + /** + * 判断下一个非空白字符是否为{ + * + * @param member DeMember + * @param typevals byte[] + * @param keyDecoder Decodeable + * @param valuedecoder Decodeable + * + * @return SIGN_NOLENGTH 或 SIGN_NULL + */ + @Override + public final int readMapB(DeMember member, byte[] typevals, Decodeable keyDecoder, Decodeable valuedecoder) { + return readArrayB(member, typevals, keyDecoder); + } + + @Override + public final void readMapE() { + } + + /** + * 判断下一个非空白字符是否为[ + * + * @param member DeMember + * @param typevals byte[] + * @param componentDecoder Decodeable + * + * @return SIGN_NOLENGTH 或 SIGN_NULL + */ + @Override + public int readArrayB(DeMember member, byte[] typevals, Decodeable componentDecoder) { + if (this.text.length == 0) return SIGN_NULL; + char ch = this.text[++this.position]; + if (ch == '[') return SIGN_NOLENGTH; + if (ch == '{') return SIGN_NOLENGTH; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == '[') return SIGN_NOLENGTH; + if (ch == '{') return SIGN_NOLENGTH; + } + if (ch == 'n' && text[++position] == 'u' && text[++position] == 'l' && text[++position] == 'l') return SIGN_NULL; + if (ch == 'N' && text[++position] == 'U' && text[++position] == 'L' && text[++position] == 'L') return SIGN_NULL; + throw new ConvertException("a json array text must begin with '[' (position = " + position + ") but '" + ch + "' in (" + new String(this.text) + ")"); + } + + @Override + public final void readArrayE() { + } + + /** + * 判断下一个非空白字符是否: + */ + @Override + public void readBlank() { + char ch = this.text[++this.position]; + if (ch == ':') return; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == ':') return; + } + throw new ConvertException("'" + new String(text) + "'expected a ':' but '" + ch + "'(position = " + position + ") in (" + new String(this.text) + ")"); + } + + @Override + public int position() { + return this.position; + } + + @Override + public int readMemberContentLength(DeMember member, Decodeable decoder) { + return -1; + } + + /** + * 判断对象是否存在下一个属性或者数组是否存在下一个元素 + * + * @param startPosition 起始位置 + * @param contentLength 内容大小, 不确定的传-1 + * + * @return 是否存在 + */ + @Override + public boolean hasNext(int startPosition, int contentLength) { + char ch = this.text[++this.position]; + if (ch == ',') return true; + if (ch == '}' || ch == ']') return false; + if (ch <= ' ') { + for (;;) { + ch = this.text[++this.position]; + if (ch > ' ') break; + } + if (ch == ',') return true; + if (ch == '}' || ch == ']') return false; + } + this.position--; // { [ 交由 readObjectB 或 readMapB 或 readArrayB 读取 + return true; + } + + @Override + public final String readClassName() { + return null; + } + + @Override + public String readSmallString() { + final int eof = this.limit; + if (this.position == eof) return null; + final char[] text0 = this.text; + int currpos = this.position; + char ch = text0[++currpos]; + if (ch <= ' ') { + for (;;) { + ch = text0[++currpos]; + if (ch > ' ') break; + } + } + if (ch == '"' || ch == '\'') { + final char quote = ch; + final int start = currpos + 1; + for (;;) { + ch = text0[++currpos]; + if (ch == '\\') { + this.position = currpos - 1; + return readEscapeValue(quote, start); + } else if (ch == quote) { + break; + } + } + this.position = currpos; + char[] chs = new char[currpos - start]; + System.arraycopy(text0, start, chs, 0, chs.length); + return new String(chs); + } else { + int start = currpos; + for (;;) { + if (currpos == eof) break; + ch = text0[++currpos]; + if (ch == ',' || ch == ']' || ch == '}' || ch <= ' ' || ch == ':') break; + } + int len = currpos - start; + if (len < 1) { + this.position = currpos; + return String.valueOf(ch); + } + this.position = currpos - 1; + if (len == 4 && text0[start] == 'n' && text0[start + 1] == 'u' && text0[start + 2] == 'l' && text0[start + 3] == 'l') return null; + return new String(text0, start, len == eof ? (len + 1) : len); + } + } + + /** + * 读取一个int值 + * + * @return int值 + */ + @Override + public int readInt() { + final char[] text0 = this.text; + final int eof = this.limit; + int currpos = this.position; + char firstchar = text0[++currpos]; + if (firstchar <= ' ') { + for (;;) { + firstchar = text0[++currpos]; + if (firstchar > ' ') break; + } + } + boolean quote = false; + if (firstchar == '"' || firstchar == '\'') { + quote = true; + firstchar = text0[++currpos]; + if (firstchar <= ' ') { + for (;;) { + firstchar = text0[++currpos]; + if (firstchar > ' ') break; + } + } + if (firstchar == '"' || firstchar == '\'') { + this.position = currpos; + return 0; + } + } + int value = 0; + final boolean negative = firstchar == '-'; + if (!negative) { + if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")"); + value = firstchar - '0'; + } + boolean dot = false; + for (;;) { + if (currpos == eof) break; + char ch = text0[++currpos]; + int val = digits[ch]; + if (quote && val == -3) continue; + if (val <= -3) break; + if (dot) continue; + if (val == -1) { + if (ch == '.') { + dot = true; + continue; + } + throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")"); + } + if (val != -2) value = value * 10 + val; + } + this.position = currpos - 1; + return negative ? -value : value; + } + + /** + * 读取一个long值 + * + * @return long值 + */ + @Override + public long readLong() { + final char[] text0 = this.text; + final int eof = this.limit; + int currpos = this.position; + char firstchar = text0[++currpos]; + if (firstchar <= ' ') { + for (;;) { + firstchar = text0[++currpos]; + if (firstchar > ' ') break; + } + } + boolean quote = false; + if (firstchar == '"' || firstchar == '\'') { + quote = true; + firstchar = text0[++currpos]; + if (firstchar <= ' ') { + for (;;) { + firstchar = text0[++currpos]; + if (firstchar > ' ') break; + } + } + if (firstchar == '"' || firstchar == '\'') { + this.position = currpos; + return 0L; + } + } + long value = 0; + final boolean negative = firstchar == '-'; + if (!negative) { + if (firstchar < '0' || firstchar > '9') throw new ConvertException("illegal escape(" + firstchar + ") (position = " + currpos + ") in (" + new String(this.text) + ")"); + value = firstchar - '0'; + } + boolean dot = false; + for (;;) { + if (currpos == eof) break; + char ch = text0[++currpos]; + int val = digits[ch]; + if (quote && val == -3) continue; + if (val <= -3) break; + if (dot) continue; + if (val == -1) { + if (ch == '.') { + dot = true; + continue; + } + throw new ConvertException("illegal escape(" + ch + ") (position = " + currpos + ") but '" + ch + "' in (" + new String(this.text) + ")"); + } + if (val != -2) value = value * 10 + val; + } + this.position = currpos - 1; + return negative ? -value : value; + } + + @Override + public final DeMember readFieldName(final DeMember[] members) { + final String exceptedfield = this.readSmallString(); + if (exceptedfield == null) return null; + final int len = members.length; + if (this.fieldIndex >= len) this.fieldIndex = 0; + for (int k = this.fieldIndex; k < len; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + this.fieldIndex = k; + return members[k]; + } + } + for (int k = 0; k < this.fieldIndex; k++) { + if (exceptedfield.equals(members[k].getAttribute().field())) { + this.fieldIndex = k; + return members[k]; + } + } + return null; + //if (result == null && len == 1 && text0[start] == '@') return REFER; + } +//------------------------------------------------------------ + + @Override + public final boolean readBoolean() { + return "true".equalsIgnoreCase(this.readSmallString()); + } + + @Override + public final byte readByte() { + return (byte) readInt(); + } + + @Override + public final byte[] readByteArray() { + int len = readArrayB(null, null, null); + int contentLength = -1; + if (len == Reader.SIGN_NULL) return null; + if (len == Reader.SIGN_NOLENBUTBYTES) { + contentLength = readMemberContentLength(null, null); + len = Reader.SIGN_NOLENGTH; + } + if (len == Reader.SIGN_NOLENGTH) { + int size = 0; + byte[] data = new byte[8]; + int startPosition = position(); + while (hasNext(startPosition, contentLength)) { + if (size >= data.length) { + byte[] newdata = new byte[data.length + 4]; + System.arraycopy(data, 0, newdata, 0, size); + data = newdata; + } + data[size++] = readByte(); + } + readArrayE(); + byte[] newdata = new byte[size]; + System.arraycopy(data, 0, newdata, 0, size); + return newdata; + } else { + byte[] values = new byte[len]; + for (int i = 0; i < values.length; i++) { + values[i] = readByte(); + } + readArrayE(); + return values; + } + } + + @Override + public final char readChar() { + return (char) readInt(); + } + + @Override + public final short readShort() { + return (short) readInt(); + } + + @Override + public final float readFloat() { + String chars = readSmallString(); + if (chars != null) chars = chars.trim(); + if (chars == null || chars.isEmpty()) return 0.f; + return Float.parseFloat(chars); + } + + @Override + public final double readDouble() { + String chars = readSmallString(); + if (chars != null) chars = chars.trim(); + if (chars == null || chars.isEmpty()) return 0.0; + return Double.parseDouble(chars); + } + + /** + * 读取字符串, 必须是"或者'包围的字符串值 + * + * @return String值 + */ + @Override + public String readString() { + final char[] text0 = this.text; + int currpos = this.position; + char expected = text0[++currpos]; + if (expected <= ' ') { + for (;;) { + expected = text0[++currpos]; + if (expected > ' ') break; + } + } + if (expected != '"' && expected != '\'') { + if (expected == 'n' && text0.length > currpos + 3 && (text0[1 + currpos] == 'u' && text0[2 + currpos] == 'l' && text0[3 + currpos] == 'l')) { + if (text0[++currpos] == 'u' && text0[++currpos] == 'l' && text0[++currpos] == 'l') { + this.position = currpos; + if (text0.length > currpos + 4) { + char ch = text0[currpos + 1]; + if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') return null; + final int start = currpos - 3; + for (;;) { + if (currpos >= text0.length) break; + ch = text0[currpos]; + if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break; + currpos++; + } + if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); + this.position = currpos - 1; + return new String(text0, start, currpos - start); + } else { + return null; + } + } + } else { + final int start = currpos; + for (;;) { + if (currpos >= text0.length) break; + char ch = text0[currpos]; + if (ch == ',' || ch <= ' ' || ch == '}' || ch == ']' || ch == ':') break; + currpos++; + } + if (currpos == start) throw new ConvertException("expected a string after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); + this.position = currpos - 1; + return new String(text0, start, currpos - start); + } + this.position = currpos; + throw new ConvertException("expected a ':' after a key but '" + text0[position] + "' (position = " + position + ") in (" + new String(this.text) + ")"); + } + final int start = ++currpos; + for (;;) { + char ch = text0[currpos]; + if (ch == expected) { + break; + } else if (ch == '\\') { + this.position = currpos - 1; + return readEscapeValue(expected, start); + } + currpos++; + } + this.position = currpos; + return new String(text0, start, currpos - start); + } + + private String readEscapeValue(final char expected, int start) { + StringBuilder array = new StringBuilder(); + final char[] text0 = this.text; + int pos = this.position; + array.append(text0, start, pos + 1 - start); + char c; + for (;;) { + c = text0[++pos]; + if (c == expected) { + this.position = pos; + return array.toString(); + } else if (c == '\\') { + c = text0[++pos]; + switch (c) { + case '"': + case '\'': + case '\\': + case '/': + array.append(c); + break; + case 'n': + array.append('\n'); + break; + case 'r': + array.append('\r'); + break; + case 'u': + array.append((char) Integer.parseInt(new String(new char[]{text0[++pos], text0[++pos], text0[++pos], text0[++pos]}), 16)); + break; + case 't': + array.append('\t'); + break; + case 'b': + array.append('\b'); + break; + case 'f': + array.append('\f'); + break; + default: + this.position = pos; + throw new ConvertException("illegal escape(" + c + ") (position = " + this.position + ") in (" + new String(this.text) + ")"); + } + } else { + array.append(c); + } + } + } + + final static int[] digits = new int[255]; + + static { + for (int i = 0; i < digits.length; i++) { + digits[i] = -1; //-1 错误 + } + for (int i = '0'; i <= '9'; i++) { + digits[i] = i - '0'; + } + for (int i = 'a'; i <= 'f'; i++) { + digits[i] = i - 'a' + 10; + } + for (int i = 'A'; i <= 'F'; i++) { + digits[i] = i - 'A' + 10; + } + digits['"'] = digits['\''] = -2; //-2 跳过 + digits[' '] = digits['\t'] = digits['\r'] = digits['\n'] = -3; //-3可能跳过 + digits[','] = digits['}'] = digits[']'] = digits[':'] = -4; //-4退出 + + } +} diff --git a/src/org/redkale/convert/json/JsonSimpledCoder.java b/src/main/java/org/redkale/convert/json/JsonSimpledCoder.java similarity index 96% rename from src/org/redkale/convert/json/JsonSimpledCoder.java rename to src/main/java/org/redkale/convert/json/JsonSimpledCoder.java index c2fec1fb2..6f5a067ce 100644 --- a/src/org/redkale/convert/json/JsonSimpledCoder.java +++ b/src/main/java/org/redkale/convert/json/JsonSimpledCoder.java @@ -1,20 +1,20 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import org.redkale.convert.SimpledCoder; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 序列化/反解析的数据类型 - */ -public abstract class JsonSimpledCoder extends SimpledCoder { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import org.redkale.convert.SimpledCoder; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 序列化/反解析的数据类型 + */ +public abstract class JsonSimpledCoder extends SimpledCoder { + +} diff --git a/src/org/redkale/convert/json/JsonStreamReader.java b/src/main/java/org/redkale/convert/json/JsonStreamReader.java similarity index 95% rename from src/org/redkale/convert/json/JsonStreamReader.java rename to src/main/java/org/redkale/convert/json/JsonStreamReader.java index bd9a6186e..1a1e9b988 100644 --- a/src/org/redkale/convert/json/JsonStreamReader.java +++ b/src/main/java/org/redkale/convert/json/JsonStreamReader.java @@ -1,43 +1,43 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.io.*; -import org.redkale.convert.*; - -/** - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -class JsonStreamReader extends JsonByteBufferReader { - - private InputStream in; - - protected JsonStreamReader(InputStream in) { - super((ConvertMask) null); - this.in = in; - } - - @Override - protected boolean recycle() { - super.recycle(); // this.position 初始化值为-1 - this.in = null; - return false; - } - - @Override - protected byte nextByte() { - try { - byte b = (byte) in.read(); - this.position++; - return b; - } catch (IOException e) { - throw new ConvertException(e); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.io.*; +import org.redkale.convert.*; + +/** + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +class JsonStreamReader extends JsonByteBufferReader { + + private InputStream in; + + protected JsonStreamReader(InputStream in) { + super((ConvertMask) null); + this.in = in; + } + + @Override + protected boolean recycle() { + super.recycle(); // this.position 初始化值为-1 + this.in = null; + return false; + } + + @Override + protected byte nextByte() { + try { + byte b = (byte) in.read(); + this.position++; + return b; + } catch (IOException e) { + throw new ConvertException(e); + } + } +} diff --git a/src/org/redkale/convert/json/JsonStreamWriter.java b/src/main/java/org/redkale/convert/json/JsonStreamWriter.java similarity index 96% rename from src/org/redkale/convert/json/JsonStreamWriter.java rename to src/main/java/org/redkale/convert/json/JsonStreamWriter.java index ad989ebc5..c900e58a3 100644 --- a/src/org/redkale/convert/json/JsonStreamWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonStreamWriter.java @@ -1,160 +1,160 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.io.*; -import java.nio.*; -import java.nio.charset.*; -import org.redkale.convert.*; -import org.redkale.util.*; - -/** - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -class JsonStreamWriter extends JsonByteBufferWriter { - - private OutputStream out; - - protected JsonStreamWriter(boolean tiny, OutputStream out) { - this(tiny, null, out); - } - - protected JsonStreamWriter(boolean tiny, Charset charset, OutputStream out) { - super(tiny, charset, null); - this.out = out; - } - - @Override - protected boolean recycle() { - super.recycle(); - this.out = null; - return false; - } - - @Override - public void writeTo(final char ch) { - if (ch > Byte.MAX_VALUE) throw new ConvertException("writeTo char(int.value = " + (int) ch + ") must be less 127"); - try { - out.write((byte) ch); - } catch (IOException e) { - throw new ConvertException(e); - } - } - - @Override - public void writeTo(final char[] chs, final int start, final int len) { - writeTo(false, chs, start, len); - } - - private void writeTo(final boolean quote, final char[] chs, final int start, final int len) { - try { - if (quote) out.write('"'); - if (charset == null) { //UTF-8 - final int limit = start + len; - for (int i = start; i < limit; i++) { - char c = chs[i]; - if (c < 0x80) { - out.write((byte) c); - } else if (c < 0x800) { - out.write((byte) (0xc0 | (c >> 6))); - out.write((byte) (0x80 | (c & 0x3f))); - } else if (Character.isSurrogate(c)) { //连取两个 - int uc = Character.toCodePoint(c, chs[i + 1]); - out.write((byte) (0xf0 | ((uc >> 18)))); - out.write((byte) (0x80 | ((uc >> 12) & 0x3f))); - out.write((byte) (0x80 | ((uc >> 6) & 0x3f))); - out.write((byte) (0x80 | (uc & 0x3f))); - i++; - } else { - out.write((byte) (0xe0 | ((c >> 12)))); - out.write((byte) (0x80 | ((c >> 6) & 0x3f))); - out.write((byte) (0x80 | (c & 0x3f))); - } - } - } else { - ByteBuffer bb = charset.encode(CharBuffer.wrap(chs, start, len)); - out.write(bb.array()); - } - if (quote) out.write('"'); - } catch (IOException e) { - throw new ConvertException(e); - } - } - - /** - * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String - * - * @param quote 是否写入双引号 - * @param value String值 - */ - @Override - public void writeLatin1To(final boolean quote, final String value) { - char[] chs = Utility.charArray(value); - writeTo(quote, chs, 0, chs.length); - } - - @Override - public void writeInt(int value) { - writeLatin1To(false, String.valueOf(value)); - } - - @Override - public void writeLong(long value) { - writeLatin1To(false, String.valueOf(value)); - } - - @Override - public void writeString(String value) { - if (value == null) { - writeNull(); - return; - } - final char[] chs = Utility.charArray(value); - int len = 0; - for (char ch : chs) { - switch (ch) { - case '\n': len += 2; - break; - case '\r': len += 2; - break; - case '\t': len += 2; - break; - case '\\': len += 2; - break; - case '"': len += 2; - break; - default: len++; - break; - } - } - if (len == chs.length) { - writeTo(true, chs, 0, len); - return; - } - StringBuilder sb = new StringBuilder(len); - for (char ch : chs) { - switch (ch) { - case '\n': sb.append("\\n"); - break; - case '\r': sb.append("\\r"); - break; - case '\t': sb.append("\\t"); - break; - case '\\': sb.append("\\\\"); - break; - case '"': sb.append("\\\""); - break; - default: sb.append(ch); - break; - } - } - char[] cs = Utility.charArray(sb); - writeTo(true, cs, 0, sb.length()); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.io.*; +import java.nio.*; +import java.nio.charset.*; +import org.redkale.convert.*; +import org.redkale.util.*; + +/** + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +class JsonStreamWriter extends JsonByteBufferWriter { + + private OutputStream out; + + protected JsonStreamWriter(boolean tiny, OutputStream out) { + this(tiny, null, out); + } + + protected JsonStreamWriter(boolean tiny, Charset charset, OutputStream out) { + super(tiny, charset, null); + this.out = out; + } + + @Override + protected boolean recycle() { + super.recycle(); + this.out = null; + return false; + } + + @Override + public void writeTo(final char ch) { + if (ch > Byte.MAX_VALUE) throw new ConvertException("writeTo char(int.value = " + (int) ch + ") must be less 127"); + try { + out.write((byte) ch); + } catch (IOException e) { + throw new ConvertException(e); + } + } + + @Override + public void writeTo(final char[] chs, final int start, final int len) { + writeTo(false, chs, start, len); + } + + private void writeTo(final boolean quote, final char[] chs, final int start, final int len) { + try { + if (quote) out.write('"'); + if (charset == null) { //UTF-8 + final int limit = start + len; + for (int i = start; i < limit; i++) { + char c = chs[i]; + if (c < 0x80) { + out.write((byte) c); + } else if (c < 0x800) { + out.write((byte) (0xc0 | (c >> 6))); + out.write((byte) (0x80 | (c & 0x3f))); + } else if (Character.isSurrogate(c)) { //连取两个 + int uc = Character.toCodePoint(c, chs[i + 1]); + out.write((byte) (0xf0 | ((uc >> 18)))); + out.write((byte) (0x80 | ((uc >> 12) & 0x3f))); + out.write((byte) (0x80 | ((uc >> 6) & 0x3f))); + out.write((byte) (0x80 | (uc & 0x3f))); + i++; + } else { + out.write((byte) (0xe0 | ((c >> 12)))); + out.write((byte) (0x80 | ((c >> 6) & 0x3f))); + out.write((byte) (0x80 | (c & 0x3f))); + } + } + } else { + ByteBuffer bb = charset.encode(CharBuffer.wrap(chs, start, len)); + out.write(bb.array()); + } + if (quote) out.write('"'); + } catch (IOException e) { + throw new ConvertException(e); + } + } + + /** + * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String + * + * @param quote 是否写入双引号 + * @param value String值 + */ + @Override + public void writeLatin1To(final boolean quote, final String value) { + char[] chs = Utility.charArray(value); + writeTo(quote, chs, 0, chs.length); + } + + @Override + public void writeInt(int value) { + writeLatin1To(false, String.valueOf(value)); + } + + @Override + public void writeLong(long value) { + writeLatin1To(false, String.valueOf(value)); + } + + @Override + public void writeString(String value) { + if (value == null) { + writeNull(); + return; + } + final char[] chs = Utility.charArray(value); + int len = 0; + for (char ch : chs) { + switch (ch) { + case '\n': len += 2; + break; + case '\r': len += 2; + break; + case '\t': len += 2; + break; + case '\\': len += 2; + break; + case '"': len += 2; + break; + default: len++; + break; + } + } + if (len == chs.length) { + writeTo(true, chs, 0, len); + return; + } + StringBuilder sb = new StringBuilder(len); + for (char ch : chs) { + switch (ch) { + case '\n': sb.append("\\n"); + break; + case '\r': sb.append("\\r"); + break; + case '\t': sb.append("\\t"); + break; + case '\\': sb.append("\\\\"); + break; + case '"': sb.append("\\\""); + break; + default: sb.append(ch); + break; + } + } + char[] cs = Utility.charArray(sb); + writeTo(true, cs, 0, sb.length()); + } +} diff --git a/src/org/redkale/convert/json/JsonWriter.java b/src/main/java/org/redkale/convert/json/JsonWriter.java similarity index 84% rename from src/org/redkale/convert/json/JsonWriter.java rename to src/main/java/org/redkale/convert/json/JsonWriter.java index 86bd8313b..2ac76dbb7 100644 --- a/src/org/redkale/convert/json/JsonWriter.java +++ b/src/main/java/org/redkale/convert/json/JsonWriter.java @@ -1,234 +1,256 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.convert.json; - -import java.lang.reflect.Type; -import org.redkale.convert.*; -import org.redkale.util.*; - -/** - * - * writeTo系列的方法输出的字符不能含特殊字符 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class JsonWriter extends Writer { - - protected static final int defaultSize = Integer.getInteger("convert.json.writer.buffer.defsize", Integer.getInteger("convert.writer.buffer.defsize", 1024)); - - protected boolean tiny; - - @Override - public boolean tiny() { - return tiny; - } - - public JsonWriter tiny(boolean tiny) { - this.tiny = tiny; - return this; - } - - public boolean isExtFuncEmpty() { - return this.objExtFunc == null && this.objFieldFunc == null; - } - - //----------------------------------------------------------------------- - public abstract void writeTo(final char ch); //只能是 0 - 127 的字符 - - public abstract void writeTo(final char[] chs, final int start, final int len); //只能是 0 - 127 的字符 - - public abstract void writeTo(final byte ch); //只能是 0 - 127 的字符 - - public abstract void writeTo(final byte[] chs, final int start, final int len); //只能是 0 - 127 的字符 - - /** - * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String - * - * @param quote 是否加双引号 - * @param value 非null且不含需要转义的字符的String值 - */ - public abstract void writeLatin1To(final boolean quote, final String value); - - @Override - public abstract void writeBoolean(boolean value); - - @Override - public abstract void writeInt(int value); - - @Override - public abstract void writeLong(long value); - - @Override - public abstract void writeString(String value); - - @Override //只容许JsonBytesWriter重写此方法 - public void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) { - if (this.comma) writeTo(','); - if (member != null) { - writeTo(member.getJsonFieldNameChars()); - } else { - writeLatin1To(true, fieldName); - writeTo(':'); - } - } - - @Override - public final void writeSmallString(String value) { - writeLatin1To(true, value); - } - - //---------------------------------------------------------------------------------------------- - public final void writeTo(final char... chs) { //只能是 0 - 127 的字符 - writeTo(chs, 0, chs.length); - } - - @Override - public final void writeByte(byte value) { - writeInt(value); - } - - public final void writeTo(final byte[] chs) { //只能是 0 - 127 的字符 - writeTo(chs, 0, chs.length); - } - - @Override - public final void writeByteArray(byte[] values) { - if (values == null) { - writeNull(); - return; - } - writeArrayB(values.length, null, null, values); - boolean flag = false; - for (byte v : values) { - if (flag) writeArrayMark(); - writeByte(v); - flag = true; - } - writeArrayE(); - } - - @Override - public final void writeChar(char value) { - writeInt(value); - } - - @Override - public final void writeShort(short value) { - writeInt(value); - } - - @Override - public final void writeFloat(float value) { - writeLatin1To(false, String.valueOf(value)); - } - - @Override - public final void writeDouble(double value) { - writeLatin1To(false, String.valueOf(value)); - } - - @Override - public final void writeWrapper(StringWrapper value) { - writeLatin1To(false, String.valueOf(value)); - } - - @Override - public final boolean needWriteClassName() { - return false; - } - - @Override - public final void writeClassName(String clazz) { - } - - @Override - public final int writeObjectB(Object obj) { - super.writeObjectB(obj); - writeTo('{'); - return -1; - } - - @Override - public final void writeObjectE(Object obj) { - writeTo('}'); - } - - @Override - public final void writeNull() { - writeTo('n', 'u', 'l', 'l'); - } - - @Override - public final int writeArrayB(int size, Encodeable arrayEncoder, Encodeable componentEncoder, Object obj) { - writeTo('['); - return -1; - } - - @Override - public final void writeArrayMark() { - writeTo(','); - } - - @Override - public final void writeArrayE() { - writeTo(']'); - } - - @Override - public final int writeMapB(int size, Encodeable keyEncoder, Encodeable valueEncoder, Object obj) { - writeTo('{'); - return -1; - } - - @Override - public final void writeMapMark() { - writeTo(':'); - } - - @Override - public final void writeMapE() { - writeTo('}'); - } - - final static char[] DigitTens = { - '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', - '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', - '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', - '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', - '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', - '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', - '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', - '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', - '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', - '9', '9', '9', '9', '9', '9', '9', '9', '9', '9' - }; - - final static char[] DigitOnes = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' - }; - - final static char[] digits = { - '0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', 'a', 'b', - 'c', 'd', 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', 'y', 'z' - }; - - final static int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE}; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.convert.json; + +import java.lang.reflect.Type; +import org.redkale.convert.*; +import org.redkale.util.*; + +/** + * + * writeTo系列的方法输出的字符不能含特殊字符 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class JsonWriter extends Writer { + + protected static final int defaultSize = Integer.getInteger("redkale.convert.json.writer.buffer.defsize", Integer.getInteger("redkale.convert.writer.buffer.defsize", 1024)); + + protected boolean tiny; + + @Override + public boolean tiny() { + return tiny; + } + + public JsonWriter tiny(boolean tiny) { + this.tiny = tiny; + return this; + } + + public boolean isExtFuncEmpty() { + return this.objExtFunc == null && this.objFieldFunc == null; + } + + //----------------------------------------------------------------------- + public abstract void writeTo(final char ch); //只能是 0 - 127 的字符 + + public abstract void writeTo(final char[] chs, final int start, final int len); //只能是 0 - 127 的字符 + + public abstract void writeTo(final byte ch); //只能是 0 - 127 的字符 + + public abstract void writeTo(final byte[] chs, final int start, final int len); //只能是 0 - 127 的字符 + + /** + * 注意: 该String值不能为null且不会进行转义, 只用于不含需要转义字符的字符串,例如enum、double、BigInteger转换的String + * + * @param quote 是否加双引号 + * @param value 非null且不含需要转义的字符的String值 + */ + public abstract void writeLatin1To(final boolean quote, final String value); + + public abstract void writeFieldShortValue(final byte[] fieldBytes, final short value); + + public abstract void writeFieldIntValue(final byte[] fieldBytes, final int value); + + public abstract void writeFieldLongValue(final byte[] fieldBytes, final long value); + + public abstract void writeFieldLatin1Value(final byte[] fieldBytes, final String value); + + public abstract void writeLastFieldShortValue(final byte[] fieldBytes, final short value); + + public abstract void writeLastFieldIntValue(final byte[] fieldBytes, final int value); + + public abstract void writeLastFieldLongValue(final byte[] fieldBytes, final long value); + + public abstract void writeLastFieldLatin1Value(final byte[] fieldBytes, final String value); + + //firstFieldBytes 必须带{开头 + public abstract void writeObjectByOnlyOneLatin1FieldValue(final byte[] firstFieldBytes, final String value); + + //firstFieldBytes 必须带{开头, lastFieldBytes必须,开头 + public abstract void writeObjectByOnlyTwoIntFieldValue(final byte[] firstFieldBytes, final int value1, final byte[] lastFieldBytes, final int value2); + + @Override + public abstract void writeBoolean(boolean value); + + @Override + public abstract void writeInt(int value); + + @Override + public abstract void writeLong(long value); + + @Override + public abstract void writeString(String value); + + @Override //只容许JsonBytesWriter重写此方法 + public void writeFieldName(EnMember member, String fieldName, Type fieldType, int fieldPos) { + if (this.comma) writeTo(','); + if (member != null) { + writeTo(member.getJsonFieldNameChars()); + } else { + writeLatin1To(true, fieldName); + writeTo(':'); + } + } + + @Override + public final void writeSmallString(String value) { + writeLatin1To(true, value); + } + + //---------------------------------------------------------------------------------------------- + public final void writeTo(final char... chs) { //只能是 0 - 127 的字符 + writeTo(chs, 0, chs.length); + } + + @Override + public final void writeByte(byte value) { + writeInt(value); + } + + public final void writeTo(final byte[] chs) { //只能是 0 - 127 的字符 + writeTo(chs, 0, chs.length); + } + + @Override + public final void writeByteArray(byte[] values) { + if (values == null) { + writeNull(); + return; + } + writeArrayB(values.length, null, null, values); + boolean flag = false; + for (byte v : values) { + if (flag) writeArrayMark(); + writeByte(v); + flag = true; + } + writeArrayE(); + } + + @Override + public final void writeChar(char value) { + writeInt(value); + } + + @Override + public final void writeShort(short value) { + writeInt(value); + } + + @Override + public final void writeFloat(float value) { + writeLatin1To(false, String.valueOf(value)); + } + + @Override + public final void writeDouble(double value) { + writeLatin1To(false, String.valueOf(value)); + } + + @Override + public final void writeWrapper(StringWrapper value) { + writeLatin1To(false, String.valueOf(value)); + } + + @Override + public final boolean needWriteClassName() { + return false; + } + + @Override + public final void writeClassName(String clazz) { + } + + @Override + public final int writeObjectB(Object obj) { + super.writeObjectB(obj); + writeTo('{'); + return -1; + } + + @Override + public final void writeObjectE(Object obj) { + writeTo('}'); + } + + @Override + public final void writeNull() { + writeTo('n', 'u', 'l', 'l'); + } + + @Override + public final int writeArrayB(int size, Encodeable arrayEncoder, Encodeable componentEncoder, Object obj) { + writeTo('['); + return -1; + } + + @Override + public final void writeArrayMark() { + writeTo(','); + } + + @Override + public final void writeArrayE() { + writeTo(']'); + } + + @Override + public final int writeMapB(int size, Encodeable keyEncoder, Encodeable valueEncoder, Object obj) { + writeTo('{'); + return -1; + } + + @Override + public final void writeMapMark() { + writeTo(':'); + } + + @Override + public final void writeMapE() { + writeTo('}'); + } + + final static char[] DigitTens = { + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', + '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', + '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', + '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', + '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', + '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', + '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', + '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', + '9', '9', '9', '9', '9', '9', '9', '9', '9', '9' + }; + + final static char[] DigitOnes = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' + }; + + final static char[] digits = { + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'a', 'b', + 'c', 'd', 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', 'y', 'z' + }; + + final static int[] sizeTable = {9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, Integer.MAX_VALUE}; +} diff --git a/src/org/redkale/convert/json/package-info.java b/src/main/java/org/redkale/convert/json/package-info.java similarity index 95% rename from src/org/redkale/convert/json/package-info.java rename to src/main/java/org/redkale/convert/json/package-info.java index 7e181f13b..2f27455b3 100644 --- a/src/org/redkale/convert/json/package-info.java +++ b/src/main/java/org/redkale/convert/json/package-info.java @@ -1,4 +1,4 @@ -/** - * 提供JSON的序列化和反解析功能 - */ -package org.redkale.convert.json; +/** + * 提供JSON的序列化和反解析功能 + */ +package org.redkale.convert.json; diff --git a/src/org/redkale/convert/package-info.java b/src/main/java/org/redkale/convert/package-info.java similarity index 95% rename from src/org/redkale/convert/package-info.java rename to src/main/java/org/redkale/convert/package-info.java index 72764d0e0..2835d5ae3 100644 --- a/src/org/redkale/convert/package-info.java +++ b/src/main/java/org/redkale/convert/package-info.java @@ -1,4 +1,4 @@ -/** - * 提供数据的序列化和反解析功能 - */ -package org.redkale.convert; +/** + * 提供数据的序列化和反解析功能 + */ +package org.redkale.convert; diff --git a/src/org/redkale/mq/HttpMessageClient.java b/src/main/java/org/redkale/mq/HttpMessageClient.java similarity index 80% rename from src/org/redkale/mq/HttpMessageClient.java rename to src/main/java/org/redkale/mq/HttpMessageClient.java index 14195cca1..2a931ae5e 100644 --- a/src/org/redkale/mq/HttpMessageClient.java +++ b/src/main/java/org/redkale/mq/HttpMessageClient.java @@ -1,198 +1,199 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.lang.reflect.Type; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.http.*; -import static org.redkale.mq.MessageRecord.CTYPE_HTTP_REQUEST; - -/** - * 不依赖MessageRecord则可兼容RPC方式 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpMessageClient extends MessageClient { - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - protected HttpMessageClient(MessageAgent messageAgent) { - super(messageAgent); - if (messageAgent != null) { // //RPC方式下无messageAgent - this.respTopic = messageAgent.generateHttpRespTopic(); - } - } - - //格式: http.req.user - public String generateHttpReqTopic(String module) { - return MessageAgent.generateHttpReqTopic(module); - } - - //格式: http.req.user-n10 - public String generateHttpReqTopic(String module, String resname) { - return MessageAgent.generateHttpReqTopic(module, resname); - } - - public String generateHttpReqTopic(HttpSimpleRequest request, String path) { - String module = request.getRequestURI(); - if (path != null && !path.isEmpty() && module.startsWith(path)) module = module.substring(path.length()); - module = module.substring(1); //去掉/ - module = module.substring(0, module.indexOf('/')); - Map headers = request.getHeaders(); - String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); - return MessageAgent.generateHttpReqTopic(module, resname); - } - - public final void produceMessage(HttpSimpleRequest request) { - produceMessage(generateHttpReqTopic(request, null), 0, null, request, null); - } - - public final void produceMessage(HttpSimpleRequest request, AtomicLong counter) { - produceMessage(generateHttpReqTopic(request, null), 0, null, request, counter); - } - - public final void produceMessage(int userid, HttpSimpleRequest request) { - produceMessage(generateHttpReqTopic(request, null), userid, null, request, null); - } - - public final void produceMessage(int userid, String groupid, HttpSimpleRequest request) { - produceMessage(generateHttpReqTopic(request, null), userid, groupid, request, null); - } - - public final void produceMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - produceMessage(generateHttpReqTopic(request, null), userid, groupid, request, counter); - } - - public final void produceMessage(String topic, HttpSimpleRequest request) { - produceMessage(topic, 0, null, request, null); - } - - public final void produceMessage(String topic, HttpSimpleRequest request, AtomicLong counter) { - produceMessage(topic, 0, null, request, counter); - } - - public final void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request) { - produceMessage(topic, userid, groupid, request, null); - } - - public final void broadcastMessage(HttpSimpleRequest request) { - broadcastMessage(generateHttpReqTopic(request, null), 0, null, request, null); - } - - public final void broadcastMessage(HttpSimpleRequest request, AtomicLong counter) { - broadcastMessage(generateHttpReqTopic(request, null), 0, null, request, counter); - } - - public final void broadcastMessage(int userid, HttpSimpleRequest request) { - broadcastMessage(generateHttpReqTopic(request, null), userid, null, request, null); - } - - public final void broadcastMessage(int userid, String groupid, HttpSimpleRequest request) { - broadcastMessage(generateHttpReqTopic(request, null), userid, groupid, request, null); - } - - public final void broadcastMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - broadcastMessage(generateHttpReqTopic(request, null), userid, groupid, request, counter); - } - - public final void broadcastMessage(String topic, HttpSimpleRequest request) { - broadcastMessage(topic, 0, null, request, null); - } - - public final void broadcastMessage(String topic, HttpSimpleRequest request, AtomicLong counter) { - broadcastMessage(topic, 0, null, request, counter); - } - - public final void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request) { - broadcastMessage(topic, userid, groupid, request, null); - } - - public CompletableFuture sendMessage(HttpSimpleRequest request, Type type) { - return sendMessage(generateHttpReqTopic(request, null), 0, null, request, null).thenApply((HttpResult httbs) -> { - if (httbs == null || httbs.getResult() == null) return null; - return JsonConvert.root().convertFrom(type, httbs.getResult()); - }); - } - - public CompletableFuture sendMessage(int userid, HttpSimpleRequest request, Type type) { - return sendMessage(generateHttpReqTopic(request, null), userid, null, request, null).thenApply((HttpResult httbs) -> { - if (httbs == null || httbs.getResult() == null) return null; - return JsonConvert.root().convertFrom(type, httbs.getResult()); - }); - } - - public CompletableFuture sendMessage(int userid, String groupid, HttpSimpleRequest request, Type type) { - return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, null).thenApply((HttpResult httbs) -> { - if (httbs == null || httbs.getResult() == null) return null; - return JsonConvert.root().convertFrom(type, httbs.getResult()); - }); - } - - public final CompletableFuture> sendMessage(HttpSimpleRequest request) { - return sendMessage(generateHttpReqTopic(request, null), 0, null, request, null); - } - - public final CompletableFuture> sendMessage(HttpSimpleRequest request, AtomicLong counter) { - return sendMessage(generateHttpReqTopic(request, null), 0, null, request, counter); - } - - public final CompletableFuture> sendMessage(int userid, HttpSimpleRequest request) { - return sendMessage(generateHttpReqTopic(request, null), userid, null, request, null); - } - - public final CompletableFuture> sendMessage(int userid, String groupid, HttpSimpleRequest request) { - return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, null); - } - - public final CompletableFuture> sendMessage(int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, counter); - } - - public final CompletableFuture> sendMessage(String topic, HttpSimpleRequest request) { - return sendMessage(topic, 0, null, request, null); - } - - public final CompletableFuture> sendMessage(String topic, HttpSimpleRequest request, AtomicLong counter) { - return sendMessage(topic, 0, null, request, counter); - } - - public final CompletableFuture> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request) { - return sendMessage(topic, userid, null, request, (AtomicLong) null); - } - - public CompletableFuture> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request)); - message.userid(userid).groupid(groupid); - //if (finest) logger.log(Level.FINEST, "HttpMessageClient.sendMessage: " + message); - return sendMessage(message, true, counter).thenApply(r -> r.decodeContent(HttpResultCoder.getInstance())); - } - - public void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request)); - message.userid(userid).groupid(groupid); - sendMessage(message, false, counter); - } - - public void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request)); - message.userid(userid).groupid(groupid); - sendMessage(message, false, counter); - } - - @Override - protected MessageProducers getProducer() { - return messageAgent.getHttpProducer(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.net.http.*; +import static org.redkale.mq.MessageRecord.CTYPE_HTTP_REQUEST; + +/** + * 不依赖MessageRecord则可兼容RPC方式 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpMessageClient extends MessageClient { + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected HttpMessageClient(MessageAgent messageAgent) { + super(messageAgent); + if (messageAgent != null) { // //RPC方式下无messageAgent + this.respTopic = messageAgent.generateHttpRespTopic(); + } + } + + //格式: http.req.user + public String generateHttpReqTopic(String module) { + return MessageAgent.generateHttpReqTopic(module); + } + + //格式: http.req.user-n10 + public String generateHttpReqTopic(String module, String resname) { + return MessageAgent.generateHttpReqTopic(module, resname); + } + + public String generateHttpReqTopic(HttpSimpleRequest request, String path) { + String module = request.getRequestURI(); + if (path != null && !path.isEmpty() && module.startsWith(path)) module = module.substring(path.length()); + module = module.substring(1); //去掉/ + module = module.substring(0, module.indexOf('/')); + Map headers = request.getHeaders(); + String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); + return MessageAgent.generateHttpReqTopic(module, resname); + } + + public final void produceMessage(HttpSimpleRequest request) { + produceMessage(generateHttpReqTopic(request, null), 0, null, request, null); + } + + public final void produceMessage(HttpSimpleRequest request, AtomicLong counter) { + produceMessage(generateHttpReqTopic(request, null), 0, null, request, counter); + } + + public final void produceMessage(Serializable userid, HttpSimpleRequest request) { + produceMessage(generateHttpReqTopic(request, null), userid, null, request, null); + } + + public final void produceMessage(Serializable userid, String groupid, HttpSimpleRequest request) { + produceMessage(generateHttpReqTopic(request, null), userid, groupid, request, null); + } + + public final void produceMessage(Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + produceMessage(generateHttpReqTopic(request, null), userid, groupid, request, counter); + } + + public final void produceMessage(String topic, HttpSimpleRequest request) { + produceMessage(topic, 0, null, request, null); + } + + public final void produceMessage(String topic, HttpSimpleRequest request, AtomicLong counter) { + produceMessage(topic, 0, null, request, counter); + } + + public final void produceMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request) { + produceMessage(topic, userid, groupid, request, null); + } + + public final void broadcastMessage(HttpSimpleRequest request) { + broadcastMessage(generateHttpReqTopic(request, null), 0, null, request, null); + } + + public final void broadcastMessage(HttpSimpleRequest request, AtomicLong counter) { + broadcastMessage(generateHttpReqTopic(request, null), 0, null, request, counter); + } + + public final void broadcastMessage(Serializable userid, HttpSimpleRequest request) { + broadcastMessage(generateHttpReqTopic(request, null), userid, null, request, null); + } + + public final void broadcastMessage(Serializable userid, String groupid, HttpSimpleRequest request) { + broadcastMessage(generateHttpReqTopic(request, null), userid, groupid, request, null); + } + + public final void broadcastMessage(Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + broadcastMessage(generateHttpReqTopic(request, null), userid, groupid, request, counter); + } + + public final void broadcastMessage(String topic, HttpSimpleRequest request) { + broadcastMessage(topic, 0, null, request, null); + } + + public final void broadcastMessage(String topic, HttpSimpleRequest request, AtomicLong counter) { + broadcastMessage(topic, 0, null, request, counter); + } + + public final void broadcastMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request) { + broadcastMessage(topic, userid, groupid, request, null); + } + + public CompletableFuture sendMessage(HttpSimpleRequest request, Type type) { + return sendMessage(generateHttpReqTopic(request, null), 0, null, request, null).thenApply((HttpResult httbs) -> { + if (httbs == null || httbs.getResult() == null) return null; + return JsonConvert.root().convertFrom(type, httbs.getResult()); + }); + } + + public CompletableFuture sendMessage(Serializable userid, HttpSimpleRequest request, Type type) { + return sendMessage(generateHttpReqTopic(request, null), userid, null, request, null).thenApply((HttpResult httbs) -> { + if (httbs == null || httbs.getResult() == null) return null; + return JsonConvert.root().convertFrom(type, httbs.getResult()); + }); + } + + public CompletableFuture sendMessage(Serializable userid, String groupid, HttpSimpleRequest request, Type type) { + return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, null).thenApply((HttpResult httbs) -> { + if (httbs == null || httbs.getResult() == null) return null; + return JsonConvert.root().convertFrom(type, httbs.getResult()); + }); + } + + public final CompletableFuture> sendMessage(HttpSimpleRequest request) { + return sendMessage(generateHttpReqTopic(request, null), 0, null, request, null); + } + + public final CompletableFuture> sendMessage(HttpSimpleRequest request, AtomicLong counter) { + return sendMessage(generateHttpReqTopic(request, null), 0, null, request, counter); + } + + public final CompletableFuture> sendMessage(Serializable userid, HttpSimpleRequest request) { + return sendMessage(generateHttpReqTopic(request, null), userid, null, request, null); + } + + public final CompletableFuture> sendMessage(Serializable userid, String groupid, HttpSimpleRequest request) { + return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, null); + } + + public final CompletableFuture> sendMessage(Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + return sendMessage(generateHttpReqTopic(request, null), userid, groupid, request, counter); + } + + public final CompletableFuture> sendMessage(String topic, HttpSimpleRequest request) { + return sendMessage(topic, 0, null, request, null); + } + + public final CompletableFuture> sendMessage(String topic, HttpSimpleRequest request, AtomicLong counter) { + return sendMessage(topic, 0, null, request, counter); + } + + public final CompletableFuture> sendMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request) { + return sendMessage(topic, userid, null, request, (AtomicLong) null); + } + + public CompletableFuture> sendMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request)); + message.userid(userid).groupid(groupid); + //if (finest) logger.log(Level.FINEST, "HttpMessageClient.sendMessage: " + message); + return sendMessage(message, true, counter).thenApply(r -> r.decodeContent(HttpResultCoder.getInstance())); + } + + public void broadcastMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request)); + message.userid(userid).groupid(groupid); + sendMessage(message, false, counter); + } + + public void produceMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + MessageRecord message = createMessageRecord(CTYPE_HTTP_REQUEST, topic, null, HttpSimpleRequestCoder.getInstance().encode(request)); + message.userid(userid).groupid(groupid); + sendMessage(message, false, counter); + } + + @Override + protected MessageProducers getProducer() { + return messageAgent.getHttpProducer(); + } +} diff --git a/src/org/redkale/mq/HttpMessageClusterClient.java b/src/main/java/org/redkale/mq/HttpMessageClusterClient.java similarity index 90% rename from src/org/redkale/mq/HttpMessageClusterClient.java rename to src/main/java/org/redkale/mq/HttpMessageClusterClient.java index b22550d74..9e1d5a7a9 100644 --- a/src/org/redkale/mq/HttpMessageClusterClient.java +++ b/src/main/java/org/redkale/mq/HttpMessageClusterClient.java @@ -1,276 +1,276 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.net.*; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.*; -import java.util.logging.Level; -import javax.annotation.Resource; -import org.redkale.cluster.ClusterAgent; -import org.redkale.net.http.*; -import org.redkale.util.Utility; - -/** - * 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpMessageClusterClient extends HttpMessageClient { - - //jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET - private static final Set DISALLOWED_HEADERS_SET = Utility.ofSet("connection", "content-length", - "date", "expect", "from", "host", "origin", "referer", "upgrade", "via", "warning"); - - protected ClusterAgent clusterAgent; - - @Resource(name = "cluster.httpClient") - protected HttpClient httpClient; - - //protected java.net.http.HttpClient httpClient; - - public HttpMessageClusterClient(ClusterAgent clusterAgent) { - super(null); - Objects.requireNonNull(clusterAgent); - this.clusterAgent = clusterAgent; - //this.httpClient = java.net.http.HttpClient.newHttpClient(); - } - - @Override - public CompletableFuture> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - return httpAsync(userid, request); - } - - @Override - public void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - httpAsync(userid, request); - } - - @Override - public void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - mqtpAsync(userid, request); - } - - private CompletableFuture> mqtpAsync(int userid, HttpSimpleRequest req) { - final boolean finest = logger.isLoggable(Level.FINEST); - String module = req.getRequestURI(); - module = module.substring(1); //去掉/ - module = module.substring(0, module.indexOf('/')); - Map headers = req.getHeaders(); - String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); - return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> { - if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture(); - final Map clientHeaders = new LinkedHashMap<>(); - byte[] clientBody = null; - if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true"); - if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); - if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); - if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); - if (userid != 0) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); - if (headers != null) headers.forEach((n, v) -> { - if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v); - }); - clientHeaders.put("Content-Type", "x-www-form-urlencoded"); - if (req.getBody() != null && req.getBody().length > 0) { - String paramstr = req.getParametersToString(); - if (paramstr != null) { - if (req.getRequestURI().indexOf('?') > 0) { - req.setRequestURI(req.getRequestURI() + "&" + paramstr); - } else { - req.setRequestURI(req.getRequestURI() + "?" + paramstr); - } - } - clientBody = req.getBody(); - } else { - String paramstr = req.getParametersToString(); - if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8); - } - List futures = new ArrayList<>(); - for (Map.Entry> en : addrmap.entrySet()) { - String realmodule = en.getKey(); - Collection addrs = en.getValue(); - if (addrs == null || addrs.isEmpty()) continue; - String suburi = req.getRequestURI(); - suburi = suburi.substring(1); //跳过 / - suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/')); - futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, clientHeaders, clientBody, addrs.iterator())); - } - if (futures.isEmpty()) return CompletableFuture.completedFuture(null); - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null); - }); - } - - private CompletableFuture> httpAsync(int userid, HttpSimpleRequest req) { - final boolean finest = logger.isLoggable(Level.FINEST); - String module = req.getRequestURI(); - module = module.substring(1); //去掉/ - module = module.substring(0, module.indexOf('/')); - Map headers = req.getHeaders(); - String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); - return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> { - if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture(); - final Map clientHeaders = new LinkedHashMap<>(); - byte[] clientBody = null; - if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true"); - if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); - if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); - if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); - if (userid != 0) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); - if (headers != null) headers.forEach((n, v) -> { - if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v); - }); - clientHeaders.put("Content-Type", "x-www-form-urlencoded"); - if (req.getBody() != null && req.getBody().length > 0) { - String paramstr = req.getParametersToString(); - if (paramstr != null) { - if (req.getRequestURI().indexOf('?') > 0) { - req.setRequestURI(req.getRequestURI() + "&" + paramstr); - } else { - req.setRequestURI(req.getRequestURI() + "?" + paramstr); - } - } - clientBody = req.getBody(); - } else { - String paramstr = req.getParametersToString(); - if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8); - } - return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), clientHeaders, clientBody, addrs.iterator()); - }); - } - - private CompletableFuture> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, final Map clientHeaders, byte[] clientBody, Iterator it) { - if (!it.hasNext()) return CompletableFuture.completedFuture(null); - InetSocketAddress addr = it.next(); - String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi; - return httpClient.postAsync(url, clientHeaders, clientBody); - } - -// private CompletableFuture> mqtpAsync(int userid, HttpSimpleRequest req) { -// final boolean finest = logger.isLoggable(Level.FINEST); -// String module = req.getRequestURI(); -// module = module.substring(1); //去掉/ -// module = module.substring(0, module.indexOf('/')); -// Map headers = req.getHeaders(); -// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); -// return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> { -// if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture(); -// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000)); -// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true"); -// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); -// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); -// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); -// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); -// if (headers != null) headers.forEach((n, v) -> { -// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v); -// }); -// builder.header("Content-Type", "x-www-form-urlencoded"); -// if (req.getBody() != null && req.getBody().length > 0) { -// String paramstr = req.getParametersToString(); -// if (paramstr != null) { -// if (req.getRequestURI().indexOf('?') > 0) { -// req.setRequestURI(req.getRequestURI() + "&" + paramstr); -// } else { -// req.setRequestURI(req.getRequestURI() + "?" + paramstr); -// } -// } -// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody())); -// } else { -// String paramstr = req.getParametersToString(); -// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr)); -// } -// List futures = new ArrayList<>(); -// for (Map.Entry> en : addrmap.entrySet()) { -// String realmodule = en.getKey(); -// Collection addrs = en.getValue(); -// if (addrs == null || addrs.isEmpty()) continue; -// String suburi = req.getRequestURI(); -// suburi = suburi.substring(1); //跳过 / -// suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/')); -// futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator())); -// } -// if (futures.isEmpty()) return CompletableFuture.completedFuture(null); -// return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null); -// }); -// } -// -// private CompletableFuture> httpAsync(int userid, HttpSimpleRequest req) { -// final boolean finest = logger.isLoggable(Level.FINEST); -// String module = req.getRequestURI(); -// module = module.substring(1); //去掉/ -// module = module.substring(0, module.indexOf('/')); -// Map headers = req.getHeaders(); -// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); -// return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> { -// if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture(); -// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000)); -// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true"); -// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); -// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); -// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); -// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); -// if (headers != null) headers.forEach((n, v) -> { -// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v); -// }); -// builder.header("Content-Type", "x-www-form-urlencoded"); -// if (req.getBody() != null && req.getBody().length > 0) { -// String paramstr = req.getParametersToString(); -// if (paramstr != null) { -// if (req.getRequestURI().indexOf('?') > 0) { -// req.setRequestURI(req.getRequestURI() + "&" + paramstr); -// } else { -// req.setRequestURI(req.getRequestURI() + "?" + paramstr); -// } -// } -// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody())); -// } else { -// String paramstr = req.getParametersToString(); -// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr)); -// } -// return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator()); -// }); -// } -// -// private CompletableFuture> forEachCollectionFuture(boolean finest, int userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator it) { -// if (!it.hasNext()) return CompletableFuture.completedFuture(null); -// InetSocketAddress addr = it.next(); -// String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi; -// return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> { -// if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it); -// HttpResult rs = new HttpResult(); -// java.net.http.HttpHeaders hs = resp.headers(); -// if (hs != null) { -// Map> hm = hs.map(); -// if (hm != null) { -// for (Map.Entry> en : hm.entrySet()) { -// if ("date".equals(en.getKey()) || "content-type".equals(en.getKey()) -// || "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue; -// List val = en.getValue(); -// if (val != null && val.size() == 1) { -// rs.header(en.getKey(), val.get(0)); -// } -// } -// } -// } -// rs.setResult(resp.body()); -// if (finest) { -// StringBuilder sb = new StringBuilder(); -// Map params = req.getParams(); -// if (params != null && !params.isEmpty()) { -// params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v)); -// } -// logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8)); -// } -// return CompletableFuture.completedFuture(rs); -// }); -// } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.io.Serializable; +import java.net.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.*; +import java.util.logging.Level; +import javax.annotation.Resource; +import org.redkale.cluster.ClusterAgent; +import org.redkale.net.http.*; +import org.redkale.util.Utility; + +/** + * 没有配置MQ的情况下依赖ClusterAgent实现的默认HttpMessageClient实例 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpMessageClusterClient extends HttpMessageClient { + + //jdk.internal.net.http.common.Utils.DISALLOWED_HEADERS_SET + private static final Set DISALLOWED_HEADERS_SET = Utility.ofSet("connection", "content-length", + "date", "expect", "from", "host", "origin", "referer", "upgrade", "via", "warning"); + + protected ClusterAgent clusterAgent; + + @Resource(name = "cluster.httpClient") + protected HttpClient httpClient; + + //protected java.net.http.HttpClient httpClient; + public HttpMessageClusterClient(ClusterAgent clusterAgent) { + super(null); + Objects.requireNonNull(clusterAgent); + this.clusterAgent = clusterAgent; + //this.httpClient = java.net.http.HttpClient.newHttpClient(); + } + + @Override + public CompletableFuture> sendMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + return httpAsync(userid, request); + } + + @Override + public void produceMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + httpAsync(userid, request); + } + + @Override + public void broadcastMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + mqtpAsync(userid, request); + } + + private CompletableFuture> mqtpAsync(Serializable userid, HttpSimpleRequest req) { + final boolean finest = logger.isLoggable(Level.FINEST); + String module = req.getRequestURI(); + module = module.substring(1); //去掉/ + module = module.substring(0, module.indexOf('/')); + Map headers = req.getHeaders(); + String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); + return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> { + if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture(); + final Map clientHeaders = new LinkedHashMap<>(); + byte[] clientBody = null; + if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true"); + if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); + if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); + if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); + if (userid != null) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); + if (headers != null) headers.forEach((n, v) -> { + if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v); + }); + clientHeaders.put("Content-Type", "x-www-form-urlencoded"); + if (req.getBody() != null && req.getBody().length > 0) { + String paramstr = req.getParametersToString(); + if (paramstr != null) { + if (req.getRequestURI().indexOf('?') > 0) { + req.setRequestURI(req.getRequestURI() + "&" + paramstr); + } else { + req.setRequestURI(req.getRequestURI() + "?" + paramstr); + } + } + clientBody = req.getBody(); + } else { + String paramstr = req.getParametersToString(); + if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8); + } + List futures = new ArrayList<>(); + for (Map.Entry> en : addrmap.entrySet()) { + String realmodule = en.getKey(); + Collection addrs = en.getValue(); + if (addrs == null || addrs.isEmpty()) continue; + String suburi = req.getRequestURI(); + suburi = suburi.substring(1); //跳过 / + suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/')); + futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, clientHeaders, clientBody, addrs.iterator())); + } + if (futures.isEmpty()) return CompletableFuture.completedFuture(null); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null); + }); + } + + private CompletableFuture> httpAsync(Serializable userid, HttpSimpleRequest req) { + final boolean finest = logger.isLoggable(Level.FINEST); + String module = req.getRequestURI(); + module = module.substring(1); //去掉/ + module = module.substring(0, module.indexOf('/')); + Map headers = req.getHeaders(); + String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); + return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> { + if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture(); + final Map clientHeaders = new LinkedHashMap<>(); + byte[] clientBody = null; + if (req.isRpc()) clientHeaders.put(Rest.REST_HEADER_RPC_NAME, "true"); + if (req.isFrombody()) clientHeaders.put(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); + if (req.getReqConvertType() != null) clientHeaders.put(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); + if (req.getRespConvertType() != null) clientHeaders.put(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); + if (userid != null) clientHeaders.put(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); + if (headers != null) headers.forEach((n, v) -> { + if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) clientHeaders.put(n, v); + }); + clientHeaders.put("Content-Type", "x-www-form-urlencoded"); + if (req.getBody() != null && req.getBody().length > 0) { + String paramstr = req.getParametersToString(); + if (paramstr != null) { + if (req.getRequestURI().indexOf('?') > 0) { + req.setRequestURI(req.getRequestURI() + "&" + paramstr); + } else { + req.setRequestURI(req.getRequestURI() + "?" + paramstr); + } + } + clientBody = req.getBody(); + } else { + String paramstr = req.getParametersToString(); + if (paramstr != null) clientBody = paramstr.getBytes(StandardCharsets.UTF_8); + } + return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), clientHeaders, clientBody, addrs.iterator()); + }); + } + + private CompletableFuture> forEachCollectionFuture(boolean finest, Serializable userid, HttpSimpleRequest req, String requesturi, final Map clientHeaders, byte[] clientBody, Iterator it) { + if (!it.hasNext()) return CompletableFuture.completedFuture(null); + InetSocketAddress addr = it.next(); + String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi; + return httpClient.postAsync(url, clientHeaders, clientBody); + } + +// private CompletableFuture> mqtpAsync(Serializable userid, HttpSimpleRequest req) { +// final boolean finest = logger.isLoggable(Level.FINEST); +// String module = req.getRequestURI(); +// module = module.substring(1); //去掉/ +// module = module.substring(0, module.indexOf('/')); +// Map headers = req.getHeaders(); +// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); +// return clusterAgent.queryMqtpAddress("mqtp", module, resname).thenCompose(addrmap -> { +// if (addrmap == null || addrmap.isEmpty()) return new HttpResult().status(404).toAnyFuture(); +// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000)); +// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true"); +// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); +// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); +// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); +// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); +// if (headers != null) headers.forEach((n, v) -> { +// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v); +// }); +// builder.header("Content-Type", "x-www-form-urlencoded"); +// if (req.getBody() != null && req.getBody().length > 0) { +// String paramstr = req.getParametersToString(); +// if (paramstr != null) { +// if (req.getRequestURI().indexOf('?') > 0) { +// req.setRequestURI(req.getRequestURI() + "&" + paramstr); +// } else { +// req.setRequestURI(req.getRequestURI() + "?" + paramstr); +// } +// } +// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody())); +// } else { +// String paramstr = req.getParametersToString(); +// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr)); +// } +// List futures = new ArrayList<>(); +// for (Map.Entry> en : addrmap.entrySet()) { +// String realmodule = en.getKey(); +// Collection addrs = en.getValue(); +// if (addrs == null || addrs.isEmpty()) continue; +// String suburi = req.getRequestURI(); +// suburi = suburi.substring(1); //跳过 / +// suburi = "/" + realmodule + suburi.substring(suburi.indexOf('/')); +// futures.add(forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + suburi, builder, addrs.iterator())); +// } +// if (futures.isEmpty()) return CompletableFuture.completedFuture(null); +// return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(v -> null); +// }); +// } +// +// private CompletableFuture> httpAsync(Serializable userid, HttpSimpleRequest req) { +// final boolean finest = logger.isLoggable(Level.FINEST); +// String module = req.getRequestURI(); +// module = module.substring(1); //去掉/ +// module = module.substring(0, module.indexOf('/')); +// Map headers = req.getHeaders(); +// String resname = headers == null ? "" : headers.getOrDefault(Rest.REST_HEADER_RESOURCE_NAME, ""); +// return clusterAgent.queryHttpAddress("http", module, resname).thenCompose(addrs -> { +// if (addrs == null || addrs.isEmpty()) return new HttpResult().status(404).toAnyFuture(); +// java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().timeout(Duration.ofMillis(30000)); +// if (req.isRpc()) builder.header(Rest.REST_HEADER_RPC_NAME, "true"); +// if (req.isFrombody()) builder.header(Rest.REST_HEADER_PARAM_FROM_BODY, "true"); +// if (req.getReqConvertType() != null) builder.header(Rest.REST_HEADER_REQ_CONVERT_TYPE, req.getReqConvertType().toString()); +// if (req.getRespConvertType() != null) builder.header(Rest.REST_HEADER_RESP_CONVERT_TYPE, req.getRespConvertType().toString()); +// if (userid != 0) builder.header(Rest.REST_HEADER_CURRUSERID_NAME, "" + userid); +// if (headers != null) headers.forEach((n, v) -> { +// if (!DISALLOWED_HEADERS_SET.contains(n.toLowerCase())) builder.header(n, v); +// }); +// builder.header("Content-Type", "x-www-form-urlencoded"); +// if (req.getBody() != null && req.getBody().length > 0) { +// String paramstr = req.getParametersToString(); +// if (paramstr != null) { +// if (req.getRequestURI().indexOf('?') > 0) { +// req.setRequestURI(req.getRequestURI() + "&" + paramstr); +// } else { +// req.setRequestURI(req.getRequestURI() + "?" + paramstr); +// } +// } +// builder.POST(java.net.http.HttpRequest.BodyPublishers.ofByteArray(req.getBody())); +// } else { +// String paramstr = req.getParametersToString(); +// if (paramstr != null) builder.POST(java.net.http.HttpRequest.BodyPublishers.ofString(paramstr)); +// } +// return forEachCollectionFuture(finest, userid, req, (req.getPath() != null && !req.getPath().isEmpty() ? req.getPath() : "") + req.getRequestURI(), builder, addrs.iterator()); +// }); +// } +// +// private CompletableFuture> forEachCollectionFuture(boolean finest, Serializable userid, HttpSimpleRequest req, String requesturi, java.net.http.HttpRequest.Builder builder, Iterator it) { +// if (!it.hasNext()) return CompletableFuture.completedFuture(null); +// InetSocketAddress addr = it.next(); +// String url = "http://" + addr.getHostString() + ":" + addr.getPort() + requesturi; +// return httpClient.sendAsync(builder.copy().uri(URI.create(url)).build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()).thenCompose(resp -> { +// if (resp.statusCode() != 200) return forEachCollectionFuture(finest, userid, req, requesturi, builder, it); +// HttpResult rs = new HttpResult(); +// java.net.http.HttpHeaders hs = resp.headers(); +// if (hs != null) { +// Map> hm = hs.map(); +// if (hm != null) { +// for (Map.Entry> en : hm.entrySet()) { +// if ("date".equals(en.getKey()) || "content-type".equals(en.getKey()) +// || "server".equals(en.getKey()) || "connection".equals(en.getKey())) continue; +// List val = en.getValue(); +// if (val != null && val.size() == 1) { +// rs.header(en.getKey(), val.get(0)); +// } +// } +// } +// } +// rs.setResult(resp.body()); +// if (finest) { +// StringBuilder sb = new StringBuilder(); +// Map params = req.getParams(); +// if (params != null && !params.isEmpty()) { +// params.forEach((n, v) -> sb.append('&').append(n).append('=').append(v)); +// } +// logger.log(Level.FINEST, url + "?userid=" + userid + sb + ", result = " + new String(resp.body(), StandardCharsets.UTF_8)); +// } +// return CompletableFuture.completedFuture(rs); +// }); +// } +} diff --git a/src/org/redkale/mq/HttpMessageLocalClient.java b/src/main/java/org/redkale/mq/HttpMessageLocalClient.java similarity index 56% rename from src/org/redkale/mq/HttpMessageLocalClient.java rename to src/main/java/org/redkale/mq/HttpMessageLocalClient.java index 17fbee0af..6ec446909 100644 --- a/src/org/redkale/mq/HttpMessageLocalClient.java +++ b/src/main/java/org/redkale/mq/HttpMessageLocalClient.java @@ -1,216 +1,273 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicLong; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.http.*; - -/** - * 没有配置MQ且也没有ClusterAgent的情况下实现的默认HttpMessageClient实例 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.4.0 - */ -public class HttpMessageLocalClient extends HttpMessageClient { - - protected final HttpServer server; - - public HttpMessageLocalClient(HttpServer server) { - super(null); - this.server = server; - } - - @Override - public CompletableFuture sendMessage(HttpSimpleRequest request, Type type) { - HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet(); - HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request); - CompletableFuture future = new CompletableFuture(); - HttpResponse resp = new HttpMessageLocalResponse(req, future); - try { - servlet.execute(req, resp); - } catch (Exception e) { - future.completeExceptionally(e); - } - return future; - } - - @Override - public CompletableFuture sendMessage(int userid, HttpSimpleRequest request, Type type) { - HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet(); - HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request); - CompletableFuture future = new CompletableFuture(); - HttpResponse resp = new HttpMessageLocalResponse(req, future); - try { - servlet.execute(req, resp); - } catch (Exception e) { - future.completeExceptionally(e); - } - return future; - } - - @Override - public CompletableFuture sendMessage(int userid, String groupid, HttpSimpleRequest request, Type type) { - HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet(); - HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request); - CompletableFuture future = new CompletableFuture(); - HttpResponse resp = new HttpMessageLocalResponse(req, future); - try { - servlet.execute(req, resp); - } catch (Exception e) { - future.completeExceptionally(e); - } - return future; - } - - @Override - public CompletableFuture> sendMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet(); - HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request); - CompletableFuture future = new CompletableFuture(); - HttpResponse resp = new HttpMessageLocalResponse(req, future); - try { - servlet.execute(req, resp); - } catch (Exception e) { - future.completeExceptionally(e); - } - return future.thenApply(rs -> { - if (rs == null) return new HttpResult(); - if (rs instanceof HttpResult) { - Object result = ((HttpResult) rs).getResult(); - if (result == null || result instanceof byte[]) return (HttpResult) rs; - return new HttpResult(JsonConvert.root().convertToBytes(result)); - } - return new HttpResult(JsonConvert.root().convertToBytes(rs)); - }); - } - - @Override - public void produceMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet(); - HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request); - HttpResponse resp = new HttpMessageLocalResponse(req, null); - try { - servlet.execute(req, resp); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public void broadcastMessage(String topic, int userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { - HttpPrepareServlet servlet = (HttpPrepareServlet) server.getPrepareServlet(); - HttpRequest req = new HttpMessageLocalRequest(server.getContext(), request); - HttpResponse resp = new HttpMessageLocalResponse(req, null); - try { - servlet.execute(req, resp); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static class HttpMessageLocalRequest extends HttpRequest { - - public HttpMessageLocalRequest(HttpContext context, HttpSimpleRequest req) { - super(context, req); - } - } - - public static class HttpMessageLocalResponse extends HttpResponse { - - private CompletableFuture future; - - public HttpMessageLocalResponse(HttpRequest req, CompletableFuture future) { - super(req.getContext(), req, null); - this.future = future; - } - - @Override - public void finishJson(org.redkale.service.RetResult ret) { - if (future == null) return; - future.complete(ret); - } - - @Override - public void finish(String obj) { - if (future == null) return; - future.complete(obj == null ? "" : obj); - } - - @Override - public void finish404() { - finish(404, null); - } - - @Override - public void finish(int status, String msg) { - if (future == null) return; - if (status == 0 || status == 200) { - future.complete(msg == null ? "" : msg); - } else { - future.complete(new HttpResult(msg == null ? "" : msg).status(status)); - } - } - - @Override - public void finish(final Convert convert, HttpResult result) { - if (future == null) return; - if (convert != null) result.convert(convert); - future.complete(result); - } - - @Override - public void finish(boolean kill, final byte[] bs, int offset, int length) { - if (future == null) return; - if (offset == 0 && bs.length == length) { - future.complete(bs); - } else { - future.complete(Arrays.copyOfRange(bs, offset, offset + length)); - } - } - - @Override - public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) { - if (future == null) return; - byte[] rs = (offset == 0 && bs.length == length) ? bs : Arrays.copyOfRange(bs, offset, offset + length); - future.complete(rs); - } - - @Override - public void finish(boolean kill, ByteBuffer buffer) { - if (future == null) return; - byte[] bs = new byte[buffer.remaining()]; - buffer.get(bs); - future.complete(bs); - } - - @Override - public void finish(boolean kill, ByteBuffer... buffers) { - if (future == null) return; - int size = 0; - for (ByteBuffer buf : buffers) { - size += buf.remaining(); - } - byte[] bs = new byte[size]; - int index = 0; - for (ByteBuffer buf : buffers) { - int r = buf.remaining(); - buf.get(bs, index, r); - index += r; - } - future.complete(bs); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import org.redkale.boot.*; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.net.http.*; + +/** + * 没有配置MQ且也没有ClusterAgent的情况下实现的默认HttpMessageClient实例 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.4.0 + */ +public class HttpMessageLocalClient extends HttpMessageClient { + + protected final Application application; + + protected final String resourceName; + + protected HttpServer server; + + public HttpMessageLocalClient(Application application, String resourceName) { + super(null); + this.application = application; + this.resourceName = resourceName; + } + + private HttpServer httpServer() { + if (this.server == null) { + NodeHttpServer nodeHttpServer = null; + List nodeServers = application.getNodeServers(); + for (NodeServer n : nodeServers) { + if (n.getClass() == NodeHttpServer.class && Objects.equals(resourceName, ((NodeHttpServer) n).getHttpServer().getName())) { + nodeHttpServer = (NodeHttpServer) n; + break; + } + } + if (nodeHttpServer == null) { + for (NodeServer n : nodeServers) { + if (n.getClass() == NodeHttpServer.class) { + nodeHttpServer = (NodeHttpServer) n; + break; + } + } + } + this.server = nodeHttpServer.getServer(); + } + return this.server; + } + + protected HttpContext context() { + return httpServer().getContext(); + } + + protected HttpPrepareServlet prepareServlet() { + return (HttpPrepareServlet) httpServer().getPrepareServlet(); + } + + @Override + public CompletableFuture sendMessage(HttpSimpleRequest request, Type type) { + HttpPrepareServlet ps = prepareServlet(); + String topic = generateHttpReqTopic(request, request.getPath()); + HttpServlet servlet = ps.findServletByTopic(topic); + CompletableFuture future = new CompletableFuture(); + if (servlet == null) { + future.completeExceptionally(new RuntimeException("404 Not Found " + topic)); + return future; + } + HttpRequest req = new HttpMessageLocalRequest(context(), request); + HttpResponse resp = new HttpMessageLocalResponse(req, future); + try { + servlet.execute(req, resp); + } catch (Exception e) { + future.completeExceptionally(e); + } + return future; + } + + @Override + public CompletableFuture sendMessage(Serializable userid, HttpSimpleRequest request, Type type) { + HttpPrepareServlet ps = prepareServlet(); + String topic = generateHttpReqTopic(request, request.getPath()); + HttpServlet servlet = ps.findServletByTopic(topic); + CompletableFuture future = new CompletableFuture(); + if (servlet == null) { + future.completeExceptionally(new RuntimeException("404 Not Found " + topic)); + return future; + } + HttpRequest req = new HttpMessageLocalRequest(context(), request); + HttpResponse resp = new HttpMessageLocalResponse(req, future); + try { + servlet.execute(req, resp); + } catch (Exception e) { + future.completeExceptionally(e); + } + return future; + } + + @Override + public CompletableFuture sendMessage(Serializable userid, String groupid, HttpSimpleRequest request, Type type) { + HttpPrepareServlet ps = prepareServlet(); + String topic = generateHttpReqTopic(request, request.getPath()); + HttpServlet servlet = ps.findServletByTopic(topic); + CompletableFuture future = new CompletableFuture(); + if (servlet == null) { + future.completeExceptionally(new RuntimeException("404 Not Found " + topic)); + return future; + } + HttpRequest req = new HttpMessageLocalRequest(context(), request); + HttpResponse resp = new HttpMessageLocalResponse(req, future); + try { + servlet.execute(req, resp); + } catch (Exception e) { + future.completeExceptionally(e); + } + return future; + } + + @Override + public CompletableFuture> sendMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + HttpPrepareServlet ps = prepareServlet(); + HttpServlet servlet = ps.findServletByTopic(topic); + if (servlet == null) return CompletableFuture.completedFuture(new HttpResult().status(404)); + HttpRequest req = new HttpMessageLocalRequest(context(), request); + CompletableFuture future = new CompletableFuture(); + HttpResponse resp = new HttpMessageLocalResponse(req, future); + try { + servlet.execute(req, resp); + } catch (Exception e) { + future.completeExceptionally(e); + } + return future.thenApply(rs -> { + if (rs == null) return new HttpResult(); + if (rs instanceof HttpResult) { + Object result = ((HttpResult) rs).getResult(); + if (result == null || result instanceof byte[]) return (HttpResult) rs; + return new HttpResult(JsonConvert.root().convertToBytes(result)); + } + return new HttpResult(JsonConvert.root().convertToBytes(rs)); + }); + } + + @Override + public void produceMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + HttpPrepareServlet ps = prepareServlet(); + HttpServlet servlet = ps.findServletByTopic(topic); + if (servlet == null) return; + HttpRequest req = new HttpMessageLocalRequest(context(), request); + HttpResponse resp = new HttpMessageLocalResponse(req, null); + try { + servlet.execute(req, resp); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void broadcastMessage(String topic, Serializable userid, String groupid, HttpSimpleRequest request, AtomicLong counter) { + HttpPrepareServlet ps = prepareServlet(); + HttpRequest req = new HttpMessageLocalRequest(context(), request); + HttpResponse resp = new HttpMessageLocalResponse(req, null); + ps.filterServletsByMmcTopic(topic).forEach(s -> { + try { + s.execute(req, resp); + } catch (Exception e) { + logger.log(Level.SEVERE, request + " execute " + s + " error", e); + } + }); + } + + public static class HttpMessageLocalRequest extends HttpRequest { + + public HttpMessageLocalRequest(HttpContext context, HttpSimpleRequest req) { + super(context, req); + } + } + + public static class HttpMessageLocalResponse extends HttpResponse { + + private CompletableFuture future; + + public HttpMessageLocalResponse(HttpRequest req, CompletableFuture future) { + super(req.getContext(), req, null); + this.future = future; + } + + @Override + public void finish(String obj) { + if (future == null) return; + future.complete(obj == null ? "" : obj); + } + + @Override + public void finish404() { + finish(404, null); + } + + @Override + public void finish(int status, String msg) { + if (future == null) return; + if (status == 0 || status == 200) { + future.complete(msg == null ? "" : msg); + } else { + future.complete(new HttpResult(msg == null ? "" : msg).status(status)); + } + } + + @Override + public void finish(final Convert convert, Type valueType, HttpResult result) { + if (future == null) return; + if (convert != null) result.convert(convert); + future.complete(result); + } + + @Override + public void finish(boolean kill, final byte[] bs, int offset, int length) { + if (future == null) return; + if (offset == 0 && bs.length == length) { + future.complete(bs); + } else { + future.complete(Arrays.copyOfRange(bs, offset, offset + length)); + } + } + + @Override + public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) { + if (future == null) return; + byte[] rs = (offset == 0 && bs.length == length) ? bs : Arrays.copyOfRange(bs, offset, offset + length); + future.complete(rs); + } + + @Override + public void finish(boolean kill, ByteBuffer buffer) { + if (future == null) return; + byte[] bs = new byte[buffer.remaining()]; + buffer.get(bs); + future.complete(bs); + } + + @Override + public void finish(boolean kill, ByteBuffer... buffers) { + if (future == null) return; + int size = 0; + for (ByteBuffer buf : buffers) { + size += buf.remaining(); + } + byte[] bs = new byte[size]; + int index = 0; + for (ByteBuffer buf : buffers) { + int r = buf.remaining(); + buf.get(bs, index, r); + index += r; + } + future.complete(bs); + } + } +} diff --git a/src/org/redkale/mq/HttpMessageProcessor.java b/src/main/java/org/redkale/mq/HttpMessageProcessor.java similarity index 93% rename from src/org/redkale/mq/HttpMessageProcessor.java rename to src/main/java/org/redkale/mq/HttpMessageProcessor.java index 6165c702d..544f39c1b 100644 --- a/src/org/redkale/mq/HttpMessageProcessor.java +++ b/src/main/java/org/redkale/mq/HttpMessageProcessor.java @@ -1,156 +1,156 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.concurrent.*; -import java.util.function.*; -import java.util.logging.*; -import org.redkale.boot.NodeHttpServer; -import org.redkale.net.http.*; -import org.redkale.service.Service; -import org.redkale.util.ObjectPool; - -/** - * 一个Service对应一个MessageProcessor - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpMessageProcessor implements MessageProcessor { - - protected final boolean finest; - - protected final boolean finer; - - protected final boolean fine; - - protected final Logger logger; - - protected HttpMessageClient messageClient; - - protected final MessageProducers producers; - - protected final NodeHttpServer server; - - protected final Service service; - - protected final HttpServlet servlet; - - protected final boolean multiconsumer; - - protected final String restmodule; // 前后有/, 例如: /user/ - - protected final String multimodule; // 前后有/, 例如: /userstat/ - - protected ThreadLocal> respPoolThreadLocal; - - protected final Supplier respSupplier; - - protected final Consumer respConsumer; - - protected CountDownLatch cdl; - - protected long starttime; - - protected final Runnable innerCallback = () -> { - if (cdl != null) cdl.countDown(); - }; - - public HttpMessageProcessor(Logger logger, HttpMessageClient messageClient, MessageProducers producers, NodeHttpServer server, Service service, HttpServlet servlet) { - this.logger = logger; - this.finest = logger.isLoggable(Level.FINEST); - this.finer = logger.isLoggable(Level.FINER); - this.fine = logger.isLoggable(Level.FINE); - this.messageClient = messageClient; - this.producers = producers; - this.server = server; - this.service = service; - this.servlet = servlet; - MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); - this.multiconsumer = mmc != null; - this.restmodule = "/" + Rest.getRestModule(service) + "/"; - this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null; - this.respSupplier = () -> respPoolThreadLocal.get().get(); - this.respConsumer = resp -> respPoolThreadLocal.get().accept(resp); - this.respPoolThreadLocal = ThreadLocal.withInitial(() -> ObjectPool.createUnsafePool(Runtime.getRuntime().availableProcessors(), - ps -> new HttpMessageResponse(server.getHttpServer().getContext(), messageClient, respSupplier, respConsumer), HttpMessageResponse::prepare, HttpMessageResponse::recycle)); - } - - @Override - public void begin(final int size, long starttime) { - this.starttime = starttime; - this.cdl = new CountDownLatch(size); - } - - @Override - public void process(final MessageRecord message, final Runnable callback) { - execute(message, innerCallback); - } - - private void execute(final MessageRecord message, final Runnable callback) { - HttpMessageRequest request = null; - try { - long now = System.currentTimeMillis(); - long cha = now - message.createtime; - long e = now - starttime; - if (multiconsumer) message.setResptopic(null); //不容许有响应 - - HttpMessageResponse response = respSupplier.get(); - request = response.request(); - response.prepare(message, callback, producers.getProducer(message)); - if (multiconsumer) request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule)); - - server.getHttpServer().getContext().execute(servlet, request, response); - long o = System.currentTimeMillis() - now; - if ((cha > 1000 || e > 100 || o > 1000) && fine) { - logger.log(Level.FINE, "HttpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message); - } else if ((cha > 50 || e > 10 || o > 50) && finer) { - logger.log(Level.FINER, "HttpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message); - } else if (finest) { - logger.log(Level.FINEST, "HttpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message); - } - } catch (Throwable ex) { - if (message.getResptopic() != null && !message.getResptopic().isEmpty()) { - HttpMessageResponse.finishHttpResult(finest, request == null ? null : request.getRespConvert(), - message, callback, messageClient, producers.getProducer(message), message.getResptopic(), new HttpResult().status(500)); - } - logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex); - } - } - - @Override - public void commit() { - if (this.cdl != null) { - try { - this.cdl.await(30, TimeUnit.SECONDS); - } catch (Exception ex) { - logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " commit error, restmodule=" + this.restmodule, ex); - } - this.cdl = null; - } - } - - public MessageProducers getProducer() { - return producers; - } - - public NodeHttpServer getServer() { - return server; - } - - public Service getService() { - return service; - } - - public HttpServlet getServlet() { - return servlet; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.concurrent.*; +import java.util.function.*; +import java.util.logging.*; +import org.redkale.boot.NodeHttpServer; +import org.redkale.net.http.*; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * 一个Service对应一个MessageProcessor + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpMessageProcessor implements MessageProcessor { + + protected final boolean finest; + + protected final boolean finer; + + protected final boolean fine; + + protected final Logger logger; + + protected HttpMessageClient messageClient; + + protected final MessageProducers producers; + + protected final NodeHttpServer server; + + protected final Service service; + + protected final HttpServlet servlet; + + protected final boolean multiconsumer; + + protected final String restmodule; // 前后有/, 例如: /user/ + + protected final String multimodule; // 前后有/, 例如: /userstat/ + + protected ThreadLocal> respPoolThreadLocal; + + protected final Supplier respSupplier; + + protected final Consumer respConsumer; + + protected CountDownLatch cdl; + + protected long starttime; + + protected final Runnable innerCallback = () -> { + if (cdl != null) cdl.countDown(); + }; + + public HttpMessageProcessor(Logger logger, HttpMessageClient messageClient, MessageProducers producers, NodeHttpServer server, Service service, HttpServlet servlet) { + this.logger = logger; + this.finest = logger.isLoggable(Level.FINEST); + this.finer = logger.isLoggable(Level.FINER); + this.fine = logger.isLoggable(Level.FINE); + this.messageClient = messageClient; + this.producers = producers; + this.server = server; + this.service = service; + this.servlet = servlet; + MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); + this.multiconsumer = mmc != null; + this.restmodule = "/" + Rest.getRestModule(service) + "/"; + this.multimodule = mmc != null ? ("/" + mmc.module() + "/") : null; + this.respSupplier = () -> respPoolThreadLocal.get().get(); + this.respConsumer = resp -> respPoolThreadLocal.get().accept(resp); + this.respPoolThreadLocal = ThreadLocal.withInitial(() -> ObjectPool.createUnsafePool(Utility.cpus(), + ps -> new HttpMessageResponse(server.getHttpServer().getContext(), messageClient, respSupplier, respConsumer), HttpMessageResponse::prepare, HttpMessageResponse::recycle)); + } + + @Override + public void begin(final int size, long starttime) { + this.starttime = starttime; + this.cdl = new CountDownLatch(size); + } + + @Override + public void process(final MessageRecord message, final Runnable callback) { + execute(message, innerCallback); + } + + private void execute(final MessageRecord message, final Runnable callback) { + HttpMessageRequest request = null; + try { + long now = System.currentTimeMillis(); + long cha = now - message.createtime; + long e = now - starttime; + if (multiconsumer) message.setResptopic(null); //不容许有响应 + + HttpMessageResponse response = respSupplier.get(); + request = response.request(); + response.prepare(message, callback, producers.getProducer(message)); + if (multiconsumer) request.setRequestURI(request.getRequestURI().replaceFirst(this.multimodule, this.restmodule)); + + server.getHttpServer().getContext().execute(servlet, request, response); + long o = System.currentTimeMillis() - now; + if ((cha > 1000 || e > 100 || o > 1000) && fine) { + logger.log(Level.FINE, "HttpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message); + } else if ((cha > 50 || e > 10 || o > 50) && finer) { + logger.log(Level.FINER, "HttpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message); + } else if (finest) { + logger.log(Level.FINEST, "HttpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message); + } + } catch (Throwable ex) { + if (message.getResptopic() != null && !message.getResptopic().isEmpty()) { + HttpMessageResponse.finishHttpResult(finest, request == null ? null : request.getRespConvert(), + null, message, callback, messageClient, producers.getProducer(message), message.getResptopic(), new HttpResult().status(500)); + } + logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex); + } + } + + @Override + public void commit() { + if (this.cdl != null) { + try { + this.cdl.await(30, TimeUnit.SECONDS); + } catch (Exception ex) { + logger.log(Level.SEVERE, HttpMessageProcessor.class.getSimpleName() + " commit error, restmodule=" + this.restmodule, ex); + } + this.cdl = null; + } + } + + public MessageProducers getProducer() { + return producers; + } + + public NodeHttpServer getServer() { + return server; + } + + public Service getService() { + return service; + } + + public HttpServlet getServlet() { + return servlet; + } + +} diff --git a/src/org/redkale/mq/HttpMessageRequest.java b/src/main/java/org/redkale/mq/HttpMessageRequest.java similarity index 93% rename from src/org/redkale/mq/HttpMessageRequest.java rename to src/main/java/org/redkale/mq/HttpMessageRequest.java index 6f7d0fcab..df890155c 100644 --- a/src/org/redkale/mq/HttpMessageRequest.java +++ b/src/main/java/org/redkale/mq/HttpMessageRequest.java @@ -1,57 +1,57 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import org.redkale.convert.Convert; -import org.redkale.net.http.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpMessageRequest extends HttpRequest { - - protected MessageRecord message; - - public HttpMessageRequest(HttpContext context) { - super(context, (HttpSimpleRequest) null); - } - - protected HttpMessageRequest prepare(MessageRecord message) { - super.initSimpleRequest(message.decodeContent(HttpSimpleRequestCoder.getInstance())); - this.message = message; - this.hashid = this.message.hash(); - this.currentUserid = message.getUserid(); - this.createtime = System.currentTimeMillis(); - return this; - } - - public void setRequestURI(String uri) { - this.requestURI = uri; - } - - @Override - public Convert getRespConvert() { - return this.respConvert; - } - - @Override - protected void prepare() { - super.prepare(); - this.keepAlive = false; - } - - @Override - protected void recycle() { - super.recycle(); - this.message = null; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import org.redkale.convert.Convert; +import org.redkale.net.http.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpMessageRequest extends HttpRequest { + + protected MessageRecord message; + + public HttpMessageRequest(HttpContext context) { + super(context, (HttpSimpleRequest) null); + } + + protected HttpMessageRequest prepare(MessageRecord message) { + super.initSimpleRequest(message.decodeContent(HttpSimpleRequestCoder.getInstance()), false); + this.message = message; + this.hashid = this.message.hash(); + this.currentUserid = message.getUserid(); + this.createtime = System.currentTimeMillis(); + return this; + } + + public void setRequestURI(String uri) { + this.requestURI = uri; + } + + @Override + public Convert getRespConvert() { + return this.respConvert; + } + + @Override + protected void prepare() { + super.prepare(); + this.keepAlive = false; + } + + @Override + protected void recycle() { + super.recycle(); + this.message = null; + } +} diff --git a/src/org/redkale/mq/HttpMessageResponse.java b/src/main/java/org/redkale/mq/HttpMessageResponse.java similarity index 80% rename from src/org/redkale/mq/HttpMessageResponse.java rename to src/main/java/org/redkale/mq/HttpMessageResponse.java index b9b41cbb7..9fc21eae3 100644 --- a/src/org/redkale/mq/HttpMessageResponse.java +++ b/src/main/java/org/redkale/mq/HttpMessageResponse.java @@ -1,212 +1,203 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.function.*; -import java.util.logging.Level; -import org.redkale.convert.*; -import org.redkale.net.http.*; -import org.redkale.service.RetResult; -import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT; -import org.redkale.net.Response; - -/** - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpMessageResponse extends HttpResponse { - - protected final HttpMessageClient messageClient; - - protected MessageRecord message; - - protected MessageProducer producer; - - protected boolean finest; - - protected Runnable callback; - - public HttpMessageResponse(HttpContext context, HttpMessageClient messageClient, final Supplier respSupplier, final Consumer respConsumer) { - super(context, new HttpMessageRequest(context), null); - this.responseSupplier = (Supplier) respSupplier; - this.responseConsumer = (Consumer) respConsumer; - this.messageClient = messageClient; - } - -// public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback, -// HttpResponseConfig config, HttpMessageClient messageClient, MessageProducer producer) { -// super(context, request, config); -// this.message = request.message; -// this.callback = callback; -// this.messageClient = messageClient; -// this.producer = producer; -// this.finest = producer.logger.isLoggable(Level.FINEST); -// } - - public void prepare(MessageRecord message, Runnable callback, MessageProducer producer) { - ((HttpMessageRequest)request).prepare(message); - this.message = message; - this.callback = callback; - this.producer = producer; - this.finest = producer.logger.isLoggable(Level.FINEST); - } - - public HttpMessageRequest request() { - return (HttpMessageRequest) request; - } - - public void finishHttpResult(HttpResult result) { - finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), this.message, this.callback, this.messageClient, this.producer, message.getResptopic(), result); - } - - public static void finishHttpResult(boolean finest, Convert respConvert, MessageRecord msg, Runnable callback, MessageClient messageClient, MessageProducer producer, String resptopic, HttpResult result) { - if (callback != null) callback.run(); - if (resptopic == null || resptopic.isEmpty()) return; - if (result.getResult() instanceof RetResult) { - RetResult ret = (RetResult) result.getResult(); - //必须要塞入retcode, 开发者可以无需反序列化ret便可确定操作是否返回成功 - if (!ret.isSuccess()) result.header("retcode", String.valueOf(ret.getRetcode())); - } - if (result.convert() == null && respConvert != null) result.convert(respConvert); - if (finest) { - Object innerrs = result.getResult(); - if (innerrs instanceof byte[]) innerrs = new String((byte[]) innerrs, StandardCharsets.UTF_8); - producer.logger.log(Level.FINEST, "HttpMessageResponse.finishHttpResult seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders()); - } - byte[] content = HttpResultCoder.getInstance().encode(result); - producer.apply(messageClient.createMessageRecord(msg.getSeqid(), CTYPE_HTTP_RESULT, resptopic, null, content)); - } - - @Override - protected void prepare() { - super.prepare(); - } - - @Override - protected boolean recycle() { - Supplier respSupplier = this.responseSupplier; - Consumer respConsumer = this.responseConsumer; - boolean rs = super.recycle(); - this.responseSupplier = respSupplier; - this.responseConsumer = respConsumer; - this.message = null; - this.producer = null; - this.callback = null; - this.finest = false; - return rs; - } - - @Override - public void finishJson(org.redkale.service.RetResult ret) { - if (message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - finishHttpResult(new HttpResult(ret.clearConvert(), ret)); - } - - @Override - public void finish(String obj) { - if (message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - finishHttpResult(new HttpResult(obj == null ? "" : obj)); - } - - @Override - public void finish404() { - finish(404, null); - } - - @Override - public void finish(int status, String msg) { - if (status > 400) { - producer.logger.log(Level.WARNING, "HttpMessageResponse.finish status: " + status + ", message: " + this.message); - } else if (finest) { - producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status); - } - if (this.message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - finishHttpResult(new HttpResult(msg == null ? "" : msg).status(status)); - } - - @Override - public void finish(final Convert convert, HttpResult result) { - if (message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - if (convert != null) result.convert(convert); - finishHttpResult(result); - } - - @Override - public void finish(boolean kill, final byte[] bs, int offset, int length) { - if (message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - if (offset == 0 && bs.length == length) { - finishHttpResult(new HttpResult(bs)); - } else { - finishHttpResult(new HttpResult(Arrays.copyOfRange(bs, offset, offset + length))); - } - } - - @Override - public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) { - if (message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - byte[] rs = (offset == 0 && bs.length == length) ? bs : Arrays.copyOfRange(bs, offset, offset + length); - finishHttpResult(new HttpResult(rs).contentType(contentType)); - } - - @Override - public void finish(boolean kill, ByteBuffer buffer) { - if (message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - byte[] bs = new byte[buffer.remaining()]; - buffer.get(bs); - finishHttpResult(new HttpResult(bs)); - } - - @Override - public void finish(boolean kill, ByteBuffer... buffers) { - if (message.isEmptyResptopic()) { - if (callback != null) callback.run(); - return; - } - int size = 0; - for (ByteBuffer buf : buffers) { - size += buf.remaining(); - } - byte[] bs = new byte[size]; - int index = 0; - for (ByteBuffer buf : buffers) { - int r = buf.remaining(); - buf.get(bs, index, r); - index += r; - } - finishHttpResult(new HttpResult(bs)); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.lang.reflect.Type; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.function.*; +import java.util.logging.Level; +import org.redkale.convert.*; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import static org.redkale.mq.MessageRecord.CTYPE_HTTP_RESULT; +import org.redkale.net.Response; + +/** + * + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpMessageResponse extends HttpResponse { + + protected final HttpMessageClient messageClient; + + protected MessageRecord message; + + protected MessageProducer producer; + + protected boolean finest; + + protected Runnable callback; + + public HttpMessageResponse(HttpContext context, HttpMessageClient messageClient, final Supplier respSupplier, final Consumer respConsumer) { + super(context, new HttpMessageRequest(context), null); + this.responseSupplier = (Supplier) respSupplier; + this.responseConsumer = (Consumer) respConsumer; + this.messageClient = messageClient; + } + +// public HttpMessageResponse(HttpContext context, HttpMessageRequest request, Runnable callback, +// HttpResponseConfig config, HttpMessageClient messageClient, MessageProducer producer) { +// super(context, request, config); +// this.message = request.message; +// this.callback = callback; +// this.messageClient = messageClient; +// this.producer = producer; +// this.finest = producer.logger.isLoggable(Level.FINEST); +// } + public void prepare(MessageRecord message, Runnable callback, MessageProducer producer) { + ((HttpMessageRequest) request).prepare(message); + this.message = message; + this.callback = callback; + this.producer = producer; + this.finest = producer.logger.isLoggable(Level.FINEST); + } + + public HttpMessageRequest request() { + return (HttpMessageRequest) request; + } + + public void finishHttpResult(Type type, HttpResult result) { + finishHttpResult(this.finest, ((HttpMessageRequest) this.request).getRespConvert(), type, this.message, this.callback, this.messageClient, this.producer, message.getResptopic(), result); + } + + public static void finishHttpResult(boolean finest, Convert respConvert, Type type, MessageRecord msg, Runnable callback, MessageClient messageClient, MessageProducer producer, String resptopic, HttpResult result) { + if (callback != null) callback.run(); + if (resptopic == null || resptopic.isEmpty()) return; + if (result.getResult() instanceof RetResult) { + RetResult ret = (RetResult) result.getResult(); + //必须要塞入retcode, 开发者可以无需反序列化ret便可确定操作是否返回成功 + if (!ret.isSuccess()) result.header("retcode", String.valueOf(ret.getRetcode())); + } + if (result.convert() == null && respConvert != null) result.convert(respConvert); + if (finest) { + Object innerrs = result.getResult(); + if (innerrs instanceof byte[]) innerrs = new String((byte[]) innerrs, StandardCharsets.UTF_8); + producer.logger.log(Level.FINEST, "HttpMessageResponse.finishHttpResult seqid=" + msg.getSeqid() + ", content: " + innerrs + ", status: " + result.getStatus() + ", headers: " + result.getHeaders()); + } + byte[] content = HttpResultCoder.getInstance().encode(result); + producer.apply(messageClient.createMessageRecord(msg.getSeqid(), CTYPE_HTTP_RESULT, resptopic, null, content)); + } + + @Override + protected void prepare() { + super.prepare(); + } + + @Override + protected boolean recycle() { + Supplier respSupplier = this.responseSupplier; + Consumer respConsumer = this.responseConsumer; + boolean rs = super.recycle(); + this.responseSupplier = respSupplier; + this.responseConsumer = respConsumer; + this.message = null; + this.producer = null; + this.callback = null; + this.finest = false; + return rs; + } + + @Override + public void finish(String obj) { + if (message.isEmptyResptopic()) { + if (callback != null) callback.run(); + return; + } + finishHttpResult(String.class, new HttpResult(obj == null ? "" : obj)); + } + + @Override + public void finish404() { + finish(404, null); + } + + @Override + public void finish(int status, String msg) { + if (status > 400) { + producer.logger.log(Level.WARNING, "HttpMessageResponse.finish status: " + status + ", uri: " + this.request.getRequestURI() + ", message: " + this.message); + } else if (finest) { + producer.logger.log(Level.FINEST, "HttpMessageResponse.finish status: " + status); + } + if (this.message.isEmptyResptopic()) { + if (callback != null) callback.run(); + return; + } + finishHttpResult(String.class, new HttpResult(msg == null ? "" : msg).status(status)); + } + + @Override + public void finish(final Convert convert, Type type, HttpResult result) { + if (message.isEmptyResptopic()) { + if (callback != null) callback.run(); + return; + } + if (convert != null) result.convert(convert); + finishHttpResult(type, result); + } + + @Override + public void finish(boolean kill, final byte[] bs, int offset, int length) { + if (message.isEmptyResptopic()) { + if (callback != null) callback.run(); + return; + } + if (offset == 0 && bs.length == length) { + finishHttpResult(null, new HttpResult(bs)); + } else { + finishHttpResult(null, new HttpResult(Arrays.copyOfRange(bs, offset, offset + length))); + } + } + + @Override + public void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) { + if (message.isEmptyResptopic()) { + if (callback != null) callback.run(); + return; + } + byte[] rs = (offset == 0 && bs.length == length) ? bs : Arrays.copyOfRange(bs, offset, offset + length); + finishHttpResult(null, new HttpResult(rs).contentType(contentType)); + } + + @Override + public void finish(boolean kill, ByteBuffer buffer) { + if (message.isEmptyResptopic()) { + if (callback != null) callback.run(); + return; + } + byte[] bs = new byte[buffer.remaining()]; + buffer.get(bs); + finishHttpResult(null, new HttpResult(bs)); + } + + @Override + public void finish(boolean kill, ByteBuffer... buffers) { + if (message.isEmptyResptopic()) { + if (callback != null) callback.run(); + return; + } + int size = 0; + for (ByteBuffer buf : buffers) { + size += buf.remaining(); + } + byte[] bs = new byte[size]; + int index = 0; + for (ByteBuffer buf : buffers) { + int r = buf.remaining(); + buf.get(bs, index, r); + index += r; + } + finishHttpResult(null, new HttpResult(bs)); + } + +} diff --git a/src/org/redkale/mq/HttpResultCoder.java b/src/main/java/org/redkale/mq/HttpResultCoder.java similarity index 97% rename from src/org/redkale/mq/HttpResultCoder.java rename to src/main/java/org/redkale/mq/HttpResultCoder.java index a6464b5b9..eb4a1221f 100644 --- a/src/org/redkale/mq/HttpResultCoder.java +++ b/src/main/java/org/redkale/mq/HttpResultCoder.java @@ -1,132 +1,132 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.net.HttpCookie; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; -import static org.redkale.mq.MessageCoder.*; -import org.redkale.net.http.HttpResult; -import org.redkale.util.Utility; - -/** - * HttpResult的MessageCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpResultCoder implements MessageCoder { - - private static final HttpResultCoder instance = new HttpResultCoder(); - - public static HttpResultCoder getInstance() { - return instance; - } - - @Override - public byte[] encode(HttpResult data) { - if (data == null) return null; - byte[] contentType = MessageCoder.getBytes(data.getContentType()); - byte[] headers = MessageCoder.getBytes(data.getHeaders()); - byte[] cookies = getBytes(data.getCookies()); - byte[] content; - if (data.getResult() == null) { - content = new byte[0]; //"" - } else if (data.getResult() instanceof byte[]) { - content = (byte[]) data.getResult(); - } else if (data.getResult() instanceof CharSequence) { - content = MessageCoder.getBytes(data.getResult().toString()); - } else { - Convert cc = data.convert(); - if (cc == null) cc = JsonConvert.root(); - content = cc.convertToBytes(data.getResult()); - } - int count = 4 + 2 + contentType.length + headers.length + cookies.length + 4 + (content == null ? 0 : content.length); - final byte[] bs = new byte[count]; - ByteBuffer buffer = ByteBuffer.wrap(bs); - buffer.putInt(data.getStatus()); - buffer.putChar((char) contentType.length); - if (contentType.length > 0) buffer.put(contentType); - buffer.put(headers); - buffer.put(cookies); - if (content == null || content.length == 0) { - buffer.putInt(0); - } else { - buffer.putInt(content.length); - buffer.put(content); - } - return bs; - } - - @Override - public HttpResult decode(byte[] data) { - if (data == null) return null; - ByteBuffer buffer = ByteBuffer.wrap(data); - HttpResult result = new HttpResult(); - result.setStatus(buffer.getInt()); - result.setContentType(MessageCoder.getShortString(buffer)); - result.setHeaders(MessageCoder.getMap(buffer)); - result.setCookies(getCookieList(buffer)); - int len = buffer.getInt(); - if (len > 0) { - byte[] bs = new byte[len]; - buffer.get(bs); - result.setResult(bs); - } - return result; - } - - public static byte[] getBytes(final List list) { - if (list == null || list.isEmpty()) return new byte[2]; - final AtomicInteger len = new AtomicInteger(2); - list.forEach(cookie -> { - len.addAndGet(2 + (cookie.getName() == null ? 0 : Utility.encodeUTF8Length(cookie.getName()))); - len.addAndGet(2 + (cookie.getValue() == null ? 0 : Utility.encodeUTF8Length(cookie.getValue()))); - len.addAndGet(2 + (cookie.getDomain() == null ? 0 : Utility.encodeUTF8Length(cookie.getDomain()))); - len.addAndGet(2 + (cookie.getPath() == null ? 0 : Utility.encodeUTF8Length(cookie.getPath()))); - len.addAndGet(2 + (cookie.getPortlist() == null ? 0 : Utility.encodeUTF8Length(cookie.getPortlist()))); - len.addAndGet(8 + 1 + 1); //maxage Secure HttpOnly - }); - final byte[] bs = new byte[len.get()]; - final ByteBuffer buffer = ByteBuffer.wrap(bs); - buffer.putChar((char) list.size()); - list.forEach(cookie -> { - putShortString(buffer, cookie.getName()); - putShortString(buffer, cookie.getValue()); - putShortString(buffer, cookie.getDomain()); - putShortString(buffer, cookie.getPath()); - putShortString(buffer, cookie.getPortlist()); - buffer.putLong(cookie.getMaxAge()); - buffer.put(cookie.getSecure() ? (byte) 1 : (byte) 0); - buffer.put(cookie.isHttpOnly() ? (byte) 1 : (byte) 0); - }); - return bs; - } - - public static List getCookieList(ByteBuffer buffer) { - int len = buffer.getChar(); - if (len == 0) return null; - final List list = new ArrayList<>(len); - for (int i = 0; i < len; i++) { - HttpCookie cookie = new HttpCookie(getShortString(buffer), getShortString(buffer)); - cookie.setDomain(getShortString(buffer)); - cookie.setPath(getShortString(buffer)); - cookie.setPortlist(getShortString(buffer)); - cookie.setMaxAge(buffer.getLong()); - cookie.setSecure(buffer.get() == 1); - cookie.setHttpOnly(buffer.get() == 1); - list.add(cookie); - } - return list; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.net.HttpCookie; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; +import static org.redkale.mq.MessageCoder.*; +import org.redkale.net.http.HttpResult; +import org.redkale.util.Utility; + +/** + * HttpResult的MessageCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpResultCoder implements MessageCoder { + + private static final HttpResultCoder instance = new HttpResultCoder(); + + public static HttpResultCoder getInstance() { + return instance; + } + + @Override + public byte[] encode(HttpResult data) { + if (data == null) return null; + byte[] contentType = MessageCoder.getBytes(data.getContentType()); + byte[] headers = MessageCoder.getBytes(data.getHeaders()); + byte[] cookies = getBytes(data.getCookies()); + byte[] content; + if (data.getResult() == null) { + content = new byte[0]; //"" + } else if (data.getResult() instanceof byte[]) { + content = (byte[]) data.getResult(); + } else if (data.getResult() instanceof CharSequence) { + content = MessageCoder.getBytes(data.getResult().toString()); + } else { + Convert cc = data.convert(); + if (cc == null) cc = JsonConvert.root(); + content = cc.convertToBytes(data.getResult()); + } + int count = 4 + 2 + contentType.length + headers.length + cookies.length + 4 + (content == null ? 0 : content.length); + final byte[] bs = new byte[count]; + ByteBuffer buffer = ByteBuffer.wrap(bs); + buffer.putInt(data.getStatus()); + buffer.putChar((char) contentType.length); + if (contentType.length > 0) buffer.put(contentType); + buffer.put(headers); + buffer.put(cookies); + if (content == null || content.length == 0) { + buffer.putInt(0); + } else { + buffer.putInt(content.length); + buffer.put(content); + } + return bs; + } + + @Override + public HttpResult decode(byte[] data) { + if (data == null) return null; + ByteBuffer buffer = ByteBuffer.wrap(data); + HttpResult result = new HttpResult(); + result.setStatus(buffer.getInt()); + result.setContentType(MessageCoder.getShortString(buffer)); + result.setHeaders(MessageCoder.getMap(buffer)); + result.setCookies(getCookieList(buffer)); + int len = buffer.getInt(); + if (len > 0) { + byte[] bs = new byte[len]; + buffer.get(bs); + result.setResult(bs); + } + return result; + } + + public static byte[] getBytes(final List list) { + if (list == null || list.isEmpty()) return new byte[2]; + final AtomicInteger len = new AtomicInteger(2); + list.forEach(cookie -> { + len.addAndGet(2 + (cookie.getName() == null ? 0 : Utility.encodeUTF8Length(cookie.getName()))); + len.addAndGet(2 + (cookie.getValue() == null ? 0 : Utility.encodeUTF8Length(cookie.getValue()))); + len.addAndGet(2 + (cookie.getDomain() == null ? 0 : Utility.encodeUTF8Length(cookie.getDomain()))); + len.addAndGet(2 + (cookie.getPath() == null ? 0 : Utility.encodeUTF8Length(cookie.getPath()))); + len.addAndGet(2 + (cookie.getPortlist() == null ? 0 : Utility.encodeUTF8Length(cookie.getPortlist()))); + len.addAndGet(8 + 1 + 1); //maxage Secure HttpOnly + }); + final byte[] bs = new byte[len.get()]; + final ByteBuffer buffer = ByteBuffer.wrap(bs); + buffer.putChar((char) list.size()); + list.forEach(cookie -> { + putShortString(buffer, cookie.getName()); + putShortString(buffer, cookie.getValue()); + putShortString(buffer, cookie.getDomain()); + putShortString(buffer, cookie.getPath()); + putShortString(buffer, cookie.getPortlist()); + buffer.putLong(cookie.getMaxAge()); + buffer.put(cookie.getSecure() ? (byte) 1 : (byte) 0); + buffer.put(cookie.isHttpOnly() ? (byte) 1 : (byte) 0); + }); + return bs; + } + + public static List getCookieList(ByteBuffer buffer) { + int len = buffer.getChar(); + if (len == 0) return null; + final List list = new ArrayList<>(len); + for (int i = 0; i < len; i++) { + HttpCookie cookie = new HttpCookie(getShortString(buffer), getShortString(buffer)); + cookie.setDomain(getShortString(buffer)); + cookie.setPath(getShortString(buffer)); + cookie.setPortlist(getShortString(buffer)); + cookie.setMaxAge(buffer.getLong()); + cookie.setSecure(buffer.get() == 1); + cookie.setHttpOnly(buffer.get() == 1); + list.add(cookie); + } + return list; + } +} diff --git a/src/org/redkale/mq/HttpSimpleRequestCoder.java b/src/main/java/org/redkale/mq/HttpSimpleRequestCoder.java similarity index 88% rename from src/org/redkale/mq/HttpSimpleRequestCoder.java rename to src/main/java/org/redkale/mq/HttpSimpleRequestCoder.java index 75bb1461a..0cbde34b6 100644 --- a/src/org/redkale/mq/HttpSimpleRequestCoder.java +++ b/src/main/java/org/redkale/mq/HttpSimpleRequestCoder.java @@ -1,109 +1,117 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import org.redkale.convert.ConvertType; -import org.redkale.net.http.HttpSimpleRequest; - -/** - * HttpSimpleRequest的MessageCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpSimpleRequestCoder implements MessageCoder { - - private static final HttpSimpleRequestCoder instance = new HttpSimpleRequestCoder(); - - public static HttpSimpleRequestCoder getInstance() { - return instance; - } - - @Override - public byte[] encode(HttpSimpleRequest data) { - byte[] requestURI = MessageCoder.getBytes(data.getRequestURI()); //long-string - byte[] path = MessageCoder.getBytes(data.getPath()); //short-string - byte[] remoteAddr = MessageCoder.getBytes(data.getRemoteAddr());//short-string - byte[] sessionid = MessageCoder.getBytes(data.getSessionid());//short-string - byte[] contentType = MessageCoder.getBytes(data.getContentType());//short-string - byte[] headers = MessageCoder.getBytes(data.getHeaders()); - byte[] params = MessageCoder.getBytes(data.getParams()); - byte[] body = MessageCoder.getBytes(data.getBody()); - int count = 1 //rpc - + 1 //frombody - + 4 //hashid - + 4 //reqConvertType - + 4 //respConvertType - + 4 + requestURI.length + 2 + path.length + 2 + remoteAddr.length + 2 + sessionid.length - + 2 + contentType.length + 4 + headers.length + params.length + 4 + body.length; - byte[] bs = new byte[count]; - ByteBuffer buffer = ByteBuffer.wrap(bs); - buffer.put((byte) (data.isRpc() ? 'T' : 'F')); - buffer.put((byte) (data.isFrombody() ? 'T' : 'F')); - buffer.putInt(data.getHashid()); - buffer.putInt(data.getReqConvertType() == null ? 0 : data.getReqConvertType().getValue()); - buffer.putInt(data.getRespConvertType() == null ? 0 : data.getRespConvertType().getValue()); - buffer.putInt(requestURI.length); - if (requestURI.length > 0) buffer.put(requestURI); - buffer.putChar((char) path.length); - if (path.length > 0) buffer.put(path); - buffer.putChar((char) remoteAddr.length); - if (remoteAddr.length > 0) buffer.put(remoteAddr); - buffer.putChar((char) sessionid.length); - if (sessionid.length > 0) buffer.put(sessionid); - buffer.putChar((char) contentType.length); - if (contentType.length > 0) buffer.put(contentType); - buffer.putInt(data.getCurrentUserid()); - buffer.put(headers); - buffer.put(params); - buffer.putInt(body.length); - if (body.length > 0) buffer.put(body); - return bs; - } - - @Override - public HttpSimpleRequest decode(byte[] data) { - if (data == null) return null; - ByteBuffer buffer = ByteBuffer.wrap(data); - HttpSimpleRequest req = new HttpSimpleRequest(); - req.setRpc(buffer.get() == 'T'); - req.setFrombody(buffer.get() == 'T'); - req.setHashid(buffer.getInt()); - int reqformat = buffer.getInt(); - int respformat = buffer.getInt(); - if (reqformat != 0) req.setReqConvertType(ConvertType.find(reqformat)); - if (respformat != 0) req.setRespConvertType(ConvertType.find(respformat)); - req.setRequestURI(MessageCoder.getLongString(buffer)); - req.setPath(MessageCoder.getShortString(buffer)); - req.setRemoteAddr(MessageCoder.getShortString(buffer)); - req.setSessionid(MessageCoder.getShortString(buffer)); - req.setContentType(MessageCoder.getShortString(buffer)); - req.setCurrentUserid(buffer.getInt()); - req.setHeaders(MessageCoder.getMap(buffer)); - req.setParams(MessageCoder.getMap(buffer)); - int len = buffer.getInt(); - if (len > 0) { - byte[] bs = new byte[len]; - buffer.get(bs); - req.setBody(bs); - } - return req; - } - - protected static String getString(ByteBuffer buffer) { - int len = buffer.getInt(); - if (len == 0) return null; - byte[] bs = new byte[len]; - buffer.get(bs); - return new String(bs, StandardCharsets.UTF_8); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import org.redkale.convert.ConvertType; +import org.redkale.net.http.HttpSimpleRequest; + +/** + * HttpSimpleRequest的MessageCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpSimpleRequestCoder implements MessageCoder { + + private static final HttpSimpleRequestCoder instance = new HttpSimpleRequestCoder(); + + public static HttpSimpleRequestCoder getInstance() { + return instance; + } + + @Override + public byte[] encode(HttpSimpleRequest data) { + byte[] requestURI = MessageCoder.getBytes(data.getRequestURI()); //long-string + byte[] path = MessageCoder.getBytes(data.getPath()); //short-string + byte[] remoteAddr = MessageCoder.getBytes(data.getRemoteAddr());//short-string + byte[] sessionid = MessageCoder.getBytes(data.getSessionid());//short-string + byte[] contentType = MessageCoder.getBytes(data.getContentType());//short-string + byte[] headers = MessageCoder.getBytes(data.getHeaders()); + byte[] params = MessageCoder.getBytes(data.getParams()); + byte[] body = MessageCoder.getBytes(data.getBody()); + byte[] userid = MessageCoder.encodeUserid(data.getCurrentUserid()); + int count = 1 //rpc + + 1 //frombody + + 4 //hashid + + 4 //reqConvertType + + 4 //respConvertType + + 4 + requestURI.length + + 2 + path.length + + 2 + remoteAddr.length + + 2 + sessionid.length + + 2 + contentType.length + + 2 + userid.length + + headers.length + params.length + + 4 + body.length; + byte[] bs = new byte[count]; + ByteBuffer buffer = ByteBuffer.wrap(bs); + buffer.put((byte) (data.isRpc() ? 'T' : 'F')); + buffer.put((byte) (data.isFrombody() ? 'T' : 'F')); + buffer.putInt(data.getHashid()); + buffer.putInt(data.getReqConvertType() == null ? 0 : data.getReqConvertType().getValue()); + buffer.putInt(data.getRespConvertType() == null ? 0 : data.getRespConvertType().getValue()); + buffer.putInt(requestURI.length); + if (requestURI.length > 0) buffer.put(requestURI); + buffer.putChar((char) path.length); + if (path.length > 0) buffer.put(path); + buffer.putChar((char) remoteAddr.length); + if (remoteAddr.length > 0) buffer.put(remoteAddr); + buffer.putChar((char) sessionid.length); + if (sessionid.length > 0) buffer.put(sessionid); + buffer.putChar((char) contentType.length); + if (contentType.length > 0) buffer.put(contentType); + buffer.putChar((char) userid.length); + if (userid.length > 0) buffer.put(userid); + buffer.put(headers); + buffer.put(params); + buffer.putInt(body.length); + if (body.length > 0) buffer.put(body); + return bs; + } + + @Override + public HttpSimpleRequest decode(byte[] data) { + if (data == null) return null; + ByteBuffer buffer = ByteBuffer.wrap(data); + HttpSimpleRequest req = new HttpSimpleRequest(); + req.setRpc(buffer.get() == 'T'); + req.setFrombody(buffer.get() == 'T'); + req.setHashid(buffer.getInt()); + int reqformat = buffer.getInt(); + int respformat = buffer.getInt(); + if (reqformat != 0) req.setReqConvertType(ConvertType.find(reqformat)); + if (respformat != 0) req.setRespConvertType(ConvertType.find(respformat)); + req.setRequestURI(MessageCoder.getLongString(buffer)); + req.setPath(MessageCoder.getShortString(buffer)); + req.setRemoteAddr(MessageCoder.getShortString(buffer)); + req.setSessionid(MessageCoder.getShortString(buffer)); + req.setContentType(MessageCoder.getShortString(buffer)); + req.setCurrentUserid(MessageCoder.decodeUserid(buffer)); + req.setHeaders(MessageCoder.getMap(buffer)); + req.setParams(MessageCoder.getMap(buffer)); + int len = buffer.getInt(); + if (len > 0) { + byte[] bs = new byte[len]; + buffer.get(bs); + req.setBody(bs); + } + return req; + } + + protected static String getString(ByteBuffer buffer) { + int len = buffer.getInt(); + if (len == 0) return null; + byte[] bs = new byte[len]; + buffer.get(bs); + return new String(bs, StandardCharsets.UTF_8); + } +} diff --git a/src/org/redkale/mq/MessageAgent.java b/src/main/java/org/redkale/mq/MessageAgent.java similarity index 96% rename from src/org/redkale/mq/MessageAgent.java rename to src/main/java/org/redkale/mq/MessageAgent.java index 79c446d68..3b6251c56 100644 --- a/src/org/redkale/mq/MessageAgent.java +++ b/src/main/java/org/redkale/mq/MessageAgent.java @@ -1,318 +1,318 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import javax.annotation.Resource; -import org.redkale.boot.*; -import static org.redkale.boot.Application.RESNAME_APP_NODEID; -import org.redkale.net.Servlet; -import org.redkale.net.http.*; -import org.redkale.net.sncp.*; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - * MQ管理器 - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public abstract class MessageAgent { - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - @Resource(name = RESNAME_APP_NODEID) - protected int nodeid; - - protected String name; - - protected AnyValue config; - - protected MessageProducers httpProducer; - - protected MessageProducers sncpProducer; - - protected final Object httpProducerLock = new Object(); - - protected final Object sncpProducerLock = new Object(); - - protected final AtomicLong msgSeqno = new AtomicLong(System.nanoTime()); - - protected HttpMessageClient httpMessageClient; - - protected SncpMessageClient sncpMessageClient; - - protected ScheduledThreadPoolExecutor timeoutExecutor; - - protected int producerCount = 1; - - //本地Service消息接收处理器, key:consumer - protected HashMap messageNodes = new LinkedHashMap<>(); - - public void init(AnyValue config) { - this.name = checkName(config.getValue("name", "")); - this.httpMessageClient = new HttpMessageClient(this); - this.sncpMessageClient = new SncpMessageClient(this); - this.producerCount = config.getIntValue("producers", Runtime.getRuntime().availableProcessors()); - // application (it doesn't execute completion handlers). - this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> { - Thread t = new Thread(r); - t.setName("Redkale-MessageAgent-Timeout-Thread"); - t.setDaemon(true); - return t; - }); - this.timeoutExecutor.setRemoveOnCancelPolicy(true); - } - - public CompletableFuture> start() { - final LinkedHashMap map = new LinkedHashMap<>(); - final List futures = new ArrayList<>(); - this.messageNodes.values().forEach(node -> { - long s = System.currentTimeMillis(); - futures.add(node.consumer.startup().whenComplete((r, t) -> map.put(node.consumer.consumerid, System.currentTimeMillis() - s))); - }); - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(r -> map); - } - - //Application.shutdown 在执行server.shutdown之前执行 - public CompletableFuture stop() { - List futures = new ArrayList<>(); - this.messageNodes.values().forEach(node -> { - futures.add(node.consumer.shutdown()); - }); - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); - } - - //Application.shutdown 在所有server.shutdown执行后执行 - public void destroy(AnyValue config) { - this.httpMessageClient.close().join(); - this.sncpMessageClient.close().join(); - if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown(); - if (this.sncpProducer != null) this.sncpProducer.shutdown().join(); - if (this.httpProducer != null) this.httpProducer.shutdown().join(); - } - - protected List getAllMessageConsumer() { - List consumers = new ArrayList<>(); - MessageConsumer one = this.httpMessageClient == null ? null : this.httpMessageClient.respConsumer; - if (one != null) consumers.add(one); - one = this.sncpMessageClient == null ? null : this.sncpMessageClient.respConsumer; - if (one != null) consumers.add(one); - consumers.addAll(messageNodes.values().stream().map(mcn -> mcn.consumer).collect(Collectors.toList())); - return consumers; - } - - protected List getAllMessageProducer() { - List producers = new ArrayList<>(); - if (this.httpProducer != null) producers.addAll(Utility.ofList(this.httpProducer.producers)); - if (this.sncpProducer != null) producers.addAll(Utility.ofList(this.sncpProducer.producers)); - MessageProducers one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer(); - if (one != null) producers.addAll(Utility.ofList(one.producers)); - one = this.sncpMessageClient == null ? null : this.sncpMessageClient.getProducer(); - if (one != null) producers.addAll(Utility.ofList(one.producers)); - return producers; - } - - public Logger getLogger() { - return logger; - } - - public String getName() { - return name; - } - - public AnyValue getConfig() { - return config; - } - - public void setConfig(AnyValue config) { - this.config = config; - } - - public HttpMessageClient getHttpMessageClient() { - return httpMessageClient; - } - - public SncpMessageClient getSncpMessageClient() { - return sncpMessageClient; - } - - protected String checkName(String name) { //不能含特殊字符 - if (name.isEmpty()) return name; - if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); - for (char ch : name.toCharArray()) { - if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 - throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); - } - } - return name; - } - - //获取指定topic的生产处理器 - public MessageProducers getSncpProducer() { - if (this.sncpProducer == null) { - synchronized (sncpProducerLock) { - if (this.sncpProducer == null) { - MessageProducer[] producers = new MessageProducer[producerCount]; - for (int i = 0; i < producers.length; i++) { - MessageProducer producer = createProducer("SncpProducer"); - producer.startup().join(); - producers[i] = producer; - } - this.sncpProducer = new MessageProducers(producers); - } - } - } - return this.sncpProducer; - } - - public MessageProducers getHttpProducer() { - if (this.httpProducer == null) { - synchronized (httpProducerLock) { - if (this.httpProducer == null) { - MessageProducer[] producers = new MessageProducer[producerCount]; - for (int i = 0; i < producers.length; i++) { - MessageProducer producer = createProducer("HttpProducer"); - producer.startup().join(); - producers[i] = producer; - } - this.httpProducer = new MessageProducers(producers); - } - } - } - return this.httpProducer; - } - - //创建指定topic的生产处理器 - protected abstract MessageProducer createProducer(String name); - - //创建topic,如果已存在则跳过 - public abstract boolean createTopic(String... topics); - - //删除topic,如果不存在则跳过 - public abstract boolean deleteTopic(String... topics); - - //查询所有topic - public abstract List queryTopic(); - - //ServiceLoader时判断配置是否符合当前实现类 - public abstract boolean match(AnyValue config); - - //创建指定topic的消费处理器 - public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor); - - public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) { - AutoLoad al = service.getClass().getAnnotation(AutoLoad.class); - if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return; - { //标记@RestService(name = " ") 需要跳过, 一般作为模板引擎 - RestService rest = service.getClass().getAnnotation(RestService.class); - if (rest != null && !rest.name().isEmpty() && rest.name().trim().isEmpty()) return; - } - String[] topics = generateHttpReqTopics(service); - String consumerid = generateHttpConsumerid(topics, service); - if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat"); - HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, httpMessageClient, getHttpProducer(), ns, service, servlet); - this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(topics, consumerid, processor))); - } - - public final synchronized void putService(NodeSncpServer ns, Service service, SncpServlet servlet) { - AutoLoad al = service.getClass().getAnnotation(AutoLoad.class); - if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return; - String topic = generateSncpReqTopic(service); - String consumerid = generateSncpConsumerid(topic, service); - if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat"); - SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, sncpMessageClient, getSncpProducer(), ns, service, servlet); - this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(new String[]{topic}, consumerid, processor))); - } - - //格式: sncp.req.user - public final String generateSncpReqTopic(Service service) { - if (service instanceof WebSocketNode) { - String resname = Sncp.getResourceName(service); - return "sncp.req.ws" + (resname.isEmpty() ? "" : ("-" + resname)) + ".node" + nodeid; - } - String resname = Sncp.getResourceName(service); - return "sncp.req." + Sncp.getResourceType(service).getSimpleName().replaceAll("Service.*$", "").toLowerCase() + (resname.isEmpty() ? "" : ("-" + resname)); - } - - //格式: consumer-sncp.req.user 不提供外部使用 - protected final String generateSncpConsumerid(String topic, Service service) { - return "consumer-" + topic; - } - - //格式: http.req.user - public static String generateHttpReqTopic(String module) { - return "http.req." + module.toLowerCase(); - } - - //格式: http.req.user - public static String generateHttpReqTopic(String module, String resname) { - return "http.req." + module.toLowerCase() + (resname == null || resname.isEmpty() ? "" : ("-" + resname)); - } - - //格式: sncp.resp.node10 - protected String generateSncpRespTopic() { - return "sncp.resp.node" + nodeid; - } - - //格式: http.resp.node10 - protected String generateHttpRespTopic() { - return "http.resp.node" + nodeid; - } - - //格式: http.req.user - protected String[] generateHttpReqTopics(Service service) { - String resname = Sncp.getResourceName(service); - String module = Rest.getRestModule(service).toLowerCase(); - MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); - if (mmc != null) return new String[]{generateHttpReqTopic(mmc.module()) + (resname.isEmpty() ? "" : ("-" + resname))}; - return new String[]{"http.req." + module + (resname.isEmpty() ? "" : ("-" + resname))}; - } - - //格式: consumer-http.req.user - protected String generateHttpConsumerid(String[] topics, Service service) { - String resname = Sncp.getResourceName(service); - String key = Rest.getRestModule(service).toLowerCase(); - return "consumer-http.req." + key + (resname.isEmpty() ? "" : ("-" + resname)); - - } - - //格式: xxxx.resp.node10 - protected String generateRespTopic(String protocol) { - return protocol + ".resp.node" + nodeid; - } - - protected static class MessageConsumerNode { - - public final NodeServer server; - - public final Service service; - - public final Servlet servlet; - - public final MessageProcessor processor; - - public final MessageConsumer consumer; - - public MessageConsumerNode(NodeServer server, Service service, Servlet servlet, MessageProcessor processor, MessageConsumer consumer) { - this.server = server; - this.service = service; - this.servlet = servlet; - this.processor = processor; - this.consumer = consumer; - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.annotation.Resource; +import org.redkale.boot.*; +import static org.redkale.boot.Application.RESNAME_APP_NODEID; +import org.redkale.net.Servlet; +import org.redkale.net.http.*; +import org.redkale.net.sncp.*; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * MQ管理器 + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public abstract class MessageAgent { + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + @Resource(name = RESNAME_APP_NODEID) + protected int nodeid; + + protected String name; + + protected AnyValue config; + + protected MessageProducers httpProducer; + + protected MessageProducers sncpProducer; + + protected final Object httpProducerLock = new Object(); + + protected final Object sncpProducerLock = new Object(); + + protected final AtomicLong msgSeqno = new AtomicLong(System.nanoTime()); + + protected HttpMessageClient httpMessageClient; + + protected SncpMessageClient sncpMessageClient; + + protected ScheduledThreadPoolExecutor timeoutExecutor; + + protected int producerCount = 1; + + //本地Service消息接收处理器, key:consumer + protected HashMap messageNodes = new LinkedHashMap<>(); + + public void init(AnyValue config) { + this.name = checkName(config.getValue("name", "")); + this.httpMessageClient = new HttpMessageClient(this); + this.sncpMessageClient = new SncpMessageClient(this); + this.producerCount = config.getIntValue("producers", Utility.cpus()); + // application (it doesn't execute completion handlers). + this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> { + Thread t = new Thread(r); + t.setName("Redkale-MessageAgent-Timeout-Thread"); + t.setDaemon(true); + return t; + }); + this.timeoutExecutor.setRemoveOnCancelPolicy(true); + } + + public CompletableFuture> start() { + final LinkedHashMap map = new LinkedHashMap<>(); + final List futures = new ArrayList<>(); + this.messageNodes.values().forEach(node -> { + long s = System.currentTimeMillis(); + futures.add(node.consumer.startup().whenComplete((r, t) -> map.put(node.consumer.consumerid, System.currentTimeMillis() - s))); + }); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).thenApply(r -> map); + } + + //Application.shutdown 在执行server.shutdown之前执行 + public CompletableFuture stop() { + List futures = new ArrayList<>(); + this.messageNodes.values().forEach(node -> { + futures.add(node.consumer.shutdown()); + }); + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); + } + + //Application.shutdown 在所有server.shutdown执行后执行 + public void destroy(AnyValue config) { + this.httpMessageClient.close().join(); + this.sncpMessageClient.close().join(); + if (this.timeoutExecutor != null) this.timeoutExecutor.shutdown(); + if (this.sncpProducer != null) this.sncpProducer.shutdown().join(); + if (this.httpProducer != null) this.httpProducer.shutdown().join(); + } + + protected List getAllMessageConsumer() { + List consumers = new ArrayList<>(); + MessageConsumer one = this.httpMessageClient == null ? null : this.httpMessageClient.respConsumer; + if (one != null) consumers.add(one); + one = this.sncpMessageClient == null ? null : this.sncpMessageClient.respConsumer; + if (one != null) consumers.add(one); + consumers.addAll(messageNodes.values().stream().map(mcn -> mcn.consumer).collect(Collectors.toList())); + return consumers; + } + + protected List getAllMessageProducer() { + List producers = new ArrayList<>(); + if (this.httpProducer != null) producers.addAll(Utility.ofList(this.httpProducer.producers)); + if (this.sncpProducer != null) producers.addAll(Utility.ofList(this.sncpProducer.producers)); + MessageProducers one = this.httpMessageClient == null ? null : this.httpMessageClient.getProducer(); + if (one != null) producers.addAll(Utility.ofList(one.producers)); + one = this.sncpMessageClient == null ? null : this.sncpMessageClient.getProducer(); + if (one != null) producers.addAll(Utility.ofList(one.producers)); + return producers; + } + + public Logger getLogger() { + return logger; + } + + public String getName() { + return name; + } + + public AnyValue getConfig() { + return config; + } + + public void setConfig(AnyValue config) { + this.config = config; + } + + public HttpMessageClient getHttpMessageClient() { + return httpMessageClient; + } + + public SncpMessageClient getSncpMessageClient() { + return sncpMessageClient; + } + + protected String checkName(String name) { //不能含特殊字符 + if (name.isEmpty()) return name; + if (name.charAt(0) >= '0' && name.charAt(0) <= '9') throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); + for (char ch : name.toCharArray()) { + if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 + throw new RuntimeException("name only 0-9 a-z A-Z _ cannot begin 0-9"); + } + } + return name; + } + + //获取指定topic的生产处理器 + public MessageProducers getSncpProducer() { + if (this.sncpProducer == null) { + synchronized (sncpProducerLock) { + if (this.sncpProducer == null) { + MessageProducer[] producers = new MessageProducer[producerCount]; + for (int i = 0; i < producers.length; i++) { + MessageProducer producer = createProducer("SncpProducer"); + producer.startup().join(); + producers[i] = producer; + } + this.sncpProducer = new MessageProducers(producers); + } + } + } + return this.sncpProducer; + } + + public MessageProducers getHttpProducer() { + if (this.httpProducer == null) { + synchronized (httpProducerLock) { + if (this.httpProducer == null) { + MessageProducer[] producers = new MessageProducer[producerCount]; + for (int i = 0; i < producers.length; i++) { + MessageProducer producer = createProducer("HttpProducer"); + producer.startup().join(); + producers[i] = producer; + } + this.httpProducer = new MessageProducers(producers); + } + } + } + return this.httpProducer; + } + + //创建指定topic的生产处理器 + protected abstract MessageProducer createProducer(String name); + + //创建topic,如果已存在则跳过 + public abstract boolean createTopic(String... topics); + + //删除topic,如果不存在则跳过 + public abstract boolean deleteTopic(String... topics); + + //查询所有topic + public abstract List queryTopic(); + + //ServiceLoader时判断配置是否符合当前实现类 + public abstract boolean acceptsConf(AnyValue config); + + //创建指定topic的消费处理器 + public abstract MessageConsumer createConsumer(String[] topics, String group, MessageProcessor processor); + + public final synchronized void putService(NodeHttpServer ns, Service service, HttpServlet servlet) { + AutoLoad al = service.getClass().getAnnotation(AutoLoad.class); + if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return; + { //标记@RestService(name = " ") 需要跳过, 一般作为模板引擎 + RestService rest = service.getClass().getAnnotation(RestService.class); + if (rest != null && !rest.name().isEmpty() && rest.name().trim().isEmpty()) return; + } + String[] topics = generateHttpReqTopics(service); + String consumerid = generateHttpConsumerid(topics, service); + if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat"); + HttpMessageProcessor processor = new HttpMessageProcessor(this.logger, httpMessageClient, getHttpProducer(), ns, service, servlet); + this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(topics, consumerid, processor))); + } + + public final synchronized void putService(NodeSncpServer ns, Service service, SncpServlet servlet) { + AutoLoad al = service.getClass().getAnnotation(AutoLoad.class); + if (al != null && !al.value() && service.getClass().getAnnotation(Local.class) != null) return; + String topic = generateSncpReqTopic(service); + String consumerid = generateSncpConsumerid(topic, service); + if (messageNodes.containsKey(consumerid)) throw new RuntimeException("consumerid(" + consumerid + ") is repeat"); + SncpMessageProcessor processor = new SncpMessageProcessor(this.logger, sncpMessageClient, getSncpProducer(), ns, service, servlet); + this.messageNodes.put(consumerid, new MessageConsumerNode(ns, service, servlet, processor, createConsumer(new String[]{topic}, consumerid, processor))); + } + + //格式: sncp.req.user + public final String generateSncpReqTopic(Service service) { + if (service instanceof WebSocketNode) { + String resname = Sncp.getResourceName(service); + return "sncp.req.ws" + (resname.isEmpty() ? "" : ("-" + resname)) + ".node" + nodeid; + } + String resname = Sncp.getResourceName(service); + return "sncp.req." + Sncp.getResourceType(service).getSimpleName().replaceAll("Service.*$", "").toLowerCase() + (resname.isEmpty() ? "" : ("-" + resname)); + } + + //格式: consumer-sncp.req.user 不提供外部使用 + protected final String generateSncpConsumerid(String topic, Service service) { + return "consumer-" + topic; + } + + //格式: http.req.user + public static String generateHttpReqTopic(String module) { + return "http.req." + module.toLowerCase(); + } + + //格式: http.req.user + public static String generateHttpReqTopic(String module, String resname) { + return "http.req." + module.toLowerCase() + (resname == null || resname.isEmpty() ? "" : ("-" + resname)); + } + + //格式: sncp.resp.node10 + protected String generateSncpRespTopic() { + return "sncp.resp.node" + nodeid; + } + + //格式: http.resp.node10 + protected String generateHttpRespTopic() { + return "http.resp.node" + nodeid; + } + + //格式: http.req.user + protected String[] generateHttpReqTopics(Service service) { + String resname = Sncp.getResourceName(service); + String module = Rest.getRestModule(service).toLowerCase(); + MessageMultiConsumer mmc = service.getClass().getAnnotation(MessageMultiConsumer.class); + if (mmc != null) return new String[]{generateHttpReqTopic(mmc.module()) + (resname.isEmpty() ? "" : ("-" + resname))}; + return new String[]{"http.req." + module + (resname.isEmpty() ? "" : ("-" + resname))}; + } + + //格式: consumer-http.req.user + protected String generateHttpConsumerid(String[] topics, Service service) { + String resname = Sncp.getResourceName(service); + String key = Rest.getRestModule(service).toLowerCase(); + return "consumer-http.req." + key + (resname.isEmpty() ? "" : ("-" + resname)); + + } + + //格式: xxxx.resp.node10 + protected String generateRespTopic(String protocol) { + return protocol + ".resp.node" + nodeid; + } + + protected static class MessageConsumerNode { + + public final NodeServer server; + + public final Service service; + + public final Servlet servlet; + + public final MessageProcessor processor; + + public final MessageConsumer consumer; + + public MessageConsumerNode(NodeServer server, Service service, Servlet servlet, MessageProcessor processor, MessageConsumer consumer) { + this.server = server; + this.service = service; + this.servlet = servlet; + this.processor = processor; + this.consumer = consumer; + } + + } +} diff --git a/src/org/redkale/mq/MessageAgentLoader.java b/src/main/java/org/redkale/mq/MessageAgentProvider.java similarity index 77% rename from src/org/redkale/mq/MessageAgentLoader.java rename to src/main/java/org/redkale/mq/MessageAgentProvider.java index bb22b1cb6..91a8dd932 100644 --- a/src/org/redkale/mq/MessageAgentLoader.java +++ b/src/main/java/org/redkale/mq/MessageAgentProvider.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import org.redkale.util.AnyValue; - -/** - * 自定义的MessageAgent加载器 - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.4.0 - */ -public interface MessageAgentLoader { - - public boolean match(AnyValue config); - - public Class agentClass(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import org.redkale.util.AnyValue; + +/** + * 自定义的MessageAgent加载器 + * + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +public interface MessageAgentProvider { + + public boolean acceptsConf(AnyValue config); + + public Class agentClass(); +} diff --git a/src/org/redkale/mq/MessageClient.java b/src/main/java/org/redkale/mq/MessageClient.java similarity index 98% rename from src/org/redkale/mq/MessageClient.java rename to src/main/java/org/redkale/mq/MessageClient.java index 3f0d1bfd4..22beb6e50 100644 --- a/src/org/redkale/mq/MessageClient.java +++ b/src/main/java/org/redkale/mq/MessageClient.java @@ -1,178 +1,178 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.nio.charset.StandardCharsets; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; -import org.redkale.convert.Convert; -import org.redkale.convert.json.JsonConvert; -import static org.redkale.mq.MessageRecord.*; -import org.redkale.net.http.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public abstract class MessageClient { - - protected final ConcurrentHashMap respNodes = new ConcurrentHashMap<>(); - - protected final MessageAgent messageAgent; - - protected final AtomicLong msgSeqno; - - protected MessageConsumer respConsumer; - - protected String respTopic; - - protected String respConsumerid; - - protected boolean finest; - - protected boolean finer; - - protected boolean fine; - - private final String clazzName; - - protected MessageClient(MessageAgent messageAgent) { - this.messageAgent = messageAgent; - this.msgSeqno = messageAgent == null ? new AtomicLong() : messageAgent.msgSeqno; - this.finest = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINEST); - this.finer = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINER); - this.fine = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINE); - this.clazzName = getClass().getSimpleName(); - } - - protected CompletableFuture close() { - if (this.respConsumer == null) return CompletableFuture.completedFuture(null); - return this.respConsumer.shutdown(); - } - - protected CompletableFuture sendMessage(final MessageRecord message, boolean needresp, AtomicLong counter) { - CompletableFuture future = new CompletableFuture<>(); - try { - if (this.respConsumer == null) { - synchronized (this) { - if (this.respConsumerid == null) this.respConsumerid = "consumer-" + this.respTopic; - if (this.respConsumer == null) { - MessageProcessor processor = (msg, callback) -> { - long now = System.currentTimeMillis(); - MessageRespFutureNode node = respNodes.remove(msg.getSeqid()); - if (node == null) { - messageAgent.logger.log(Level.WARNING, MessageClient.this.getClass().getSimpleName() + " process " + msg + " error, not found mqresp.futurenode"); - return; - } - if (node.scheduledFuture != null) node.scheduledFuture.cancel(true); - AtomicLong ncer = node.getCounter(); - if (ncer != null) ncer.decrementAndGet(); - node.future.complete(msg); - long cha = now - msg.createtime; - if (cha > 1000 && fine) { - messageAgent.logger.log(Level.FINE, clazzName + ".MessageRespFutureNode.process (mqs.delays = " + cha + "ms, mqs.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg)); - } else if (cha > 50 && finer) { - messageAgent.logger.log(Level.FINER, clazzName + ".MessageRespFutureNode.process (mq.delays = " + cha + "ms, mq.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg)); - } else if (finest) { - messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.process (mq.delay = " + cha + "ms, mq.counter = " + ncer + ") mqresp.msg: " + formatRespMessage(msg)); - } - }; - MessageConsumer one = messageAgent.createConsumer(new String[]{respTopic}, respConsumerid, processor); - one.startup().join(); - this.respConsumer = one; - } - } - } - if (needresp && (message.getResptopic() == null || message.getResptopic().isEmpty())) { - message.setResptopic(respTopic); - } - if (counter != null) counter.incrementAndGet(); - getProducer().apply(message); - if (needresp) { - MessageRespFutureNode node = new MessageRespFutureNode(messageAgent.logger, message, respNodes, counter, future); - respNodes.put(message.getSeqid(), node); - ScheduledThreadPoolExecutor executor = messageAgent.timeoutExecutor; - if (executor != null) { - node.scheduledFuture = executor.schedule(node, 30, TimeUnit.SECONDS); - } - } else { - future.complete(null); - } - } catch (Exception ex) { - future.completeExceptionally(ex); - } finally { - return future; - } - } - - protected MessageRecord formatRespMessage(MessageRecord message) { - return message; - } - - protected abstract MessageProducers getProducer(); - - public MessageRecord createMessageRecord(String resptopic, String content) { - return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, null, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8)); - } - - public MessageRecord createMessageRecord(String topic, String resptopic, String content) { - return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8)); - } - - public MessageRecord createMessageRecord(int userid, String topic, String resptopic, String content) { - return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8)); - } - - public MessageRecord createMessageRecord(String topic, String resptopic, Convert convert, Object bean) { - return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, convert.convertToBytes(bean)); - } - - public MessageRecord createMessageRecord(int userid, String topic, String resptopic, Convert convert, Object bean) { - return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, convert.convertToBytes(bean)); - } - - public MessageRecord createMessageRecord(int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) { - return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean)); - } - - public MessageRecord createMessageRecord(int flag, int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) { - return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean)); - } - - public MessageRecord createMessageRecord(String topic, String resptopic, byte[] content) { - return new MessageRecord(msgSeqno.incrementAndGet(), (byte) 0, topic, resptopic, content); - } - - public MessageRecord createMessageRecord(long seqid, String topic, String resptopic, byte[] content) { - return new MessageRecord(seqid, (byte) 0, topic, resptopic, content); - } - - protected MessageRecord createMessageRecord(byte ctype, String topic, String resptopic, byte[] content) { - return new MessageRecord(msgSeqno.incrementAndGet(), ctype, topic, resptopic, content); - } - - protected MessageRecord createMessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) { - return new MessageRecord(seqid, ctype, topic, resptopic, content); - } - - private byte ctype(Convert convert, Object bean) { - byte ctype = 0; - if (convert instanceof JsonConvert) { - if (bean instanceof HttpSimpleRequest) { - ctype = CTYPE_HTTP_REQUEST; - } else if (bean instanceof HttpResult) { - ctype = CTYPE_HTTP_RESULT; - } - } - return ctype; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import org.redkale.convert.Convert; +import org.redkale.convert.json.JsonConvert; +import static org.redkale.mq.MessageRecord.*; +import org.redkale.net.http.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public abstract class MessageClient { + + protected final ConcurrentHashMap respNodes = new ConcurrentHashMap<>(); + + protected final MessageAgent messageAgent; + + protected final AtomicLong msgSeqno; + + protected MessageConsumer respConsumer; + + protected String respTopic; + + protected String respConsumerid; + + protected boolean finest; + + protected boolean finer; + + protected boolean fine; + + private final String clazzName; + + protected MessageClient(MessageAgent messageAgent) { + this.messageAgent = messageAgent; + this.msgSeqno = messageAgent == null ? new AtomicLong() : messageAgent.msgSeqno; + this.finest = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINEST); + this.finer = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINER); + this.fine = messageAgent == null ? false : messageAgent.logger.isLoggable(Level.FINE); + this.clazzName = getClass().getSimpleName(); + } + + protected CompletableFuture close() { + if (this.respConsumer == null) return CompletableFuture.completedFuture(null); + return this.respConsumer.shutdown(); + } + + protected CompletableFuture sendMessage(final MessageRecord message, boolean needresp, AtomicLong counter) { + CompletableFuture future = new CompletableFuture<>(); + try { + if (this.respConsumer == null) { + synchronized (this) { + if (this.respConsumerid == null) this.respConsumerid = "consumer-" + this.respTopic; + if (this.respConsumer == null) { + MessageProcessor processor = (msg, callback) -> { + long now = System.currentTimeMillis(); + MessageRespFutureNode node = respNodes.remove(msg.getSeqid()); + if (node == null) { + messageAgent.logger.log(Level.WARNING, MessageClient.this.getClass().getSimpleName() + " process " + msg + " error, not found mqresp.futurenode"); + return; + } + if (node.scheduledFuture != null) node.scheduledFuture.cancel(true); + AtomicLong ncer = node.getCounter(); + if (ncer != null) ncer.decrementAndGet(); + node.future.complete(msg); + long cha = now - msg.createtime; + if (cha > 1000 && fine) { + messageAgent.logger.log(Level.FINE, clazzName + ".MessageRespFutureNode.process (mqs.delays = " + cha + "ms, mqs.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg)); + } else if (cha > 50 && finer) { + messageAgent.logger.log(Level.FINER, clazzName + ".MessageRespFutureNode.process (mq.delays = " + cha + "ms, mq.counters = " + ncer + ") mqresp.msg: " + formatRespMessage(msg)); + } else if (finest) { + messageAgent.logger.log(Level.FINEST, clazzName + ".MessageRespFutureNode.process (mq.delay = " + cha + "ms, mq.counter = " + ncer + ") mqresp.msg: " + formatRespMessage(msg)); + } + }; + MessageConsumer one = messageAgent.createConsumer(new String[]{respTopic}, respConsumerid, processor); + one.startup().join(); + this.respConsumer = one; + } + } + } + if (needresp && (message.getResptopic() == null || message.getResptopic().isEmpty())) { + message.setResptopic(respTopic); + } + if (counter != null) counter.incrementAndGet(); + getProducer().apply(message); + if (needresp) { + MessageRespFutureNode node = new MessageRespFutureNode(messageAgent.logger, message, respNodes, counter, future); + respNodes.put(message.getSeqid(), node); + ScheduledThreadPoolExecutor executor = messageAgent.timeoutExecutor; + if (executor != null) { + node.scheduledFuture = executor.schedule(node, 30, TimeUnit.SECONDS); + } + } else { + future.complete(null); + } + } catch (Exception ex) { + future.completeExceptionally(ex); + } finally { + return future; + } + } + + protected MessageRecord formatRespMessage(MessageRecord message) { + return message; + } + + protected abstract MessageProducers getProducer(); + + public MessageRecord createMessageRecord(String resptopic, String content) { + return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, null, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8)); + } + + public MessageRecord createMessageRecord(String topic, String resptopic, String content) { + return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8)); + } + + public MessageRecord createMessageRecord(int userid, String topic, String resptopic, String content) { + return new MessageRecord(msgSeqno.incrementAndGet(), CTYPE_STRING, 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, content == null ? null : content.getBytes(StandardCharsets.UTF_8)); + } + + public MessageRecord createMessageRecord(String topic, String resptopic, Convert convert, Object bean) { + return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, convert.convertToBytes(bean)); + } + + public MessageRecord createMessageRecord(int userid, String topic, String resptopic, Convert convert, Object bean) { + return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, null, topic, resptopic, convert.convertToBytes(bean)); + } + + public MessageRecord createMessageRecord(int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) { + return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, 0, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean)); + } + + public MessageRecord createMessageRecord(int flag, int userid, String groupid, String topic, String resptopic, Convert convert, Object bean) { + return new MessageRecord(msgSeqno.incrementAndGet(), ctype(convert, bean), 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, convert.convertToBytes(bean)); + } + + public MessageRecord createMessageRecord(String topic, String resptopic, byte[] content) { + return new MessageRecord(msgSeqno.incrementAndGet(), (byte) 0, topic, resptopic, content); + } + + public MessageRecord createMessageRecord(long seqid, String topic, String resptopic, byte[] content) { + return new MessageRecord(seqid, (byte) 0, topic, resptopic, content); + } + + protected MessageRecord createMessageRecord(byte ctype, String topic, String resptopic, byte[] content) { + return new MessageRecord(msgSeqno.incrementAndGet(), ctype, topic, resptopic, content); + } + + protected MessageRecord createMessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) { + return new MessageRecord(seqid, ctype, topic, resptopic, content); + } + + private byte ctype(Convert convert, Object bean) { + byte ctype = 0; + if (convert instanceof JsonConvert) { + if (bean instanceof HttpSimpleRequest) { + ctype = CTYPE_HTTP_REQUEST; + } else if (bean instanceof HttpResult) { + ctype = CTYPE_HTTP_RESULT; + } + } + return ctype; + } +} diff --git a/src/org/redkale/mq/MessageCoder.java b/src/main/java/org/redkale/mq/MessageCoder.java similarity index 69% rename from src/org/redkale/mq/MessageCoder.java rename to src/main/java/org/redkale/mq/MessageCoder.java index ce9686fba..965a8111c 100644 --- a/src/org/redkale/mq/MessageCoder.java +++ b/src/main/java/org/redkale/mq/MessageCoder.java @@ -1,107 +1,136 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import org.redkale.util.Utility; - -/** - * 将MessageRecord.content内容加解密 - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - * - * @param 泛型 - */ -public interface MessageCoder { - - //编码 - public byte[] encode(T data); - - //解码 - public T decode(byte[] data); - - public static byte[] getBytes(byte[] value) { - if (value == null) return MessageRecord.EMPTY_BYTES; - return value; - } - - public static byte[] getBytes(String value) { - if (value == null || value.isEmpty()) return MessageRecord.EMPTY_BYTES; - return value.getBytes(StandardCharsets.UTF_8); - } - - public static byte[] getBytes(final Map map) { - if (map == null || map.isEmpty()) return new byte[2]; - final AtomicInteger len = new AtomicInteger(2); - map.forEach((key, value) -> { - len.addAndGet(2 + (key == null ? 0 : Utility.encodeUTF8Length(key))); - len.addAndGet(4 + (value == null ? 0 : Utility.encodeUTF8Length(value))); - }); - final byte[] bs = new byte[len.get()]; - final ByteBuffer buffer = ByteBuffer.wrap(bs); - buffer.putChar((char) map.size()); - map.forEach((key, value) -> { - putShortString(buffer, key); - putLongString(buffer, value); - }); - return bs; - } - - public static void putLongString(ByteBuffer buffer, String value) { - if (value == null || value.isEmpty()) { - buffer.putInt(0); - } else { - byte[] bs = value.getBytes(StandardCharsets.UTF_8); - buffer.putInt(bs.length); - buffer.put(bs); - } - } - - public static String getLongString(ByteBuffer buffer) { - int len = buffer.getInt(); - if (len == 0) return null; - byte[] bs = new byte[len]; - buffer.get(bs); - return new String(bs, StandardCharsets.UTF_8); - } - - public static void putShortString(ByteBuffer buffer, String value) { - if (value == null || value.isEmpty()) { - buffer.putChar((char) 0); - } else { - byte[] bs = value.getBytes(StandardCharsets.UTF_8); - buffer.putChar((char) bs.length); - buffer.put(bs); - } - } - - public static String getShortString(ByteBuffer buffer) { - int len = buffer.getChar(); - if (len == 0) return null; - byte[] bs = new byte[len]; - buffer.get(bs); - return new String(bs, StandardCharsets.UTF_8); - } - - public static Map getMap(ByteBuffer buffer) { - int len = buffer.getChar(); - if (len == 0) return null; - Map map = new HashMap<>(len); - for (int i = 0; i < len; i++) { - map.put(getShortString(buffer), getLongString(buffer)); - } - return map; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import org.redkale.util.Utility; + +/** + * 将MessageRecord.content内容加解密 + * + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + * + * @param 泛型 + */ +public interface MessageCoder { + + //编码 + public byte[] encode(T data); + + //解码 + public T decode(byte[] data); + + //type: 1:string, 2:int, 3:long + public static byte[] encodeUserid(Serializable value) { + if (value == null) return MessageRecord.EMPTY_BYTES; + if (value instanceof Integer) { + int val = (Integer) value; + return new byte[]{(byte) 2, (byte) (val >> 24 & 0xFF), (byte) (val >> 16 & 0xFF), (byte) (val >> 8 & 0xFF), (byte) (val & 0xFF)}; + } else if (value instanceof Long) { + long val = (Long) value; + return new byte[]{(byte) 3, (byte) (val >> 56 & 0xFF), (byte) (val >> 48 & 0xFF), (byte) (val >> 40 & 0xFF), + (byte) (val >> 32 & 0xFF), (byte) (val >> 24 & 0xFF), (byte) (val >> 16 & 0xFF), (byte) (val >> 8 & 0xFF), (byte) (val & 0xFF)}; + } + String str = value.toString(); + if (str.isEmpty()) return MessageRecord.EMPTY_BYTES; + return Utility.append(new byte[]{(byte) 1}, str.getBytes(StandardCharsets.UTF_8)); + } + + //type: 1:string, 2:int, 3:long + public static Serializable decodeUserid(ByteBuffer buffer) { + int len = buffer.getChar(); + if (len == 0) return null; + byte type = buffer.get(); + if (type == 2) return buffer.getInt(); + if (type == 3) return buffer.getLong(); + byte[] bs = new byte[len - 1]; + buffer.get(bs); + return new String(bs, StandardCharsets.UTF_8); + } + + public static byte[] getBytes(byte[] value) { + if (value == null) return MessageRecord.EMPTY_BYTES; + return value; + } + + public static byte[] getBytes(String value) { + if (value == null || value.isEmpty()) return MessageRecord.EMPTY_BYTES; + return value.getBytes(StandardCharsets.UTF_8); + } + + public static byte[] getBytes(final Map map) { + if (map == null || map.isEmpty()) return new byte[2]; + final AtomicInteger len = new AtomicInteger(2); + map.forEach((key, value) -> { + len.addAndGet(2 + (key == null ? 0 : Utility.encodeUTF8Length(key))); + len.addAndGet(4 + (value == null ? 0 : Utility.encodeUTF8Length(value))); + }); + final byte[] bs = new byte[len.get()]; + final ByteBuffer buffer = ByteBuffer.wrap(bs); + buffer.putChar((char) map.size()); + map.forEach((key, value) -> { + putShortString(buffer, key); + putLongString(buffer, value); + }); + return bs; + } + + public static void putLongString(ByteBuffer buffer, String value) { + if (value == null || value.isEmpty()) { + buffer.putInt(0); + } else { + byte[] bs = value.getBytes(StandardCharsets.UTF_8); + buffer.putInt(bs.length); + buffer.put(bs); + } + } + + public static String getLongString(ByteBuffer buffer) { + int len = buffer.getInt(); + if (len == 0) return null; + byte[] bs = new byte[len]; + buffer.get(bs); + return new String(bs, StandardCharsets.UTF_8); + } + + public static void putShortString(ByteBuffer buffer, String value) { + if (value == null || value.isEmpty()) { + buffer.putChar((char) 0); + } else { + byte[] bs = value.getBytes(StandardCharsets.UTF_8); + buffer.putChar((char) bs.length); + buffer.put(bs); + } + } + + public static String getShortString(ByteBuffer buffer) { + int len = buffer.getChar(); + if (len == 0) return null; + byte[] bs = new byte[len]; + buffer.get(bs); + return new String(bs, StandardCharsets.UTF_8); + } + + public static Map getMap(ByteBuffer buffer) { + int len = buffer.getChar(); + if (len == 0) return null; + Map map = new HashMap<>(len); + for (int i = 0; i < len; i++) { + map.put(getShortString(buffer), getLongString(buffer)); + } + return map; + } +} diff --git a/src/org/redkale/mq/MessageConsumer.java b/src/main/java/org/redkale/mq/MessageConsumer.java similarity index 96% rename from src/org/redkale/mq/MessageConsumer.java rename to src/main/java/org/redkale/mq/MessageConsumer.java index a1ffd20e3..2488d7165 100644 --- a/src/org/redkale/mq/MessageConsumer.java +++ b/src/main/java/org/redkale/mq/MessageConsumer.java @@ -1,63 +1,63 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; - -/** - * - *

    - * 详情见: https://redkale.org - * - * - * @author zhangjx - * - * @since 2.1.0 - */ -public abstract class MessageConsumer { - - protected final String[] topics; - - protected final String consumerid; - - protected MessageAgent messageAgent; - - protected final MessageProcessor processor; - - protected final Logger logger; - - protected volatile boolean closed; - - protected MessageConsumer(MessageAgent messageAgent, String[] topics, final String consumerid, MessageProcessor processor) { - Objects.requireNonNull(messageAgent); - Objects.requireNonNull(topics); - Objects.requireNonNull(consumerid); - Objects.requireNonNull(processor); - this.messageAgent = messageAgent; - this.logger = messageAgent.logger; - this.topics = topics; - this.consumerid = consumerid; - this.processor = processor; - } - - public MessageProcessor getProcessor() { - return processor; - } - - public String[] getTopics() { - return topics; - } - - public abstract CompletableFuture startup(); - - public boolean isClosed() { - return closed; - } - - public abstract CompletableFuture shutdown(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; + +/** + * + *

    + * 详情见: https://redkale.org + * + * + * @author zhangjx + * + * @since 2.1.0 + */ +public abstract class MessageConsumer { + + protected final String[] topics; + + protected final String consumerid; + + protected MessageAgent messageAgent; + + protected final MessageProcessor processor; + + protected final Logger logger; + + protected volatile boolean closed; + + protected MessageConsumer(MessageAgent messageAgent, String[] topics, final String consumerid, MessageProcessor processor) { + Objects.requireNonNull(messageAgent); + Objects.requireNonNull(topics); + Objects.requireNonNull(consumerid); + Objects.requireNonNull(processor); + this.messageAgent = messageAgent; + this.logger = messageAgent.logger; + this.topics = topics; + this.consumerid = consumerid; + this.processor = processor; + } + + public MessageProcessor getProcessor() { + return processor; + } + + public String[] getTopics() { + return topics; + } + + public abstract CompletableFuture startup(); + + public boolean isClosed() { + return closed; + } + + public abstract CompletableFuture shutdown(); +} diff --git a/src/org/redkale/mq/MessageMultiConsumer.java b/src/main/java/org/redkale/mq/MessageMultiConsumer.java similarity index 91% rename from src/org/redkale/mq/MessageMultiConsumer.java rename to src/main/java/org/redkale/mq/MessageMultiConsumer.java index a08b1b4db..78a54392c 100644 --- a/src/org/redkale/mq/MessageMultiConsumer.java +++ b/src/main/java/org/redkale/mq/MessageMultiConsumer.java @@ -1,61 +1,61 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 多消费组,需要同 @RestService 一起使用 - *

    - * 通常一个topic只会被一个RestService消费, 当一个topic需要被其他RestService消费时,就需要使用@MessageMultiConsumer - * - *

    - * @RestService(name = "user", comment = "用户服务")
    - * public class UserService implements Service{
    - *
    - *      @RestMapping(comment = "用户登录")
    - *      public RetResult login(LoginBean bean){
    - *          //do something
    - *      }
    - * }
    - * 
    - * - * 需求:统计用户登录次数, 可以创建一个MessageMultiConsumer 的 RestService: - *
    - * @MessageMultiConsumer(module = "user") 
    - * @RestService(name = "loginstat", comment = "用户统计服务")
    - * public class LoginStatService implements Service{
    - *
    - *      private AtomicLong counter = new AtomicLong();
    - *
    - *      @RestMapping(name = "login", comment = "用户登录统计")
    - *      public void stat(LoginBean bean){     //参数必须和UserService.login方法一致
    - *          counter.incrementAndGet();
    - *      }
    - * }
    - * 
    - * - *

    - * 注: 标记 @MessageMultiConsumer 的Service的@RestMapping方法都只能是void返回类型 - * - *

    - * 详情见: https://redkale.org - * - * - * @author zhangjx - * - * @since 2.1.0 - */ -@Inherited -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface MessageMultiConsumer { - - String module(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 多消费组,需要同 @RestService 一起使用 + *

    + * 通常一个topic只会被一个RestService消费, 当一个topic需要被其他RestService消费时,就需要使用@MessageMultiConsumer + * + *

    + * @RestService(name = "user", comment = "用户服务")
    + * public class UserService implements Service{
    + *
    + *      @RestMapping(comment = "用户登录")
    + *      public RetResult login(LoginBean bean){
    + *          //do something
    + *      }
    + * }
    + * 
    + * + * 需求:统计用户登录次数, 可以创建一个MessageMultiConsumer 的 RestService: + *
    + * @MessageMultiConsumer(module = "user") 
    + * @RestService(name = "loginstat", comment = "用户统计服务")
    + * public class LoginStatService implements Service{
    + *
    + *      private LongAdder counter = new LongAdder();
    + *
    + *      @RestMapping(name = "login", comment = "用户登录统计")
    + *      public void stat(LoginBean bean){     //参数必须和UserService.login方法一致
    + *          counter.increment();
    + *      }
    + * }
    + * 
    + * + *

    + * 注: 标记 @MessageMultiConsumer 的Service的@RestMapping方法都只能是void返回类型 + * + *

    + * 详情见: https://redkale.org + * + * + * @author zhangjx + * + * @since 2.1.0 + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface MessageMultiConsumer { + + String module(); +} diff --git a/src/org/redkale/mq/MessageProcessor.java b/src/main/java/org/redkale/mq/MessageProcessor.java similarity index 95% rename from src/org/redkale/mq/MessageProcessor.java rename to src/main/java/org/redkale/mq/MessageProcessor.java index 288c0fb06..79ac2042a 100644 --- a/src/org/redkale/mq/MessageProcessor.java +++ b/src/main/java/org/redkale/mq/MessageProcessor.java @@ -1,27 +1,27 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -/** - * 一个Service对应一个MessageProcessor - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public interface MessageProcessor { - - default void begin(int size, long starttime) { - } - - public void process(MessageRecord message, Runnable callback); - - default void commit() { - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +/** + * 一个Service对应一个MessageProcessor + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public interface MessageProcessor { + + default void begin(int size, long starttime) { + } + + public void process(MessageRecord message, Runnable callback); + + default void commit() { + } +} diff --git a/src/org/redkale/mq/MessageProducer.java b/src/main/java/org/redkale/mq/MessageProducer.java similarity index 95% rename from src/org/redkale/mq/MessageProducer.java rename to src/main/java/org/redkale/mq/MessageProducer.java index dcbabecc6..41de2cd03 100644 --- a/src/org/redkale/mq/MessageProducer.java +++ b/src/main/java/org/redkale/mq/MessageProducer.java @@ -1,42 +1,42 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public abstract class MessageProducer { - - protected final Logger logger; - - protected final String name; - - protected volatile boolean closed; - - protected MessageProducer(String name, Logger logger) { - this.name = name; - this.logger = logger; - } - - public abstract CompletableFuture apply(MessageRecord message); - - public abstract CompletableFuture startup(); - - public boolean isClosed() { - return closed; - } - - public abstract CompletableFuture shutdown(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public abstract class MessageProducer { + + protected final Logger logger; + + protected final String name; + + protected volatile boolean closed; + + protected MessageProducer(String name, Logger logger) { + this.name = name; + this.logger = logger; + } + + public abstract CompletableFuture apply(MessageRecord message); + + public abstract CompletableFuture startup(); + + public boolean isClosed() { + return closed; + } + + public abstract CompletableFuture shutdown(); +} diff --git a/src/org/redkale/mq/MessageProducers.java b/src/main/java/org/redkale/mq/MessageProducers.java similarity index 96% rename from src/org/redkale/mq/MessageProducers.java rename to src/main/java/org/redkale/mq/MessageProducers.java index 91a0507c8..2f8afd31a 100644 --- a/src/org/redkale/mq/MessageProducers.java +++ b/src/main/java/org/redkale/mq/MessageProducers.java @@ -1,62 +1,62 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class MessageProducers { - - protected final MessageProducer[] producers; - - protected final AtomicInteger index = new AtomicInteger(); - - public MessageProducers(MessageProducer[] producers) { - this.producers = producers; - } - - public MessageProducer getProducer(MessageRecord message) { - if (this.producers.length == 1) return this.producers[0]; - int hash = index.incrementAndGet(); - if (index.get() > 1000 * producers.length) { - synchronized (index) { - if (index.get() > 1000 * producers.length) { - index.addAndGet(-1000 * producers.length); - } - } - } - return producers[hash % producers.length]; - } - - public CompletableFuture apply(MessageRecord message) { - return getProducer(message).apply(message); - } - - public CompletableFuture startup() { - CompletableFuture[] futures = new CompletableFuture[producers.length]; - for (int i = 0; i < producers.length; i++) { - futures[i] = producers[i].startup(); - } - return CompletableFuture.allOf(futures); - } - - public CompletableFuture shutdown() { - CompletableFuture[] futures = new CompletableFuture[producers.length]; - for (int i = 0; i < producers.length; i++) { - futures[i] = producers[i].shutdown(); - } - return CompletableFuture.allOf(futures); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class MessageProducers { + + protected final MessageProducer[] producers; + + protected final AtomicInteger index = new AtomicInteger(); + + public MessageProducers(MessageProducer[] producers) { + this.producers = producers; + } + + public MessageProducer getProducer(MessageRecord message) { + if (this.producers.length == 1) return this.producers[0]; + int hash = index.incrementAndGet(); + if (index.get() > 1000 * producers.length) { + synchronized (index) { + if (index.get() > 1000 * producers.length) { + index.addAndGet(-1000 * producers.length); + } + } + } + return producers[hash % producers.length]; + } + + public CompletableFuture apply(MessageRecord message) { + return getProducer(message).apply(message); + } + + public CompletableFuture startup() { + CompletableFuture[] futures = new CompletableFuture[producers.length]; + for (int i = 0; i < producers.length; i++) { + futures[i] = producers[i].startup(); + } + return CompletableFuture.allOf(futures); + } + + public CompletableFuture shutdown() { + CompletableFuture[] futures = new CompletableFuture[producers.length]; + for (int i = 0; i < producers.length; i++) { + futures[i] = producers[i].shutdown(); + } + return CompletableFuture.allOf(futures); + } +} diff --git a/src/org/redkale/mq/MessageRecord.java b/src/main/java/org/redkale/mq/MessageRecord.java similarity index 89% rename from src/org/redkale/mq/MessageRecord.java rename to src/main/java/org/redkale/mq/MessageRecord.java index 4a12402db..5634ab430 100644 --- a/src/org/redkale/mq/MessageRecord.java +++ b/src/main/java/org/redkale/mq/MessageRecord.java @@ -1,308 +1,308 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.io.Serializable; -import java.nio.charset.StandardCharsets; -import org.redkale.convert.*; -import org.redkale.convert.bson.BsonConvert; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.http.HttpSimpleRequest; -import org.redkale.net.sncp.SncpRequest; -import org.redkale.util.Comment; - -/** - * 存在MQ里面的数据结构

    - * groupid + userid 来确定partition, 优先使用 groupid - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class MessageRecord implements Serializable { - - static final byte[] EMPTY_BYTES = new byte[0]; - - protected static final byte CTYPE_STRING = 1; - - protected static final byte CTYPE_HTTP_REQUEST = 2; - - protected static final byte CTYPE_HTTP_RESULT = 3; - - protected static final byte CTYPE_BSON_RESULT = 4; - - @ConvertColumn(index = 1) - @Comment("消息序列号") - protected long seqid; - - @ConvertColumn(index = 2) - @Comment("版本") - protected int version; - - @ConvertColumn(index = 3) - @Comment("标记位, 自定义时使用") - protected int flag; - - @ConvertColumn(index = 4) - @Comment("创建时间") - protected long createtime; - - @ConvertColumn(index = 5) - @Comment("用户ID,无用户信息视为0") - protected int userid; - - @ConvertColumn(index = 6) - @Comment("组ID") - protected String groupid; - - @ConvertColumn(index = 7) - @Comment("当前topic") - protected String topic; - - @ConvertColumn(index = 8) - @Comment("目标topic, 为空表示无目标topic") - protected String resptopic; - - @ConvertColumn(index = 9) - @Comment("消息内容") - protected byte[] content; - - @ConvertColumn(index = 10) - @Comment("消息内容的类型") - protected byte ctype; - - @Comment("本地附加对象,不会被序列化") - protected Object localattach; - - public MessageRecord() { - } - - protected MessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) { - this(seqid, ctype, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content); - } - - protected MessageRecord(long seqid, byte ctype, int flag, int userid, String groupid, String topic, String resptopic, byte[] content) { - this(seqid, ctype, 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, content); - } - - protected MessageRecord(long seqid, byte ctype, int version, int flag, long createtime, int userid, String groupid, String topic, String resptopic, byte[] content) { - this.seqid = seqid; - this.ctype = ctype; - this.version = version; - this.flag = flag; - this.createtime = createtime; - this.userid = userid; - this.groupid = groupid; - this.topic = topic; - this.resptopic = resptopic; - this.content = content; - } - - public String contentString() { - return content == null ? null : new String(content, StandardCharsets.UTF_8); - } - - public MessageRecord attach(Object attach) { - this.localattach = attach; - return this; - } - - @ConvertDisabled - public boolean isEmptyTopic() { - return this.topic == null || this.topic.isEmpty(); - } - - @ConvertDisabled - public boolean isEmptyResptopic() { - return this.resptopic == null || this.resptopic.isEmpty(); - } - - public T convertFromContent(Convert convert, java.lang.reflect.Type type) { - if (this.content == null || this.content.length == 0) return null; - return (T) convert.convertFrom(type, this.content); - } - - public T decodeContent(MessageCoder coder) { - if (this.content == null || this.content.length == 0) return null; - return (T) coder.decode(this.content); - } - - public MessageRecord encodeContent(MessageCoder coder, T data) { - this.content = coder.encode(data); - return this; - } - - public int hash() { - if (groupid != null && !groupid.isEmpty()) { - return groupid.hashCode(); - } else if (userid > 0) { - return userid; - } else { - return 0; - } - } - - public MessageRecord version(int version) { - this.version = version; - return this; - } - - public MessageRecord flag(int flag) { - this.flag = flag; - return this; - } - - public MessageRecord createtime(long createtime) { - this.createtime = createtime; - return this; - } - - public MessageRecord userid(int userid) { - this.userid = userid; - return this; - } - - public MessageRecord groupid(String groupid) { - this.groupid = groupid; - return this; - } - - public MessageRecord topic(String topic) { - this.topic = topic; - return this; - } - - public MessageRecord resptopic(String resptopic) { - this.resptopic = resptopic; - return this; - } - - public MessageRecord content(byte[] content) { - this.content = content; - return this; - } - - public MessageRecord contentString(String content) { - this.content = content == null ? null : content.getBytes(StandardCharsets.UTF_8); - return this; - } - - public long getSeqid() { - return seqid; - } - - public void setSeqid(long seqid) { - this.seqid = seqid; - } - - public int getVersion() { - return version; - } - - public void setVersion(int version) { - this.version = version; - } - - public int getFlag() { - return flag; - } - - public void setFlag(int flag) { - this.flag = flag; - } - - public long getCreatetime() { - return createtime; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } - - public int getUserid() { - return userid; - } - - public void setUserid(int userid) { - this.userid = userid; - } - - public String getGroupid() { - return groupid; - } - - public void setGroupid(String groupid) { - this.groupid = groupid; - } - - public String getTopic() { - return topic; - } - - public void setTopic(String topic) { - this.topic = topic; - } - - public String getResptopic() { - return resptopic; - } - - public void setResptopic(String resptopic) { - this.resptopic = resptopic; - } - - public byte[] getContent() { - return content; - } - - public void setContent(byte[] content) { - this.content = content; - } - - @Override - public String toString() { - //return JsonConvert.root().convertTo(this); - StringBuilder sb = new StringBuilder(128); - sb.append("{\"seqid\":").append(this.seqid); - sb.append(",\"version\":").append(this.version); - if (this.flag != 0) sb.append(",\"flag\":").append(this.flag); - if (this.createtime != 0) sb.append(",\"createtime\":").append(this.createtime); - if (this.userid != 0) sb.append(",\"userid\":").append(this.userid); - if (this.groupid != null) sb.append(",\"groupid\":\"").append(this.groupid).append("\""); - if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).append("\""); - if (this.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\""); - if (this.content != null) { - if (this.ctype == CTYPE_BSON_RESULT && this.content.length > SncpRequest.HEADER_SIZE) { - int offset = SncpRequest.HEADER_SIZE + 1; //循环占位符 - Object rs = BsonConvert.root().convertFrom(Object.class, this.content, offset, this.content.length - offset); - sb.append(",\"content\":").append(rs); - } else if (this.ctype == CTYPE_HTTP_REQUEST) { - HttpSimpleRequest req = HttpSimpleRequestCoder.getInstance().decode(this.content); - if (req != null) { - if (req.getCurrentUserid() == 0) req.setCurrentUserid(this.userid); - if (req.getHashid() == 0) req.setHashid(this.hash()); - } - sb.append(",\"content\":").append(req); - } else if (this.ctype == CTYPE_HTTP_RESULT) { - sb.append(",\"content\":").append(HttpResultCoder.getInstance().decode(this.content)); - } else if (localattach != null) { - sb.append(",\"attach\":").append(JsonConvert.root().convertTo(localattach)); - } else { - sb.append(",\"content\":\"").append(new String(this.content, StandardCharsets.UTF_8)).append("\""); - } - } - sb.append("}"); - return sb.toString(); - } - -// public static void main(String[] args) throws Throwable { -// System.out.println(new MessageRecord(333, 2, 3, null, "tt", null, "xxx".getBytes())); -// } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import org.redkale.convert.*; +import org.redkale.convert.bson.BsonConvert; +import org.redkale.convert.json.JsonConvert; +import org.redkale.net.http.HttpSimpleRequest; +import org.redkale.net.sncp.SncpRequest; +import org.redkale.util.Comment; + +/** + * 存在MQ里面的数据结构

    + * groupid + userid 来确定partition, 优先使用 groupid + * + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class MessageRecord implements Serializable { + + static final byte[] EMPTY_BYTES = new byte[0]; + + protected static final byte CTYPE_STRING = 1; + + protected static final byte CTYPE_HTTP_REQUEST = 2; + + protected static final byte CTYPE_HTTP_RESULT = 3; + + protected static final byte CTYPE_BSON_RESULT = 4; + + @ConvertColumn(index = 1) + @Comment("消息序列号") + protected long seqid; + + @ConvertColumn(index = 2) + @Comment("版本") + protected int version; + + @ConvertColumn(index = 3) + @Comment("标记位, 自定义时使用") + protected int flag; + + @ConvertColumn(index = 4) + @Comment("创建时间") + protected long createtime; + + @ConvertColumn(index = 5) + @Comment("用户ID,无用户信息视为null或0, 具体数据类型只能是int、long、String") //@since 2.5.0 由int改成Serializable + protected Serializable userid; + + @ConvertColumn(index = 6) + @Comment("组ID") + protected String groupid; + + @ConvertColumn(index = 7) + @Comment("当前topic") + protected String topic; + + @ConvertColumn(index = 8) + @Comment("目标topic, 为空表示无目标topic") + protected String resptopic; + + @ConvertColumn(index = 9) + @Comment("消息内容") + protected byte[] content; + + @ConvertColumn(index = 10) + @Comment("消息内容的类型") + protected byte ctype; + + @Comment("本地附加对象,不会被序列化") + protected Object localattach; + + public MessageRecord() { + } + + protected MessageRecord(long seqid, byte ctype, String topic, String resptopic, byte[] content) { + this(seqid, ctype, 1, 0, System.currentTimeMillis(), 0, null, topic, resptopic, content); + } + + protected MessageRecord(long seqid, byte ctype, int flag, Serializable userid, String groupid, String topic, String resptopic, byte[] content) { + this(seqid, ctype, 1, flag, System.currentTimeMillis(), userid, groupid, topic, resptopic, content); + } + + protected MessageRecord(long seqid, byte ctype, int version, int flag, long createtime, Serializable userid, String groupid, String topic, String resptopic, byte[] content) { + this.seqid = seqid; + this.ctype = ctype; + this.version = version; + this.flag = flag; + this.createtime = createtime; + this.userid = userid; + this.groupid = groupid; + this.topic = topic; + this.resptopic = resptopic; + this.content = content; + } + + public String contentString() { + return content == null ? null : new String(content, StandardCharsets.UTF_8); + } + + public MessageRecord attach(Object attach) { + this.localattach = attach; + return this; + } + + @ConvertDisabled + public boolean isEmptyTopic() { + return this.topic == null || this.topic.isEmpty(); + } + + @ConvertDisabled + public boolean isEmptyResptopic() { + return this.resptopic == null || this.resptopic.isEmpty(); + } + + public T convertFromContent(Convert convert, java.lang.reflect.Type type) { + if (this.content == null || this.content.length == 0) return null; + return (T) convert.convertFrom(type, this.content); + } + + public T decodeContent(MessageCoder coder) { + if (this.content == null || this.content.length == 0) return null; + return (T) coder.decode(this.content); + } + + public MessageRecord encodeContent(MessageCoder coder, T data) { + this.content = coder.encode(data); + return this; + } + + public int hash() { + if (groupid != null && !groupid.isEmpty()) { + return groupid.hashCode(); + } else if (userid != null) { + return userid.hashCode(); + } else { + return 0; + } + } + + public MessageRecord version(int version) { + this.version = version; + return this; + } + + public MessageRecord flag(int flag) { + this.flag = flag; + return this; + } + + public MessageRecord createtime(long createtime) { + this.createtime = createtime; + return this; + } + + public MessageRecord userid(Serializable userid) { + this.userid = userid; + return this; + } + + public MessageRecord groupid(String groupid) { + this.groupid = groupid; + return this; + } + + public MessageRecord topic(String topic) { + this.topic = topic; + return this; + } + + public MessageRecord resptopic(String resptopic) { + this.resptopic = resptopic; + return this; + } + + public MessageRecord content(byte[] content) { + this.content = content; + return this; + } + + public MessageRecord contentString(String content) { + this.content = content == null ? null : content.getBytes(StandardCharsets.UTF_8); + return this; + } + + public long getSeqid() { + return seqid; + } + + public void setSeqid(long seqid) { + this.seqid = seqid; + } + + public int getVersion() { + return version; + } + + public void setVersion(int version) { + this.version = version; + } + + public int getFlag() { + return flag; + } + + public void setFlag(int flag) { + this.flag = flag; + } + + public long getCreatetime() { + return createtime; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } + + public Serializable getUserid() { + return userid; + } + + public void setUserid(Serializable userid) { + this.userid = userid; + } + + public String getGroupid() { + return groupid; + } + + public void setGroupid(String groupid) { + this.groupid = groupid; + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public String getResptopic() { + return resptopic; + } + + public void setResptopic(String resptopic) { + this.resptopic = resptopic; + } + + public byte[] getContent() { + return content; + } + + public void setContent(byte[] content) { + this.content = content; + } + + @Override + public String toString() { + //return JsonConvert.root().convertTo(this); + StringBuilder sb = new StringBuilder(128); + sb.append("{\"seqid\":").append(this.seqid); + sb.append(",\"version\":").append(this.version); + if (this.flag != 0) sb.append(",\"flag\":").append(this.flag); + if (this.createtime != 0) sb.append(",\"createtime\":").append(this.createtime); + if (this.userid != null) sb.append(",\"userid\":").append(this.userid); + if (this.groupid != null) sb.append(",\"groupid\":\"").append(this.groupid).append("\""); + if (this.topic != null) sb.append(",\"topic\":\"").append(this.topic).append("\""); + if (this.resptopic != null) sb.append(",\"resptopic\":\"").append(this.resptopic).append("\""); + if (this.content != null) { + if (this.ctype == CTYPE_BSON_RESULT && this.content.length > SncpRequest.HEADER_SIZE) { + int offset = SncpRequest.HEADER_SIZE + 1; //循环占位符 + Object rs = BsonConvert.root().convertFrom(Object.class, this.content, offset, this.content.length - offset); + sb.append(",\"content\":").append(rs); + } else if (this.ctype == CTYPE_HTTP_REQUEST) { + HttpSimpleRequest req = HttpSimpleRequestCoder.getInstance().decode(this.content); + if (req != null) { + if (req.getCurrentUserid() == null) req.setCurrentUserid(this.userid); + if (req.getHashid() == 0) req.setHashid(this.hash()); + } + sb.append(",\"content\":").append(req); + } else if (this.ctype == CTYPE_HTTP_RESULT) { + sb.append(",\"content\":").append(HttpResultCoder.getInstance().decode(this.content)); + } else if (localattach != null) { + sb.append(",\"attach\":").append(JsonConvert.root().convertTo(localattach)); + } else { + sb.append(",\"content\":\"").append(new String(this.content, StandardCharsets.UTF_8)).append("\""); + } + } + sb.append("}"); + return sb.toString(); + } + +// public static void main(String[] args) throws Throwable { +// System.out.println(new MessageRecord(333, 2, 3, null, "tt", null, "xxx".getBytes())); +// } +} diff --git a/src/org/redkale/mq/MessageRecordCoder.java b/src/main/java/org/redkale/mq/MessageRecordCoder.java similarity index 81% rename from src/org/redkale/mq/MessageRecordCoder.java rename to src/main/java/org/redkale/mq/MessageRecordCoder.java index c3dcb9e0c..736075572 100644 --- a/src/org/redkale/mq/MessageRecordCoder.java +++ b/src/main/java/org/redkale/mq/MessageRecordCoder.java @@ -1,82 +1,94 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.nio.ByteBuffer; - -/** - * MessageRecord的MessageCoder实现 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class MessageRecordCoder implements MessageCoder { - - private static final MessageRecordCoder instance = new MessageRecordCoder(); - - public static MessageRecordCoder getInstance() { - return instance; - } - - @Override - public byte[] encode(MessageRecord data) { - if (data == null) return null; - byte[] stopics = MessageCoder.getBytes(data.getTopic()); - byte[] dtopics = MessageCoder.getBytes(data.getResptopic()); - byte[] groupid = MessageCoder.getBytes(data.getGroupid()); - int count = 8 + 1 + 4 + 4 + 8 + 4 + 2 + stopics.length + 2 + dtopics.length + 2 + groupid.length + 4 + (data.getContent() == null ? 0 : data.getContent().length); - final byte[] bs = new byte[count]; - ByteBuffer buffer = ByteBuffer.wrap(bs); - buffer.putLong(data.getSeqid()); - buffer.put(data.ctype); - buffer.putInt(data.getVersion()); - buffer.putInt(data.getFlag()); - buffer.putLong(data.getCreatetime()); - buffer.putInt(data.getUserid()); - buffer.putChar((char) groupid.length); - if (groupid.length > 0) buffer.put(groupid); - buffer.putChar((char) stopics.length); - if (stopics.length > 0) buffer.put(stopics); - buffer.putChar((char) dtopics.length); - if (dtopics.length > 0) buffer.put(dtopics); - if (data.getContent() == null) { - buffer.putInt(0); - } else { - buffer.putInt(data.getContent().length); - buffer.put(data.getContent()); - } - return bs; - } - - @Override - public MessageRecord decode(byte[] data) { - if (data == null) return null; - ByteBuffer buffer = ByteBuffer.wrap(data); - long seqid = buffer.getLong(); - byte ctype = buffer.get(); - int version = buffer.getInt(); - int flag = buffer.getInt(); - long createtime = buffer.getLong(); - int userid = buffer.getInt(); - - String groupid = MessageCoder.getShortString(buffer); - String topic = MessageCoder.getShortString(buffer); - String resptopic = MessageCoder.getShortString(buffer); - - byte[] content = null; - int contentlen = buffer.getInt(); - if (contentlen > 0) { - content = new byte[contentlen]; - buffer.get(content); - } - return new MessageRecord(seqid, ctype, version, flag, createtime, userid, groupid, topic, resptopic, content); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.io.Serializable; +import java.nio.ByteBuffer; + +/** + * MessageRecord的MessageCoder实现 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class MessageRecordCoder implements MessageCoder { + + private static final MessageRecordCoder instance = new MessageRecordCoder(); + + public static MessageRecordCoder getInstance() { + return instance; + } + + @Override + public byte[] encode(MessageRecord data) { + if (data == null) return null; + byte[] stopics = MessageCoder.getBytes(data.getTopic()); + byte[] dtopics = MessageCoder.getBytes(data.getResptopic()); + byte[] groupid = MessageCoder.getBytes(data.getGroupid()); + byte[] userid = MessageCoder.encodeUserid(data.getUserid()); + int count = 8 //seqid + + 1 //ctype + + 4 //version + + 4 //flag + + 8 //createtime + + 2 + userid.length + + 2 + groupid.length + + 2 + stopics.length + + 2 + dtopics.length + + 4 + (data.getContent() == null ? 0 : data.getContent().length); + final byte[] bs = new byte[count]; + ByteBuffer buffer = ByteBuffer.wrap(bs); + buffer.putLong(data.getSeqid()); + buffer.put(data.ctype); + buffer.putInt(data.getVersion()); + buffer.putInt(data.getFlag()); + buffer.putLong(data.getCreatetime()); + buffer.putChar((char) userid.length); + if (userid.length > 0) buffer.put(userid); + buffer.putChar((char) groupid.length); + if (groupid.length > 0) buffer.put(groupid); + buffer.putChar((char) stopics.length); + if (stopics.length > 0) buffer.put(stopics); + buffer.putChar((char) dtopics.length); + if (dtopics.length > 0) buffer.put(dtopics); + if (data.getContent() == null) { + buffer.putInt(0); + } else { + buffer.putInt(data.getContent().length); + buffer.put(data.getContent()); + } + return bs; + } + + @Override + public MessageRecord decode(byte[] data) { + if (data == null) return null; + ByteBuffer buffer = ByteBuffer.wrap(data); + long seqid = buffer.getLong(); + byte ctype = buffer.get(); + int version = buffer.getInt(); + int flag = buffer.getInt(); + long createtime = buffer.getLong(); + + Serializable userid = MessageCoder.decodeUserid(buffer); + String groupid = MessageCoder.getShortString(buffer); + String topic = MessageCoder.getShortString(buffer); + String resptopic = MessageCoder.getShortString(buffer); + + byte[] content = null; + int contentlen = buffer.getInt(); + if (contentlen > 0) { + content = new byte[contentlen]; + buffer.get(content); + } + return new MessageRecord(seqid, ctype, version, flag, createtime, userid, groupid, topic, resptopic, content); + } + +} diff --git a/src/org/redkale/mq/MessageRespFutureNode.java b/src/main/java/org/redkale/mq/MessageRespFutureNode.java similarity index 88% rename from src/org/redkale/mq/MessageRespFutureNode.java rename to src/main/java/org/redkale/mq/MessageRespFutureNode.java index 332ad1537..65d794bd5 100644 --- a/src/org/redkale/mq/MessageRespFutureNode.java +++ b/src/main/java/org/redkale/mq/MessageRespFutureNode.java @@ -1,73 +1,73 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.*; - -/** - * MQ管理器 - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class MessageRespFutureNode implements Runnable { - - protected final long seqid; - - protected final long createtime; - - protected final AtomicLong counter; - - protected final CompletableFuture future; - - protected final Logger logger; - - protected final MessageRecord message; - - protected final ConcurrentHashMap respNodes; - - protected ScheduledFuture scheduledFuture; - - public MessageRespFutureNode(Logger logger, MessageRecord message, ConcurrentHashMap respNodes, AtomicLong counter, CompletableFuture future) { - this.logger = logger; - this.message = message; - this.seqid = message.getSeqid(); - this.respNodes = respNodes; - this.counter = counter; - this.future = future; - this.createtime = System.currentTimeMillis(); - } - - @Override //超时后被timeoutExecutor调用 - public void run() { //timeout - respNodes.remove(this.seqid); - future.completeExceptionally(new TimeoutException()); - logger.log(Level.WARNING, getClass().getSimpleName() + " wait msg: " + message + " timeout " + (System.currentTimeMillis() - createtime) + "ms" - + (message.userid > 0 || (message.groupid != null && !message.groupid.isEmpty()) ? (message.userid > 0 ? (", userid:" + message.userid) : (", groupid:" + message.groupid)) : "")); - } - - public long getSeqid() { - return seqid; - } - - public long getCreatetime() { - return createtime; - } - - public AtomicLong getCounter() { - return counter; - } - - public CompletableFuture getFuture() { - return future; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.*; + +/** + * MQ管理器 + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class MessageRespFutureNode implements Runnable { + + protected final long seqid; + + protected final long createtime; + + protected final AtomicLong counter; + + protected final CompletableFuture future; + + protected final Logger logger; + + protected final MessageRecord message; + + protected final ConcurrentHashMap respNodes; + + protected ScheduledFuture scheduledFuture; + + public MessageRespFutureNode(Logger logger, MessageRecord message, ConcurrentHashMap respNodes, AtomicLong counter, CompletableFuture future) { + this.logger = logger; + this.message = message; + this.seqid = message.getSeqid(); + this.respNodes = respNodes; + this.counter = counter; + this.future = future; + this.createtime = System.currentTimeMillis(); + } + + @Override //超时后被timeoutExecutor调用 + public void run() { //timeout + respNodes.remove(this.seqid); + future.completeExceptionally(new TimeoutException()); + logger.log(Level.WARNING, getClass().getSimpleName() + " wait msg: " + message + " timeout " + (System.currentTimeMillis() - createtime) + "ms" + + (message.userid != null || (message.groupid != null && !message.groupid.isEmpty()) ? (message.userid != null ? (", userid:" + message.userid) : (", groupid:" + message.groupid)) : "")); + } + + public long getSeqid() { + return seqid; + } + + public long getCreatetime() { + return createtime; + } + + public AtomicLong getCounter() { + return counter; + } + + public CompletableFuture getFuture() { + return future; + } +} diff --git a/src/org/redkale/mq/MessageResponse.java b/src/main/java/org/redkale/mq/MessageResponse.java similarity index 95% rename from src/org/redkale/mq/MessageResponse.java rename to src/main/java/org/redkale/mq/MessageResponse.java index 0dab904b3..24cfe5258 100644 --- a/src/org/redkale/mq/MessageResponse.java +++ b/src/main/java/org/redkale/mq/MessageResponse.java @@ -1,20 +1,20 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public interface MessageResponse { - - public void finish(MessageRecord message); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public interface MessageResponse { + + public void finish(MessageRecord message); +} diff --git a/src/org/redkale/mq/SncpMessageClient.java b/src/main/java/org/redkale/mq/SncpMessageClient.java similarity index 96% rename from src/org/redkale/mq/SncpMessageClient.java rename to src/main/java/org/redkale/mq/SncpMessageClient.java index 471961769..839fb1444 100644 --- a/src/org/redkale/mq/SncpMessageClient.java +++ b/src/main/java/org/redkale/mq/SncpMessageClient.java @@ -1,61 +1,61 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicLong; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class SncpMessageClient extends MessageClient { - - protected SncpMessageClient(MessageAgent messageAgent) { - super(messageAgent); - this.respTopic = messageAgent.generateSncpRespTopic(); - } - - @Override - protected MessageProducers getProducer() { - return messageAgent.getSncpProducer(); - } - - public String getRespTopic() { - return this.respTopic; - } - - //只发送消息,不需要响应 - public final void produceMessage(MessageRecord message) { - produceMessage(message, null); - } - - //只发送消息,不需要响应 - public final void produceMessage(MessageRecord message, AtomicLong counter) { - sendMessage(message, false, counter); - } - - //发送消息,需要响应 - public final CompletableFuture sendMessage(MessageRecord message) { - return sendMessage(message, null); - } - - //发送消息,需要响应 - public final CompletableFuture sendMessage(MessageRecord message, AtomicLong counter) { - return sendMessage(message, true, counter); - } - - @Override - protected MessageRecord formatRespMessage(MessageRecord message) { - if (message != null) message.ctype = MessageRecord.CTYPE_BSON_RESULT; - return message; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicLong; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class SncpMessageClient extends MessageClient { + + protected SncpMessageClient(MessageAgent messageAgent) { + super(messageAgent); + this.respTopic = messageAgent.generateSncpRespTopic(); + } + + @Override + protected MessageProducers getProducer() { + return messageAgent.getSncpProducer(); + } + + public String getRespTopic() { + return this.respTopic; + } + + //只发送消息,不需要响应 + public final void produceMessage(MessageRecord message) { + produceMessage(message, null); + } + + //只发送消息,不需要响应 + public final void produceMessage(MessageRecord message, AtomicLong counter) { + sendMessage(message, false, counter); + } + + //发送消息,需要响应 + public final CompletableFuture sendMessage(MessageRecord message) { + return sendMessage(message, null); + } + + //发送消息,需要响应 + public final CompletableFuture sendMessage(MessageRecord message, AtomicLong counter) { + return sendMessage(message, true, counter); + } + + @Override + protected MessageRecord formatRespMessage(MessageRecord message) { + if (message != null) message.ctype = MessageRecord.CTYPE_BSON_RESULT; + return message; + } +} diff --git a/src/org/redkale/mq/SncpMessageProcessor.java b/src/main/java/org/redkale/mq/SncpMessageProcessor.java similarity index 97% rename from src/org/redkale/mq/SncpMessageProcessor.java rename to src/main/java/org/redkale/mq/SncpMessageProcessor.java index 8174f0544..1ed3090fe 100644 --- a/src/org/redkale/mq/SncpMessageProcessor.java +++ b/src/main/java/org/redkale/mq/SncpMessageProcessor.java @@ -1,127 +1,127 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.util.concurrent.*; -import java.util.logging.*; -import org.redkale.boot.NodeSncpServer; -import org.redkale.net.sncp.*; -import org.redkale.service.Service; - -/** - * 一个Service对应一个MessageProcessor - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class SncpMessageProcessor implements MessageProcessor { - - protected final boolean finest; - - protected final boolean finer; - - protected final boolean fine; - - protected final Logger logger; - - protected MessageClient messageClient; - - protected final MessageProducers producer; - - protected final NodeSncpServer server; - - protected final Service service; - - protected final SncpServlet servlet; - - protected CountDownLatch cdl; - - protected long starttime; - - protected final Runnable innerCallback = () -> { - if (cdl != null) cdl.countDown(); - }; - - public SncpMessageProcessor(Logger logger, SncpMessageClient messageClient, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) { - this.logger = logger; - this.finest = logger.isLoggable(Level.FINEST); - this.finer = logger.isLoggable(Level.FINER); - this.fine = logger.isLoggable(Level.FINE); - this.messageClient = messageClient; - this.producer = producer; - this.server = server; - this.service = service; - this.servlet = servlet; - } - - @Override - public void begin(final int size, long starttime) { - this.starttime = starttime; - this.cdl = new CountDownLatch(size); - } - - @Override - public void process(final MessageRecord message, final Runnable callback) { - execute(message, innerCallback); - } - - private void execute(final MessageRecord message, final Runnable callback) { - SncpMessageResponse response = null; - try { - long now = System.currentTimeMillis(); - long cha = now - message.createtime; - long e = now - starttime; - SncpContext context = server.getSncpServer().getContext(); - SncpMessageRequest request = new SncpMessageRequest(context, message); - response = new SncpMessageResponse(context, request, callback, messageClient, producer.getProducer(message)); - - context.execute(servlet, request, response); - long o = System.currentTimeMillis() - now; - if ((cha > 1000 || e > 100 || o > 1000) && fine) { - logger.log(Level.FINE, "SncpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message); - } else if ((cha > 50 || e > 10 || o > 50) && finer) { - logger.log(Level.FINER, "SncpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message); - } else if (finest) { - logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message); - } - } catch (Throwable ex) { - if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); - logger.log(Level.SEVERE, SncpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex); - } - } - - @Override - public void commit() { - if (this.cdl != null) { - try { - this.cdl.await(30, TimeUnit.SECONDS); - } catch (Exception ex) { - } - this.cdl = null; - } - } - - public MessageProducers getProducer() { - return producer; - } - - public NodeSncpServer getServer() { - return server; - } - - public Service getService() { - return service; - } - - public SncpServlet getServlet() { - return servlet; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.util.concurrent.*; +import java.util.logging.*; +import org.redkale.boot.NodeSncpServer; +import org.redkale.net.sncp.*; +import org.redkale.service.Service; + +/** + * 一个Service对应一个MessageProcessor + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class SncpMessageProcessor implements MessageProcessor { + + protected final boolean finest; + + protected final boolean finer; + + protected final boolean fine; + + protected final Logger logger; + + protected MessageClient messageClient; + + protected final MessageProducers producer; + + protected final NodeSncpServer server; + + protected final Service service; + + protected final SncpServlet servlet; + + protected CountDownLatch cdl; + + protected long starttime; + + protected final Runnable innerCallback = () -> { + if (cdl != null) cdl.countDown(); + }; + + public SncpMessageProcessor(Logger logger, SncpMessageClient messageClient, MessageProducers producer, NodeSncpServer server, Service service, SncpServlet servlet) { + this.logger = logger; + this.finest = logger.isLoggable(Level.FINEST); + this.finer = logger.isLoggable(Level.FINER); + this.fine = logger.isLoggable(Level.FINE); + this.messageClient = messageClient; + this.producer = producer; + this.server = server; + this.service = service; + this.servlet = servlet; + } + + @Override + public void begin(final int size, long starttime) { + this.starttime = starttime; + this.cdl = new CountDownLatch(size); + } + + @Override + public void process(final MessageRecord message, final Runnable callback) { + execute(message, innerCallback); + } + + private void execute(final MessageRecord message, final Runnable callback) { + SncpMessageResponse response = null; + try { + long now = System.currentTimeMillis(); + long cha = now - message.createtime; + long e = now - starttime; + SncpContext context = server.getSncpServer().getContext(); + SncpMessageRequest request = new SncpMessageRequest(context, message); + response = new SncpMessageResponse(context, request, callback, messageClient, producer.getProducer(message)); + + context.execute(servlet, request, response); + long o = System.currentTimeMillis() - now; + if ((cha > 1000 || e > 100 || o > 1000) && fine) { + logger.log(Level.FINE, "SncpMessageProcessor.process (mqs.delays = " + cha + " ms, mqs.blocks = " + e + " ms, mqs.executes = " + o + " ms) message: " + message); + } else if ((cha > 50 || e > 10 || o > 50) && finer) { + logger.log(Level.FINER, "SncpMessageProcessor.process (mq.delays = " + cha + " ms, mq.blocks = " + e + " ms, mq.executes = " + o + " ms) message: " + message); + } else if (finest) { + logger.log(Level.FINEST, "SncpMessageProcessor.process (mq.delay = " + cha + " ms, mq.block = " + e + " ms, mq.execute = " + o + " ms) message: " + message); + } + } catch (Throwable ex) { + if (response != null) response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); + logger.log(Level.SEVERE, SncpMessageProcessor.class.getSimpleName() + " process error, message=" + message, ex instanceof CompletionException ? ((CompletionException) ex).getCause() : ex); + } + } + + @Override + public void commit() { + if (this.cdl != null) { + try { + this.cdl.await(30, TimeUnit.SECONDS); + } catch (Exception ex) { + } + this.cdl = null; + } + } + + public MessageProducers getProducer() { + return producer; + } + + public NodeSncpServer getServer() { + return server; + } + + public Service getService() { + return service; + } + + public SncpServlet getServlet() { + return servlet; + } + +} diff --git a/src/org/redkale/mq/SncpMessageRequest.java b/src/main/java/org/redkale/mq/SncpMessageRequest.java similarity index 96% rename from src/org/redkale/mq/SncpMessageRequest.java rename to src/main/java/org/redkale/mq/SncpMessageRequest.java index 75b6639a6..548e5735e 100644 --- a/src/org/redkale/mq/SncpMessageRequest.java +++ b/src/main/java/org/redkale/mq/SncpMessageRequest.java @@ -1,45 +1,45 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import java.nio.ByteBuffer; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.redkale.net.sncp.*; -import org.redkale.util.Utility; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class SncpMessageRequest extends SncpRequest { - - protected MessageRecord message; - - @SuppressWarnings("OverridableMethodCallInConstructor") - public SncpMessageRequest(SncpContext context, MessageRecord message) { - super(context); - this.message = message; - this.hashid = this.message.hash(); - this.createtime = System.currentTimeMillis(); - readHeader(ByteBuffer.wrap(message.getContent()), null); - } - - @Override //被SncpAsyncHandler.sncp_setParams调用 - protected void sncp_setParams(SncpDynServlet.SncpServletAction action, Logger logger, Object... params) { - if (message.localattach != null) return; - if (logger.isLoggable(Level.FINER)) { - message.attach(Utility.append(new Object[]{action.actionName()}, params)); - } else { - message.attach(params); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import java.nio.ByteBuffer; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.redkale.net.sncp.*; +import org.redkale.util.Utility; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class SncpMessageRequest extends SncpRequest { + + protected MessageRecord message; + + @SuppressWarnings("OverridableMethodCallInConstructor") + public SncpMessageRequest(SncpContext context, MessageRecord message) { + super(context); + this.message = message; + this.hashid = this.message.hash(); + this.createtime = System.currentTimeMillis(); + readHeader(ByteBuffer.wrap(message.getContent()), null); + } + + @Override //被SncpAsyncHandler.sncp_setParams调用 + protected void sncp_setParams(SncpDynServlet.SncpServletAction action, Logger logger, Object... params) { + if (message.localattach != null) return; + if (logger.isLoggable(Level.FINER)) { + message.attach(Utility.append(new Object[]{action.actionName()}, params)); + } else { + message.attach(params); + } + } +} diff --git a/src/org/redkale/mq/SncpMessageResponse.java b/src/main/java/org/redkale/mq/SncpMessageResponse.java similarity index 97% rename from src/org/redkale/mq/SncpMessageResponse.java rename to src/main/java/org/redkale/mq/SncpMessageResponse.java index 78d935754..07dc3f457 100644 --- a/src/org/redkale/mq/SncpMessageResponse.java +++ b/src/main/java/org/redkale/mq/SncpMessageResponse.java @@ -1,62 +1,62 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.mq; - -import org.redkale.convert.bson.BsonWriter; -import org.redkale.net.sncp.*; -import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE; -import org.redkale.util.ByteArray; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class SncpMessageResponse extends SncpResponse { - - protected MessageClient messageClient; - - protected MessageRecord message; - - protected MessageProducer producer; - - protected Runnable callback; - - public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, MessageClient messageClient, MessageProducer producer) { - super(context, request); - this.message = request.message; - this.callback = callback; - this.messageClient = messageClient; - this.producer = producer; - } - - public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, MessageClient messageClient, MessageProducer producer) { - super(context, new SncpMessageRequest(context, message)); - this.message = message; - this.callback = callback; - this.messageClient = messageClient; - this.producer = producer; - } - - @Override - public void finish(final int retcode, final BsonWriter out) { - if (callback != null) callback.run(); - if (out == null) { - final ByteArray result = new ByteArray(SncpRequest.HEADER_SIZE); - fillHeader(result, 0, retcode); - producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, (byte[]) null)); - return; - } - final int respBodyLength = out.count(); //body总长度 - final ByteArray result = out.toByteArray(); - fillHeader(result, respBodyLength - HEADER_SIZE, retcode); - producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, result.getBytes())); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.mq; + +import org.redkale.convert.bson.BsonWriter; +import org.redkale.net.sncp.*; +import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE; +import org.redkale.util.ByteArray; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class SncpMessageResponse extends SncpResponse { + + protected MessageClient messageClient; + + protected MessageRecord message; + + protected MessageProducer producer; + + protected Runnable callback; + + public SncpMessageResponse(SncpContext context, SncpMessageRequest request, Runnable callback, MessageClient messageClient, MessageProducer producer) { + super(context, request); + this.message = request.message; + this.callback = callback; + this.messageClient = messageClient; + this.producer = producer; + } + + public SncpMessageResponse(SncpContext context, MessageRecord message, Runnable callback, MessageClient messageClient, MessageProducer producer) { + super(context, new SncpMessageRequest(context, message)); + this.message = message; + this.callback = callback; + this.messageClient = messageClient; + this.producer = producer; + } + + @Override + public void finish(final int retcode, final BsonWriter out) { + if (callback != null) callback.run(); + if (out == null) { + final ByteArray result = new ByteArray(SncpRequest.HEADER_SIZE); + fillHeader(result, 0, retcode); + producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, (byte[]) null)); + return; + } + final int respBodyLength = out.count(); //body总长度 + final ByteArray result = out.toByteArray(); + fillHeader(result, respBodyLength - HEADER_SIZE, retcode); + producer.apply(messageClient.createMessageRecord(message.getSeqid(), message.getResptopic(), null, result.getBytes())); + } +} diff --git a/src/main/java/org/redkale/net/AsyncConnection.java b/src/main/java/org/redkale/net/AsyncConnection.java new file mode 100644 index 000000000..e403b15bd --- /dev/null +++ b/src/main/java/org/redkale/net/AsyncConnection.java @@ -0,0 +1,947 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.HandshakeStatus; +import static javax.net.ssl.SSLEngineResult.Status.*; +import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class AsyncConnection implements ChannelContext, Channel, AutoCloseable { + + private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); + + //SSL + protected SSLEngine sslEngine; + + protected volatile long readtime; + + protected volatile long writetime; + + private Map attributes; //用于存储绑定在Connection上的对象集合 + + private Object subobject; //用于存储绑定在Connection上的对象, 同attributes, 只绑定单个对象时尽量使用subobject而非attributes + + protected final AsyncGroup ioGroup; + + protected final AsyncThread ioThread; + + protected final boolean client; + + protected final int bufferCapacity; + + private final Supplier bufferSupplier; + + private final Consumer bufferConsumer; + + private ByteBufferWriter pipelineWriter; + + private PipelineDataNode pipelineDataNode; + + private ByteBuffer readBuffer; + + private ByteBuffer readSSLHalfBuffer; + + //在线数 + private LongAdder livingCounter; + + //关闭数 + private LongAdder closedCounter; + + private Consumer beforeCloseListener; + + //关联的事件数, 小于1表示没有事件 + private final AtomicInteger eventing = new AtomicInteger(); + + //用于服务端的Socket, 等同于一直存在的readCompletionHandler + ProtocolCodec protocolCodec; + + protected AsyncConnection(boolean client, AsyncGroup ioGroup, AsyncThread ioThread, final int bufferCapacity, ObjectPool bufferPool, + SSLBuilder sslBuilder, SSLContext sslContext, final LongAdder livingCounter, final LongAdder closedCounter) { + this(client, ioGroup, ioThread, bufferCapacity, bufferPool, bufferPool, sslBuilder, sslContext, livingCounter, closedCounter); + } + + protected AsyncConnection(boolean client, AsyncGroup ioGroup, AsyncThread ioThread, final int bufferCapacity, Supplier bufferSupplier, + Consumer bufferConsumer, SSLBuilder sslBuilder, SSLContext sslContext, final LongAdder livingCounter, final LongAdder closedCounter) { + Objects.requireNonNull(bufferSupplier); + Objects.requireNonNull(bufferConsumer); + this.client = client; + this.ioGroup = ioGroup; + this.ioThread = ioThread; + this.bufferCapacity = bufferCapacity; + this.bufferSupplier = bufferSupplier; + this.bufferConsumer = bufferConsumer; + this.livingCounter = livingCounter; + this.closedCounter = closedCounter; + if (client) { //client模式下无SSLBuilder + if (sslContext != null) { + if (sslBuilder != null) { + this.sslEngine = sslBuilder.createSSLEngine(sslContext, client); + } else { + this.sslEngine = sslContext.createSSLEngine(); + } + } + } else { + if (sslBuilder != null && sslContext != null) { + this.sslEngine = sslBuilder.createSSLEngine(sslContext, client); + } + } + } + + public Supplier getBufferSupplier() { + return this.bufferSupplier; + } + + public Consumer getBufferConsumer() { + return this.bufferConsumer; + } + + public final long getLastReadTime() { + return readtime; + } + + public final long getLastWriteTime() { + return writetime; + } + + public final boolean ssl() { + return sslEngine != null; + } + + public final int increEventing() { + return eventing.incrementAndGet(); + } + + public final int decreEventing() { + return eventing.decrementAndGet(); + } + + public final void execute(Runnable command) { + ioThread.execute(command); + } + + public final boolean inCurrThread() { + return ioThread.inCurrThread(); + } + + public final AsyncThread getAsyncThread() { + return ioThread; + } + + @Override + public abstract boolean isOpen(); + + public abstract boolean isTCP(); + + public abstract boolean shutdownInput(); + + public abstract boolean shutdownOutput(); + + public abstract boolean setOption(SocketOption name, T value); + + public abstract Set> supportedOptions(); + + public abstract SocketAddress getRemoteAddress(); + + public abstract SocketAddress getLocalAddress(); + + public abstract int getReadTimeoutSeconds(); + + public abstract int getWriteTimeoutSeconds(); + + public abstract void setReadTimeoutSeconds(int readTimeoutSeconds); + + public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds); + + protected abstract void readImpl(CompletionHandler handler); + + //src写完才会回调 + protected abstract void writeImpl(ByteBuffer src, A attachment, CompletionHandler handler); + + //srcs写完才会回调 + protected abstract void writeImpl(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler handler); + + protected void startRead(CompletionHandler handler) { + read(handler); + } + + public final void read(CompletionHandler handler) { + if (sslEngine == null) { + readImpl(handler); + } else { + sslReadImpl(false, handler); + } + } + + //src写完才会回调 + public final void write(ByteBuffer src, A attachment, CompletionHandler handler) { + if (sslEngine == null) { + writeImpl(src, attachment, handler); + } else { + try { + int remain = src.remaining(); + sslWriteImpl(false, src, t -> { + if (t == null) { + handler.completed(remain - src.remaining(), attachment); + } else { + handler.failed(t, attachment); + } + }); + } catch (SSLException e) { + handler.failed(e, attachment); + } + } + } + + //srcs写完才会回调 + public final void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler handler) { + if (sslEngine == null) { + writeImpl(srcs, offset, length, attachment, handler); + } else { + try { + int remain = ByteBufferReader.remaining(srcs, offset, length); + sslWriteImpl(false, srcs, offset, length, t -> { + if (t == null) { + handler.completed(remain - ByteBufferReader.remaining(srcs, offset, length), attachment); + } else { + handler.failed(t, attachment); + } + }); + } catch (SSLException e) { + handler.failed(e, attachment); + } + } + } + + //srcs写完才会回调 + public final void write(ByteBuffer[] srcs, A attachment, CompletionHandler handler) { + write(srcs, 0, srcs.length, attachment, handler); + } + + public final void write(byte[] bytes, CompletionHandler handler) { + write(bytes, 0, bytes.length, null, 0, 0, null, null, handler); + } + + public final void write(ByteTuple array, CompletionHandler handler) { + write(array.content(), array.offset(), array.length(), null, 0, 0, null, null, handler); + } + + public final void write(byte[] bytes, int offset, int length, CompletionHandler handler) { + write(bytes, offset, length, null, 0, 0, null, null, handler); + } + + public final void write(ByteTuple header, ByteTuple body, CompletionHandler handler) { + write(header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length(), null, null, handler); + } + + public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, Consumer bodyCallback, Object bodyAttachment, CompletionHandler handler) { + final ByteBuffer buffer = sslEngine == null ? pollWriteBuffer() : pollWriteSSLBuffer(); + if (buffer.remaining() >= headerLength + bodyLength) { + buffer.put(headerContent, headerOffset, headerLength); + if (bodyLength > 0) { + buffer.put(bodyContent, bodyOffset, bodyLength); + if (bodyCallback != null) bodyCallback.accept(bodyAttachment); + } + buffer.flip(); + CompletionHandler newhandler = new CompletionHandler() { + @Override + public void completed(Integer result, Void attachment) { + offerBuffer(buffer); + handler.completed(result, attachment); + } + + @Override + public void failed(Throwable exc, Void attachment) { + offerBuffer(buffer); + handler.failed(exc, attachment); + } + }; + write(buffer, null, newhandler); + } else { + ByteBufferWriter writer = ByteBufferWriter.create(sslEngine == null ? bufferSupplier : () -> pollWriteSSLBuffer(), buffer); + writer.put(headerContent, headerOffset, headerLength); + if (bodyLength > 0) { + writer.put(bodyContent, bodyOffset, bodyLength); + if (bodyCallback != null) bodyCallback.accept(bodyAttachment); + } + final ByteBuffer[] buffers = writer.toBuffers(); + CompletionHandler newhandler = new CompletionHandler() { + @Override + public void completed(Integer result, Void attachment) { + offerBuffer(buffers); + handler.completed(result, attachment); + } + + @Override + public void failed(Throwable exc, Void attachment) { + offerBuffer(buffers); + handler.failed(exc, attachment); + } + }; + write(buffers, null, newhandler); + } + } + + public void setReadBuffer(ByteBuffer buffer) { + if (this.readBuffer != null) throw new RuntimeException("repeat AsyncConnection.setReadBuffer"); + this.readBuffer = buffer; + } + + public boolean hasPipelineData() { + ByteBufferWriter writer = this.pipelineWriter; + return writer != null && writer.position() > 0; + } + + public final void flushPipelineData(CompletionHandler handler) { + flushPipelineData(null, handler); + } + + public void flushPipelineData(A attachment, CompletionHandler handler) { + ByteBufferWriter writer = this.pipelineWriter; + this.pipelineWriter = null; + if (writer == null) { + handler.completed(0, attachment); + } else { + ByteBuffer[] srcs = writer.toBuffers(); + CompletionHandler newhandler = new CompletionHandler() { + @Override + public void completed(Integer result, A attachment) { + offerBuffer(srcs); + handler.completed(result, attachment); + } + + @Override + public void failed(Throwable exc, A attachment) { + offerBuffer(srcs); + handler.failed(exc, attachment); + } + }; + if (srcs.length == 1) { + write(srcs[0], attachment, newhandler); + } else { + write(srcs, attachment, newhandler); + } + } + } + + //返回 是否over + public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple array) { + return writePipelineData(pipelineIndex, pipelineCount, array.content(), array.offset(), array.length()); + } + + //返回 是否over + public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] bs, int offset, int length) { + synchronized (this) { + ByteBufferWriter writer = this.pipelineWriter; + if (writer == null) { + writer = ByteBufferWriter.create(getBufferSupplier()); + this.pipelineWriter = writer; + } + if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) { + writer.put(bs, offset, length); + return (pipelineIndex == pipelineCount); + } else { + PipelineDataNode dataNode = this.pipelineDataNode; + if (dataNode == null) { + dataNode = new PipelineDataNode(); + this.pipelineDataNode = dataNode; + } + if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值 + dataNode.pipelineCount = pipelineCount; + } + dataNode.put(pipelineIndex, bs, offset, length); + if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) { + for (PipelineDataItem item : dataNode.arrayItems()) { + writer.put(item.data); + } + this.pipelineDataNode = null; + return true; + } + return false; + } + } + } + + //返回 是否over + public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple header, ByteTuple body) { + return writePipelineData(pipelineIndex, pipelineCount, header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length()); + } + + //返回 是否over + public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { + synchronized (this) { + ByteBufferWriter writer = this.pipelineWriter; + if (writer == null) { + writer = ByteBufferWriter.create(getBufferSupplier()); + this.pipelineWriter = writer; + } + if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) { + writer.put(headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); + return (pipelineIndex == pipelineCount); + } else { + PipelineDataNode dataNode = this.pipelineDataNode; + if (dataNode == null) { + dataNode = new PipelineDataNode(); + this.pipelineDataNode = dataNode; + } + if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值 + dataNode.pipelineCount = pipelineCount; + } + dataNode.put(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); + if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) { + for (PipelineDataItem item : dataNode.arrayItems()) { + writer.put(item.data); + } + this.pipelineDataNode = null; + return true; + } + return false; + } + } + } + + private static class PipelineDataNode { + + public int pipelineCount; + + public int itemsize; + + private PipelineDataItem head; + + private PipelineDataItem tail; + + public PipelineDataItem[] arrayItems() { + PipelineDataItem[] items = new PipelineDataItem[itemsize]; + PipelineDataItem item = head; + int i = 0; + while (item != null) { + items[i] = item; + item = item.next; + items[i].next = null; + i++; + } + Arrays.sort(items); + return items; + } + + public void put(int pipelineIndex, byte[] bs, int offset, int length) { + if (tail == null) { + head = new PipelineDataItem(pipelineIndex, bs, offset, length); + tail = head; + } else { + PipelineDataItem item = new PipelineDataItem(pipelineIndex, bs, offset, length); + tail.next = item; + tail = item; + } + itemsize++; + } + + public void put(int pipelineIndex, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { + if (tail == null) { + head = new PipelineDataItem(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); + tail = head; + } else { + PipelineDataItem item = new PipelineDataItem(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); + tail.next = item; + tail = item; + } + itemsize++; + } + + } + + private static class PipelineDataItem implements Comparable { + + final byte[] data; + + final int index; + + public PipelineDataItem next; + + public PipelineDataItem(int index, byte[] bs, int offset, int length) { + this.index = index; + this.data = Arrays.copyOfRange(bs, offset, offset + length); + } + + public PipelineDataItem(int index, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { + this.index = index; + this.data = bodyLength > 0 ? copyOfRange(headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength) + : Arrays.copyOfRange(headerContent, headerOffset, headerOffset + headerLength); + } + + private static byte[] copyOfRange(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { + byte[] result = new byte[headerLength + bodyLength]; + System.arraycopy(headerContent, headerOffset, result, 0, headerLength); + System.arraycopy(bodyContent, bodyOffset, result, headerLength, bodyLength); + return result; + } + + @Override + public int compareTo(PipelineDataItem o) { + return this.index - o.index; + } + + @Override + public String toString() { + return "{\"index\":" + index + "}"; + } + } + + protected void setReadSSLBuffer(ByteBuffer buffer) { + if (this.readSSLHalfBuffer != null) throw new RuntimeException("repeat AsyncConnection.setReadSSLBuffer"); + this.readSSLHalfBuffer = buffer; + } + + protected ByteBuffer pollReadSSLBuffer() { + ByteBuffer rs = this.readSSLHalfBuffer; + if (rs != null) { + this.readSSLHalfBuffer = null; + return rs; + } + return bufferSupplier.get(); + } + + public ByteBuffer pollReadBuffer() { + ByteBuffer rs = this.readBuffer; + if (rs != null) { + this.readBuffer = null; + return rs; + } + return bufferSupplier.get(); + } + + public void offerBuffer(ByteBuffer buffer) { + if (buffer == null) return; + bufferConsumer.accept(buffer); + } + + public void offerBuffer(ByteBuffer... buffers) { + if (buffers == null) return; + Consumer consumer = this.bufferConsumer; + for (ByteBuffer buffer : buffers) { + consumer.accept(buffer); + } + } + + public ByteBuffer pollWriteSSLBuffer() { + return bufferSupplier.get(); + } + + public ByteBuffer pollWriteBuffer() { + return bufferSupplier.get(); + } + + public void dispose() {//同close, 只是去掉throws IOException + try { + this.close(); + } catch (IOException io) { + } + } + + public AsyncConnection beforeCloseListener(Consumer beforeCloseListener) { + this.beforeCloseListener = beforeCloseListener; + return this; + } + + @Override + public void close() throws IOException { + if (closedCounter != null) { + closedCounter.increment(); + closedCounter = null; + } + if (livingCounter != null) { + livingCounter.decrement(); + livingCounter = null; + } + if (sslEngine != null) { + try { + sslEngine.closeInbound(); + sslEngine.closeOutbound(); + } catch (SSLException e) { + } + sslEngine = null; + } + if (beforeCloseListener != null) { + try { + beforeCloseListener.accept(this); + } catch (Exception io) { + } + } + if (this.readBuffer != null) { + Consumer consumer = this.bufferConsumer; + if (consumer != null) consumer.accept(this.readBuffer); + } + if (attributes == null) return; + try { + for (Object obj : attributes.values()) { + if (obj instanceof AutoCloseable) ((AutoCloseable) obj).close(); + } + attributes.clear(); + } catch (Exception io) { + } + } + + @SuppressWarnings("unchecked") + public final T getSubobject() { + return (T) this.subobject; + } + + public void setSubobject(Object value) { + this.subobject = value; + } + + @Override + public void setAttribute(String name, Object value) { + if (this.attributes == null) this.attributes = new HashMap<>(); + this.attributes.put(name, value); + } + + @Override + @SuppressWarnings("unchecked") + public final T getAttribute(String name) { + return (T) (this.attributes == null ? null : this.attributes.get(name)); + } + + @Override + public final void removeAttribute(String name) { + if (this.attributes != null) this.attributes.remove(name); + } + + @Override + public final Map getAttributes() { + return this.attributes; + } + + @Override + public final void clearAttribute() { + if (this.attributes != null) this.attributes.clear(); + } + + protected void startHandshake(final Consumer callback) { + if (sslEngine == null) { + callback.accept(null); + return; + } + SSLEngine engine = this.sslEngine; + try { + engine.beginHandshake(); + doHandshake(callback); + } catch (Throwable t) { + callback.accept(t); + } + } + + //解密ssl网络数据, 返回null表示CLOSED + protected ByteBuffer sslUnwrap(final boolean handshake, ByteBuffer netBuffer) throws SSLException { + ByteBuffer appBuffer = pollReadBuffer(); + SSLEngine engine = this.sslEngine; + HandshakeStatus hss; + do { //status只有 CLOSED、OK、BUFFER_UNDERFLOW、BUFFER_OVERFLOW + //BUFFER_OVERFLOW: appBuffer可用空间不足, redkale确保appBuffer不会出现空间不足的情况 + //BUFFER_UNDERFLOW: netBuffer可用内容不足 + SSLEngineResult engineResult = engine.unwrap(netBuffer, appBuffer); + if (engineResult.getStatus() == SSLEngineResult.Status.CLOSED + && (engineResult.getHandshakeStatus() == NOT_HANDSHAKING || engineResult.getHandshakeStatus() == FINISHED)) { + offerBuffer(netBuffer); + offerBuffer(appBuffer); + return null; + } + hss = engineResult.getHandshakeStatus(); + if (hss == NEED_TASK) { + Runnable task; + while ((task = engine.getDelegatedTask()) != null) { + task.run(); + } + hss = engine.getHandshakeStatus(); + } + } while (hss == NEED_UNWRAP && netBuffer.hasRemaining()); + if (netBuffer.hasRemaining()) { + netBuffer.compact(); + setReadSSLBuffer(netBuffer); + } + return appBuffer; + } + + protected void sslReadImpl(final boolean handshake, final CompletionHandler handler) { + readImpl(new CompletionHandler() { + + @Override + public void completed(Integer count, ByteBuffer attachment) { + // System.out.println(AsyncConnection.this + " 进来了读到的字节数: " + count); + if (count < 0) { + handler.completed(count, attachment); + return; + } + ByteBuffer netBuffer = attachment; + netBuffer.flip(); + try { + ByteBuffer appBuffer = sslUnwrap(handshake, netBuffer); + if (appBuffer == null) return; //CLOSED,netBuffer已被回收 + if (AsyncConnection.this.readSSLHalfBuffer != netBuffer) { + offerBuffer(netBuffer); + } + if (AsyncConnection.this.readBuffer != null) { + ByteBuffer rsBuffer = AsyncConnection.this.readBuffer; + AsyncConnection.this.readBuffer = null; + appBuffer.flip(); + if (rsBuffer.remaining() >= appBuffer.remaining()) { + rsBuffer.put(appBuffer); + offerBuffer(appBuffer); + appBuffer = rsBuffer; + } else { + while (rsBuffer.hasRemaining()) rsBuffer.put(appBuffer.get()); + AsyncConnection.this.readBuffer = appBuffer.compact(); + appBuffer = rsBuffer; + } + } + handler.completed(count, appBuffer); + } catch (SSLException e) { + failed(e, attachment); + } + } + + @Override + public void failed(Throwable t, ByteBuffer attachment) { + handler.failed(t, attachment); + } + }); + } + + //加密ssl内容数据 + protected ByteBuffer[] sslWrap(final boolean handshake, ByteBuffer appBuffer) throws SSLException { + final SSLEngine engine = this.sslEngine; + final int netSize = engine.getSession().getPacketBufferSize(); + ByteBuffer netBuffer = pollWriteBuffer(); + ByteBuffer[] netBuffers = new ByteBuffer[]{netBuffer}; + SSLEngineResult engineResult = engine.wrap(appBuffer, netBuffer); + //status只有 CLOSED、OK、BUFFER_OVERFLOW + //BUFFER_OVERFLOW: netBuffer可用空间不足, redkale确保netBuffer不会出现空间不足的情况 + while (appBuffer.hasRemaining() || (handshake && engine.getHandshakeStatus() == NEED_WRAP)) { + boolean enough = true; + if (engineResult.getHandshakeStatus() == NEED_TASK) { + Runnable task; + while ((task = engine.getDelegatedTask()) != null) { + task.run(); + } + } else if (engineResult.getStatus() == BUFFER_OVERFLOW) { //需要重新wrap + enough = false; + } + if (enough && netBuffer.remaining() >= netSize) { + engineResult = engine.wrap(appBuffer, netBuffer); + if (engineResult.getStatus() != OK) { + netBuffer.flip(); + netBuffer = pollWriteBuffer(); + netBuffers = Utility.append(netBuffers, netBuffer); + engineResult = engine.wrap(appBuffer, netBuffer); + } + } else { + netBuffer.flip(); + netBuffer = pollWriteBuffer(); + netBuffers = Utility.append(netBuffers, netBuffer); + engineResult = engine.wrap(appBuffer, netBuffer); + } + } + netBuffer.flip(); + return netBuffers; + } + + //加密ssl内容数据 + protected ByteBuffer[] sslWrap(final boolean handshake, ByteBuffer[] appBuffers, int offset, int length) throws SSLException { + final SSLEngine engine = this.sslEngine; + final int netSize = engine.getSession().getPacketBufferSize(); + ByteBuffer netBuffer = pollWriteSSLBuffer(); + ByteBuffer[] netBuffers = new ByteBuffer[]{netBuffer}; + SSLEngineResult engineResult = engine.wrap(appBuffers, offset, length, netBuffer); + while (ByteBufferReader.remaining(appBuffers, offset, length) > 0) { + boolean enough = true; + if (engineResult.getHandshakeStatus() == NEED_TASK) { + Runnable task; + while ((task = engine.getDelegatedTask()) != null) { + task.run(); + } + } else if (engineResult.getStatus() == BUFFER_OVERFLOW) { //需要重新wrap + enough = false; + } + if (enough && netBuffer.remaining() >= netSize) { + engineResult = engine.wrap(appBuffers, offset, length, netBuffer); + if (engineResult.getStatus() != OK) { + netBuffer.flip(); + netBuffer = pollWriteSSLBuffer(); + netBuffers = Utility.append(netBuffers, netBuffer); + engineResult = engine.wrap(appBuffers, offset, length, netBuffer); + } + } else { + netBuffer.flip(); + netBuffer = pollWriteSSLBuffer(); + netBuffers = Utility.append(netBuffers, netBuffer); + engineResult = engine.wrap(appBuffers, offset, length, netBuffer); + } + } + netBuffer.flip(); + return netBuffers; + } + + protected boolean sslWriteImpl(final boolean handshake, ByteBuffer appBuffer, final Consumer callback) throws SSLException { + ByteBuffer[] netBuffers = sslWrap(handshake, appBuffer); + if (netBuffers.length > 0) { + if (netBuffers.length == 1) { + writeImpl(netBuffers[0], null, new CompletionHandler() { + @Override + public void completed(Integer count, Void attachment) { + offerBuffer(netBuffers[0]); + callback.accept(null); + } + + @Override + public void failed(Throwable t, Void attachment) { + offerBuffer(netBuffers[0]); + callback.accept(t); + } + }); + } else { + writeImpl(netBuffers, 0, netBuffers.length, null, new CompletionHandler() { + @Override + public void completed(Integer count, Void attachment) { + offerBuffer(netBuffers); + callback.accept(null); + } + + @Override + public void failed(Throwable t, Void attachment) { + offerBuffer(netBuffers); + callback.accept(t); + } + }); + } + return true; + } else { + offerBuffer(netBuffers); + return false; + } + } + + protected boolean sslWriteImpl(final boolean handshake, ByteBuffer[] appBuffers, int offset, int length, final Consumer callback) throws SSLException { + ByteBuffer[] netBuffers = sslWrap(handshake, appBuffers, offset, length); + if (netBuffers.length > 0) { + if (netBuffers.length == 1) { + writeImpl(netBuffers[0], null, new CompletionHandler() { + @Override + public void completed(Integer count, Void attachment) { + offerBuffer(netBuffers[0]); + callback.accept(null); + } + + @Override + public void failed(Throwable t, Void attachment) { + offerBuffer(netBuffers[0]); + callback.accept(t); + } + }); + } else { + writeImpl(netBuffers, 0, netBuffers.length, null, new CompletionHandler() { + @Override + public void completed(Integer count, Void attachment) { + offerBuffer(netBuffers); + callback.accept(null); + } + + @Override + public void failed(Throwable t, Void attachment) { + offerBuffer(netBuffers); + callback.accept(t); + } + }); + } + return true; + } else { + offerBuffer(netBuffers); + return false; + } + } + + private void doHandshake(final Consumer callback) { + HandshakeStatus handshakeStatus; + final SSLEngine engine = this.sslEngine; + while ((handshakeStatus = engine.getHandshakeStatus()) != null) { + // System.out.println(AsyncConnection.this + " handshakeStatus = " + handshakeStatus); + switch (handshakeStatus) { + case FINISHED: + case NOT_HANDSHAKING: + // System.out.println(AsyncConnection.this + " doHandshakde完毕,开始进入读写操作-----"); + callback.accept(null); + return; + case NEED_TASK: { + Runnable task; + while ((task = engine.getDelegatedTask()) != null) { + task.run(); + } + break; + } + case NEED_WRAP: { + try { // + boolean rs = sslWriteImpl(true, EMPTY_BUFFER, t -> { + if (t == null) { + doHandshake(callback); + } else { + callback.accept(t); + } + }); + if (rs) return; + } catch (SSLException e) { + callback.accept(e); + return; + } + break; + } + case NEED_UNWRAP: { + sslReadImpl(true, new CompletionHandler() { + @Override + public void completed(Integer count, ByteBuffer attachment) { + if (count < 1) { + callback.accept(new IOException("read data error")); + } else { + offerBuffer(attachment); + doHandshake(callback); + } + } + + @Override + public void failed(Throwable t, ByteBuffer attachment) { + callback.accept(t); + } + }); + return; + } + } + } + } + + @Override + public String toString() { + String s = super.toString(); + int pos = s.lastIndexOf('@'); + if (pos < 1) return s; + int cha = pos + 10 - s.length(); + if (cha < 1) return s; + for (int i = 0; i < cha; i++) s += ' '; + return s; + } +} diff --git a/src/main/java/org/redkale/net/AsyncGroup.java b/src/main/java/org/redkale/net/AsyncGroup.java new file mode 100644 index 000000000..a8e9ff5e5 --- /dev/null +++ b/src/main/java/org/redkale/net/AsyncGroup.java @@ -0,0 +1,66 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.util.concurrent.*; +import org.redkale.util.ObjectPool; + +/** + * Client模式的AsyncConnection连接构造器 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + */ +public abstract class AsyncGroup { + + public static AsyncGroup create(String threadPrefixName, final ExecutorService workExecutor, final int bufferCapacity, final int bufferPoolSize) { + return new AsyncIOGroup(true, threadPrefixName, workExecutor, bufferCapacity, bufferPoolSize); + } + + public static AsyncGroup create(String threadPrefixName, ExecutorService workExecutor, final int bufferCapacity, ObjectPool safeBufferPool) { + return new AsyncIOGroup(true, threadPrefixName, workExecutor, bufferCapacity, safeBufferPool); + } + + public static AsyncGroup create(boolean client, String threadPrefixName, final ExecutorService workExecutor, final int bufferCapacity, final int bufferPoolSize) { + return new AsyncIOGroup(client, threadPrefixName, workExecutor, bufferCapacity, bufferPoolSize); + } + + public static AsyncGroup create(boolean client, String threadPrefixName, ExecutorService workExecutor, final int bufferCapacity, ObjectPool safeBufferPool) { + return new AsyncIOGroup(client, threadPrefixName, workExecutor, bufferCapacity, safeBufferPool); + } + + public CompletableFuture createTCPClient(final SocketAddress address) { + return createTCPClient(address, 0, 0); + } + + public abstract CompletableFuture createTCPClient(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds); + + public CompletableFuture createUDPClient(final SocketAddress address) { + return createUDPClient(address, 0, 0); + } + + public abstract CompletableFuture createUDPClient(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds); + + public CompletableFuture createClient(final boolean tcp, final SocketAddress address) { + return tcp ? createTCPClient(address) : createUDPClient(address); + } + + public CompletableFuture createClient(final boolean tcp, final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) { + return tcp ? createTCPClient(address, readTimeoutSeconds, writeTimeoutSeconds) : createUDPClient(address, readTimeoutSeconds, writeTimeoutSeconds); + } + + public abstract ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit); + + public abstract AsyncGroup start(); + + public abstract AsyncGroup close(); +} diff --git a/src/org/redkale/net/AsyncIOGroup.java b/src/main/java/org/redkale/net/AsyncIOGroup.java similarity index 59% rename from src/org/redkale/net/AsyncIOGroup.java rename to src/main/java/org/redkale/net/AsyncIOGroup.java index 27eb8b1c4..a68d30af9 100644 --- a/src/org/redkale/net/AsyncIOGroup.java +++ b/src/main/java/org/redkale/net/AsyncIOGroup.java @@ -1,217 +1,270 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import org.redkale.util.*; - -/** - * 协议处理的IO线程组 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -@ResourceType(AsyncGroup.class) -public class AsyncIOGroup extends AsyncGroup { - - private boolean started; - - private boolean closed; - - AsyncIOThread[] ioThreads; - - private AsyncIOThread connectThread; - - final int bufferCapacity; - - private final AtomicInteger readIndex = new AtomicInteger(); - - //创建数 - final AtomicLong connCreateCounter = new AtomicLong(); - - //关闭数 - final AtomicLong connLivingCounter = new AtomicLong(); - - //在线数 - final AtomicLong connClosedCounter = new AtomicLong(); - - private ScheduledThreadPoolExecutor timeoutExecutor; - - public AsyncIOGroup(final int bufferCapacity, final int bufferPoolSize) { - this(null, Runtime.getRuntime().availableProcessors(), bufferCapacity, bufferPoolSize); - } - - public AsyncIOGroup(final ExecutorService workExecutor, - final int iothreads, final int bufferCapacity, final int bufferPoolSize) { - this(null, workExecutor, iothreads, bufferCapacity, ObjectPool.createSafePool(null, null, bufferPoolSize, - (Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> { - if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false; - e.clear(); - return true; - })); - } - - public AsyncIOGroup(String threadPrefixName0, ExecutorService workExecutor, - int iothreads, final int bufferCapacity, ObjectPool safeBufferPool) { - this.bufferCapacity = bufferCapacity; - final String threadPrefixName = threadPrefixName0 == null ? "Redkale-Client-IOThread" : threadPrefixName0; - this.ioThreads = new AsyncIOThread[Math.max(iothreads, 1)]; - try { - for (int i = 0; i < this.ioThreads.length; i++) { - ObjectPool unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(), - safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler()); - String name = threadPrefixName + "-" + (i >= 9 ? (i + 1) : ("0" + (i + 1))); - - this.ioThreads[i] = new AsyncIOThread(true, name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool); - } - { - ObjectPool unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(), - safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler()); - String name = threadPrefixName.replace("ServletThread", "ConnectThread").replace("Redkale-Client-IOThread", "Redkale-Client-ConnectThread"); - this.connectThread = new AsyncIOThread(false, name, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> { - Thread t = new Thread(r); - t.setName(threadPrefixName + "-Timeout"); - t.setDaemon(true); - return t; - }); - } - - public int size() { - return this.ioThreads.length; - } - - public void start() { - if (started) return; - if (closed) throw new RuntimeException("group is closed"); - for (AsyncIOThread thread : ioThreads) { - thread.start(); - } - connectThread.start(); - started = true; - } - - public void close() { - if (closed) return; - for (AsyncIOThread thread : ioThreads) { - thread.close(); - } - connectThread.close(); - this.timeoutExecutor.shutdownNow(); - closed = true; - } - - public AtomicLong getCreateConnectionCount() { - return connCreateCounter; - } - - public AtomicLong getClosedConnectionCount() { - return connLivingCounter; - } - - public AtomicLong getLivingConnectionCount() { - return connClosedCounter; - } - - public AsyncIOThread nextIOThread() { - return ioThreads[Math.abs(readIndex.getAndIncrement()) % ioThreads.length]; - } - - public AsyncIOThread connectThread() { - return connectThread; - } - - @Override - public ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit) { - return timeoutExecutor.schedule(callable, delay, unit); - } - - public void interestOpsOr(AsyncIOThread thread, SelectionKey key, int opt) { - if (key == null) return; - if (key.selector() != thread.selector) throw new RuntimeException("NioThread.selector not the same to SelectionKey.selector"); - if ((key.interestOps() & opt) != 0) return; - key.interestOps(key.interestOps() | opt); - if (thread.inCurrThread()) return; - //非IO线程中 - key.selector().wakeup(); - } - - @Override - public CompletableFuture createTCP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) { - SocketChannel channel; - try { - channel = SocketChannel.open(); - channel.configureBlocking(false); - channel.setOption(StandardSocketOptions.TCP_NODELAY, true); - channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); - channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - } catch (IOException e) { - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future; - } - AsyncIOThread readThread = nextIOThread(); - final AsyncNioTcpConnection conn = new AsyncNioTcpConnection(true, this, readThread, connectThread, channel, null, address, connLivingCounter, connClosedCounter); - final CompletableFuture future = new CompletableFuture<>(); - conn.connect(address, null, new CompletionHandler() { - @Override - public void completed(Void result, Void attachment) { - conn.setReadTimeoutSeconds(readTimeoutSeconds); - conn.setWriteTimeoutSeconds(writeTimeoutSeconds); - connCreateCounter.incrementAndGet(); - connLivingCounter.incrementAndGet(); - future.complete(conn); - } - - @Override - public void failed(Throwable exc, Void attachment) { - future.completeExceptionally(exc); - } - }); - return Utility.orTimeout(future, 30, TimeUnit.SECONDS); - } - - @Override - public CompletableFuture createUDP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) { - DatagramChannel channel; - try { - channel = DatagramChannel.open(); - } catch (IOException e) { - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future; - } - AsyncIOThread readThread = nextIOThread(); - AsyncNioUdpConnection conn = new AsyncNioUdpConnection(true, this, readThread, connectThread, channel, null, address, connLivingCounter, connClosedCounter); - CompletableFuture future = new CompletableFuture(); - conn.connect(address, null, new CompletionHandler() { - @Override - public void completed(Void result, Void attachment) { - future.complete(conn); - } - - @Override - public void failed(Throwable exc, Void attachment) { - future.completeExceptionally(exc); - } - }); - return future; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import org.redkale.util.*; + +/** + * 协议处理的IO线程组 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +@ResourceType(AsyncGroup.class) +public class AsyncIOGroup extends AsyncGroup { + + private boolean started; + + private boolean closed; + + AsyncIOThread[] ioThreads; + + private AsyncIOThread connectThread; + + final int bufferCapacity; + + final AtomicInteger shareCount = new AtomicInteger(1); + + private final AtomicInteger readIndex = new AtomicInteger(); + + //创建数 + final LongAdder connCreateCounter = new LongAdder(); + + //关闭数 + final LongAdder connLivingCounter = new LongAdder(); + + //在线数 + final LongAdder connClosedCounter = new LongAdder(); + + private ScheduledThreadPoolExecutor timeoutExecutor; + + public AsyncIOGroup(final int bufferCapacity, final int bufferPoolSize) { + this(true, null, null, bufferCapacity, bufferPoolSize); + } + + public AsyncIOGroup(boolean client, String threadPrefixName, final ExecutorService workExecutor, final int bufferCapacity, final int bufferPoolSize) { + this(client, threadPrefixName, workExecutor, bufferCapacity, ObjectPool.createSafePool(null, null, bufferPoolSize, + (Object... params) -> ByteBuffer.allocateDirect(bufferCapacity), null, (e) -> { + if (e == null || e.isReadOnly() || e.capacity() != bufferCapacity) return false; + e.clear(); + return true; + })); + } + + public AsyncIOGroup(boolean client, String threadPrefixName0, ExecutorService workExecutor, final int bufferCapacity, ObjectPool safeBufferPool) { + this.bufferCapacity = bufferCapacity; + final String threadPrefixName = threadPrefixName0 == null ? "Redkale-Client-IOThread" : threadPrefixName0; + this.ioThreads = new AsyncIOThread[Utility.cpus()]; + try { + for (int i = 0; i < this.ioThreads.length; i++) { + ObjectPool unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(), + safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler()); + String name = threadPrefixName + "-" + (i >= 9 ? (i + 1) : ("0" + (i + 1))); + + this.ioThreads[i] = new AsyncIOThread(true, name, i, ioThreads.length, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool); + } + if (client) { + ObjectPool unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(), + safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler()); + String name = threadPrefixName.replace("ServletThread", "ConnectThread").replace("IOThread", "ConnectThread"); + this.connectThread = new AsyncIOThread(false, name, 0, 0, workExecutor, Selector.open(), unsafeBufferPool, safeBufferPool); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + this.timeoutExecutor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1, (Runnable r) -> { + Thread t = new Thread(r); + t.setName(threadPrefixName + "-Timeout"); + t.setDaemon(true); + return t; + }); + } + + public int size() { + return this.ioThreads.length; + } + + @Override + public AsyncGroup start() { + if (started) return this; + if (closed) throw new RuntimeException("group is closed"); + for (AsyncIOThread thread : ioThreads) { + thread.start(); + } + if (connectThread != null) connectThread.start(); + started = true; + return this; + } + + @Override + public AsyncGroup close() { + if (shareCount.decrementAndGet() > 0) return this; + if (closed) return this; + for (AsyncIOThread thread : ioThreads) { + thread.close(); + } + if (connectThread != null) connectThread.close(); + this.timeoutExecutor.shutdownNow(); + closed = true; + return this; + } + + public LongAdder getCreateConnectionCount() { + return connCreateCounter; + } + + public LongAdder getClosedConnectionCount() { + return connLivingCounter; + } + + public LongAdder getLivingConnectionCount() { + return connClosedCounter; + } + + public AsyncIOThread nextIOThread() { + return ioThreads[Math.abs(readIndex.getAndIncrement()) % ioThreads.length]; + } + + public AsyncIOThread connectThread() { + return connectThread; + } + + @Override + public ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit) { + return timeoutExecutor.schedule(callable, delay, unit); + } + + public void interestOpsOr(AsyncIOThread thread, SelectionKey key, int opt) { + if (key == null) return; + if (key.selector() != thread.selector) throw new RuntimeException("NioThread.selector not the same to SelectionKey.selector"); + if ((key.interestOps() & opt) != 0) return; + key.interestOps(key.interestOps() | opt); + if (thread.inCurrThread()) { +// timeoutExecutor.execute(() -> { +// try { +// key.selector().wakeup(); +// } catch (Throwable t) { +// } +// }); + } else { + //非IO线程中 + key.selector().wakeup(); + } + } + + @Override + public CompletableFuture createTCPClient(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) { + SocketChannel channel; + try { + channel = SocketChannel.open(); + channel.configureBlocking(false); + channel.setOption(StandardSocketOptions.TCP_NODELAY, true); + channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); + } catch (IOException e) { + CompletableFuture future = new CompletableFuture(); + future.completeExceptionally(e); + return future; + } + AsyncIOThread ioThread = null; + Thread currThread = Thread.currentThread(); + if (currThread instanceof AsyncIOThread) { + for (AsyncIOThread thread : ioThreads) { + if (thread == currThread) { + ioThread = thread; + break; + } + } + } + if (ioThread == null) ioThread = nextIOThread(); + final AsyncNioTcpConnection conn = new AsyncNioTcpConnection(true, this, ioThread, connectThread, channel, null, null, address, connLivingCounter, connClosedCounter); + final CompletableFuture future = new CompletableFuture<>(); + conn.connect(address, null, new CompletionHandler() { + @Override + public void completed(Void result, Void attachment) { + conn.setReadTimeoutSeconds(readTimeoutSeconds); + conn.setWriteTimeoutSeconds(writeTimeoutSeconds); + if (connCreateCounter != null) connCreateCounter.increment(); + if (connLivingCounter != null) connLivingCounter.increment(); + if (conn.sslEngine == null) { + future.complete(conn); + } else { + conn.startHandshake(t -> { + if (t == null) { + future.complete(conn); + } else { + future.completeExceptionally(t); + } + }); + } + } + + @Override + public void failed(Throwable exc, Void attachment) { + future.completeExceptionally(exc); + } + }); + return Utility.orTimeout(future, 30, TimeUnit.SECONDS); + } + + @Override + public CompletableFuture createUDPClient(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) { + DatagramChannel channel; + try { + channel = DatagramChannel.open(); + } catch (IOException e) { + CompletableFuture future = new CompletableFuture(); + future.completeExceptionally(e); + return future; + } + AsyncIOThread ioThread = null; + Thread currThread = Thread.currentThread(); + if (currThread instanceof AsyncIOThread) { + for (AsyncIOThread thread : ioThreads) { + if (thread == currThread) { + ioThread = thread; + break; + } + } + } + if (ioThread == null) ioThread = nextIOThread(); + AsyncNioUdpConnection conn = new AsyncNioUdpConnection(true, this, ioThread, connectThread, channel, null, null, address, connLivingCounter, connClosedCounter); + CompletableFuture future = new CompletableFuture(); + conn.connect(address, null, new CompletionHandler() { + @Override + public void completed(Void result, Void attachment) { + if (conn.sslEngine == null) { + future.complete(conn); + } else { + conn.startHandshake(t -> { + if (t == null) { + future.complete(conn); + } else { + future.completeExceptionally(t); + } + }); + } + } + + @Override + public void failed(Throwable exc, Void attachment) { + future.completeExceptionally(exc); + } + }); + return future; + } + +} diff --git a/src/org/redkale/net/AsyncIOThread.java b/src/main/java/org/redkale/net/AsyncIOThread.java similarity index 65% rename from src/org/redkale/net/AsyncIOThread.java rename to src/main/java/org/redkale/net/AsyncIOThread.java index a7b57ede7..dd78cbba2 100644 --- a/src/org/redkale/net/AsyncIOThread.java +++ b/src/main/java/org/redkale/net/AsyncIOThread.java @@ -1,133 +1,163 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.*; -import java.util.logging.*; -import org.redkale.util.*; - -/** - * 协议处理的IO线程类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class AsyncIOThread extends WorkThread { - - protected static final Logger logger = Logger.getLogger(AsyncIOThread.class.getSimpleName()); - - final Selector selector; - - final AtomicInteger connCounter = new AtomicInteger(); - - private final Supplier bufferSupplier; - - private final Consumer bufferConsumer; - - private final ConcurrentLinkedQueue> registers = new ConcurrentLinkedQueue<>(); - - private boolean closed; - - int invoker = 0; - - public AsyncIOThread(final boolean readable, String name, ExecutorService workExecutor, Selector selector, - ObjectPool unsafeBufferPool, ObjectPool safeBufferPool) { - super(name, workExecutor, null); - this.selector = selector; - this.setDaemon(true); - this.bufferSupplier = () -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).get(); - this.bufferConsumer = (v) -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).accept(v); - } - - public void register(Consumer consumer) { - registers.offer(consumer); - selector.wakeup(); - } - - public Supplier getBufferSupplier() { - return bufferSupplier; - } - - public Consumer getBufferConsumer() { - return bufferConsumer; - } - - public int currConnections() { - return connCounter.get(); - } - - @Override - public void run() { - this.localThread = Thread.currentThread(); - while (!this.closed) { - try { - Consumer register; - while ((register = registers.poll()) != null) { - register.accept(selector); - } - int count = selector.select(); - if (count == 0) continue; - Set keys = selector.selectedKeys(); - Iterator it = keys.iterator(); - while (it.hasNext()) { - SelectionKey key = it.next(); - it.remove(); - invoker = 0; - if (!key.isValid()) continue; - AsyncNioConnection conn = (AsyncNioConnection) key.attachment(); - if (conn.client) { - if (key.isConnectable()) { - key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT); - conn.doConnect(); - } else if (key.isReadable()) { - conn.currReadInvoker = 0; - key.interestOps(key.interestOps() & ~SelectionKey.OP_READ); - conn.doRead(true); - } else if (key.isWritable()) { - conn.currWriteInvoker = 0; - key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); - conn.doWrite(true); - } - } else { - if (key.isReadable()) { - conn.currReadInvoker = 0; - //key.interestOps(key.interestOps() & ~SelectionKey.OP_READ); - conn.doRead(true); - } else if (conn.writeCompletionHandler != null && key.isWritable()) { - conn.currWriteInvoker = 0; - key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); - conn.doWrite(true); - } else if (key.isConnectable()) { - key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT); - conn.doConnect(); - } - } - } - } catch (Exception ex) { - if (!this.closed) logger.log(Level.FINE, getName() + " selector run failed", ex); - } - } - } - - public void close() { - this.closed = true; - this.interrupt(); - try { - this.selector.close(); - } catch (Exception e) { - logger.log(Level.FINE, getName() + " selector close failed", e); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.*; +import java.util.logging.*; +import org.redkale.util.*; + +/** + * 协议处理的IO线程类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class AsyncIOThread extends AsyncThread { + + protected static final Logger logger = Logger.getLogger(AsyncIOThread.class.getSimpleName()); + + final Selector selector; + + final AtomicInteger connCounter = new AtomicInteger(); + + private final Supplier bufferSupplier; + + private final Consumer bufferConsumer; + + private final Queue commandQueue = Utility.unsafe() != null ? new MpscGrowableArrayQueue<>(16, 1 << 16) : new ConcurrentLinkedQueue<>(); + + private final Queue> registerQueue = Utility.unsafe() != null ? new MpscGrowableArrayQueue<>(16, 1 << 16) : new ConcurrentLinkedQueue<>(); + + private boolean closed; + + int invoker = 0; + + public AsyncIOThread(final boolean readable, String name, int index, int threads, ExecutorService workExecutor, Selector selector, + ObjectPool unsafeBufferPool, ObjectPool safeBufferPool) { + super(name, index, threads, workExecutor, null); + this.selector = selector; + this.setDaemon(true); + this.bufferSupplier = () -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).get(); + this.bufferConsumer = (v) -> (inCurrThread() ? unsafeBufferPool : safeBufferPool).accept(v); + } + + @Override + public void execute(Runnable command) { + commandQueue.offer(command); + selector.wakeup(); + } + + @Override + public void execute(Runnable... commands) { + for (Runnable command : commands) { + commandQueue.offer(command); + } + selector.wakeup(); + } + + public void register(Consumer consumer) { + registerQueue.offer(consumer); + selector.wakeup(); + } + + public Supplier getBufferSupplier() { + return bufferSupplier; + } + + public Consumer getBufferConsumer() { + return bufferConsumer; + } + + public int currConnections() { + return connCounter.get(); + } + + @Override + public void run() { + final Queue commands = this.commandQueue; + final Queue> registers = this.registerQueue; + while (!this.closed) { + try { + Consumer register; + while ((register = registers.poll()) != null) { + try { + register.accept(selector); + } catch (Throwable t) { + if (!this.closed) logger.log(Level.INFO, getName() + " register run failed", t); + } + } + Runnable command; + while ((command = commands.poll()) != null) { + try { + command.run(); + } catch (Throwable t) { + if (!this.closed) logger.log(Level.INFO, getName() + " command run failed", t); + } + } + int count = selector.select(); + if (count == 0) continue; + + Set keys = selector.selectedKeys(); + Iterator it = keys.iterator(); + while (it.hasNext()) { + SelectionKey key = it.next(); + it.remove(); + invoker = 0; + if (!key.isValid()) continue; + AsyncNioConnection conn = (AsyncNioConnection) key.attachment(); + if (conn.client) { + if (key.isConnectable()) { + key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT); + conn.doConnect(); + } else if (conn.readCompletionHandler != null && key.isReadable()) { + conn.currReadInvoker = 0; + key.interestOps(key.interestOps() & ~SelectionKey.OP_READ); + conn.doRead(true); + } else if (conn.writeCompletionHandler != null && key.isWritable()) { + conn.currWriteInvoker = 0; + key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); + conn.doWrite(true); + } + } else { + if (conn.readCompletionHandler != null && key.isReadable()) { + conn.currReadInvoker = 0; + key.interestOps(key.interestOps() & ~SelectionKey.OP_READ); //不放开这行,在CompletableFuture时容易ReadPending + conn.doRead(true); + } else if (conn.writeCompletionHandler != null && key.isWritable()) { + conn.currWriteInvoker = 0; + key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE); + conn.doWrite(true); + } else if (key.isConnectable()) { + key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT); + conn.doConnect(); + } + } + } + } catch (Throwable ex) { + if (!this.closed) logger.log(Level.FINE, getName() + " selector run failed", ex); + } + } + } + + public void close() { + this.closed = true; + this.interrupt(); + try { + this.selector.close(); + } catch (Exception e) { + logger.log(Level.FINE, getName() + " selector close failed", e); + } + } +} diff --git a/src/org/redkale/net/AsyncNioCompletionHandler.java b/src/main/java/org/redkale/net/AsyncNioCompletionHandler.java similarity index 96% rename from src/org/redkale/net/AsyncNioCompletionHandler.java rename to src/main/java/org/redkale/net/AsyncNioCompletionHandler.java index d96cc0fea..b9774efc8 100644 --- a/src/org/redkale/net/AsyncNioCompletionHandler.java +++ b/src/main/java/org/redkale/net/AsyncNioCompletionHandler.java @@ -1,119 +1,119 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -class AsyncNioCompletionHandler implements CompletionHandler, Runnable { - - private final AsyncNioConnection conn; - - private CompletionHandler handler; - - private A attachment; - - public ScheduledFuture timeoutFuture; - - private ByteBuffer[] buffers; - - private ByteBuffer buffer; - - public AsyncNioCompletionHandler(AsyncNioConnection conn) { - this.conn = conn; - } - - public void handler(CompletionHandler handler, A attachment) { - this.handler = handler; - this.attachment = attachment; - } - - public void attachment(A attachment) { - this.attachment = attachment; - } - - public void buffers(ByteBuffer... buffs) { - this.buffers = buffs; - } - - public void buffer(ByteBuffer buff) { - this.buffer = buff; - } - - private void clear() { - this.handler = null; - this.attachment = null; - this.timeoutFuture = null; - this.buffers = null; - this.buffer = null; - } - - @Override - public void completed(Integer result, A attach) { - ScheduledFuture future = this.timeoutFuture; - if (future != null) { - this.timeoutFuture = null; - future.cancel(true); - } - if (conn != null) { - if (buffers != null) { - conn.offerBuffer(buffers); - } else if (buffer != null) { - conn.offerBuffer(buffer); - } - } - CompletionHandler handler0 = handler; - A attachment0 = attachment; - clear(); - handler0.completed(result, attachment0); - } - - @Override - public void failed(Throwable exc, A attach) { - ScheduledFuture future = this.timeoutFuture; - if (future != null) { - this.timeoutFuture = null; - future.cancel(true); - } - if (conn != null) { - if (buffers != null) { - conn.offerBuffer(buffers); - } else if (buffer != null) { - conn.offerBuffer(buffer); - } - } - CompletionHandler handler0 = handler; - A attachment0 = attachment; - clear(); - handler0.failed(exc, attachment0); - } - - @Override - public void run() { - if (conn != null) { - if (buffers != null) { - conn.offerBuffer(buffers); - } else if (buffer != null) { - conn.offerBuffer(buffer); - } - } - CompletionHandler handler0 = handler; - A attachment0 = attachment; - clear(); - handler0.failed(new TimeoutException(), attachment0); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.nio.ByteBuffer; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +class AsyncNioCompletionHandler implements CompletionHandler, Runnable { + + private final AsyncNioConnection conn; + + private CompletionHandler handler; + + private A attachment; + + public ScheduledFuture timeoutFuture; + + private ByteBuffer[] buffers; + + private ByteBuffer buffer; + + public AsyncNioCompletionHandler(AsyncNioConnection conn) { + this.conn = conn; + } + + public void handler(CompletionHandler handler, A attachment) { + this.handler = handler; + this.attachment = attachment; + } + + public void attachment(A attachment) { + this.attachment = attachment; + } + + public void buffers(ByteBuffer... buffs) { + this.buffers = buffs; + } + + public void buffer(ByteBuffer buff) { + this.buffer = buff; + } + + private void clear() { + this.handler = null; + this.attachment = null; + this.timeoutFuture = null; + this.buffers = null; + this.buffer = null; + } + + @Override + public void completed(Integer result, A attach) { + ScheduledFuture future = this.timeoutFuture; + if (future != null) { + this.timeoutFuture = null; + future.cancel(true); + } + if (conn != null) { + if (buffers != null) { + conn.offerBuffer(buffers); + } else if (buffer != null) { + conn.offerBuffer(buffer); + } + } + CompletionHandler handler0 = handler; + A attachment0 = attachment; + clear(); + handler0.completed(result, attachment0); + } + + @Override + public void failed(Throwable exc, A attach) { + ScheduledFuture future = this.timeoutFuture; + if (future != null) { + this.timeoutFuture = null; + future.cancel(true); + } + if (conn != null) { + if (buffers != null) { + conn.offerBuffer(buffers); + } else if (buffer != null) { + conn.offerBuffer(buffer); + } + } + CompletionHandler handler0 = handler; + A attachment0 = attachment; + clear(); + handler0.failed(exc, attachment0); + } + + @Override + public void run() { + if (conn != null) { + if (buffers != null) { + conn.offerBuffer(buffers); + } else if (buffer != null) { + conn.offerBuffer(buffer); + } + } + CompletionHandler handler0 = handler; + A attachment0 = attachment; + clear(); + handler0.failed(new TimeoutException(), attachment0); + } + +} diff --git a/src/org/redkale/net/AsyncNioConnection.java b/src/main/java/org/redkale/net/AsyncNioConnection.java similarity index 73% rename from src/org/redkale/net/AsyncNioConnection.java rename to src/main/java/org/redkale/net/AsyncNioConnection.java index b5a8c5ff3..a65893f72 100644 --- a/src/org/redkale/net/AsyncNioConnection.java +++ b/src/main/java/org/redkale/net/AsyncNioConnection.java @@ -1,478 +1,570 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.*; -import javax.net.ssl.SSLContext; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.3.0 - */ -abstract class AsyncNioConnection extends AsyncConnection { - - protected static final int MAX_INVOKER_ONSTACK = Integer.getInteger("net.invoker.max.onstack", 16); - - final AsyncIOThread ioThread; - - final AsyncIOThread connectThread; - - final AsyncIOGroup ioGroup; - - protected SocketAddress remoteAddress; - - //-------------------------------- 连操作 -------------------------------------- - protected Object connectAttachment; - - protected CompletionHandler connectCompletionHandler; - - protected boolean connectPending; - - protected SelectionKey connectKey; - - //-------------------------------- 读操作 -------------------------------------- - protected final AsyncNioCompletionHandler readTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this); - - protected int readTimeoutSeconds; - - int currReadInvoker; - - protected ByteBuffer readByteBuffer; - - protected CompletionHandler readCompletionHandler; - - protected boolean readPending; - - protected SelectionKey readKey; - - //-------------------------------- 写操作 -------------------------------------- - protected final AsyncNioCompletionHandler writeTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this); - - protected int writeTimeoutSeconds; - - int currWriteInvoker; - - protected byte[] writeByteTuple1Array; - - protected int writeByteTuple1Offset; - - protected int writeByteTuple1Length; - - protected byte[] writeByteTuple2Array; - - protected int writeByteTuple2Offset; - - protected int writeByteTuple2Length; - - protected Consumer writeByteTuple2Callback; - - protected Object writeByteTuple2Attachment; - - //写操作, 二选一,要么writeByteBuffer有值,要么writeByteBuffers、writeOffset、writeLength有值 - protected ByteBuffer writeByteBuffer; - - protected ByteBuffer[] writeByteBuffers; - - protected int writeOffset; - - protected int writeLength; - - protected Object writeAttachment; - - protected CompletionHandler writeCompletionHandler; - - protected boolean writePending; - - protected SelectionKey writeKey; - - public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, - final int bufferCapacity, ObjectPool bufferPool, SSLContext sslContext, AtomicLong livingCounter, AtomicLong closedCounter) { - super(client, bufferCapacity, bufferPool, sslContext, livingCounter, closedCounter); - this.ioGroup = ioGroup; - this.ioThread = ioThread; - this.connectThread = connectThread; - } - - public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, - final int bufferCapacity, Supplier bufferSupplier, Consumer bufferConsumer, SSLContext sslContext, AtomicLong livingCounter, AtomicLong closedCounter) { - super(client, bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter); - this.ioGroup = ioGroup; - this.ioThread = ioThread; - this.connectThread = connectThread; - } - - @Override - public SocketAddress getRemoteAddress() { - return remoteAddress; - } - - @Override - public void setReadTimeoutSeconds(int readTimeoutSeconds) { - this.readTimeoutSeconds = readTimeoutSeconds; - } - - @Override - public void setWriteTimeoutSeconds(int writeTimeoutSeconds) { - this.writeTimeoutSeconds = writeTimeoutSeconds; - } - - @Override - public int getReadTimeoutSeconds() { - return this.readTimeoutSeconds; - } - - @Override - public int getWriteTimeoutSeconds() { - return this.writeTimeoutSeconds; - } - - @Override - protected void continueRead() { - if (readKey == null) { - ioThread.register(selector -> { - try { - readKey = implRegister(selector, SelectionKey.OP_READ); - readKey.attach(this); - } catch (ClosedChannelException e) { - handleRead(0, e); - } - }); - } else { - ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ); - } - } - - @Override - public void read(CompletionHandler handler) { - Objects.requireNonNull(handler); - if (!this.isConnected()) { - handler.failed(new NotYetConnectedException(), null); - return; - } - if (this.readPending) { - handler.failed(new ReadPendingException(), null); - return; - } - this.readPending = true; - if (this.readTimeoutSeconds > 0) { - AsyncNioCompletionHandler newhandler = this.readTimeoutCompletionHandler; - newhandler.handler(handler, this.readByteBuffer); // new AsyncNioCompletionHandler(handler, this.readByteBuffer); - this.readCompletionHandler = newhandler; - newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS); - } else { - this.readCompletionHandler = handler; - } - doRead(currReadInvoker < MAX_INVOKER_ONSTACK || this.ioThread.inCurrThread()); //同一线程中Selector.wakeup无效 - } - - @Override - public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, Consumer bodyCallback, Object bodyAttachment, CompletionHandler handler) { - Objects.requireNonNull(headerContent); - Objects.requireNonNull(handler); - if (!this.isConnected()) { - handler.failed(new NotYetConnectedException(), null); - return; - } - if (this.writePending) { - handler.failed(new WritePendingException(), null); - return; - } - this.writePending = true; - this.writeByteTuple1Array = headerContent; - this.writeByteTuple1Offset = headerOffset; - this.writeByteTuple1Length = headerLength; - this.writeByteTuple2Array = bodyContent; - this.writeByteTuple2Offset = bodyOffset; - this.writeByteTuple2Length = bodyLength; - this.writeByteTuple2Callback = bodyCallback; - this.writeByteTuple2Attachment = bodyAttachment; - this.writeAttachment = null; - if (this.writeTimeoutSeconds > 0) { - AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; - newhandler.handler(handler, null); // new AsyncNioCompletionHandler(handler, null); - this.writeCompletionHandler = newhandler; - newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); - } else { - AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; - newhandler.handler(handler, null); // new AsyncNioCompletionHandler(handler, null); - this.writeCompletionHandler = newhandler; - } - doWrite(true); //如果不是true,则bodyCallback的执行可能会切换线程 - } - - @Override - public void write(ByteBuffer src, A attachment, CompletionHandler handler) { - Objects.requireNonNull(src); - Objects.requireNonNull(handler); - if (!this.isConnected()) { - handler.failed(new NotYetConnectedException(), attachment); - return; - } - if (this.writePending) { - handler.failed(new WritePendingException(), attachment); - return; - } - this.writePending = true; - this.writeByteBuffer = src; - this.writeAttachment = attachment; - if (this.writeTimeoutSeconds > 0) { - AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; - newhandler.handler(handler, attachment); // new AsyncNioCompletionHandler(handler, attachment); - this.writeCompletionHandler = newhandler; - newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); - } else { - this.writeCompletionHandler = (CompletionHandler) handler; - } - doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null - } - - @Override - public void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler handler) { - Objects.requireNonNull(srcs); - Objects.requireNonNull(handler); - if (!this.isConnected()) { - handler.failed(new NotYetConnectedException(), attachment); - return; - } - if (this.writePending) { - handler.failed(new WritePendingException(), attachment); - return; - } - this.writePending = true; - this.writeByteBuffers = srcs; - this.writeOffset = offset; - this.writeLength = length; - this.writeAttachment = attachment; - if (this.writeTimeoutSeconds > 0) { - AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; - newhandler.handler(handler, attachment); // new AsyncNioCompletionHandler(handler, attachment); - this.writeCompletionHandler = newhandler; - newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); - } else { - this.writeCompletionHandler = (CompletionHandler) handler; - } - doWrite(true || !client || currWriteInvoker < MAX_INVOKER_ONSTACK); // this.ioThread.inCurrThread() // !client && ioThread.workExecutor == null - } - - public void doRead(boolean direct) { - try { - this.readtime = System.currentTimeMillis(); - int readCount = 0; - if (direct) { - currReadInvoker++; - if (this.readByteBuffer == null) { - this.readByteBuffer = pollReadBuffer(); - if (this.readTimeoutSeconds > 0) { - this.readTimeoutCompletionHandler.attachment(this.readByteBuffer); - } - } - readCount = implRead(readByteBuffer); - } - - if (readCount != 0) { - handleRead(readCount, null); - } else if (readKey == null) { - ioThread.register(selector -> { - try { - readKey = implRegister(selector, SelectionKey.OP_READ); - readKey.attach(this); - } catch (ClosedChannelException e) { - handleRead(0, e); - } - }); - } else { - if (client || !direct) ioGroup.interestOpsOr(ioThread, readKey, SelectionKey.OP_READ); - } - } catch (Exception e) { - handleRead(0, e); - } - } - - public void doWrite(boolean direct) { - try { - this.writetime = System.currentTimeMillis(); - final boolean invokeDirect = direct; - int totalCount = 0; - boolean hasRemain = true; - if (invokeDirect) currWriteInvoker++; - while (invokeDirect && hasRemain) { //必须要将buffer写完为止 - if (writeByteTuple1Array != null) { - final ByteBuffer buffer = pollWriteBuffer(); - if (buffer.remaining() >= writeByteTuple1Length + writeByteTuple2Length) { - buffer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length); - if (writeByteTuple2Length > 0) { - buffer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length); - if (writeByteTuple2Callback != null) writeByteTuple2Callback.accept(writeByteTuple2Attachment); - } - buffer.flip(); - writeByteBuffer = buffer; - writeByteTuple1Array = null; - writeByteTuple1Offset = 0; - writeByteTuple1Length = 0; - writeByteTuple2Array = null; - writeByteTuple2Offset = 0; - writeByteTuple2Length = 0; - writeByteTuple2Callback = null; - writeByteTuple2Attachment = null; - } else { - ByteBufferWriter writer = ByteBufferWriter.create(getBufferSupplier(), buffer); - writer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length); - if (writeByteTuple2Length > 0) { - writer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length); - if (writeByteTuple2Callback != null) writeByteTuple2Callback.accept(writeByteTuple2Attachment); - } - final ByteBuffer[] buffers = writer.toBuffers(); - writeByteBuffers = buffers; - writeOffset = 0; - writeLength = buffers.length; - writeByteTuple1Array = null; - writeByteTuple1Offset = 0; - writeByteTuple1Length = 0; - writeByteTuple2Array = null; - writeByteTuple2Offset = 0; - writeByteTuple2Length = 0; - writeByteTuple2Callback = null; - writeByteTuple2Attachment = null; - } - if (this.writeCompletionHandler == this.writeTimeoutCompletionHandler) { - if (writeByteBuffer == null) { - this.writeTimeoutCompletionHandler.buffers(writeByteBuffers); - } else { - this.writeTimeoutCompletionHandler.buffer(writeByteBuffer); - } - } - } - int writeCount; - if (writeByteBuffer != null) { - writeCount = implWrite(writeByteBuffer); - hasRemain = writeByteBuffer.hasRemaining(); - } else { - writeCount = implWrite(writeByteBuffers, writeOffset, writeLength); - boolean remain = false; - for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) { - if (writeByteBuffers[i].hasRemaining()) { - remain = true; - break; - } - } - hasRemain = remain; - } - if (writeCount <= 0) { - if (totalCount == 0) totalCount = writeCount; - break; - } else { - totalCount += writeCount; - } - if (!hasRemain) break; - } - - if (totalCount != 0 || !hasRemain) { - handleWrite(totalCount, null); - } else if (writeKey == null) { - ioThread.register(selector -> { - try { - writeKey = implRegister(selector, SelectionKey.OP_WRITE); - writeKey.attach(this); - } catch (ClosedChannelException e) { - handleWrite(0, e); - } - }); - } else { - ioGroup.interestOpsOr(ioThread, writeKey, SelectionKey.OP_WRITE); - } - } catch (IOException e) { - handleWrite(0, e); - } - } - - protected void handleConnect(Throwable t) { - if (connectKey != null) { - connectKey.cancel(); - connectKey = null; - } - CompletionHandler handler = this.connectCompletionHandler; - Object attach = this.connectAttachment; - - this.connectCompletionHandler = null; - this.connectAttachment = null; - this.connectPending = false;//必须放最后 - - if (handler != null) { - if (t == null) { - handler.completed(null, attach); - } else { - handler.failed(t, attach); - } - } - } - - protected void handleRead(final int totalCount, Throwable t) { - CompletionHandler handler = this.readCompletionHandler; - ByteBuffer attach = this.readByteBuffer; - //清空读参数 - this.readCompletionHandler = null; - this.readByteBuffer = null; - this.readPending = false; //必须放最后 - - if (handler == null) { - if (t == null) { - protocolCodec.completed(totalCount, attach); - } else { - protocolCodec.failed(t, attach); - } - } else { - if (t == null) { - handler.completed(totalCount, attach); - } else { - handler.failed(t, attach); - } - } - } - - protected void handleWrite(final int totalCount, Throwable t) { - CompletionHandler handler = this.writeCompletionHandler; - Object attach = this.writeAttachment; - //清空写参数 - this.writeCompletionHandler = null; - this.writeAttachment = null; - this.writeByteBuffer = null; - this.writeByteBuffers = null; - this.writeOffset = 0; - this.writeLength = 0; - this.writePending = false; //必须放最后 - - if (t == null) { - handler.completed(totalCount, attach); - } else { - handler.failed(t, attach); - } - } - - protected abstract SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException; - - protected abstract int implRead(ByteBuffer dst) throws IOException; - - protected abstract int implWrite(ByteBuffer src) throws IOException; - - protected abstract int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException; - - public abstract boolean isConnected(); - - public abstract void doConnect(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.*; +import javax.net.ssl.SSLContext; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + */ +abstract class AsyncNioConnection extends AsyncConnection { + + protected static final int MAX_INVOKER_ONSTACK = Integer.getInteger("redkale.net.invoker.max.onstack", 16); + + final AsyncIOThread connectThread; + + protected SocketAddress remoteAddress; + + //-------------------------------- 连操作 -------------------------------------- + protected Object connectAttachment; + + protected CompletionHandler connectCompletionHandler; + + protected boolean connectPending; + + protected SelectionKey connectKey; + + //-------------------------------- 读操作 -------------------------------------- + protected final AsyncNioCompletionHandler readTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this); + + protected int readTimeoutSeconds; + + int currReadInvoker; + + protected ByteBuffer readByteBuffer; + + protected CompletionHandler readCompletionHandler; + + protected boolean readPending; + + protected SelectionKey readKey; + + //-------------------------------- 写操作 -------------------------------------- + protected final AsyncNioCompletionHandler writeTimeoutCompletionHandler = new AsyncNioCompletionHandler<>(this); + + protected int writeTimeoutSeconds; + + int currWriteInvoker; + + protected byte[] writeByteTuple1Array; + + protected int writeByteTuple1Offset; + + protected int writeByteTuple1Length; + + protected byte[] writeByteTuple2Array; + + protected int writeByteTuple2Offset; + + protected int writeByteTuple2Length; + + protected Consumer writeByteTuple2Callback; + + protected Object writeByteTuple2Attachment; + + //写操作, 二选一,要么writeByteBuffer有值,要么writeByteBuffers、writeOffset、writeLength有值 + protected ByteBuffer writeByteBuffer; + + protected ByteBuffer[] writeByteBuffers; + + protected int writeOffset; + + protected int writeLength; + + protected int writeTotal; + + protected Object writeAttachment; + + protected CompletionHandler writeCompletionHandler; + + protected boolean writePending; + + protected SelectionKey writeKey; + + public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, + final int bufferCapacity, ObjectPool bufferPool, SSLBuilder sslBuilder, SSLContext sslContext, LongAdder livingCounter, LongAdder closedCounter) { + super(client, ioGroup, ioThread, bufferCapacity, bufferPool, sslBuilder, sslContext, livingCounter, closedCounter); + this.connectThread = connectThread; + } + + public AsyncNioConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, + final int bufferCapacity, Supplier bufferSupplier, Consumer bufferConsumer, SSLBuilder sslBuilder, SSLContext sslContext, LongAdder livingCounter, LongAdder closedCounter) { + super(client, ioGroup, ioThread, bufferCapacity, bufferSupplier, bufferConsumer, sslBuilder, sslContext, livingCounter, closedCounter); + this.connectThread = connectThread; + } + + @Override + public SocketAddress getRemoteAddress() { + return remoteAddress; + } + + @Override + public void setReadTimeoutSeconds(int readTimeoutSeconds) { + this.readTimeoutSeconds = readTimeoutSeconds; + } + + @Override + public void setWriteTimeoutSeconds(int writeTimeoutSeconds) { + this.writeTimeoutSeconds = writeTimeoutSeconds; + } + + @Override + public int getReadTimeoutSeconds() { + return this.readTimeoutSeconds; + } + + @Override + public int getWriteTimeoutSeconds() { + return this.writeTimeoutSeconds; + } + + @Override + protected void startHandshake(final Consumer callback) { + ((AsyncIOThread) ioThread).register(t -> super.startHandshake(callback)); + } + + @Override + protected void startRead(CompletionHandler handler) { + currReadInvoker = MAX_INVOKER_ONSTACK; + read(handler); + } + + @Override + public void readImpl(CompletionHandler handler) { + Objects.requireNonNull(handler); + if (!this.isConnected()) { + handler.failed(new NotYetConnectedException(), null); + return; + } + if (this.readPending) { + handler.failed(new ReadPendingException(), null); + return; + } + this.readPending = true; + if (this.readTimeoutSeconds > 0) { + AsyncNioCompletionHandler newhandler = this.readTimeoutCompletionHandler; + newhandler.handler(handler, this.readByteBuffer); // new AsyncNioCompletionHandler(handler, this.readByteBuffer); + this.readCompletionHandler = newhandler; + newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.readTimeoutSeconds, TimeUnit.SECONDS); + } else { + this.readCompletionHandler = handler; + } + if (client) { + doRead(this.ioThread.inCurrThread()); + } else { + doRead(currReadInvoker < MAX_INVOKER_ONSTACK || this.ioThread.inCurrThread()); //同一线程中Selector.wakeup无效 + } + } + + @Override + public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, Consumer bodyCallback, Object bodyAttachment, CompletionHandler handler) { + if (sslEngine != null) { + super.write(headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength, bodyCallback, bodyAttachment, handler); + return; + } + Objects.requireNonNull(headerContent); + Objects.requireNonNull(handler); + if (!this.isConnected()) { + handler.failed(new NotYetConnectedException(), null); + return; + } + if (this.writePending) { + handler.failed(new WritePendingException(), null); + return; + } + this.writePending = true; + this.writeByteTuple1Array = headerContent; + this.writeByteTuple1Offset = headerOffset; + this.writeByteTuple1Length = headerLength; + this.writeByteTuple2Array = bodyContent; + this.writeByteTuple2Offset = bodyOffset; + this.writeByteTuple2Length = bodyLength; + this.writeByteTuple2Callback = bodyCallback; + this.writeByteTuple2Attachment = bodyAttachment; + this.writeAttachment = null; + if (this.writeTimeoutSeconds > 0) { + AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; + newhandler.handler(handler, null); // new AsyncNioCompletionHandler(handler, null); + this.writeCompletionHandler = newhandler; + newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); + } else { + AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; + newhandler.handler(handler, null); // new AsyncNioCompletionHandler(handler, null); + this.writeCompletionHandler = newhandler; + } + doWrite(true); //如果不是true,则bodyCallback的执行可能会切换线程 + } + + @Override + public void writeImpl(ByteBuffer src, A attachment, CompletionHandler handler) { + Objects.requireNonNull(src); + Objects.requireNonNull(handler); + if (!this.isConnected()) { + handler.failed(new NotYetConnectedException(), attachment); + return; + } + if (this.writePending) { + handler.failed(new WritePendingException(), attachment); + return; + } + this.writePending = true; + this.writeByteBuffer = src; + this.writeAttachment = attachment; + if (this.writeTimeoutSeconds > 0) { + AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; + newhandler.handler(handler, attachment); // new AsyncNioCompletionHandler(handler, attachment); + this.writeCompletionHandler = newhandler; + newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); + } else { + this.writeCompletionHandler = (CompletionHandler) handler; + } + doWrite(true); // || !client || currWriteInvoker < MAX_INVOKER_ONSTACK // !client && ioThread.workExecutor == null + } + + @Override + public void writeImpl(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler handler) { + Objects.requireNonNull(srcs); + Objects.requireNonNull(handler); + if (!this.isConnected()) { + handler.failed(new NotYetConnectedException(), attachment); + return; + } + if (this.writePending) { + handler.failed(new WritePendingException(), attachment); + return; + } + this.writePending = true; + this.writeByteBuffers = srcs; + this.writeOffset = offset; + this.writeLength = length; + this.writeAttachment = attachment; + if (this.writeTimeoutSeconds > 0) { + AsyncNioCompletionHandler newhandler = this.writeTimeoutCompletionHandler; + newhandler.handler(handler, attachment); // new AsyncNioCompletionHandler(handler, attachment); + this.writeCompletionHandler = newhandler; + newhandler.timeoutFuture = ioGroup.scheduleTimeout(newhandler, this.writeTimeoutSeconds, TimeUnit.SECONDS); + } else { + this.writeCompletionHandler = (CompletionHandler) handler; + } + doWrite(true); // || !client || currWriteInvoker < MAX_INVOKER_ONSTACK // !client && ioThread.workExecutor == null + } + + public void doRead(boolean direct) { + try { + this.readtime = System.currentTimeMillis(); + int readCount = 0; + if (direct) { + currReadInvoker++; + if (this.readByteBuffer == null) { + this.readByteBuffer = sslEngine == null ? pollReadBuffer() : pollReadSSLBuffer(); + if (this.readTimeoutSeconds > 0) { + this.readTimeoutCompletionHandler.attachment(this.readByteBuffer); + } + } + readCount = implRead(readByteBuffer); + } + + if (readCount != 0) { + handleRead(readCount, null); + } else if (readKey == null) { + ((AsyncIOThread) ioThread).register(selector -> { + try { + readKey = implRegister(selector, SelectionKey.OP_READ); + readKey.attach(this); + } catch (ClosedChannelException e) { + handleRead(0, e); + } + }); + } else { + ((AsyncIOGroup) ioGroup).interestOpsOr((AsyncIOThread) ioThread, readKey, SelectionKey.OP_READ); + } + } catch (Exception e) { + handleRead(0, e); + } + } + + public void doWrite(boolean direct) { + try { + this.writetime = System.currentTimeMillis(); + final boolean invokeDirect = direct; + int totalCount = 0; + boolean hasRemain = true; + boolean writeOver = true; + if (invokeDirect) currWriteInvoker++; + while (invokeDirect && hasRemain) { //必须要将buffer写完为止 + if (writeByteTuple1Array != null) { + final ByteBuffer buffer = pollWriteBuffer(); + if (buffer.remaining() >= writeByteTuple1Length + writeByteTuple2Length) { + buffer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length); + if (writeByteTuple2Length > 0) { + buffer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length); + if (writeByteTuple2Callback != null) writeByteTuple2Callback.accept(writeByteTuple2Attachment); + } + buffer.flip(); + writeByteBuffer = buffer; + writeByteTuple1Array = null; + writeByteTuple1Offset = 0; + writeByteTuple1Length = 0; + writeByteTuple2Array = null; + writeByteTuple2Offset = 0; + writeByteTuple2Length = 0; + writeByteTuple2Callback = null; + writeByteTuple2Attachment = null; + } else { + ByteBufferWriter writer = ByteBufferWriter.create(getBufferSupplier(), buffer); + writer.put(writeByteTuple1Array, writeByteTuple1Offset, writeByteTuple1Length); + if (writeByteTuple2Length > 0) { + writer.put(writeByteTuple2Array, writeByteTuple2Offset, writeByteTuple2Length); + if (writeByteTuple2Callback != null) writeByteTuple2Callback.accept(writeByteTuple2Attachment); + } + final ByteBuffer[] buffers = writer.toBuffers(); + writeByteBuffers = buffers; + writeOffset = 0; + writeLength = buffers.length; + writeByteTuple1Array = null; + writeByteTuple1Offset = 0; + writeByteTuple1Length = 0; + writeByteTuple2Array = null; + writeByteTuple2Offset = 0; + writeByteTuple2Length = 0; + writeByteTuple2Callback = null; + writeByteTuple2Attachment = null; + } + if (this.writeCompletionHandler == this.writeTimeoutCompletionHandler) { + if (writeByteBuffer == null) { + this.writeTimeoutCompletionHandler.buffers(writeByteBuffers); + } else { + this.writeTimeoutCompletionHandler.buffer(writeByteBuffer); + } + } + } + int writeCount; + if (writeByteBuffer != null) { + writeCount = implWrite(writeByteBuffer); + hasRemain = writeByteBuffer.hasRemaining(); + } else { + writeCount = implWrite(writeByteBuffers, writeOffset, writeLength); + boolean remain = false; + for (int i = writeByteBuffers.length - 1; i >= writeOffset; i--) { + if (writeByteBuffers[i].hasRemaining()) { + remain = true; + break; + } + } + hasRemain = remain; + } + if (writeCount == 0) { + if (hasRemain) { + writeOver = false; + writeTotal = totalCount; + } + break; + } else if (writeCount < 0) { + if (totalCount == 0) totalCount = writeCount; + break; + } else { + totalCount += writeCount; + } + if (!hasRemain) break; + } + + if (writeOver && (totalCount != 0 || !hasRemain)) { + handleWrite(writeTotal + totalCount, null); + } else if (writeKey == null) { + ((AsyncIOThread) ioThread).register(selector -> { + try { + writeKey = implRegister(selector, SelectionKey.OP_WRITE); + writeKey.attach(this); + } catch (ClosedChannelException e) { + handleWrite(0, e); + } + }); + } else { + ((AsyncIOGroup) ioGroup).interestOpsOr((AsyncIOThread) ioThread, writeKey, SelectionKey.OP_WRITE); + } + } catch (IOException e) { + handleWrite(0, e); + } + } + + protected void handleConnect(Throwable t) { + if (connectKey != null) { + connectKey.cancel(); + connectKey = null; + } + CompletionHandler handler = this.connectCompletionHandler; + Object attach = this.connectAttachment; + + this.connectCompletionHandler = null; + this.connectAttachment = null; + this.connectPending = false;//必须放最后 + + if (handler != null) { + if (!client || inCurrThread()) { //client模式下必须保证read、write在ioThread内运行 + if (t == null) { + handler.completed(null, attach); + } else { + handler.failed(t, attach); + } + } else { + ioThread.execute(() -> { + if (t == null) { + handler.completed(null, attach); + } else { + handler.failed(t, attach); + } + }); + } + } + } + + protected void handleRead(final int totalCount, Throwable t) { + CompletionHandler handler = this.readCompletionHandler; + ByteBuffer attach = this.readByteBuffer; + //清空读参数 + this.readCompletionHandler = null; + this.readByteBuffer = null; + this.readPending = false; //必须放最后 + + if (handler == null) { + if (t == null) { + protocolCodec.completed(totalCount, attach); + } else { + protocolCodec.failed(t, attach); + } + } else { + if (t == null) { + handler.completed(totalCount, attach); + } else { + handler.failed(t, attach); + } + } + } + + protected void handleWrite(final int totalCount, Throwable t) { + CompletionHandler handler = this.writeCompletionHandler; + Object attach = this.writeAttachment; + //清空写参数 + this.writeCompletionHandler = null; + this.writeAttachment = null; + this.writeByteBuffer = null; + this.writeByteBuffers = null; + this.writeOffset = 0; + this.writeLength = 0; + this.writeTotal = 0; + this.writePending = false; //必须放最后 + + if (t == null) { + handler.completed(totalCount, attach); + } else { + handler.failed(t, attach); + } + } + + @Deprecated //@since 2.5.0 + protected abstract ReadableByteChannel readableByteChannel(); + + @Deprecated //@since 2.5.0 + protected abstract WritableByteChannel writableByteChannel(); + + protected InputStream newInputStream() { + final ReadableByteChannel reader = readableByteChannel(); + return new InputStream() { + + ByteBuffer bb; + + int count; + + @Override + public synchronized int read() throws IOException { + if (bb == null || !bb.hasRemaining()) { + int r = readBuffer(); + if (r < 1) return -1; + } + return bb.get() & 0xff; + } + + @Override + public synchronized int read(byte b[], int off, int len) throws IOException { + if (b == null) { + throw new NullPointerException(); + } else if (off < 0 || len < 0 || len > b.length - off) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return 0; + } + if (bb == null || !bb.hasRemaining()) { + int r = readBuffer(); + if (r < 1) return -1; + } + int size = Math.min(b.length, Math.min(len, bb.remaining())); + bb.get(b, off, size); + return size; + } + + @Override + public void close() throws IOException { + if (bb != null) { + offerBuffer(bb); + bb = null; + } + reader.close(); + } + + @Override + public int available() throws IOException { + if (bb == null || !bb.hasRemaining()) return 0; + return bb.remaining(); + } + + private int readBuffer() throws IOException { + if (bb == null) { + bb = pollReadBuffer(); + } else { + bb.clear(); + } + try { + int size = reader.read(bb); + bb.flip(); + return size; + } catch (IOException ioe) { + throw ioe; + } catch (Exception e) { + throw new IOException(e); + } + + } + + }; + } + + protected abstract SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException; + + protected abstract int implRead(ByteBuffer dst) throws IOException; + + protected abstract int implWrite(ByteBuffer src) throws IOException; + + protected abstract int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException; + + public abstract boolean isConnected(); + + public abstract void doConnect(); +} diff --git a/src/org/redkale/net/AsyncNioTcpConnection.java b/src/main/java/org/redkale/net/AsyncNioTcpConnection.java similarity index 62% rename from src/org/redkale/net/AsyncNioTcpConnection.java rename to src/main/java/org/redkale/net/AsyncNioTcpConnection.java index 5a6887481..860ff96e2 100644 --- a/src/org/redkale/net/AsyncNioTcpConnection.java +++ b/src/main/java/org/redkale/net/AsyncNioTcpConnection.java @@ -1,273 +1,292 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.*; -import javax.net.ssl.SSLContext; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -class AsyncNioTcpConnection extends AsyncNioConnection { - - private final SocketChannel channel; - - public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, - SocketChannel ch, SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) { - super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter); - this.channel = ch; - SocketAddress addr = addr0; - if (addr == null) { - try { - addr = ch.getRemoteAddress(); - } catch (Exception e) { - //do nothing - } - } - this.remoteAddress = addr; - ioThread.connCounter.incrementAndGet(); - } - - public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, Supplier bufferSupplier, Consumer bufferConsumer, - SocketChannel ch, SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) { - super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter); - this.channel = ch; - SocketAddress addr = addr0; - if (addr == null) { - try { - addr = ch.getRemoteAddress(); - } catch (Exception e) { - //do nothing - } - } - this.remoteAddress = addr; - } - - @Override - public boolean isOpen() { - return this.channel.isOpen(); - } - - @Override - public boolean isTCP() { - return true; - } - - @Override - public boolean shutdownInput() { - try { - this.channel.shutdownInput(); - return true; - } catch (IOException e) { - return false; - } - } - - @Override - public boolean shutdownOutput() { - try { - this.channel.shutdownOutput(); - return true; - } catch (IOException e) { - return false; - } - } - - @Override - public boolean setOption(SocketOption name, T value) { - try { - this.channel.setOption(name, value); - return true; - } catch (IOException e) { - return false; - } - } - - @Override - public Set> supportedOptions() { - return this.channel.supportedOptions(); - } - - @Override - public SocketAddress getLocalAddress() { - try { - return channel.getLocalAddress(); - } catch (IOException e) { - return null; - } - } - - @Override - public ReadableByteChannel readableByteChannel() { - return this.channel; - } - - @Override - public WritableByteChannel writableByteChannel() { - return this.channel; - } - - @Override - public boolean isConnected() { - return this.channel.isConnected(); - } - - @Override - protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException { - return this.channel.register(sel, ops); - } - - @Override - protected int implRead(ByteBuffer dst) throws IOException { - return this.channel.read(dst); - } - - @Override - protected int implWrite(ByteBuffer src) throws IOException { - return this.channel.write(src); - } - - @Override - protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException { - return (int) this.channel.write(srcs, offset, length); - } - - public void connect(SocketAddress remote, A attachment, CompletionHandler handler) { - if (channel.isConnected()) { - throw new AlreadyConnectedException(); - } - if (connectPending) { - throw new ConnectionPendingException(); - } - connectPending = true; - this.connectAttachment = attachment; - this.connectCompletionHandler = (CompletionHandler) handler; - this.remoteAddress = remote; - doConnect(); - } - - @Override - public void doConnect() { - try { - boolean connected = channel.isConnectionPending(); - if (connected || channel.connect(remoteAddress)) { - connected = channel.finishConnect(); - } - if (connected) { - handleConnect(null); - } else if (connectKey == null) { - connectThread.register(selector -> { - try { - connectKey = channel.register(selector, SelectionKey.OP_CONNECT); - connectKey.attach(this); - } catch (ClosedChannelException e) { - handleConnect(e); - } - }); - } else { - handleConnect(new IOException()); - } - } catch (IOException e) { - handleConnect(e); - } - } - - @Override - public final void close() throws IOException { - super.close(); - ioThread.connCounter.decrementAndGet(); - channel.shutdownInput(); - channel.shutdownOutput(); - channel.close(); - if (this.connectKey != null) this.connectKey.cancel(); - if (this.readKey != null) this.readKey.cancel(); - if (this.writeKey != null) this.writeKey.cancel(); - } - - @Override - public InputStream newInputStream() { - - return new InputStream() { - - ByteBuffer bb; - - int count; - - @Override - public synchronized int read() throws IOException { - if (bb == null || !bb.hasRemaining()) { - int r = readBuffer(); - if (r < 1) return -1; - } - return bb.get() & 0xff; - } - - @Override - public synchronized int read(byte b[], int off, int len) throws IOException { - if (b == null) { - throw new NullPointerException(); - } else if (off < 0 || len < 0 || len > b.length - off) { - throw new IndexOutOfBoundsException(); - } else if (len == 0) { - return 0; - } - if (bb == null || !bb.hasRemaining()) { - int r = readBuffer(); - if (r < 1) return -1; - } - int size = Math.min(b.length, Math.min(len, bb.remaining())); - bb.get(b, off, size); - return size; - } - - @Override - public void close() throws IOException { - if (bb != null) { - offerBuffer(bb); - bb = null; - } - channel.close(); - } - - @Override - public int available() throws IOException { - if (bb == null || !bb.hasRemaining()) return 0; - return bb.remaining(); - } - - private int readBuffer() throws IOException { - if (bb == null) { - bb = pollReadBuffer(); - } else { - bb.clear(); - } - try { - int size = channel.read(bb); - bb.flip(); - return size; - } catch (IOException ioe) { - throw ioe; - } catch (Exception e) { - throw new IOException(e); - } - - } - - }; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.*; +import javax.net.ssl.SSLContext; +import org.redkale.util.ByteBufferReader; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +class AsyncNioTcpConnection extends AsyncNioConnection { + + private final SocketChannel channel; + + public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, + SocketChannel ch, SSLBuilder sslBuilder, SSLContext sslContext, final SocketAddress addr0, LongAdder livingCounter, LongAdder closedCounter) { + super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslBuilder, sslContext, livingCounter, closedCounter); + this.channel = ch; + SocketAddress addr = addr0; + if (addr == null) { + try { + addr = ch.getRemoteAddress(); + } catch (Exception e) { + //do nothing + } + } + this.remoteAddress = addr; + ioThread.connCounter.incrementAndGet(); + } + + public AsyncNioTcpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, Supplier bufferSupplier, Consumer bufferConsumer, + SocketChannel ch, SSLBuilder sslBuilder, SSLContext sslContext, final SocketAddress addr0, LongAdder livingCounter, LongAdder closedCounter) { + super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslBuilder, sslContext, livingCounter, closedCounter); + this.channel = ch; + SocketAddress addr = addr0; + if (addr == null) { + try { + addr = ch.getRemoteAddress(); + } catch (Exception e) { + //do nothing + } + } + this.remoteAddress = addr; + } + + @Override + public boolean isOpen() { + return this.channel.isOpen(); + } + + @Override + public boolean isTCP() { + return true; + } + + @Override + public boolean shutdownInput() { + try { + this.channel.shutdownInput(); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public boolean shutdownOutput() { + try { + this.channel.shutdownOutput(); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public boolean setOption(SocketOption name, T value) { + try { + this.channel.setOption(name, value); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public Set> supportedOptions() { + return this.channel.supportedOptions(); + } + + @Override + public SocketAddress getLocalAddress() { + try { + return channel.getLocalAddress(); + } catch (IOException e) { + return null; + } + } + + public ReadableByteChannel readableByteChannel() { + if (this.sslEngine == null) return this.channel; + + AsyncConnection self = this; + return new ReadableByteChannel() { + //ssl解密后的半包数据 + ByteBuffer halfBuffer; + + @Override + public int read(final ByteBuffer bb) throws IOException { + if (halfBuffer != null) { + int pos = bb.position(); + while (bb.hasRemaining() && halfBuffer.hasRemaining()) { + bb.put(halfBuffer.get()); + } + if (!halfBuffer.hasRemaining()) { + offerBuffer(halfBuffer); + halfBuffer = null; + } + return bb.position() - pos; + } + ByteBuffer netBuffer = pollReadSSLBuffer(); + channel.read(netBuffer); + netBuffer.flip(); + if (netBuffer.hasRemaining()) { + ByteBuffer appBuffer = sslUnwrap(false, netBuffer); + if (appBuffer == null) return -1; //CLOSED,netBuffer已被回收 + + appBuffer.flip(); + int pos = bb.position(); + while (bb.hasRemaining() && appBuffer.hasRemaining()) bb.put(appBuffer.get()); + if (appBuffer.hasRemaining()) { + halfBuffer = appBuffer; + } else { + offerBuffer(appBuffer); + } + return bb.position() - pos; + } else { + offerBuffer(netBuffer); + return 0; + } + } + + @Override + public boolean isOpen() { + return self.isOpen(); + } + + @Override + public void close() throws IOException { + if (halfBuffer != null) { + offerBuffer(halfBuffer); + halfBuffer = null; + } + self.close(); + } + + }; + } + + @Override + public WritableByteChannel writableByteChannel() { + if (this.sslEngine == null) return this.channel; + + AsyncConnection self = this; + return new WritableByteChannel() { + + @Override + public int write(final ByteBuffer src) throws IOException { + int remain = src.remaining(); + ByteBuffer[] netBuffers = sslWrap(false, src); + int len = remain - src.remaining(); + if (netBuffers.length == 1) { + while (netBuffers[0].hasRemaining()) { + channel.write(netBuffers[0]); + } + } else { + while (ByteBufferReader.hasRemaining(netBuffers)) { + channel.write(netBuffers); + } + } + offerBuffer(netBuffers); + return len; + } + + @Override + public boolean isOpen() { + return self.isOpen(); + } + + @Override + public void close() throws IOException { + self.close(); + } + }; + } + + @Override + public boolean isConnected() { + return this.channel.isConnected(); + } + + @Override + protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException { + return this.channel.register(sel, ops); + } + + @Override + protected int implRead(ByteBuffer dst) throws IOException { + return this.channel.read(dst); + } + + @Override + protected int implWrite(ByteBuffer src) throws IOException { + return this.channel.write(src); + } + + @Override + protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException { + return (int) this.channel.write(srcs, offset, length); + } + + public void connect(SocketAddress remote, A attachment, CompletionHandler handler) { + if (channel.isConnected()) { + throw new AlreadyConnectedException(); + } + if (connectPending) { + throw new ConnectionPendingException(); + } + connectPending = true; + this.connectAttachment = attachment; + this.connectCompletionHandler = (CompletionHandler) handler; + this.remoteAddress = remote; + doConnect(); + } + + @Override + public void doConnect() { + try { + boolean connected = channel.isConnectionPending(); + if (connected || channel.connect(remoteAddress)) { + connected = channel.finishConnect(); + } + if (connected) { + handleConnect(null); + } else if (connectKey == null) { + connectThread.register(selector -> { + try { + connectKey = channel.register(selector, SelectionKey.OP_CONNECT); + connectKey.attach(this); + } catch (ClosedChannelException e) { + handleConnect(e); + } + }); + } else { + handleConnect(new IOException()); + } + } catch (ConnectException ex) { + handleConnect(new IOException(remoteAddress + " connect error", ex)); + } catch (IOException e) { + handleConnect(e); + } + } + + @Override + public final void close() throws IOException { + super.close(); + ((AsyncIOThread) ioThread).connCounter.decrementAndGet(); + channel.shutdownInput(); + channel.shutdownOutput(); + channel.close(); + if (this.connectKey != null) this.connectKey.cancel(); + if (this.readKey != null) this.readKey.cancel(); + if (this.writeKey != null) this.writeKey.cancel(); + } + +} diff --git a/src/org/redkale/net/AsyncNioTcpProtocolServer.java b/src/main/java/org/redkale/net/AsyncNioTcpProtocolServer.java similarity index 73% rename from src/org/redkale/net/AsyncNioTcpProtocolServer.java rename to src/main/java/org/redkale/net/AsyncNioTcpProtocolServer.java index 7610d892b..4331d7e55 100644 --- a/src/org/redkale/net/AsyncNioTcpProtocolServer.java +++ b/src/main/java/org/redkale/net/AsyncNioTcpProtocolServer.java @@ -1,181 +1,209 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.*; -import org.redkale.boot.Application; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -class AsyncNioTcpProtocolServer extends ProtocolServer { - - private ServerSocketChannel serverChannel; - - private Selector selector; - - private AsyncIOGroup ioGroup; - - private Thread acceptThread; - - private boolean closed; - - private Supplier responseSupplier; - - private Consumer responseConsumer; - - public AsyncNioTcpProtocolServer(Context context) { - super(context); - } - - @Override - public void open(AnyValue config) throws IOException { - this.serverChannel = ServerSocketChannel.open(); - this.serverChannel.configureBlocking(false); - this.selector = Selector.open(); - final Set> options = this.serverChannel.supportedOptions(); - if (options.contains(StandardSocketOptions.TCP_NODELAY)) { - this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true); - } - if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) { - this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); - } - if (options.contains(StandardSocketOptions.SO_REUSEADDR)) { - this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - } - if (options.contains(StandardSocketOptions.SO_RCVBUF)) { - this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); - } - if (options.contains(StandardSocketOptions.SO_SNDBUF)) { - this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); - } - } - - @Override - public void bind(SocketAddress local, int backlog) throws IOException { - this.serverChannel.bind(local, backlog); - } - - @Override - public Set> supportedOptions() { - return this.serverChannel.supportedOptions(); - } - - @Override - public void setOption(SocketOption name, T value) throws IOException { - this.serverChannel.setOption(name, value); - } - - @Override - public void accept(Application application, Server server) throws IOException { - this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT); - - AtomicLong createBufferCounter = new AtomicLong(); - AtomicLong cycleBufferCounter = new AtomicLong(); - - ObjectPool bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize); - AtomicLong createResponseCounter = new AtomicLong(); - AtomicLong cycleResponseCounter = new AtomicLong(); - ObjectPool safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); - final int threads = Runtime.getRuntime().availableProcessors(); - ThreadLocal> localResponsePool = ThreadLocal.withInitial(() -> { - if (!(Thread.currentThread() instanceof WorkThread)) return null; - return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(), - safeResponsePool.getCycleCounter(), 16, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler()); - }); - this.responseSupplier = () -> { - ObjectPool pool = localResponsePool.get(); - return pool == null ? safeResponsePool.get() : pool.get(); - }; - this.responseConsumer = (v) -> { - ObjectPool pool = localResponsePool.get(); - (pool == null ? safeResponsePool : pool).accept(v); - }; - final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread"); - this.ioGroup = new AsyncIOGroup(threadPrefixName, null, threads, server.bufferCapacity, bufferPool); - this.ioGroup.start(); - this.acceptThread = new Thread() { - { - setName(threadPrefixName.replace("ServletThread", "AcceptThread")); - } - - @Override - public void run() { - while (!closed) { - try { - int count = selector.select(); - if (count == 0) continue; - Set keys = selector.selectedKeys(); - Iterator it = keys.iterator(); - while (it.hasNext()) { - SelectionKey key = it.next(); - it.remove(); - if (key.isAcceptable()) accept(key); - } - } catch (Throwable t) { - t.printStackTrace(); - } - } - } - }; - this.acceptThread.start(); - } - - private void accept(SelectionKey key) throws IOException { - SocketChannel channel = this.serverChannel.accept(); - channel.configureBlocking(false); - channel.setOption(StandardSocketOptions.TCP_NODELAY, true); - channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); - channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); - channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); - AsyncIOThread readThread = ioGroup.nextIOThread(); - ioGroup.connCreateCounter.incrementAndGet(); - ioGroup.connLivingCounter.incrementAndGet(); - AsyncNioTcpConnection conn = new AsyncNioTcpConnection(false, ioGroup, readThread, ioGroup.connectThread(), channel, context.getSSLContext(), null, ioGroup.connLivingCounter, ioGroup.connClosedCounter); - ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn); - conn.protocolCodec = codec; - codec.run(null); - } - - @Override - public void close() throws IOException { - if (this.closed) return; - this.closed = true; - this.selector.wakeup(); - this.ioGroup.close(); - this.serverChannel.close(); - this.selector.close(); - } - - @Override - public long getCreateConnectionCount() { - return ioGroup.connCreateCounter.get(); - } - - @Override - public long getClosedConnectionCount() { - return ioGroup.connClosedCounter.get(); - } - - @Override - public long getLivingConnectionCount() { - return ioGroup.connLivingCounter.get(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.*; +import java.util.logging.Level; +import org.redkale.boot.Application; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +class AsyncNioTcpProtocolServer extends ProtocolServer { + + private ServerSocketChannel serverChannel; + + private Selector selector; + + private AsyncIOGroup ioGroup; + + private Thread acceptThread; + + private boolean closed; + + private Supplier responseSupplier; + + private Consumer responseConsumer; + + public AsyncNioTcpProtocolServer(Context context) { + super(context); + } + + @Override + public void open(AnyValue config) throws IOException { + this.serverChannel = ServerSocketChannel.open(); + this.serverChannel.configureBlocking(false); + this.selector = Selector.open(); + final Set> options = this.serverChannel.supportedOptions(); + if (options.contains(StandardSocketOptions.TCP_NODELAY)) { + this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true); + } + if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) { + this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + } + if (options.contains(StandardSocketOptions.SO_REUSEADDR)) { + this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); + } + if (options.contains(StandardSocketOptions.SO_RCVBUF)) { + this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); + } + if (options.contains(StandardSocketOptions.SO_SNDBUF)) { + this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); + } + } + + @Override + public void bind(SocketAddress local, int backlog) throws IOException { + this.serverChannel.bind(local, backlog); + } + + @Override + public Set> supportedOptions() { + return this.serverChannel.supportedOptions(); + } + + @Override + public void setOption(SocketOption name, T value) throws IOException { + this.serverChannel.setOption(name, value); + } + + @Override + public void accept(Application application, Server server) throws IOException { + this.serverChannel.register(this.selector, SelectionKey.OP_ACCEPT); + + LongAdder createBufferCounter = new LongAdder(); + LongAdder cycleBufferCounter = new LongAdder(); + LongAdder createResponseCounter = new LongAdder(); + LongAdder cycleResponseCounter = new LongAdder(); + + ObjectPool bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize); + ObjectPool safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); + final int respPoolMax = server.getResponsePoolSize(); + ThreadLocal> localResponsePool = ThreadLocal.withInitial(() -> { + if (!(Thread.currentThread() instanceof WorkThread)) return null; + return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(), + safeResponsePool.getCycleCounter(), respPoolMax, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler()); + }); + this.responseSupplier = () -> { + ObjectPool pool = localResponsePool.get(); + return pool == null ? safeResponsePool.get() : pool.get(); + }; + this.responseConsumer = (v) -> { + if (Thread.currentThread() != v.thread && v.thread != null) { + v.thread.execute(() -> { + ObjectPool pool = localResponsePool.get(); + (pool == null ? safeResponsePool : pool).accept(v); + }); + return; + } + ObjectPool pool = localResponsePool.get(); + (pool == null ? safeResponsePool : pool).accept(v); + }; + final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread"); + this.ioGroup = new AsyncIOGroup(false, threadPrefixName, null, server.bufferCapacity, bufferPool); + this.ioGroup.start(); + + this.acceptThread = new Thread() { + { + setName(threadPrefixName.replace("ServletThread", "AcceptThread")); + } + + @Override + public void run() { + while (!closed) { + try { + int count = selector.select(); + if (count == 0) continue; + Set keys = selector.selectedKeys(); + Iterator it = keys.iterator(); + while (it.hasNext()) { + SelectionKey key = it.next(); + it.remove(); + if (key.isAcceptable()) accept(key); + } + } catch (Throwable t) { + server.logger.log(Level.SEVERE, "server accept error", t); + } + } + } + }; + this.acceptThread.start(); + } + + private void accept(SelectionKey key) throws IOException { + SocketChannel channel = this.serverChannel.accept(); + channel.configureBlocking(false); + channel.setOption(StandardSocketOptions.TCP_NODELAY, true); + channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); + channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); + channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); + AsyncIOThread readThread = ioGroup.nextIOThread(); + LongAdder connCreateCounter = ioGroup.connCreateCounter; + if (connCreateCounter != null) connCreateCounter.increment(); + LongAdder connLivingCounter = ioGroup.connLivingCounter; + if (connLivingCounter != null) connLivingCounter.increment(); + AsyncNioTcpConnection conn = new AsyncNioTcpConnection(false, ioGroup, readThread, ioGroup.connectThread(), channel, context.getSSLBuilder(), context.getSSLContext(), null, connLivingCounter, ioGroup.connClosedCounter); + ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn); + conn.protocolCodec = codec; + if (conn.sslEngine == null) { + codec.start(null); + } else { + conn.startHandshake(t -> { + if (t == null) { + codec.start(null); + } else if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } else { + throw new RuntimeException(t); + } + }); + } + } + + @Override + public void close() throws IOException { + if (this.closed) return; + this.closed = true; + this.selector.wakeup(); + this.ioGroup.close(); + this.serverChannel.close(); + this.selector.close(); + } + + @Override + public AsyncGroup getAsyncGroup() { + return ioGroup; + } + + @Override + public long getCreateConnectionCount() { + return ioGroup.connCreateCounter == null ? -1 : ioGroup.connCreateCounter.longValue(); + } + + @Override + public long getClosedConnectionCount() { + return ioGroup.connClosedCounter == null ? -1 : ioGroup.connClosedCounter.longValue(); + } + + @Override + public long getLivingConnectionCount() { + return ioGroup.connLivingCounter == null ? -1 : ioGroup.connLivingCounter.longValue(); + } +} diff --git a/src/org/redkale/net/AsyncNioUdpConnection.java b/src/main/java/org/redkale/net/AsyncNioUdpConnection.java similarity index 84% rename from src/org/redkale/net/AsyncNioUdpConnection.java rename to src/main/java/org/redkale/net/AsyncNioUdpConnection.java index d7be75d5d..b47d0babc 100644 --- a/src/org/redkale/net/AsyncNioUdpConnection.java +++ b/src/main/java/org/redkale/net/AsyncNioUdpConnection.java @@ -1,176 +1,173 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.*; -import javax.net.ssl.SSLContext; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -class AsyncNioUdpConnection extends AsyncNioConnection { - - private final DatagramChannel channel; - - public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, DatagramChannel ch, - SSLContext sslContext, final SocketAddress addr0, AtomicLong livingCounter, AtomicLong closedCounter) { - super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslContext, livingCounter, closedCounter); - this.channel = ch; - SocketAddress addr = addr0; - if (addr == null) { - try { - addr = ch.getRemoteAddress(); - } catch (Exception e) { - //do nothing - } - } - this.remoteAddress = addr; - } - - public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, - Supplier bufferSupplier, Consumer bufferConsumer, - DatagramChannel ch, SSLContext sslContext, final SocketAddress addr0, - AtomicLong livingCounter, AtomicLong closedCounter) { - super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter); - this.channel = ch; - SocketAddress addr = addr0; - if (addr == null) { - try { - addr = ch.getRemoteAddress(); - } catch (Exception e) { - //do nothing - } - } - this.remoteAddress = addr; - } - - @Override - public boolean isOpen() { - return this.channel.isOpen(); - } - - @Override - public boolean isTCP() { - return false; - } - - @Override - public boolean shutdownInput() { - return true; - } - - @Override - public boolean shutdownOutput() { - return true; - } - - @Override - public boolean setOption(SocketOption name, T value) { - try { - this.channel.setOption(name, value); - return true; - } catch (IOException e) { - return false; - } - } - - @Override - public Set> supportedOptions() { - return this.channel.supportedOptions(); - } - - @Override - public SocketAddress getLocalAddress() { - try { - return channel.getLocalAddress(); - } catch (IOException e) { - return null; - } - } - - @Override - public ReadableByteChannel readableByteChannel() { - return this.channel; - } - - @Override - public WritableByteChannel writableByteChannel() { - return this.channel; - } - - @Override - public boolean isConnected() { - if (!client) return true; - return this.channel.isConnected(); - } - - @Override - protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException { - return this.channel.register(sel, ops); - } - - @Override - protected int implRead(ByteBuffer dst) throws IOException { - return this.channel.read(dst); - } - - @Override - protected int implWrite(ByteBuffer src) throws IOException { - return this.channel.send(src, remoteAddress); - } - - @Override - protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException { - int end = offset + length; - for (int i = offset; i < end; i++) { - ByteBuffer buf = srcs[i]; - if (buf.hasRemaining()) return this.channel.send(buf, remoteAddress); - } - return 0; - } - - public void connect(SocketAddress remote, A attachment, CompletionHandler handler) { - this.connectAttachment = attachment; - this.connectCompletionHandler = (CompletionHandler) handler; - this.remoteAddress = remote; - doConnect(); - } - - @Override - public void doConnect() { - try { - channel.connect(remoteAddress); - handleConnect(null); - } catch (IOException e) { - handleConnect(e); - } - } - - @Override - public final void close() throws IOException { - super.close(); - if (client) channel.close(); //不能关闭channel - if (this.connectKey != null) this.connectKey.cancel(); - if (this.readKey != null) this.readKey.cancel(); - if (this.writeKey != null) this.writeKey.cancel(); - } - - @Override - protected InputStream newInputStream() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.*; +import javax.net.ssl.SSLContext; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +class AsyncNioUdpConnection extends AsyncNioConnection { + + private final DatagramChannel channel; + + public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, DatagramChannel ch, + SSLBuilder sslBuilder, SSLContext sslContext, final SocketAddress addr0, LongAdder livingCounter, LongAdder closedCounter) { + super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, ioThread.getBufferSupplier(), ioThread.getBufferConsumer(), sslBuilder, sslContext, livingCounter, closedCounter); + this.channel = ch; + SocketAddress addr = addr0; + if (addr == null) { + try { + addr = ch.getRemoteAddress(); + } catch (Exception e) { + //do nothing + } + } + this.remoteAddress = addr; + } + + public AsyncNioUdpConnection(boolean client, AsyncIOGroup ioGroup, AsyncIOThread ioThread, AsyncIOThread connectThread, + Supplier bufferSupplier, Consumer bufferConsumer, + DatagramChannel ch, SSLBuilder sslBuilder, SSLContext sslContext, final SocketAddress addr0, + LongAdder livingCounter, LongAdder closedCounter) { + super(client, ioGroup, ioThread, connectThread, ioGroup.bufferCapacity, bufferSupplier, bufferConsumer, sslBuilder, sslContext, livingCounter, closedCounter); + this.channel = ch; + SocketAddress addr = addr0; + if (addr == null) { + try { + addr = ch.getRemoteAddress(); + } catch (Exception e) { + //do nothing + } + } + this.remoteAddress = addr; + } + + @Override + public boolean isOpen() { + return this.channel.isOpen(); + } + + @Override + public boolean isTCP() { + return false; + } + + @Override + public boolean shutdownInput() { + return true; + } + + @Override + public boolean shutdownOutput() { + return true; + } + + @Override + public boolean setOption(SocketOption name, T value) { + try { + this.channel.setOption(name, value); + return true; + } catch (IOException e) { + return false; + } + } + + @Override + public Set> supportedOptions() { + return this.channel.supportedOptions(); + } + + @Override + public SocketAddress getLocalAddress() { + try { + return channel.getLocalAddress(); + } catch (IOException e) { + return null; + } + } + + @Override + public ReadableByteChannel readableByteChannel() { + if (this.sslEngine == null) return this.channel; + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public WritableByteChannel writableByteChannel() { + if (this.sslEngine == null) return this.channel; + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public boolean isConnected() { + if (!client) return true; + return this.channel.isConnected(); + } + + @Override + protected SelectionKey implRegister(Selector sel, int ops) throws ClosedChannelException { + return this.channel.register(sel, ops); + } + + @Override + protected int implRead(ByteBuffer dst) throws IOException { + return this.channel.read(dst); + } + + @Override + protected int implWrite(ByteBuffer src) throws IOException { + return this.channel.send(src, remoteAddress); + } + + @Override + protected int implWrite(ByteBuffer[] srcs, int offset, int length) throws IOException { + int end = offset + length; + for (int i = offset; i < end; i++) { + ByteBuffer buf = srcs[i]; + if (buf.hasRemaining()) return this.channel.send(buf, remoteAddress); + } + return 0; + } + + public void connect(SocketAddress remote, A attachment, CompletionHandler handler) { + this.connectAttachment = attachment; + this.connectCompletionHandler = (CompletionHandler) handler; + this.remoteAddress = remote; + doConnect(); + } + + @Override + public void doConnect() { + try { + channel.connect(remoteAddress); + handleConnect(null); + } catch (IOException e) { + handleConnect(e); + } + } + + @Override + public final void close() throws IOException { + super.close(); + if (client) channel.close(); //不能关闭channel + if (this.connectKey != null) this.connectKey.cancel(); + if (this.readKey != null) this.readKey.cancel(); + if (this.writeKey != null) this.writeKey.cancel(); + } + +} diff --git a/src/org/redkale/net/AsyncNioUdpProtocolServer.java b/src/main/java/org/redkale/net/AsyncNioUdpProtocolServer.java similarity index 81% rename from src/org/redkale/net/AsyncNioUdpProtocolServer.java rename to src/main/java/org/redkale/net/AsyncNioUdpProtocolServer.java index e1b0722e7..bff0563e4 100644 --- a/src/org/redkale/net/AsyncNioUdpProtocolServer.java +++ b/src/main/java/org/redkale/net/AsyncNioUdpProtocolServer.java @@ -1,169 +1,188 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.*; -import org.redkale.boot.Application; -import org.redkale.util.*; - -/** - * 协议底层Server - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -class AsyncNioUdpProtocolServer extends ProtocolServer { - - private DatagramChannel serverChannel; - - private Selector selector; - - private AsyncIOGroup ioGroup; - - private Thread acceptThread; - - private boolean closed; - - private Supplier responseSupplier; - - private Consumer responseConsumer; - - public AsyncNioUdpProtocolServer(Context context) { - super(context); - } - - @Override - public void open(AnyValue config) throws IOException { - this.serverChannel = DatagramChannel.open(); - this.serverChannel.configureBlocking(false); - this.selector = Selector.open(); - final Set> options = this.serverChannel.supportedOptions(); - if (options.contains(StandardSocketOptions.TCP_NODELAY)) { - this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true); - } - if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) { - this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); - } - if (options.contains(StandardSocketOptions.SO_REUSEADDR)) { - this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - } - if (options.contains(StandardSocketOptions.SO_RCVBUF)) { - this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); - } - if (options.contains(StandardSocketOptions.SO_SNDBUF)) { - this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); - } - } - - @Override - public void bind(SocketAddress local, int backlog) throws IOException { - this.serverChannel.bind(local); - } - - @Override - public void setOption(SocketOption name, T value) throws IOException { - this.serverChannel.setOption(name, value); - } - - @Override - public Set> supportedOptions() { - return this.serverChannel.supportedOptions(); - } - - @Override - public void accept(Application application, Server server) throws IOException { - AtomicLong createBufferCounter = new AtomicLong(); - AtomicLong cycleBufferCounter = new AtomicLong(); - - ObjectPool safeBufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize); - AtomicLong createResponseCounter = new AtomicLong(); - AtomicLong cycleResponseCounter = new AtomicLong(); - ObjectPool safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); - final int threads = Runtime.getRuntime().availableProcessors(); - ThreadLocal> localResponsePool = ThreadLocal.withInitial(() -> { - if (!(Thread.currentThread() instanceof WorkThread)) return null; - return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(), - safeResponsePool.getCycleCounter(), 16, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler()); - }); - this.responseSupplier = () -> { - ObjectPool pool = localResponsePool.get(); - return pool == null ? safeResponsePool.get() : pool.get(); - }; - this.responseConsumer = (v) -> { - ObjectPool pool = localResponsePool.get(); - (pool == null ? safeResponsePool : pool).accept(v); - }; - final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread"); - this.ioGroup = new AsyncIOGroup(threadPrefixName, null, threads, server.bufferCapacity, safeBufferPool); - this.ioGroup.start(); - this.serverChannel.register(this.selector, SelectionKey.OP_READ); - - this.acceptThread = new Thread() { - ObjectPool unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(), - safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler()); - - { - setName(threadPrefixName.replace("ServletThread", "AcceptThread")); - } - - @Override - public void run() { - while (!closed) { - final ByteBuffer buffer = unsafeBufferPool.get(); - try { - SocketAddress address = serverChannel.receive(buffer); - buffer.flip(); - accept(address, buffer); - } catch (Throwable t) { - unsafeBufferPool.accept(buffer); - } - } - } - }; - this.acceptThread.start(); - } - - private void accept(SocketAddress address, ByteBuffer buffer) throws IOException { - AsyncIOThread readThread = ioGroup.nextIOThread(); - ioGroup.connCreateCounter.incrementAndGet(); - ioGroup.connLivingCounter.incrementAndGet(); - AsyncNioUdpConnection conn = new AsyncNioUdpConnection(false, ioGroup, readThread, ioGroup.connectThread(), this.serverChannel, context.getSSLContext(), address, ioGroup.connLivingCounter, ioGroup.connClosedCounter); - ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn); - conn.protocolCodec = codec; - codec.run(buffer); - } - - @Override - public void close() throws IOException { - if (this.closed) return; - this.closed = true; - this.ioGroup.close(); - this.serverChannel.close(); - } - - @Override - public long getCreateConnectionCount() { - return -1; - } - - @Override - public long getClosedConnectionCount() { - return -1; - } - - @Override - public long getLivingConnectionCount() { - return -1; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.*; +import org.redkale.boot.Application; +import org.redkale.util.*; + +/** + * 协议底层Server + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +class AsyncNioUdpProtocolServer extends ProtocolServer { + + private DatagramChannel serverChannel; + + private Selector selector; + + private AsyncIOGroup ioGroup; + + private Thread acceptThread; + + private boolean closed; + + private Supplier responseSupplier; + + private Consumer responseConsumer; + + public AsyncNioUdpProtocolServer(Context context) { + super(context); + } + + @Override + public void open(AnyValue config) throws IOException { + this.serverChannel = DatagramChannel.open(); + this.serverChannel.configureBlocking(false); + this.selector = Selector.open(); + final Set> options = this.serverChannel.supportedOptions(); + if (options.contains(StandardSocketOptions.TCP_NODELAY)) { + this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true); + } + if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) { + this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); + } + if (options.contains(StandardSocketOptions.SO_REUSEADDR)) { + this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); + } + if (options.contains(StandardSocketOptions.SO_RCVBUF)) { + this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); + } + if (options.contains(StandardSocketOptions.SO_SNDBUF)) { + this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); + } + } + + @Override + public void bind(SocketAddress local, int backlog) throws IOException { + this.serverChannel.bind(local); + } + + @Override + public void setOption(SocketOption name, T value) throws IOException { + this.serverChannel.setOption(name, value); + } + + @Override + public Set> supportedOptions() { + return this.serverChannel.supportedOptions(); + } + + @Override + public void accept(Application application, Server server) throws IOException { + + LongAdder createBufferCounter = new LongAdder(); + LongAdder cycleBufferCounter = new LongAdder(); + LongAdder createResponseCounter = new LongAdder(); + LongAdder cycleResponseCounter = new LongAdder(); + + ObjectPool safeBufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize); + ObjectPool safeResponsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); + ThreadLocal> localResponsePool = ThreadLocal.withInitial(() -> { + if (!(Thread.currentThread() instanceof WorkThread)) return null; + return ObjectPool.createUnsafePool(safeResponsePool, safeResponsePool.getCreatCounter(), + safeResponsePool.getCycleCounter(), 16, safeResponsePool.getCreator(), safeResponsePool.getPrepare(), safeResponsePool.getRecycler()); + }); + this.responseSupplier = () -> { + ObjectPool pool = localResponsePool.get(); + return pool == null ? safeResponsePool.get() : pool.get(); + }; + this.responseConsumer = (v) -> { + ObjectPool pool = localResponsePool.get(); + (pool == null ? safeResponsePool : pool).accept(v); + }; + final String threadPrefixName = server.name == null || server.name.isEmpty() ? "Redkale-IOServletThread" : ("Redkale-" + server.name.replace("Server-", "") + "-IOServletThread"); + this.ioGroup = new AsyncIOGroup(false, threadPrefixName, null, server.bufferCapacity, safeBufferPool); + this.ioGroup.start(); + this.serverChannel.register(this.selector, SelectionKey.OP_READ); + + this.acceptThread = new Thread() { + ObjectPool unsafeBufferPool = ObjectPool.createUnsafePool(safeBufferPool, safeBufferPool.getCreatCounter(), + safeBufferPool.getCycleCounter(), 512, safeBufferPool.getCreator(), safeBufferPool.getPrepare(), safeBufferPool.getRecycler()); + + { + setName(threadPrefixName.replace("ServletThread", "AcceptThread")); + } + + @Override + public void run() { + while (!closed) { + final ByteBuffer buffer = unsafeBufferPool.get(); + try { + SocketAddress address = serverChannel.receive(buffer); + buffer.flip(); + accept(address, buffer); + } catch (Throwable t) { + unsafeBufferPool.accept(buffer); + } + } + } + }; + this.acceptThread.start(); + } + + private void accept(SocketAddress address, ByteBuffer buffer) throws IOException { + AsyncIOThread readThread = ioGroup.nextIOThread(); + LongAdder connCreateCounter = ioGroup.connCreateCounter; + if (connCreateCounter != null) connCreateCounter.increment(); + LongAdder connLivingCounter = ioGroup.connLivingCounter; + if (connLivingCounter != null) connLivingCounter.increment(); + AsyncNioUdpConnection conn = new AsyncNioUdpConnection(false, ioGroup, readThread, ioGroup.connectThread(), this.serverChannel, context.getSSLBuilder(), context.getSSLContext(), address, connLivingCounter, ioGroup.connClosedCounter); + ProtocolCodec codec = new ProtocolCodec(context, responseSupplier, responseConsumer, conn); + conn.protocolCodec = codec; + if (conn.sslEngine == null) { + codec.start(buffer); + } else { + conn.startHandshake(t -> { + if (t == null) { + codec.start(buffer); + } else if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } else { + throw new RuntimeException(t); + } + }); + } + } + + @Override + public void close() throws IOException { + if (this.closed) return; + this.closed = true; + this.ioGroup.close(); + this.serverChannel.close(); + } + + @Override + public AsyncGroup getAsyncGroup() { + return ioGroup; + } + + @Override + public long getCreateConnectionCount() { + return -1; + } + + @Override + public long getClosedConnectionCount() { + return -1; + } + + @Override + public long getLivingConnectionCount() { + return -1; + } +} diff --git a/src/main/java/org/redkale/net/AsyncThread.java b/src/main/java/org/redkale/net/AsyncThread.java new file mode 100644 index 000000000..58ebc7354 --- /dev/null +++ b/src/main/java/org/redkale/net/AsyncThread.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.util.concurrent.ExecutorService; + +/** + * 协议处理的IO线程类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.5.0 + */ +public abstract class AsyncThread extends WorkThread { + + public AsyncThread(String name, int index, int threads, ExecutorService workExecutor, Runnable target) { + super(name, index, threads, workExecutor, target); + } + + public static AsyncThread currAsyncThread() { + Thread t = Thread.currentThread(); + return t instanceof AsyncThread ? (AsyncThread) t : null; + } + +} diff --git a/src/org/redkale/net/ChannelContext.java b/src/main/java/org/redkale/net/ChannelContext.java similarity index 95% rename from src/org/redkale/net/ChannelContext.java rename to src/main/java/org/redkale/net/ChannelContext.java index acd990347..2399303f2 100644 --- a/src/org/redkale/net/ChannelContext.java +++ b/src/main/java/org/redkale/net/ChannelContext.java @@ -1,31 +1,31 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.util.*; - -/** - * 当前一个Request绑定的AsyncConnection, 类似Session,但概念上不同于sessionid对应的对象 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.4.0 - */ -public interface ChannelContext { - - public void setAttribute(String name, Object value); - - @SuppressWarnings("unchecked") - public T getAttribute(String name); - - public void removeAttribute(String name); - - public Map getAttributes(); - - public void clearAttribute(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.util.*; + +/** + * 当前一个Request绑定的AsyncConnection, 类似Session,但概念上不同于sessionid对应的对象 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.4.0 + */ +public interface ChannelContext { + + public void setAttribute(String name, Object value); + + @SuppressWarnings("unchecked") + public T getAttribute(String name); + + public void removeAttribute(String name); + + public Map getAttributes(); + + public void clearAttribute(); +} diff --git a/src/org/redkale/net/Context.java b/src/main/java/org/redkale/net/Context.java similarity index 95% rename from src/org/redkale/net/Context.java rename to src/main/java/org/redkale/net/Context.java index c23a78345..219189e24 100644 --- a/src/org/redkale/net/Context.java +++ b/src/main/java/org/redkale/net/Context.java @@ -1,260 +1,271 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.net.*; -import java.nio.charset.*; -import java.util.concurrent.*; -import java.util.logging.*; -import javax.net.ssl.SSLContext; -import org.redkale.convert.bson.*; -import org.redkale.convert.json.*; -import org.redkale.util.*; - -/** - * 服务器上下文对象 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class Context { - - //服务启动时间 - protected final long serverStartTime; - - //Server的线程池 - protected final ExecutorService workExecutor; - - protected final ThreadHashExecutor workHashExecutor; - - //SSL - protected final SSLContext sslContext; - - //ByteBuffer的容量,默认8K - protected final int bufferCapacity; - - //服务的根Servlet - protected final PrepareServlet prepare; - - //日志Logger - protected final Logger logger; - - //BSON操作工厂 - protected final BsonFactory bsonFactory; - - //JSON操作工厂 - protected final JsonFactory jsonFactory; - - //依赖注入工厂类 - protected final ResourceFactory resourceFactory; - - //最大连接数, 为0表示没限制 - protected int maxconns; - - //请求内容的大小上限, 默认64K - protected int maxbody; - - //keep alive IO读取的超时时间 - protected int aliveTimeoutSeconds; - - //IO读取的超时时间 - protected int readTimeoutSeconds; - - //IO写入的超时时间 - protected int writeTimeoutSeconds; - - //服务的监听地址 - protected InetSocketAddress address; - - //字符集 - protected Charset charset; - - public Context(ContextConfig config) { - this(config.serverStartTime, config.logger, config.workExecutor, config.sslContext, - config.bufferCapacity, config.maxconns, config.maxbody, config.charset, config.address, config.resourceFactory, - config.prepare, config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds); - } - - public Context(long serverStartTime, Logger logger, ExecutorService workExecutor, SSLContext sslContext, - int bufferCapacity, final int maxconns, final int maxbody, Charset charset, InetSocketAddress address, - ResourceFactory resourceFactory, PrepareServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) { - this.serverStartTime = serverStartTime; - this.logger = logger; - this.workExecutor = workExecutor; - this.sslContext = sslContext; - this.bufferCapacity = bufferCapacity; - this.maxconns = maxconns; - this.maxbody = maxbody; - this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset; - this.address = address; - this.prepare = prepare; - this.resourceFactory = resourceFactory; - this.aliveTimeoutSeconds = aliveTimeoutSeconds; - this.readTimeoutSeconds = readTimeoutSeconds; - this.writeTimeoutSeconds = writeTimeoutSeconds; - this.jsonFactory = JsonFactory.root(); - this.bsonFactory = BsonFactory.root(); - if (workExecutor instanceof ThreadHashExecutor) { - this.workHashExecutor = (ThreadHashExecutor) workExecutor; - } else { - this.workHashExecutor = null; - } - } - - protected void executePrepareServlet(Request request, Response response) { - if (workHashExecutor != null) { - workHashExecutor.execute(request.getHashid(), () -> prepare.prepare(request, response)); - } else if (workExecutor != null) { - workExecutor.execute(() -> prepare.prepare(request, response)); - } else { - prepare.prepare(request, response); - } - } - - public void execute(Servlet servlet, Request request, Response response) { - if (workHashExecutor != null) { - workHashExecutor.execute(request.getHashid(), () -> { - try { - long cha = System.currentTimeMillis() - request.getCreatetime(); - servlet.execute(request, response); - if (cha > 1000 && response.context.logger.isLoggable(Level.WARNING)) { - response.context.logger.log(Level.WARNING, "hash execute servlet delays=" + cha + "ms, request=" + request); - } else if (cha > 100 && response.context.logger.isLoggable(Level.FINE)) { - response.context.logger.log(Level.FINE, "hash execute servlet delay=" + cha + "ms, request=" + request); - } - } catch (Throwable t) { - response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t); - response.finish(true); - } - }); - } else if (workExecutor != null) { - workExecutor.execute(() -> { - try { - servlet.execute(request, response); - } catch (Throwable t) { - response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t); - response.finish(true); - } - }); - } else { - try { - servlet.execute(request, response); - } catch (Throwable t) { - response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t); - response.finish(true); - } - } - - } - - public ResourceFactory getResourceFactory() { - return resourceFactory; - } - - public SSLContext getSSLContext() { - return sslContext; - } - - public int getMaxconns() { - return maxconns; - } - - public int getMaxbody() { - return maxbody; - } - - public InetSocketAddress getServerAddress() { - return address; - } - - public long getServerStartTime() { - return serverStartTime; - } - - public Charset getCharset() { - return charset; - } - - public int getBufferCapacity() { - return bufferCapacity; - } - - public Logger getLogger() { - return logger; - } - - public int getAliveTimeoutSeconds() { - return aliveTimeoutSeconds; - } - - public int getReadTimeoutSeconds() { - return readTimeoutSeconds; - } - - public int getWriteTimeoutSeconds() { - return writeTimeoutSeconds; - } - - public JsonConvert getJsonConvert() { - return jsonFactory.getConvert(); - } - - public BsonConvert getBsonConvert() { - return bsonFactory.getConvert(); - } - - public static class ContextConfig { - - //服务启动时间 - public long serverStartTime; - - //Server的线程池 - public ExecutorService workExecutor; - - //SSL - public SSLContext sslContext; - - //ByteBuffer的容量,默认8K - public int bufferCapacity; - - //服务的根Servlet - public PrepareServlet prepare; - - //服务的监听地址 - public InetSocketAddress address; - - //字符集 - public Charset charset; - - //请求内容的大小上限, 默认64K - public int maxbody; - - //最大连接数, 为0表示没限制 - public int maxconns; - - //keep alive IO读取的超时时间 - public int aliveTimeoutSeconds; - - //IO读取的超时时间 - public int readTimeoutSeconds; - - //IO写入的超时时间 - public int writeTimeoutSeconds; - - //日志Logger - public Logger logger; - - //依赖注入工厂类 - public ResourceFactory resourceFactory; - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.net.*; +import java.nio.charset.*; +import java.util.concurrent.*; +import java.util.logging.*; +import javax.net.ssl.SSLContext; +import org.redkale.convert.bson.*; +import org.redkale.convert.json.*; +import org.redkale.util.*; + +/** + * 服务器上下文对象 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class Context { + + //服务启动时间 + protected final long serverStartTime; + + //Server的线程池 + protected final ExecutorService workExecutor; + + protected final ThreadHashExecutor workHashExecutor; + + //SSL + protected final SSLBuilder sslBuilder; + + //SSL + protected final SSLContext sslContext; + + //ByteBuffer的容量,默认8K + protected final int bufferCapacity; + + //服务的根Servlet + protected final PrepareServlet prepare; + + //日志Logger + protected final Logger logger; + + //BSON操作工厂 + protected final BsonFactory bsonFactory; + + //JSON操作工厂 + protected final JsonFactory jsonFactory; + + //依赖注入工厂类 + protected final ResourceFactory resourceFactory; + + //最大连接数, 为0表示没限制 + protected int maxconns; + + //请求内容的大小上限, 默认64K + protected int maxbody; + + //keep alive IO读取的超时时间 + protected int aliveTimeoutSeconds; + + //IO读取的超时时间 + protected int readTimeoutSeconds; + + //IO写入的超时时间 + protected int writeTimeoutSeconds; + + //服务的监听地址 + protected InetSocketAddress address; + + //字符集 + protected Charset charset; + + public Context(ContextConfig config) { + this(config.serverStartTime, config.logger, config.workExecutor, config.sslBuilder, config.sslContext, + config.bufferCapacity, config.maxconns, config.maxbody, config.charset, config.address, config.resourceFactory, + config.prepare, config.aliveTimeoutSeconds, config.readTimeoutSeconds, config.writeTimeoutSeconds); + } + + public Context(long serverStartTime, Logger logger, ExecutorService workExecutor, SSLBuilder sslBuilder, SSLContext sslContext, + int bufferCapacity, final int maxconns, final int maxbody, Charset charset, InetSocketAddress address, + ResourceFactory resourceFactory, PrepareServlet prepare, int aliveTimeoutSeconds, int readTimeoutSeconds, int writeTimeoutSeconds) { + this.serverStartTime = serverStartTime; + this.logger = logger; + this.workExecutor = workExecutor; + this.sslBuilder = sslBuilder; + this.sslContext = sslContext; + this.bufferCapacity = bufferCapacity; + this.maxconns = maxconns; + this.maxbody = maxbody; + this.charset = StandardCharsets.UTF_8.equals(charset) ? null : charset; + this.address = address; + this.prepare = prepare; + this.resourceFactory = resourceFactory; + this.aliveTimeoutSeconds = aliveTimeoutSeconds; + this.readTimeoutSeconds = readTimeoutSeconds; + this.writeTimeoutSeconds = writeTimeoutSeconds; + this.jsonFactory = JsonFactory.root(); + this.bsonFactory = BsonFactory.root(); + if (workExecutor instanceof ThreadHashExecutor) { + this.workHashExecutor = (ThreadHashExecutor) workExecutor; + } else { + this.workHashExecutor = null; + } + } + + protected void executePrepareServlet(Request request, Response response) { + if (workHashExecutor != null) { + workHashExecutor.execute(request.getHashid(), () -> prepare.prepare(request, response)); + } else if (workExecutor != null) { + workExecutor.execute(() -> prepare.prepare(request, response)); + } else { + prepare.prepare(request, response); + } + } + + public void execute(Servlet servlet, Request request, Response response) { + if (workHashExecutor != null) { + workHashExecutor.execute(request.getHashid(), () -> { + try { + long cha = System.currentTimeMillis() - request.getCreatetime(); + servlet.execute(request, response); + if (cha > 1000 && response.context.logger.isLoggable(Level.WARNING)) { + response.context.logger.log(Level.WARNING, "hash execute servlet delays=" + cha + "ms, request=" + request); + } else if (cha > 100 && response.context.logger.isLoggable(Level.FINE)) { + response.context.logger.log(Level.FINE, "hash execute servlet delay=" + cha + "ms, request=" + request); + } + } catch (Throwable t) { + response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t); + response.finish(true); + } + }); + } else if (workExecutor != null) { + workExecutor.execute(() -> { + try { + servlet.execute(request, response); + } catch (Throwable t) { + response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t); + response.finish(true); + } + }); + } else { + try { + servlet.execute(request, response); + } catch (Throwable t) { + response.context.logger.log(Level.WARNING, "execute servlet abort, force to close channel ", t); + response.finish(true); + } + } + + } + + public ResourceFactory getResourceFactory() { + return resourceFactory; + } + + public SSLBuilder getSSLBuilder() { + return sslBuilder; + } + + public SSLContext getSSLContext() { + return sslContext; + } + + public int getMaxconns() { + return maxconns; + } + + public int getMaxbody() { + return maxbody; + } + + public InetSocketAddress getServerAddress() { + return address; + } + + public long getServerStartTime() { + return serverStartTime; + } + + public Charset getCharset() { + return charset; + } + + public int getBufferCapacity() { + return bufferCapacity; + } + + public Logger getLogger() { + return logger; + } + + public int getAliveTimeoutSeconds() { + return aliveTimeoutSeconds; + } + + public int getReadTimeoutSeconds() { + return readTimeoutSeconds; + } + + public int getWriteTimeoutSeconds() { + return writeTimeoutSeconds; + } + + public JsonConvert getJsonConvert() { + return jsonFactory.getConvert(); + } + + public BsonConvert getBsonConvert() { + return bsonFactory.getConvert(); + } + + public static class ContextConfig { + + //服务启动时间 + public long serverStartTime; + + //Server的线程池 + public ExecutorService workExecutor; + + //SSL + public SSLBuilder sslBuilder; + + //SSL + public SSLContext sslContext; + + //ByteBuffer的容量,默认8K + public int bufferCapacity; + + //服务的根Servlet + public PrepareServlet prepare; + + //服务的监听地址 + public InetSocketAddress address; + + //字符集 + public Charset charset; + + //请求内容的大小上限, 默认64K + public int maxbody; + + //最大连接数, 为0表示没限制 + public int maxconns; + + //keep alive IO读取的超时时间 + public int aliveTimeoutSeconds; + + //IO读取的超时时间 + public int readTimeoutSeconds; + + //IO写入的超时时间 + public int writeTimeoutSeconds; + + //日志Logger + public Logger logger; + + //依赖注入工厂类 + public ResourceFactory resourceFactory; + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/org/redkale/net/Cryptor.java b/src/main/java/org/redkale/net/Cryptor.java similarity index 96% rename from src/org/redkale/net/Cryptor.java rename to src/main/java/org/redkale/net/Cryptor.java index 4cf1e36a0..e77aa2a1d 100644 --- a/src/org/redkale/net/Cryptor.java +++ b/src/main/java/org/redkale/net/Cryptor.java @@ -1,42 +1,42 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.nio.ByteBuffer; -import java.util.function.*; - -/** - * 加密解密接口 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface Cryptor { - - /** - * 加密 - * - * @param buffers 待加密数据 - * @param supplier ByteBuffer生成器 - * @param consumer ByteBuffer回收器 - * - * @return 加密后数据 - */ - public ByteBuffer[] encrypt(ByteBuffer[] buffers, final Supplier supplier, final Consumer consumer); - - /** - * 解密 - * - * @param buffers 待解密数据 - * @param supplier ByteBuffer生成器 - * @param consumer ByteBuffer回收器 - * - * @return 解密后数据 - */ - public ByteBuffer[] decrypt(ByteBuffer[] buffers, final Supplier supplier, final Consumer consumer); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.nio.ByteBuffer; +import java.util.function.*; + +/** + * 加密解密接口 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface Cryptor { + + /** + * 加密 + * + * @param buffers 待加密数据 + * @param supplier ByteBuffer生成器 + * @param consumer ByteBuffer回收器 + * + * @return 加密后数据 + */ + public ByteBuffer[] encrypt(ByteBuffer[] buffers, final Supplier supplier, final Consumer consumer); + + /** + * 解密 + * + * @param buffers 待解密数据 + * @param supplier ByteBuffer生成器 + * @param consumer ByteBuffer回收器 + * + * @return 解密后数据 + */ + public ByteBuffer[] decrypt(ByteBuffer[] buffers, final Supplier supplier, final Consumer consumer); +} diff --git a/src/org/redkale/net/Filter.java b/src/main/java/org/redkale/net/Filter.java similarity index 96% rename from src/org/redkale/net/Filter.java rename to src/main/java/org/redkale/net/Filter.java index 6b145f7d4..3a5d08d42 100644 --- a/src/org/redkale/net/Filter.java +++ b/src/main/java/org/redkale/net/Filter.java @@ -1,44 +1,44 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import javax.annotation.Priority; -import org.redkale.util.*; - -/** - * 协议拦截器类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Context的子类型 - * @param Request的子类型 - * @param

    Response的子类型 - */ -public abstract class Filter, P extends Response> implements Comparable { - - AnyValue _conf; //当前Filter的配置 - - Filter _next; //下一个Filter - - public void init(C context, AnyValue config) { - } - - public abstract void doFilter(R request, P response) throws IOException; - - public void destroy(C context, AnyValue config) { - } - - @Override - public int compareTo(Object o) { - if (!(o instanceof Filter)) return 1; - Priority p1 = this.getClass().getAnnotation(Priority.class); - Priority p2 = o.getClass().getAnnotation(Priority.class); - return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.IOException; +import javax.annotation.Priority; +import org.redkale.util.*; + +/** + * 协议拦截器类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Context的子类型 + * @param Request的子类型 + * @param

    Response的子类型 + */ +public abstract class Filter, P extends Response> implements Comparable { + + AnyValue _conf; //当前Filter的配置 + + Filter _next; //下一个Filter + + public void init(C context, AnyValue config) { + } + + public abstract void doFilter(R request, P response) throws IOException; + + public void destroy(C context, AnyValue config) { + } + + @Override + public int compareTo(Object o) { + if (!(o instanceof Filter)) return 1; + Priority p1 = this.getClass().getAnnotation(Priority.class); + Priority p2 = o.getClass().getAnnotation(Priority.class); + return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); + } +} diff --git a/src/org/redkale/net/PrepareServlet.java b/src/main/java/org/redkale/net/PrepareServlet.java similarity index 94% rename from src/org/redkale/net/PrepareServlet.java rename to src/main/java/org/redkale/net/PrepareServlet.java index d7c3ebe57..214da4303 100644 --- a/src/org/redkale/net/PrepareServlet.java +++ b/src/main/java/org/redkale/net/PrepareServlet.java @@ -1,234 +1,243 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.util.*; -import java.util.concurrent.atomic.*; -import java.util.function.Predicate; -import java.util.logging.Level; -import org.redkale.util.*; - -/** - * 根Servlet, 一个Server只能存在一个根Servlet - * - * 用于分发Request请求 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param SessionID的类型 - * @param Context的子类型 - * @param Request的子类型 - * @param

    Response的子类型 - * @param Servlet的子类型 - */ -public abstract class PrepareServlet, P extends Response, S extends Servlet> extends Servlet { - - protected final AtomicLong executeCounter = new AtomicLong(); //执行请求次数 - - protected final AtomicLong illRequestCounter = new AtomicLong(); //错误请求次数 - - private final Object lock1 = new Object(); - - private Set servlets = new HashSet<>(); - - private final Object lock2 = new Object(); - - private Map mappings = new HashMap<>(); - - private final List> filters = new ArrayList<>(); - - protected Filter headFilter; - - protected void putServlet(S servlet) { - synchronized (lock1) { - Set newservlets = new HashSet<>(servlets); - newservlets.add(servlet); - this.servlets = newservlets; - } - } - - protected void removeServlet(S servlet) { - synchronized (lock1) { - Set newservlets = new HashSet<>(servlets); - newservlets.remove(servlet); - this.servlets = newservlets; - } - } - - public boolean containsServlet(Class servletClass) { - synchronized (lock1) { - for (S servlet : new HashSet<>(servlets)) { - if (servlet.getClass().equals(servletClass)) return true; - } - return false; - } - } - - public boolean containsServlet(String servletClassName) { - synchronized (lock1) { - for (S servlet : new HashSet<>(servlets)) { - if (servlet.getClass().getName().equals(servletClassName)) return true; - } - return false; - } - } - - protected void putMapping(K key, S servlet) { - synchronized (lock2) { - Map newmappings = new HashMap<>(mappings); - newmappings.put(key, servlet); - this.mappings = newmappings; - } - } - - protected void removeMapping(K key) { - synchronized (lock2) { - if (mappings.containsKey(key)) { - Map newmappings = new HashMap<>(mappings); - newmappings.remove(key); - this.mappings = newmappings; - } - } - } - - protected void removeMapping(S servlet) { - synchronized (lock2) { - List keys = new ArrayList<>(); - Map newmappings = new HashMap<>(mappings); - for (Map.Entry en : newmappings.entrySet()) { - if (en.getValue().equals(servlet)) { - keys.add(en.getKey()); - } - } - for (K key : keys) newmappings.remove(key); - this.mappings = newmappings; - } - } - - protected S mappingServlet(K key) { - return mappings.get(key); - } - - @Override - @SuppressWarnings("unchecked") - public void init(C context, AnyValue config) { - synchronized (filters) { - if (!filters.isEmpty()) { - Collections.sort(filters); - for (Filter filter : filters) { - filter.init(context, config); - } - this.headFilter = filters.get(0); - Filter filter = this.headFilter; - for (int i = 1; i < filters.size(); i++) { - filter._next = filters.get(i); - filter = filter._next; - } - } - } - } - - @Override - @SuppressWarnings("unchecked") - public void destroy(C context, AnyValue config) { - synchronized (filters) { - if (!filters.isEmpty()) { - for (Filter filter : filters) { - filter.destroy(context, config); - } - } - } - } - - @SuppressWarnings("unchecked") - public void addFilter(Filter filter, AnyValue conf) { - filter._conf = conf; - synchronized (filters) { - this.filters.add(filter); - Collections.sort(this.filters); - } - } - - public > T removeFilter(Class filterClass) { - return removeFilter(f -> filterClass.equals(f.getClass())); - } - - public boolean containsFilter(Class filterClass) { - if (this.headFilter == null || filterClass == null) return false; - Filter filter = this.headFilter; - do { - if (filter.getClass().equals(filterClass)) return true; - } while ((filter = filter._next) != null); - return false; - } - - public boolean containsFilter(String filterClassName) { - if (this.headFilter == null || filterClassName == null) return false; - Filter filter = this.headFilter; - do { - if (filter.getClass().getName().equals(filterClassName)) return true; - } while ((filter = filter._next) != null); - return false; - } - - @SuppressWarnings("unchecked") - public > T removeFilter(Predicate predicate) { - if (this.headFilter == null || predicate == null) return null; - synchronized (filters) { - Filter filter = this.headFilter; - Filter prev = null; - do { - if (predicate.test((T) filter)) break; - prev = filter; - } while ((filter = filter._next) != null); - if (filter != null) { - if (prev == null) { - this.headFilter = filter._next; - } else { - prev._next = filter._next; - } - filter._next = null; - this.filters.remove(filter); - } - return (T) filter; - } - } - - @SuppressWarnings("unchecked") - public > List getFilters() { - return (List) new ArrayList<>(filters); - } - - @SuppressWarnings("unchecked") - public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings); - - public final void prepare(final R request, final P response) { - try { - request.prepare(); - response.filter = this.headFilter; - response.servlet = this; - response.nextEvent(); - } catch (Throwable t) { - response.context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t); - response.finish(true); - } - } - - protected AnyValue getServletConf(Servlet servlet) { - return servlet._conf; - } - - protected void setServletConf(Servlet servlet, AnyValue conf) { - servlet._conf = conf; - } - - public List getServlets() { - return new ArrayList<>(servlets); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.util.*; +import java.util.concurrent.atomic.*; +import java.util.function.Predicate; +import java.util.logging.Level; +import java.util.stream.Stream; +import org.redkale.boot.Application; +import org.redkale.util.*; + +/** + * 根Servlet, 一个Server只能存在一个根Servlet + * + * 用于分发Request请求 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param SessionID的类型 + * @param Context的子类型 + * @param Request的子类型 + * @param

    Response的子类型 + * @param Servlet的子类型 + */ +public abstract class PrepareServlet, P extends Response, S extends Servlet> extends Servlet { + + protected final LongAdder executeCounter = new LongAdder(); //执行请求次数 + + protected final LongAdder illRequestCounter = new LongAdder(); //错误请求次数 + + protected Application application; + + private final Object lock1 = new Object(); + + private Set servlets = new HashSet<>(); + + private final Object lock2 = new Object(); + + private Map mappings = new HashMap<>(); + + private final List> filters = new ArrayList<>(); + + protected Filter headFilter; + + protected void putServlet(S servlet) { + synchronized (lock1) { + Set newservlets = new HashSet<>(servlets); + newservlets.add(servlet); + this.servlets = newservlets; + } + } + + protected void removeServlet(S servlet) { + synchronized (lock1) { + Set newservlets = new HashSet<>(servlets); + newservlets.remove(servlet); + this.servlets = newservlets; + } + } + + public boolean containsServlet(Class servletClass) { + synchronized (lock1) { + for (S servlet : new HashSet<>(servlets)) { + if (servlet.getClass().equals(servletClass)) return true; + } + return false; + } + } + + public boolean containsServlet(String servletClassName) { + synchronized (lock1) { + for (S servlet : new HashSet<>(servlets)) { + if (servlet.getClass().getName().equals(servletClassName)) return true; + } + return false; + } + } + + protected void putMapping(K key, S servlet) { + synchronized (lock2) { + Map newmappings = new HashMap<>(mappings); + newmappings.put(key, servlet); + this.mappings = newmappings; + } + } + + protected void removeMapping(K key) { + synchronized (lock2) { + if (mappings.containsKey(key)) { + Map newmappings = new HashMap<>(mappings); + newmappings.remove(key); + this.mappings = newmappings; + } + } + } + + protected void removeMapping(S servlet) { + synchronized (lock2) { + List keys = new ArrayList<>(); + Map newmappings = new HashMap<>(mappings); + for (Map.Entry en : newmappings.entrySet()) { + if (en.getValue().equals(servlet)) { + keys.add(en.getKey()); + } + } + for (K key : keys) newmappings.remove(key); + this.mappings = newmappings; + } + } + + protected S mappingServlet(K key) { + return mappings.get(key); + } + + @Override + @SuppressWarnings("unchecked") + public void init(C context, AnyValue config) { + if (application != null && application.isCompileMode()) return; + synchronized (filters) { + if (!filters.isEmpty()) { + Collections.sort(filters); + for (Filter filter : filters) { + filter.init(context, config); + } + this.headFilter = filters.get(0); + Filter filter = this.headFilter; + for (int i = 1; i < filters.size(); i++) { + filter._next = filters.get(i); + filter = filter._next; + } + } + } + } + + @Override + @SuppressWarnings("unchecked") + public void destroy(C context, AnyValue config) { + synchronized (filters) { + if (!filters.isEmpty()) { + for (Filter filter : filters) { + filter.destroy(context, config); + } + } + } + } + + @SuppressWarnings("unchecked") + public void addFilter(Filter filter, AnyValue conf) { + filter._conf = conf; + synchronized (filters) { + this.filters.add(filter); + Collections.sort(this.filters); + } + } + + public > T removeFilter(Class filterClass) { + return removeFilter(f -> filterClass.equals(f.getClass())); + } + + public boolean containsFilter(Class filterClass) { + if (this.headFilter == null || filterClass == null) return false; + Filter filter = this.headFilter; + do { + if (filter.getClass().equals(filterClass)) return true; + } while ((filter = filter._next) != null); + return false; + } + + public boolean containsFilter(String filterClassName) { + if (this.headFilter == null || filterClassName == null) return false; + Filter filter = this.headFilter; + do { + if (filter.getClass().getName().equals(filterClassName)) return true; + } while ((filter = filter._next) != null); + return false; + } + + @SuppressWarnings("unchecked") + public > T removeFilter(Predicate predicate) { + if (this.headFilter == null || predicate == null) return null; + synchronized (filters) { + Filter filter = this.headFilter; + Filter prev = null; + do { + if (predicate.test((T) filter)) break; + prev = filter; + } while ((filter = filter._next) != null); + if (filter != null) { + if (prev == null) { + this.headFilter = filter._next; + } else { + prev._next = filter._next; + } + filter._next = null; + this.filters.remove(filter); + } + return (T) filter; + } + } + + @SuppressWarnings("unchecked") + public > List getFilters() { + return (List) new ArrayList<>(filters); + } + + @SuppressWarnings("unchecked") + public abstract void addServlet(S servlet, Object attachment, AnyValue conf, K... mappings); + + public final void prepare(final R request, final P response) { + try { + request.prepare(); + response.filter = this.headFilter; + response.servlet = this; + response.nextEvent(); + } catch (Throwable t) { + response.context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t); + response.finish(true); + } + } + + protected AnyValue getServletConf(Servlet servlet) { + return servlet._conf; + } + + protected void setServletConf(Servlet servlet, AnyValue conf) { + servlet._conf = conf; + } + + public List getServlets() { + return new ArrayList<>(servlets); + } + + protected Stream servletStream() { + return servlets.stream(); + } +} diff --git a/src/org/redkale/net/ProtocolCodec.java b/src/main/java/org/redkale/net/ProtocolCodec.java similarity index 81% rename from src/org/redkale/net/ProtocolCodec.java rename to src/main/java/org/redkale/net/ProtocolCodec.java index 46f4a2617..a8fa7559c 100644 --- a/src/org/redkale/net/ProtocolCodec.java +++ b/src/main/java/org/redkale/net/ProtocolCodec.java @@ -1,171 +1,197 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.util.function.*; -import java.util.logging.Level; - -/** - * - * @author zhangjx - */ -class ProtocolCodec implements CompletionHandler { - - private final AsyncConnection channel; - - private final Context context; - - private Supplier responseSupplier; - - private Consumer responseConsumer; - - private Response resp; - - public ProtocolCodec(Context context, Supplier responseSupplier, - Consumer responseConsumer, AsyncConnection channel) { - this.context = context; - this.channel = channel; - this.responseSupplier = responseSupplier; - this.responseConsumer = responseConsumer; - } - - public ProtocolCodec response(Response resp) { - this.resp = resp; - return this; - } - - private Response createResponse() { - Response response = resp; - if (response == null) { - response = responseSupplier.get(); - } else { - response.prepare(); - } - response.responseSupplier = responseSupplier; - response.responseConsumer = responseConsumer; - return response; - } - - @Override - public void completed(Integer count, ByteBuffer buffer) { - if (count < 1) { - channel.offerBuffer(buffer); - channel.dispose(); // response.init(channel); 在调用之前异常 - return; - } -// { //测试 -// buffer.flip(); -// byte[] bs = new byte[buffer.remaining()]; -// buffer.get(bs); -// System.println(new String(bs)); -// } - buffer.flip(); - final Response response = createResponse(); - try { - decode(buffer, response, 0, null); - } catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer - context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t); - response.finish(true); - } - } - - @Override - public void failed(Throwable exc, ByteBuffer buffer) { - channel.offerBuffer(buffer); - channel.dispose();// response.init(channel); 在调用之前异常 - if (exc != null && context.logger.isLoggable(Level.FINEST)) { - context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc); - } - } - - public void run(final ByteBuffer data) { - if (data != null) { //pipeline模式或UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了 - final Response response = createResponse(); - try { - decode(data, response, 0, null); - } catch (Throwable t) { - context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t); - response.finish(true); - } - return; - } - try { - channel.read(this); - } catch (Exception te) { - channel.dispose();// response.init(channel); 在调用之前异常 - if (context.logger.isLoggable(Level.FINEST)) { - context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te); - } - } - } - - protected void decode(final ByteBuffer buffer, final Response response, final int pipelineIndex, final Request lastreq) { - response.init(channel); - final Request request = response.request; - final int rs = request.readHeader(buffer, lastreq); - if (rs < 0) { //表示数据格式不正确 - final PrepareServlet preparer = context.prepare; - preparer.executeCounter.incrementAndGet(); - channel.offerBuffer(buffer); - if (rs != Integer.MIN_VALUE) preparer.illRequestCounter.incrementAndGet(); - response.finish(true); - if (context.logger.isLoggable(Level.FINEST)) { - context.logger.log(Level.FINEST, "request.readHeader erroneous (" + rs + "), force to close channel "); - } - } else if (rs == 0) { - final PrepareServlet preparer = context.prepare; - preparer.executeCounter.incrementAndGet(); - int pindex = pipelineIndex; - boolean pipeline = false; - Request hreq = lastreq; - if (buffer.hasRemaining()) { - pipeline = true; - if (pindex == 0) pindex++; - request.pipeline(pindex, pindex + 1); - if (hreq == null) hreq = request.copyHeader(); - } else { - request.pipeline(pindex, pindex); - channel.setReadBuffer((ByteBuffer) buffer.clear()); - } - context.executePrepareServlet(request, response); - if (pipeline) { - final Response pipelineResponse = createResponse(); - try { - decode(buffer, pipelineResponse, pindex + 1, hreq); - } catch (Throwable t) { //此处不可 offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer - context.logger.log(Level.WARNING, "prepare pipeline servlet abort, force to close channel ", t); - pipelineResponse.finish(true); - } - } - } else { - channel.setReadBuffer(buffer); - channel.read(new CompletionHandler() { - - @Override - public void completed(Integer count, ByteBuffer attachment) { - if (count < 1) { - channel.offerBuffer(attachment); - channel.dispose(); - return; - } - attachment.flip(); - decode(attachment, response, pipelineIndex, lastreq); - } - - @Override - public void failed(Throwable exc, ByteBuffer attachment) { - context.prepare.illRequestCounter.incrementAndGet(); - channel.offerBuffer(attachment); - response.finish(true); - if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc); - } - }); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.*; +import java.util.logging.Level; + +/** + * + * @author zhangjx + */ +class ProtocolCodec implements CompletionHandler { + + private final AsyncConnection channel; + + private final Context context; + + private Supplier responseSupplier; + + private Consumer responseConsumer; + + private Response resp; + + public ProtocolCodec(Context context, Supplier responseSupplier, + Consumer responseConsumer, AsyncConnection channel) { + this.context = context; + this.channel = channel; + this.responseSupplier = responseSupplier; + this.responseConsumer = responseConsumer; + } + + public ProtocolCodec response(Response resp) { + this.resp = resp; + return this; + } + + private Response createResponse() { + Response response = resp; + if (response == null) { + response = responseSupplier.get(); + } else { + response.prepare(); + } + response.responseSupplier = responseSupplier; + response.responseConsumer = responseConsumer; + return response; + } + + @Override + public void completed(Integer count, ByteBuffer buffer) { + if (count < 1) { + channel.offerBuffer(buffer); + channel.dispose(); // response.init(channel); 在调用之前异常 + return; + } +// { //测试 +// buffer.flip(); +// byte[] bs = new byte[buffer.remaining()]; +// buffer.get(bs); +// System.println(new String(bs)); +// } + buffer.flip(); + final Response response = createResponse(); + try { + decode(buffer, response, 0, null); + } catch (Throwable t) { //此处不可 context.offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer + context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t); + response.finish(true); + } + } + + @Override + public void failed(Throwable exc, ByteBuffer buffer) { + channel.offerBuffer(buffer); + channel.dispose();// response.init(channel); 在调用之前异常 + if (exc != null && context.logger.isLoggable(Level.FINEST) + && !(exc instanceof SocketException && "Connection reset".equals(exc.getMessage()))) { + context.logger.log(Level.FINEST, "Servlet Handler read channel erroneous, force to close channel ", exc); + } + } + + public void start(final ByteBuffer data) { + if (data != null) { //pipeline模式或UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了 + final Response response = createResponse(); + try { + decode(data, response, 0, null); + } catch (Throwable t) { + context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t); + response.finish(true); + } + return; + } + try { + channel.startRead(this); + } catch (Exception te) { + channel.dispose();// response.init(channel); 在调用之前异常 + if (context.logger.isLoggable(Level.FINEST)) { + context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te); + } + } + } + + public void run(final ByteBuffer data) { + if (data != null) { //pipeline模式或UDP连接创建AsyncConnection时已经获取到ByteBuffer数据了 + final Response response = createResponse(); + try { + decode(data, response, 0, null); + } catch (Throwable t) { + context.logger.log(Level.WARNING, "prepare servlet abort, force to close channel ", t); + response.finish(true); + } + return; + } + try { + channel.read(this); + } catch (Exception te) { + channel.dispose();// response.init(channel); 在调用之前异常 + if (context.logger.isLoggable(Level.FINEST)) { + context.logger.log(Level.FINEST, "Servlet read channel erroneous, force to close channel ", te); + } + } + } + + protected void decode(final ByteBuffer buffer, final Response response, final int pipelineIndex, final Request lastreq) { + response.init(channel); + final Request request = response.request; + final int rs = request.readHeader(buffer, lastreq); + if (rs < 0) { //表示数据格式不正确 + final PrepareServlet preparer = context.prepare; + LongAdder ec = preparer.executeCounter; + if (ec != null) ec.increment(); + channel.offerBuffer(buffer); + if (rs != Integer.MIN_VALUE && preparer.illRequestCounter != null) preparer.illRequestCounter.increment(); + response.finish(true); + if (context.logger.isLoggable(Level.FINEST)) { + context.logger.log(Level.FINEST, "request.readHeader erroneous (" + rs + "), force to close channel "); + } + } else if (rs == 0) { + final PrepareServlet preparer = context.prepare; + LongAdder ec = preparer.executeCounter; + if (ec != null) ec.increment(); + int pindex = pipelineIndex; + boolean pipeline = false; + Request hreq = lastreq; + if (buffer.hasRemaining()) { + pipeline = true; + if (pindex == 0) pindex++; + request.pipeline(pindex, pindex + 1); + if (hreq == null) hreq = request.copyHeader(); + } else { + request.pipeline(pindex, pindex); + channel.setReadBuffer((ByteBuffer) buffer.clear()); + } + context.executePrepareServlet(request, response); + if (pipeline) { + final Response pipelineResponse = createResponse(); + try { + decode(buffer, pipelineResponse, pindex + 1, hreq); + } catch (Throwable t) { //此处不可 offerBuffer(buffer); 以免prepare.prepare内部异常导致重复 offerBuffer + context.logger.log(Level.WARNING, "prepare pipeline servlet abort, force to close channel ", t); + pipelineResponse.finish(true); + } + } + } else { + channel.setReadBuffer(buffer); + channel.read(new CompletionHandler() { + + @Override + public void completed(Integer count, ByteBuffer attachment) { + if (count < 1) { + channel.offerBuffer(attachment); + channel.dispose(); + return; + } + attachment.flip(); + decode(attachment, response, pipelineIndex, lastreq); + } + + @Override + public void failed(Throwable exc, ByteBuffer attachment) { + if (context.prepare.illRequestCounter != null) context.prepare.illRequestCounter.increment(); + channel.offerBuffer(attachment); + response.finish(true); + if (exc != null) request.context.logger.log(Level.FINER, "Servlet read channel erroneous, force to close channel ", exc); + } + }); + } + } + +} diff --git a/src/org/redkale/net/ProtocolServer.java b/src/main/java/org/redkale/net/ProtocolServer.java similarity index 73% rename from src/org/redkale/net/ProtocolServer.java rename to src/main/java/org/redkale/net/ProtocolServer.java index be04e263a..0dcc9b5a5 100644 --- a/src/org/redkale/net/ProtocolServer.java +++ b/src/main/java/org/redkale/net/ProtocolServer.java @@ -1,74 +1,68 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.net.*; -import java.util.*; -import javax.annotation.Resource; -import org.redkale.boot.Application; -import org.redkale.util.AnyValue; - -/** - * 协议底层Server - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class ProtocolServer { - - protected final Context context; - - //最大连接数,小于1表示无限制 - protected int maxconns; - - @Resource - protected Application application; - - public abstract void open(AnyValue config) throws IOException; - - public abstract void bind(SocketAddress local, int backlog) throws IOException; - - public abstract Set> supportedOptions(); - - public abstract void setOption(SocketOption name, T value) throws IOException; - - public abstract void accept(Application application, Server server) throws IOException; - - public abstract void close() throws IOException; - - protected ProtocolServer(Context context) { - this.context = context; - this.maxconns = context.getMaxconns(); - } - - //--------------------------------------------------------------------- - public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader, String netimpl) { - if (netimpl != null) netimpl = netimpl.trim(); - if ("TCP".equalsIgnoreCase(protocol)) { - return new AsyncNioTcpProtocolServer(context); - } else if ("UDP".equalsIgnoreCase(protocol)) { - return new AsyncNioUdpProtocolServer(context); - } else if (netimpl == null || netimpl.isEmpty()) { - throw new RuntimeException(ProtocolServer.class.getSimpleName() + " not support protocol " + protocol); - } - try { - if (classLoader == null) classLoader = Thread.currentThread().getContextClassLoader(); - Class clazz = classLoader.loadClass(netimpl); - return (ProtocolServer) clazz.getDeclaredConstructor(Context.class).newInstance(context); - } catch (Exception e) { - throw new RuntimeException(ProtocolServer.class.getSimpleName() + "(netimple=" + netimpl + ") newinstance error", e); - } - } - - public abstract long getCreateConnectionCount(); - - public abstract long getClosedConnectionCount(); - - public abstract long getLivingConnectionCount(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.IOException; +import java.net.*; +import java.util.*; +import javax.annotation.Resource; +import org.redkale.boot.Application; +import org.redkale.util.AnyValue; + +/** + * 协议底层Server + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class ProtocolServer { + + protected final Context context; + + //最大连接数,小于1表示无限制 + protected int maxconns; + + @Resource + protected Application application; + + public abstract void open(AnyValue config) throws IOException; + + public abstract void bind(SocketAddress local, int backlog) throws IOException; + + public abstract Set> supportedOptions(); + + public abstract void setOption(SocketOption name, T value) throws IOException; + + public abstract void accept(Application application, Server server) throws IOException; + + public abstract void close() throws IOException; + + protected ProtocolServer(Context context) { + this.context = context; + this.maxconns = context.getMaxconns(); + } + + //--------------------------------------------------------------------- + public static ProtocolServer create(String protocol, Context context, ClassLoader classLoader) { + if ("TCP".equalsIgnoreCase(protocol)) { + return new AsyncNioTcpProtocolServer(context); + } else if ("UDP".equalsIgnoreCase(protocol)) { + return new AsyncNioUdpProtocolServer(context); + } else { + throw new RuntimeException(ProtocolServer.class.getSimpleName() + " not support protocol " + protocol); + } + } + + public abstract AsyncGroup getAsyncGroup(); + + public abstract long getCreateConnectionCount(); + + public abstract long getClosedConnectionCount(); + + public abstract long getLivingConnectionCount(); +} diff --git a/src/org/redkale/net/Request.java b/src/main/java/org/redkale/net/Request.java similarity index 94% rename from src/org/redkale/net/Request.java rename to src/main/java/org/redkale/net/Request.java index 396bc306b..fde648ab3 100644 --- a/src/org/redkale/net/Request.java +++ b/src/main/java/org/redkale/net/Request.java @@ -1,155 +1,155 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.nio.ByteBuffer; -import java.util.*; -import org.redkale.convert.bson.BsonConvert; -import org.redkale.convert.json.JsonConvert; - -/** - * 协议请求对象 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Context子类型 - */ -public abstract class Request { - - protected final C context; - - protected final BsonConvert bsonConvert; - - protected final JsonConvert jsonConvert; - - protected long createtime; - - protected boolean keepAlive; - - protected int pipelineIndex; - - protected int pipelineCount; - - protected boolean pipelineOver; - - protected int hashid; - - protected AsyncConnection channel; - - /** - * properties 与 attributes 的区别在于:调用recycle时, attributes会被清空而properties会保留; - * properties 通常存放需要永久绑定在request里的一些对象 - */ - private final Map properties = new HashMap<>(); - - protected final Map attributes = new HashMap<>(); - - protected Request(C context) { - this.context = context; - this.bsonConvert = context.getBsonConvert(); - this.jsonConvert = context.getJsonConvert(); - } - - protected Request copyHeader() { - return null; - } - - protected Request pipeline(int pipelineIndex, int pipelineCount) { - this.pipelineIndex = pipelineIndex; - this.pipelineCount = pipelineCount; - return this; - } - - /** - * 返回值:Integer.MIN_VALUE: 帧数据; -1:数据不合法; 0:解析完毕; >0: 需再读取的字节数。 - * - * @param buffer ByteBuffer对象 - * @param last 同一Channel的上一个Request - * - * @return 缺少的字节数 - */ - protected abstract int readHeader(ByteBuffer buffer, Request last); - - protected abstract void prepare(); - - protected void recycle() { - hashid = 0; - createtime = 0; - pipelineIndex = 0; - pipelineCount = 0; - pipelineOver = false; - keepAlive = false; - attributes.clear(); - channel = null; // close it by response - } - - protected T setProperty(String name, T value) { - properties.put(name, value); - return value; - } - - @SuppressWarnings("unchecked") - protected T getProperty(String name) { - return (T) properties.get(name); - } - - @SuppressWarnings("unchecked") - protected T removeProperty(String name) { - return (T) properties.remove(name); - } - - protected Map getProperties() { - return properties; - } - - protected InputStream newInputStream() { - return channel.newInputStream(); - } - - public T setAttribute(String name, T value) { - attributes.put(name, value); - return value; - } - - @SuppressWarnings("unchecked") - public T getAttribute(String name) { - return (T) attributes.get(name); - } - - @SuppressWarnings("unchecked") - public T removeAttribute(String name) { - return (T) attributes.remove(name); - } - - public Map getAttributes() { - return attributes; - } - - public ChannelContext getChannelContext() { - return channel; - } - - public C getContext() { - return this.context; - } - - public long getCreatetime() { - return createtime; - } - - public int getHashid() { - return hashid; - } - - public Request hashid(int hashid) { - this.hashid = hashid; - return this; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.nio.ByteBuffer; +import java.util.*; +import org.redkale.convert.bson.BsonConvert; +import org.redkale.convert.json.JsonConvert; + +/** + * 协议请求对象 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Context子类型 + */ +public abstract class Request { + + protected final C context; + + protected final BsonConvert bsonConvert; + + protected final JsonConvert jsonConvert; + + protected long createtime; + + protected boolean keepAlive; + + protected int pipelineIndex; + + protected int pipelineCount; + + protected boolean pipelineOver; + + protected int hashid; + + protected AsyncConnection channel; + + /** + * properties 与 attributes 的区别在于:调用recycle时, attributes会被清空而properties会保留; + * properties 通常存放需要永久绑定在request里的一些对象 + */ + private final Map properties = new HashMap<>(); + + protected final Map attributes = new HashMap<>(); + + protected Request(C context) { + this.context = context; + this.bsonConvert = context.getBsonConvert(); + this.jsonConvert = context.getJsonConvert(); + } + + protected Request copyHeader() { + return null; + } + + protected Request pipeline(int pipelineIndex, int pipelineCount) { + this.pipelineIndex = pipelineIndex; + this.pipelineCount = pipelineCount; + return this; + } + + /** + * 返回值:Integer.MIN_VALUE: 帧数据; -1:数据不合法; 0:解析完毕; >0: 需再读取的字节数。 + * + * @param buffer ByteBuffer对象 + * @param last 同一Channel的上一个Request + * + * @return 缺少的字节数 + */ + protected abstract int readHeader(ByteBuffer buffer, Request last); + + protected abstract void prepare(); + + protected void recycle() { + hashid = 0; + createtime = 0; + pipelineIndex = 0; + pipelineCount = 0; + pipelineOver = false; + keepAlive = false; + attributes.clear(); + channel = null; // close it by response + } + + protected T setProperty(String name, T value) { + properties.put(name, value); + return value; + } + + @SuppressWarnings("unchecked") + protected T getProperty(String name) { + return (T) properties.get(name); + } + + @SuppressWarnings("unchecked") + protected T removeProperty(String name) { + return (T) properties.remove(name); + } + + protected Map getProperties() { + return properties; + } + + protected InputStream newInputStream() { + return ((AsyncNioConnection) channel).newInputStream(); + } + + public T setAttribute(String name, T value) { + attributes.put(name, value); + return value; + } + + @SuppressWarnings("unchecked") + public T getAttribute(String name) { + return (T) attributes.get(name); + } + + @SuppressWarnings("unchecked") + public T removeAttribute(String name) { + return (T) attributes.remove(name); + } + + public Map getAttributes() { + return attributes; + } + + public ChannelContext getChannelContext() { + return channel; + } + + public C getContext() { + return this.context; + } + + public long getCreatetime() { + return createtime; + } + + public int getHashid() { + return hashid; + } + + public Request hashid(int hashid) { + this.hashid = hashid; + return this; + } + +} diff --git a/src/org/redkale/net/Response.java b/src/main/java/org/redkale/net/Response.java similarity index 96% rename from src/org/redkale/net/Response.java rename to src/main/java/org/redkale/net/Response.java index a5bd2da90..7c402a18f 100644 --- a/src/org/redkale/net/Response.java +++ b/src/main/java/org/redkale/net/Response.java @@ -1,387 +1,390 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.util.function.*; -import java.util.logging.Level; -import org.redkale.util.ByteTuple; - -/** - * 协议响应对象 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Context的子类型 - * @param Request的子类型 - */ -@SuppressWarnings("unchecked") -public abstract class Response> { - - protected final C context; - - protected Supplier responseSupplier; //虚拟构建的Response可能不存在responseSupplier - - protected Consumer responseConsumer; //虚拟构建的Response可能不存在responseConsumer - - protected final R request; - - protected AsyncConnection channel; - - private volatile boolean inited = true; - - protected Object output; //输出的结果对象 - - protected BiConsumer> recycleListener; - - protected Filter> filter; - - protected Servlet> servlet; - - private final CompletionHandler finishBytesHandler = new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - finish(); - } - - @Override - public void failed(Throwable exc, Void attachment) { - finish(true); - } - - }; - - private final CompletionHandler finishBufferHandler = new CompletionHandler() { - - @Override - public void completed(Integer result, ByteBuffer attachment) { - channel.offerBuffer(attachment); - finish(); - } - - @Override - public void failed(Throwable exc, ByteBuffer attachment) { - channel.offerBuffer(attachment); - finish(true); - } - - }; - - private final CompletionHandler finishBuffersHandler = new CompletionHandler() { - - @Override - public void completed(final Integer result, final ByteBuffer[] attachments) { - if (attachments != null) { - for (ByteBuffer attachment : attachments) { - channel.offerBuffer(attachment); - } - } - finish(); - } - - @Override - public void failed(Throwable exc, final ByteBuffer[] attachments) { - if (attachments != null) { - for (ByteBuffer attachment : attachments) { - channel.offerBuffer(attachment); - } - } - finish(true); - } - - }; - - protected Response(C context, final R request) { - this.context = context; - this.request = request; - } - - protected AsyncConnection removeChannel() { - AsyncConnection ch = this.channel; - this.channel = null; - this.request.channel = null; - return ch; - } - - protected void prepare() { - inited = true; - request.prepare(); - } - - protected boolean recycle() { - if (!inited) return false; - this.output = null; - this.filter = null; - this.servlet = null; - boolean notpipeline = request.pipelineIndex == 0 || request.pipelineOver; - request.recycle(); - if (channel != null) { - if (notpipeline) channel.dispose(); - channel = null; - } - this.responseSupplier = null; - this.responseConsumer = null; - this.inited = false; - return true; - } - - protected void refuseAlive() { - this.request.keepAlive = false; - } - - protected void init(AsyncConnection channel) { - this.channel = channel; - this.request.channel = channel; - this.request.createtime = System.currentTimeMillis(); - } - - protected void setFilter(Filter> filter) { - this.filter = filter; - } - - protected void thenEvent(Servlet servlet) { - this.servlet = servlet; - } - - @SuppressWarnings("unchecked") - public void nextEvent() throws IOException { - if (this.filter != null) { - Filter runner = this.filter; - this.filter = this.filter._next; - runner.doFilter(request, this); - return; - } - if (this.servlet != null) { - Servlet s = this.servlet; - this.servlet = null; - s.execute(request, this); - } - } - - public void recycleListener(BiConsumer> recycleListener) { - this.recycleListener = recycleListener; - } - - public Object getOutput() { - return output; - } - - /** - * 是否已关闭 - * - * @return boolean - */ - public boolean isClosed() { - return !this.inited; - } - - public void finish() { - this.finish(false); - } - - public void finish(boolean kill) { - if (!this.inited) return; //避免重复关闭 - //System.println("耗时: " + (System.currentTimeMillis() - request.createtime)); - if (kill) refuseAlive(); - if (this.recycleListener != null) { - try { - this.recycleListener.accept(request, this); - } catch (Exception e) { - context.logger.log(Level.WARNING, "Response.recycleListener error, request = " + request, e); - } - this.recycleListener = null; - } - if (request.keepAlive && (request.pipelineIndex == 0 || request.pipelineOver)) { - AsyncConnection conn = removeChannel(); - if (conn != null && conn.protocolCodec != null) { - this.responseConsumer.accept(this); - conn.read(conn.protocolCodec); - } else { - Supplier poolSupplier = this.responseSupplier; - Consumer poolConsumer = this.responseConsumer; - this.recycle(); - new ProtocolCodec(context, poolSupplier, poolConsumer, conn).response(this).run(null); - } - } else { - this.responseConsumer.accept(this); - } - } - - public final void finish(final byte[] bs) { - finish(false, bs, 0, bs.length); - } - - public final void finish(final byte[] bs, int offset, int length) { - finish(false, bs, offset, length); - } - - public final void finish(final ByteTuple array) { - finish(false, array.content(), array.offset(), array.length()); - } - - public final void finish(boolean kill, final byte[] bs) { - finish(kill, bs, 0, bs.length); - } - - public final void finish(boolean kill, final ByteTuple array) { - finish(kill, array.content(), array.offset(), array.length()); - } - - public void finish(boolean kill, final byte[] bs, int offset, int length) { - if (!this.inited) return; //避免重复关闭 - if (kill) refuseAlive(); - if (this.channel.hasPipelineData()) { - this.channel.flushPipelineData(null, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - channel.write(bs, offset, length, finishBytesHandler); - } - - @Override - public void failed(Throwable exc, Void attachment) { - finishBytesHandler.failed(exc, attachment); - } - }); - } else { - this.channel.write(bs, offset, length, finishBytesHandler); - } - } - - public void finish(boolean kill, final byte[] bs, int offset, int length, final byte[] bs2, int offset2, int length2, Consumer callback, A attachment) { - if (!this.inited) return; //避免重复关闭 - if (kill) refuseAlive(); - if (this.channel.hasPipelineData()) { - this.channel.flushPipelineData(null, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - channel.write(bs, offset, length, bs2, offset2, length2, callback, attachment, finishBytesHandler); - } - - @Override - public void failed(Throwable exc, Void attachment) { - finishBytesHandler.failed(exc, attachment); - } - }); - } else { - this.channel.write(bs, offset, length, bs2, offset2, length2, callback, attachment, finishBytesHandler); - } - } - - protected final void finish(ByteBuffer buffer) { - finish(false, buffer); - } - - protected final void finish(ByteBuffer... buffers) { - finish(false, buffers); - } - - protected void finish(boolean kill, ByteBuffer buffer) { - if (!this.inited) return; //避免重复关闭 - if (kill) refuseAlive(); - if (this.channel.hasPipelineData()) { - this.channel.flushPipelineData(null, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - channel.write(buffer, buffer, finishBufferHandler); - } - - @Override - public void failed(Throwable exc, Void attachment) { - finishBufferHandler.failed(exc, buffer); - } - }); - } else { - this.channel.write(buffer, buffer, finishBufferHandler); - } - } - - protected void finish(boolean kill, ByteBuffer... buffers) { - if (!this.inited) return; //避免重复关闭 - if (kill) refuseAlive(); - if (this.channel.hasPipelineData()) { - this.channel.flushPipelineData(null, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - channel.write(buffers, buffers, finishBuffersHandler); - } - - @Override - public void failed(Throwable exc, Void attachment) { - finishBuffersHandler.failed(exc, buffers); - } - }); - } else { - this.channel.write(buffers, buffers, finishBuffersHandler); - } - } - - protected void send(final ByteTuple array, final CompletionHandler handler) { - this.channel.write(array, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - if (handler != null) handler.completed(result, attachment); - } - - @Override - public void failed(Throwable exc, Void attachment) { - if (handler != null) handler.failed(exc, attachment); - } - - }); - } - - protected void send(final ByteBuffer buffer, final A attachment, final CompletionHandler handler) { - this.channel.write(buffer, attachment, new CompletionHandler() { - - @Override - public void completed(Integer result, A attachment) { - channel.offerBuffer(buffer); - if (handler != null) handler.completed(result, attachment); - } - - @Override - public void failed(Throwable exc, A attachment) { - channel.offerBuffer(buffer); - if (handler != null) handler.failed(exc, attachment); - } - - }); - } - - protected void send(final ByteBuffer[] buffers, A attachment, final CompletionHandler handler) { - this.channel.write(buffers, attachment, new CompletionHandler() { - - @Override - public void completed(Integer result, A attachment) { - channel.offerBuffer(buffers); - if (handler != null) handler.completed(result, attachment); - } - - @Override - public void failed(Throwable exc, A attachment) { - for (ByteBuffer buffer : buffers) { - channel.offerBuffer(buffer); - } - if (handler != null) handler.failed(exc, attachment); - } - - }); - } - - public C getContext() { - return context; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.CompletionHandler; +import java.util.function.*; +import java.util.logging.Level; +import org.redkale.util.ByteTuple; + +/** + * 协议响应对象 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Context的子类型 + * @param Request的子类型 + */ +@SuppressWarnings("unchecked") +public abstract class Response> { + + protected final C context; + + protected Supplier responseSupplier; //虚拟构建的Response可能不存在responseSupplier + + protected Consumer responseConsumer; //虚拟构建的Response可能不存在responseConsumer + + protected final R request; + + protected final WorkThread thread; + + protected AsyncConnection channel; + + private volatile boolean inited = true; + + protected Object output; //输出的结果对象 + + protected BiConsumer> recycleListener; + + protected Filter> filter; + + protected Servlet> servlet; + + private final CompletionHandler finishBytesHandler = new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + finish(); + } + + @Override + public void failed(Throwable exc, Void attachment) { + finish(true); + } + + }; + + private final CompletionHandler finishBufferHandler = new CompletionHandler() { + + @Override + public void completed(Integer result, ByteBuffer attachment) { + channel.offerBuffer(attachment); + finish(); + } + + @Override + public void failed(Throwable exc, ByteBuffer attachment) { + channel.offerBuffer(attachment); + finish(true); + } + + }; + + private final CompletionHandler finishBuffersHandler = new CompletionHandler() { + + @Override + public void completed(final Integer result, final ByteBuffer[] attachments) { + if (attachments != null) { + for (ByteBuffer attachment : attachments) { + channel.offerBuffer(attachment); + } + } + finish(); + } + + @Override + public void failed(Throwable exc, final ByteBuffer[] attachments) { + if (attachments != null) { + for (ByteBuffer attachment : attachments) { + channel.offerBuffer(attachment); + } + } + finish(true); + } + + }; + + protected Response(C context, final R request) { + this.context = context; + this.request = request; + this.thread = WorkThread.currWorkThread(); + } + + protected AsyncConnection removeChannel() { + AsyncConnection ch = this.channel; + this.channel = null; + this.request.channel = null; + return ch; + } + + protected void prepare() { + inited = true; + request.prepare(); + } + + protected boolean recycle() { + if (!inited) return false; + this.output = null; + this.filter = null; + this.servlet = null; + boolean notpipeline = request.pipelineIndex == 0 || request.pipelineOver; + request.recycle(); + if (channel != null) { + if (notpipeline) channel.dispose(); + channel = null; + } + this.responseSupplier = null; + this.responseConsumer = null; + this.inited = false; + return true; + } + + protected void refuseAlive() { + this.request.keepAlive = false; + } + + protected void init(AsyncConnection channel) { + this.channel = channel; + this.request.channel = channel; + this.request.createtime = System.currentTimeMillis(); + } + + protected void setFilter(Filter> filter) { + this.filter = filter; + } + + protected void thenEvent(Servlet servlet) { + this.servlet = servlet; + } + + @SuppressWarnings("unchecked") + public void nextEvent() throws IOException { + if (this.filter != null) { + Filter runner = this.filter; + this.filter = this.filter._next; + runner.doFilter(request, this); + return; + } + if (this.servlet != null) { + Servlet s = this.servlet; + this.servlet = null; + s.execute(request, this); + } + } + + public void recycleListener(BiConsumer> recycleListener) { + this.recycleListener = recycleListener; + } + + public Object getOutput() { + return output; + } + + /** + * 是否已关闭 + * + * @return boolean + */ + public boolean isClosed() { + return !this.inited; + } + + public void finish() { + this.finish(false); + } + + public void finish(boolean kill) { + if (!this.inited) return; //避免重复关闭 + //System.println("耗时: " + (System.currentTimeMillis() - request.createtime)); + if (kill) refuseAlive(); + if (this.recycleListener != null) { + try { + this.recycleListener.accept(request, this); + } catch (Exception e) { + context.logger.log(Level.WARNING, "Response.recycleListener error, request = " + request, e); + } + this.recycleListener = null; + } + if (request.keepAlive && (request.pipelineIndex == 0 || request.pipelineOver)) { + AsyncConnection conn = removeChannel(); + if (conn != null && conn.protocolCodec != null) { + this.responseConsumer.accept(this); + conn.read(conn.protocolCodec); + } else { + Supplier poolSupplier = this.responseSupplier; + Consumer poolConsumer = this.responseConsumer; + this.recycle(); + new ProtocolCodec(context, poolSupplier, poolConsumer, conn).response(this).run(null); + } + } else { + this.responseConsumer.accept(this); + } + } + + public final void finish(final byte[] bs) { + finish(false, bs, 0, bs.length); + } + + public final void finish(final byte[] bs, int offset, int length) { + finish(false, bs, offset, length); + } + + public final void finish(final ByteTuple array) { + finish(false, array.content(), array.offset(), array.length()); + } + + public final void finish(boolean kill, final byte[] bs) { + finish(kill, bs, 0, bs.length); + } + + public final void finish(boolean kill, final ByteTuple array) { + finish(kill, array.content(), array.offset(), array.length()); + } + + public void finish(boolean kill, final byte[] bs, int offset, int length) { + if (!this.inited) return; //避免重复关闭 + if (kill) refuseAlive(); + if (this.channel.hasPipelineData()) { + this.channel.flushPipelineData(null, new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + channel.write(bs, offset, length, finishBytesHandler); + } + + @Override + public void failed(Throwable exc, Void attachment) { + finishBytesHandler.failed(exc, attachment); + } + }); + } else { + this.channel.write(bs, offset, length, finishBytesHandler); + } + } + + public void finish(boolean kill, final byte[] bs, int offset, int length, final byte[] bs2, int offset2, int length2, Consumer callback, A attachment) { + if (!this.inited) return; //避免重复关闭 + if (kill) refuseAlive(); + if (this.channel.hasPipelineData()) { + this.channel.flushPipelineData(null, new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + channel.write(bs, offset, length, bs2, offset2, length2, callback, attachment, finishBytesHandler); + } + + @Override + public void failed(Throwable exc, Void attachment) { + finishBytesHandler.failed(exc, attachment); + } + }); + } else { + this.channel.write(bs, offset, length, bs2, offset2, length2, callback, attachment, finishBytesHandler); + } + } + + protected final void finish(ByteBuffer buffer) { + finish(false, buffer); + } + + protected final void finish(ByteBuffer... buffers) { + finish(false, buffers); + } + + protected void finish(boolean kill, ByteBuffer buffer) { + if (!this.inited) return; //避免重复关闭 + if (kill) refuseAlive(); + if (this.channel.hasPipelineData()) { + this.channel.flushPipelineData(null, new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + channel.write(buffer, buffer, finishBufferHandler); + } + + @Override + public void failed(Throwable exc, Void attachment) { + finishBufferHandler.failed(exc, buffer); + } + }); + } else { + this.channel.write(buffer, buffer, finishBufferHandler); + } + } + + protected void finish(boolean kill, ByteBuffer... buffers) { + if (!this.inited) return; //避免重复关闭 + if (kill) refuseAlive(); + if (this.channel.hasPipelineData()) { + this.channel.flushPipelineData(null, new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + channel.write(buffers, buffers, finishBuffersHandler); + } + + @Override + public void failed(Throwable exc, Void attachment) { + finishBuffersHandler.failed(exc, buffers); + } + }); + } else { + this.channel.write(buffers, buffers, finishBuffersHandler); + } + } + + protected void send(final ByteTuple array, final CompletionHandler handler) { + this.channel.write(array, new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + if (handler != null) handler.completed(result, attachment); + } + + @Override + public void failed(Throwable exc, Void attachment) { + if (handler != null) handler.failed(exc, attachment); + } + + }); + } + + protected void send(final ByteBuffer buffer, final A attachment, final CompletionHandler handler) { + this.channel.write(buffer, attachment, new CompletionHandler() { + + @Override + public void completed(Integer result, A attachment) { + channel.offerBuffer(buffer); + if (handler != null) handler.completed(result, attachment); + } + + @Override + public void failed(Throwable exc, A attachment) { + channel.offerBuffer(buffer); + if (handler != null) handler.failed(exc, attachment); + } + + }); + } + + protected void send(final ByteBuffer[] buffers, A attachment, final CompletionHandler handler) { + this.channel.write(buffers, attachment, new CompletionHandler() { + + @Override + public void completed(Integer result, A attachment) { + channel.offerBuffer(buffers); + if (handler != null) handler.completed(result, attachment); + } + + @Override + public void failed(Throwable exc, A attachment) { + for (ByteBuffer buffer : buffers) { + channel.offerBuffer(buffer); + } + if (handler != null) handler.failed(exc, attachment); + } + + }); + } + + public C getContext() { + return context; + } +} diff --git a/src/main/java/org/redkale/net/SSLBuilder.java b/src/main/java/org/redkale/net/SSLBuilder.java new file mode 100644 index 000000000..0020f7198 --- /dev/null +++ b/src/main/java/org/redkale/net/SSLBuilder.java @@ -0,0 +1,168 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.security.*; +import java.security.cert.*; +import java.util.*; +import java.util.logging.*; +import javax.net.ssl.*; +import org.redkale.util.*; + +/** + * 根据配置生成SSLContext + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class SSLBuilder { + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected String[] ciphers; + + protected String[] protocols; + + protected boolean wantClientAuth; + + protected boolean needClientAuth; + + public SSLContext createSSLContext(Server server, AnyValue sslConf) throws Exception { + String protocol = sslConf.getValue("protocol", "TLS"); + String clientauth = sslConf.getValue("clientAuth", "none"); + String sslProviderImpl = sslConf.getValue("sslProvider"); + String jsseProviderImpl = sslConf.getValue("jsseProvider"); + String enabledProtocols = sslConf.getValue("protocols", "").replaceAll("\\s+", "") + .replace(';', ',').replace(':', ',').replaceAll(",+", ",").replaceAll(",$", ""); + String enabledCiphers = sslConf.getValue("ciphers", "").replaceAll("\\s+", "") + .replace(';', ',').replace(':', ',').replaceAll(",+", ",").replaceAll(",$", ""); + + String keyfile = sslConf.getValue("keystoreFile"); + String keypass = sslConf.getValue("keystorePass", ""); + String keyType = sslConf.getValue("keystoreType", "JKS"); + String keyAlgorithm = sslConf.getValue("keystoreAlgorithm", "SunX509"); + + String trustfile = sslConf.getValue("truststoreFile"); + String trustpass = sslConf.getValue("truststorePass", ""); + String trustType = sslConf.getValue("truststoreType", "JKS"); + String trustAlgorithm = sslConf.getValue("truststoreAlgorithm", "SunX509"); + + Provider sslProvider = null; + Provider jsseProvider = null; + if (sslProviderImpl != null) { + Class providerClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(sslProviderImpl); + sslProvider = providerClass.getConstructor().newInstance(); + } + if (jsseProviderImpl != null) { + Class providerClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(jsseProviderImpl); + jsseProvider = providerClass.getConstructor().newInstance(); + } + + KeyManager[] keyManagers = null; + if (keyfile != null) { + KeyManagerFactory kmf = jsseProvider == null ? KeyManagerFactory.getInstance(keyAlgorithm) : KeyManagerFactory.getInstance(keyAlgorithm, jsseProvider); + KeyStore ks = jsseProvider == null ? KeyStore.getInstance(keyType) : KeyStore.getInstance(keyType, jsseProvider); + ks.load(new FileInputStream(keyfile), keypass.toCharArray()); + kmf.init(ks, keypass.toCharArray()); + keyManagers = kmf.getKeyManagers(); + } + + if ("WANT".equalsIgnoreCase(clientauth)) { + this.wantClientAuth = true; + } else if ("NEED".equalsIgnoreCase(clientauth)) { + this.needClientAuth = true; + } + + TrustManager[] trustManagers; + if (trustfile != null) { + KeyStore ts = jsseProvider == null ? KeyStore.getInstance(trustType) : KeyStore.getInstance(trustType, jsseProvider); + ts.load(new FileInputStream(trustfile), trustpass.toCharArray()); + TrustManagerFactory tmf = jsseProvider == null ? TrustManagerFactory.getInstance(trustAlgorithm) : TrustManagerFactory.getInstance(trustAlgorithm, jsseProvider); + tmf.init(ts); + trustManagers = tmf.getTrustManagers(); + } else { + trustManagers = new TrustManager[]{new X509TrustManager() { + @Override + public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return new X509Certificate[0]; + } + }}; + } + + SSLContext sslContext; + if (sslProvider == null) { + sslContext = SSLContext.getInstance(protocol); + } else { + sslContext = SSLContext.getInstance(protocol, sslProvider); + } + sslContext.init(keyManagers, trustManagers, new SecureRandom()); + if (!enabledProtocols.isEmpty()) { + HashSet set = new HashSet<>(); + HashSet unset = new HashSet<>(); + String[] protocolArray = sslContext.getSupportedSSLParameters().getProtocols(); + for (String p : enabledProtocols.split(",")) { + if (Utility.contains(protocolArray, p)) { + set.add(p); + } else { + unset.add(p); + } + } + if (!set.isEmpty()) { + this.protocols = set.toArray(new String[set.size()]); + } + if (!unset.isEmpty()) { + logger.log(Level.WARNING, "protocols " + unset + " is not supported, only support: " + Arrays.toString(protocolArray)); + } + } + if (!enabledCiphers.isEmpty()) { + HashSet set = new HashSet<>(); + HashSet unset = new HashSet<>(); + String[] cipherArray = sslContext.getSupportedSSLParameters().getCipherSuites(); + for (String c : enabledCiphers.split(",")) { + if (Utility.contains(cipherArray, c)) { + set.add(c); + } else { + unset.add(c); + } + } + if (!set.isEmpty()) { + this.ciphers = set.toArray(new String[set.size()]); + } + if (!unset.isEmpty()) { + logger.log(Level.WARNING, "cipherSuites " + unset + " is not supported, only support: " + Arrays.toString(cipherArray)); + } + } + return sslContext; + } + + public SSLEngine createSSLEngine(SSLContext sslContext, boolean client) { + SSLEngine engine = sslContext.createSSLEngine(); + if (protocols != null) { + engine.setEnabledProtocols(protocols); + } + if (ciphers != null) { + engine.setEnabledCipherSuites(ciphers); + } + engine.setUseClientMode(client); + if (wantClientAuth) { + engine.setWantClientAuth(true); + } else if (needClientAuth) { + engine.setNeedClientAuth(true); + } + return engine; + } +} diff --git a/src/org/redkale/net/Server.java b/src/main/java/org/redkale/net/Server.java similarity index 79% rename from src/org/redkale/net/Server.java rename to src/main/java/org/redkale/net/Server.java index ed20b7e30..85ce99881 100644 --- a/src/org/redkale/net/Server.java +++ b/src/main/java/org/redkale/net/Server.java @@ -1,463 +1,503 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; -import java.util.*; -import java.util.concurrent.atomic.*; -import java.util.logging.*; -import javax.net.ssl.SSLContext; -import org.redkale.boot.Application; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 请求ID的数据类型, 例如HTTP协议请求标识为url,请求ID的数据类型就是String - * @param Context - * @param Request - * @param

    Response - * @param Servlet - */ -public abstract class Server, P extends Response, S extends Servlet> { - - public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT"; - - //@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR - //public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR"; - - public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY"; - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - //------------------------------------------------------------- - //服务的启动时间 - protected final long serverStartTime; - - //服务的名称 - protected String name; - - //应用层协议名 - protected final String netprotocol; - - //依赖注入工厂类 - protected final ResourceFactory resourceFactory; - - //服务的根Servlet - protected final PrepareServlet prepare; - - //ClassLoader - protected RedkaleClassLoader serverClassLoader; - - //SSL - protected SSLContext sslContext; - - //服务的上下文对象 - protected C context; - - //服务的配置信息 - protected AnyValue config; - - //服务数据的编解码,null视为UTF-8 - protected Charset charset; - - //服务的监听端口 - protected InetSocketAddress address; - - //连接队列大小 - protected int backlog; - - //传输层协议的服务 - protected ProtocolServer serverChannel; - - //ByteBuffer的容量大小 - protected int bufferCapacity; - - //ByteBuffer池大小 - protected int bufferPoolSize; - - //Response池大小 - protected int responsePoolSize; - - //最大连接数, 为0表示没限制 - protected int maxconns; - - //请求包大小的上限,单位:字节 - protected int maxbody; - - //Keep-Alive IO读取的超时秒数,小于1视为不设置 - protected int aliveTimeoutSeconds; - - //IO读取的超时秒数,小于1视为不设置 - protected int readTimeoutSeconds; - - //IO写入 的超时秒数,小于1视为不设置 - protected int writeTimeoutSeconds; - - protected Server(Application application, long serverStartTime, String netprotocol, ResourceFactory resourceFactory, PrepareServlet servlet) { - this.serverStartTime = serverStartTime; - this.netprotocol = netprotocol; - this.resourceFactory = resourceFactory; - this.prepare = servlet; - } - - public void init(final AnyValue config) throws Exception { - Objects.requireNonNull(config); - this.config = config; - this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80)); - this.charset = Charset.forName(config.getValue("charset", "UTF-8")); - this.maxconns = config.getIntValue("maxconns", 0); - this.aliveTimeoutSeconds = config.getIntValue("aliveTimeoutSeconds", 30); - this.readTimeoutSeconds = config.getIntValue("readTimeoutSeconds", 0); - this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0); - this.backlog = parseLenth(config.getValue("backlog"), 1024); - this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024); - int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(netprotocol) ? 1350 : 32 * 1024); - this.bufferCapacity = "UDP".equalsIgnoreCase(netprotocol) ? bufCapacity : (bufCapacity < 1024 ? 1024 : bufCapacity); - this.bufferPoolSize = config.getIntValue("bufferPoolSize", Runtime.getRuntime().availableProcessors() * 4); - this.responsePoolSize = config.getIntValue("responsePoolSize", Runtime.getRuntime().availableProcessors() * 2); - this.name = config.getValue("name", "Server-" + (config == null ? netprotocol : config.getValue("protocol", netprotocol).replaceFirst("\\..+", "").toUpperCase()) + "-" + this.address.getPort()); - if (!this.name.matches("^[a-zA-Z][\\w_-]{1,64}$")) throw new RuntimeException("server.name (" + this.name + ") is illegal"); - AnyValue sslConf = config.getAnyValue("ssl"); - if (sslConf != null) { - String creatorClass = sslConf.getValue("creator", SSLCreator.class.getName()); - SSLCreator creator = null; - if (SSLCreator.class.getName().equals(creatorClass) || creatorClass.isEmpty()) { - creator = new SSLCreator() { - }; - } else { - ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); - creator = ((SSLCreator) classLoader.loadClass(creatorClass).getDeclaredConstructor().newInstance()); - } - this.resourceFactory.inject(creator); - this.sslContext = creator.create(this, sslConf); - } - } - - protected static int parseLenth(String value, int defValue) { - return (int) parseLenth(value, defValue + 0L); - } - - protected static long parseLenth(String value, long defValue) { - if (value == null) return defValue; - value = value.toUpperCase().replace("B", ""); - if (value.endsWith("G")) return Long.decode(value.replace("G", "")) * 1024 * 1024 * 1024; - if (value.endsWith("M")) return Long.decode(value.replace("M", "")) * 1024 * 1024; - if (value.endsWith("K")) return Long.decode(value.replace("K", "")) * 1024; - return Long.decode(value); - } - - protected static String formatLenth(long value) { - if (value < 1) return "" + value; - if (value % (1024 * 1024 * 1024) == 0) return value / (1024 * 1024 * 1024) + "G"; - if (value % (1024 * 1024) == 0) return value / (1024 * 1024) + "M"; - if (value % 1024 == 0) return value / (1024) + "K"; - return value + "B"; - } - - public void destroy(final AnyValue config) throws Exception { - this.prepare.destroy(context, config); - } - - public ResourceFactory getResourceFactory() { - return resourceFactory; - } - - public InetSocketAddress getSocketAddress() { - return address; - } - - public String getName() { - return name; - } - - public String getNetprotocol() { - return netprotocol; - } - - public Logger getLogger() { - return this.logger; - } - - public PrepareServlet getPrepareServlet() { - return this.prepare; - } - - public C getContext() { - return this.context; - } - - public long getServerStartTime() { - return serverStartTime; - } - - public Charset getCharset() { - return charset; - } - - public int getBacklog() { - return backlog; - } - - public int getBufferCapacity() { - return bufferCapacity; - } - - public int getBufferPoolSize() { - return bufferPoolSize; - } - - public int getResponsePoolSize() { - return responsePoolSize; - } - - public int getMaxbody() { - return maxbody; - } - - public int getAliveTimeoutSeconds() { - return aliveTimeoutSeconds; - } - - public int getReadTimeoutSeconds() { - return readTimeoutSeconds; - } - - public int getWriteTimeoutSeconds() { - return writeTimeoutSeconds; - } - - public int getMaxconns() { - return maxconns; - } - - @SuppressWarnings("unchecked") - public void addServlet(S servlet, final Object attachment, AnyValue conf, K... mappings) { - this.prepare.addServlet(servlet, attachment, conf, mappings); - } - - public void start(Application application) throws IOException { - this.context = this.createContext(application); - this.prepare.init(this.context, config); - this.serverChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl")); - if (application != null) { //main函数调试时可能为null - application.getResourceFactory().inject(this.serverChannel); - } - this.serverChannel.open(config); - serverChannel.bind(address, backlog); - serverChannel.accept(application, this); - final String threadName = "[" + Thread.currentThread().getName() + "] "; - postStart(); - logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol)) + " listen: " + (address.getHostString() + ":" + address.getPort()) - + ", cpu: " + Runtime.getRuntime().availableProcessors() + ", maxbody: " + formatLenth(context.maxbody) + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", bufferPoolSize: " + bufferPoolSize + ", responsePoolSize: " + responsePoolSize - + ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms"); - } - - protected void postStart() { - } - - public void changeAddress(Application application, final InetSocketAddress addr) throws IOException { - long s = System.currentTimeMillis(); - Objects.requireNonNull(addr); - final InetSocketAddress oldAddress = context.address; - final ProtocolServer oldServerChannel = this.serverChannel; - context.address = addr; - ProtocolServer newServerChannel = null; - try { - newServerChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader, config == null ? null : config.getValue("netimpl")); - newServerChannel.open(config); - newServerChannel.bind(addr, backlog); - newServerChannel.accept(application, this); - } catch (IOException e) { - context.address = oldAddress; - throw e; - } - this.address = context.address; - this.serverChannel = newServerChannel; - final String threadName = "[" + Thread.currentThread().getName() + "] "; - logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol)) - + " change address listen: " + address + ", started in " + (System.currentTimeMillis() - s) + " ms"); - if (oldServerChannel != null) { - new Thread() { - - @Override - public void run() { - try { - Thread.sleep(10_000); - oldServerChannel.close(); - } catch (Exception e) { - logger.log(Level.WARNING, "Server.changeInetSocketAddress(addr=" + addr + ") error", e); - } - } - }.start(); - } - } - - public void changeMaxconns(final int newmaxconns) { - this.maxconns = newmaxconns; - if (this.context != null) this.context.maxconns = newmaxconns; - if (this.serverChannel != null) this.serverChannel.maxconns = newmaxconns; - } - - public void changeCharset(final Charset newcharset) { - this.charset = newcharset; - if (this.context != null) this.context.charset = newcharset; - } - - public void changeMaxbody(final int newmaxbody) { - this.maxbody = newmaxbody; - if (this.context != null) this.context.maxbody = newmaxbody; - } - - public void changeReadTimeoutSeconds(final int newReadTimeoutSeconds) { - this.readTimeoutSeconds = newReadTimeoutSeconds; - if (this.context != null) this.context.readTimeoutSeconds = newReadTimeoutSeconds; - } - - public void changeWriteTimeoutSeconds(final int newWriteTimeoutSeconds) { - this.writeTimeoutSeconds = newWriteTimeoutSeconds; - if (this.context != null) this.context.writeTimeoutSeconds = newWriteTimeoutSeconds; - } - - public void changeAliveTimeoutSeconds(final int newAliveTimeoutSeconds) { - this.aliveTimeoutSeconds = newAliveTimeoutSeconds; - if (this.context != null) this.context.aliveTimeoutSeconds = newAliveTimeoutSeconds; - } - - protected abstract C createContext(Application application); - - //必须在 createContext()之后调用 - protected abstract ObjectPool createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize); - - //必须在 createContext()之后调用 - protected abstract ObjectPool createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize); - - public void shutdown() throws IOException { - long s = System.currentTimeMillis(); - logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdowning"); - try { - this.serverChannel.close(); - } catch (Exception e) { - } - logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdow prepare servlet"); - this.prepare.destroy(this.context, config); - long e = System.currentTimeMillis() - s; - logger.info(this.getClass().getSimpleName() + " shutdown in " + e + " ms"); - } - - public RedkaleClassLoader getServerClassLoader() { - return serverClassLoader; - } - - public void setServerClassLoader(RedkaleClassLoader serverClassLoader) { - this.serverClassLoader = serverClassLoader; - } - - /** - * 判断是否存在Filter - * - * @param 泛型 - * @param filterClass Filter类 - * - * @return boolean - */ - public boolean containsFilter(Class filterClass) { - return this.prepare.containsFilter(filterClass); - } - - /** - * 判断是否存在Filter - * - * @param 泛型 - * @param filterClassName Filter类 - * - * @return boolean - */ - public boolean containsFilter(String filterClassName) { - return this.prepare.containsFilter(filterClassName); - } - - /** - * 判断是否存在Servlet - * - * @param servletClass Servlet类 - * - * @return boolean - */ - public boolean containsServlet(Class servletClass) { - return this.prepare.containsServlet(servletClass); - } - - /** - * 判断是否存在Servlet - * - * @param servletClassName Servlet类 - * - * @return boolean - */ - public boolean containsServlet(String servletClassName) { - return this.prepare.containsServlet(servletClassName); - } - - /** - * 销毁Servlet - * - * @param servlet Servlet - */ - public void destroyServlet(S servlet) { - servlet.destroy(context, this.prepare.getServletConf(servlet)); - } - - //创建数 - public long getCreateConnectionCount() { - return serverChannel == null ? -1 : serverChannel.getCreateConnectionCount(); - } - - //关闭数 - public long getClosedConnectionCount() { - return serverChannel == null ? -1 : serverChannel.getClosedConnectionCount(); - } - - //在线数 - public long getLivingConnectionCount() { - return serverChannel == null ? -1 : serverChannel.getLivingConnectionCount(); - } - - public static URL[] loadLib(final RedkaleClassLoader classLoader, final Logger logger, final String lib) throws Exception { - if (lib == null || lib.isEmpty()) return new URL[0]; - final Set set = new HashSet<>(); - for (String s : lib.split(";")) { - if (s.isEmpty()) continue; - if (s.endsWith("*")) { - File root = new File(s.substring(0, s.length() - 1)); - if (root.isDirectory()) { - File[] lfs = root.listFiles(); - if (lfs == null) throw new RuntimeException("File(" + root + ") cannot listFiles()"); - for (File f : lfs) { - set.add(f.toURI().toURL()); - } - } - } else { - File f = new File(s); - if (f.canRead()) set.add(f.toURI().toURL()); - } - } - if (set.isEmpty()) return new URL[0]; - for (URL url : set) { - classLoader.addURL(url); - } - List list = new ArrayList<>(set); - list.sort((URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile())); - return list.toArray(new URL[list.size()]); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.*; +import java.util.concurrent.atomic.*; +import java.util.logging.*; +import javax.net.ssl.*; +import org.redkale.boot.Application; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 请求ID的数据类型, 例如HTTP协议请求标识为url,请求ID的数据类型就是String + * @param Context + * @param Request + * @param

    Response + * @param Servlet + */ +public abstract class Server, P extends Response, S extends Servlet> { + + public static final String RESNAME_SERVER_ROOT = "SERVER_ROOT"; + + //@Deprecated //@deprecated 2.3.0 使用RESNAME_APP_EXECUTOR + //public static final String RESNAME_SERVER_EXECUTOR2 = "SERVER_EXECUTOR"; + public static final String RESNAME_SERVER_RESFACTORY = "SERVER_RESFACTORY"; + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + //------------------------------------------------------------- + //Application + protected Application application; + + //服务的启动时间 + protected final long serverStartTime; + + //服务的名称 + protected String name; + + //应用层协议名 + protected final String netprotocol; + + //依赖注入工厂类 + protected final ResourceFactory resourceFactory; + + //服务的根Servlet + protected final PrepareServlet prepare; + + //ClassLoader + protected RedkaleClassLoader serverClassLoader; + + //SSL + protected SSLBuilder sslBuilder; + + //SSL + protected SSLContext sslContext; + + //服务的上下文对象 + protected C context; + + //服务的配置信息 + protected AnyValue config; + + //服务数据的编解码,null视为UTF-8 + protected Charset charset; + + //服务的监听端口 + protected InetSocketAddress address; + + //连接队列大小 + protected int backlog; + + //传输层协议的服务 + protected ProtocolServer serverChannel; + + //ByteBuffer的容量大小 + protected int bufferCapacity; + + //ByteBuffer池大小 + protected int bufferPoolSize; + + //Response池大小 + protected int responsePoolSize; + + //最大连接数, 为0表示没限制 + protected int maxconns; + + //请求包大小的上限,单位:字节 + protected int maxbody; + + //Keep-Alive IO读取的超时秒数,小于1视为不设置 + protected int aliveTimeoutSeconds; + + //IO读取的超时秒数,小于1视为不设置 + protected int readTimeoutSeconds; + + //IO写入 的超时秒数,小于1视为不设置 + protected int writeTimeoutSeconds; + + protected Server(Application application, long serverStartTime, String netprotocol, ResourceFactory resourceFactory, PrepareServlet servlet) { + this.application = application; + this.serverStartTime = serverStartTime; + this.netprotocol = netprotocol; + this.resourceFactory = resourceFactory; + this.prepare = servlet; + this.prepare.application = application; + } + + public void init(final AnyValue config) throws Exception { + Objects.requireNonNull(config); + this.config = config; + this.address = new InetSocketAddress(config.getValue("host", "0.0.0.0"), config.getIntValue("port", 80)); + this.charset = Charset.forName(config.getValue("charset", "UTF-8")); + this.maxconns = config.getIntValue("maxconns", 0); + this.aliveTimeoutSeconds = config.getIntValue("aliveTimeoutSeconds", 30); + this.readTimeoutSeconds = config.getIntValue("readTimeoutSeconds", 0); + this.writeTimeoutSeconds = config.getIntValue("writeTimeoutSeconds", 0); + this.backlog = parseLenth(config.getValue("backlog"), 1024); + this.maxbody = parseLenth(config.getValue("maxbody"), 64 * 1024); + int bufCapacity = parseLenth(config.getValue("bufferCapacity"), "UDP".equalsIgnoreCase(netprotocol) ? 1350 : 32 * 1024); + this.bufferCapacity = "UDP".equalsIgnoreCase(netprotocol) ? bufCapacity : (bufCapacity < 1024 ? 1024 : bufCapacity); + this.bufferPoolSize = config.getIntValue("bufferPoolSize", Utility.cpus() * 8); + this.responsePoolSize = config.getIntValue("responsePoolSize", 1024); + this.name = config.getValue("name", "Server-" + config.getValue("protocol", netprotocol).replaceFirst("\\..+", "").toUpperCase() + "-" + this.address.getPort()); + if (!this.name.matches("^[a-zA-Z][\\w_-]{1,64}$")) throw new RuntimeException("server.name (" + this.name + ") is illegal"); + AnyValue sslConf = config.getAnyValue("ssl"); + if (sslConf != null) { + String builderClass = sslConf.getValue("builder", SSLBuilder.class.getName()); + SSLBuilder builder = null; + if (SSLBuilder.class.getName().equals(builderClass) || builderClass.isEmpty()) { + builder = new SSLBuilder(); + } else { + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + Class clazz = classLoader.loadClass(builderClass); + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName()); + builder = ((SSLBuilder) classLoader.loadClass(builderClass).getDeclaredConstructor().newInstance()); + } + this.resourceFactory.inject(builder); + SSLContext sslc = builder.createSSLContext(this, sslConf); + if (sslc != null) { + this.sslBuilder = builder; + this.sslContext = sslc; + final boolean dtls = sslc.getProtocol().toUpperCase().startsWith("DTLS"); + //SSL模式下, size必须大于 5+16+16384+256+48+(isDTLS?0:16384) = 16k*1/2+325 = 16709/33093 见: sun.security.ssl.SSLRecord.maxLargeRecordSize + int maxLen = dtls ? 16709 : 33093; + if (maxLen > this.bufferCapacity) { + final String threadName = "[" + Thread.currentThread().getName() + "] "; + int newLen = dtls ? (17 * 1024) : (33 * 1024); //取个1024的整倍数 + logger.info(threadName + this.getClass().getSimpleName() + " change bufferCapacity " + this.bufferCapacity + " to " + newLen + " for SSL size " + maxLen); + this.bufferCapacity = newLen; + } + } + } + this.context = this.createContext(); + } + + protected static int parseLenth(String value, int defValue) { + return (int) parseLenth(value, defValue + 0L); + } + + protected static long parseLenth(String value, long defValue) { + if (value == null) return defValue; + value = value.toUpperCase().replace("B", ""); + if (value.endsWith("G")) return Long.decode(value.replace("G", "")) * 1024 * 1024 * 1024; + if (value.endsWith("M")) return Long.decode(value.replace("M", "")) * 1024 * 1024; + if (value.endsWith("K")) return Long.decode(value.replace("K", "")) * 1024; + return Long.decode(value); + } + + protected static String formatLenth(long value) { + if (value < 1) return "" + value; + if (value % (1024 * 1024 * 1024) == 0) return value / (1024 * 1024 * 1024) + "G"; + if (value % (1024 * 1024) == 0) return value / (1024 * 1024) + "M"; + if (value % 1024 == 0) return value / (1024) + "K"; + return value + "B"; + } + + public void destroy(final AnyValue config) throws Exception { + this.prepare.destroy(context, config); + } + + public ResourceFactory getResourceFactory() { + return resourceFactory; + } + + public InetSocketAddress getSocketAddress() { + return address; + } + + public String getName() { + return name; + } + + public String getNetprotocol() { + return netprotocol; + } + + public Logger getLogger() { + return this.logger; + } + + public PrepareServlet getPrepareServlet() { + return this.prepare; + } + + public C getContext() { + return this.context; + } + + public long getServerStartTime() { + return serverStartTime; + } + + public Charset getCharset() { + return charset; + } + + public int getBacklog() { + return backlog; + } + + public int getBufferCapacity() { + return bufferCapacity; + } + + public int getBufferPoolSize() { + return bufferPoolSize; + } + + public int getResponsePoolSize() { + return responsePoolSize; + } + + public int getMaxbody() { + return maxbody; + } + + public int getAliveTimeoutSeconds() { + return aliveTimeoutSeconds; + } + + public int getReadTimeoutSeconds() { + return readTimeoutSeconds; + } + + public int getWriteTimeoutSeconds() { + return writeTimeoutSeconds; + } + + public int getMaxconns() { + return maxconns; + } + + @SuppressWarnings("unchecked") + public void addServlet(S servlet, final Object attachment, AnyValue conf, K... mappings) { + this.prepare.addServlet(servlet, attachment, conf, mappings); + } + + public void start() throws IOException { + this.prepare.init(this.context, config); //不能在init方法内执行,因Server.init执行后会调用loadService,loadServlet, 再执行Server.start + this.serverChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader); + if (application != null) { //main函数调试时可能为nulli + application.getResourceFactory().inject(this.serverChannel); + } + this.serverChannel.open(config); + serverChannel.bind(address, backlog); + serverChannel.accept(application, this); + final String threadName = "[" + Thread.currentThread().getName() + "] "; + postStart(); + logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol)) + " listen: " + (address.getHostString() + ":" + address.getPort()) + + ", cpu: " + Utility.cpus() + ", responsePoolSize: " + responsePoolSize + ", bufferPoolSize: " + bufferPoolSize + + ", bufferCapacity: " + formatLenth(bufferCapacity) + ", maxbody: " + formatLenth(context.maxbody) + + ", started in " + (System.currentTimeMillis() - context.getServerStartTime()) + " ms"); + } + + protected void postStart() { + } + + public void changeAddress(Application application, final InetSocketAddress addr) throws IOException { + long s = System.currentTimeMillis(); + Objects.requireNonNull(addr); + final InetSocketAddress oldAddress = context.address; + final ProtocolServer oldServerChannel = this.serverChannel; + context.address = addr; + ProtocolServer newServerChannel = null; + try { + newServerChannel = ProtocolServer.create(this.netprotocol, context, this.serverClassLoader); + newServerChannel.open(config); + newServerChannel.bind(addr, backlog); + newServerChannel.accept(application, this); + } catch (IOException e) { + context.address = oldAddress; + throw e; + } + this.address = context.address; + this.serverChannel = newServerChannel; + final String threadName = "[" + Thread.currentThread().getName() + "] "; + logger.info(threadName + this.getClass().getSimpleName() + ("TCP".equalsIgnoreCase(netprotocol) ? "" : ("." + netprotocol)) + + " change address listen: " + address + ", started in " + (System.currentTimeMillis() - s) + " ms"); + if (oldServerChannel != null) { + new Thread() { + + @Override + public void run() { + try { + Thread.sleep(10_000); + oldServerChannel.close(); + } catch (Exception e) { + logger.log(Level.WARNING, "Server.changeInetSocketAddress(addr=" + addr + ") error", e); + } + } + }.start(); + } + } + + public void changeMaxconns(final int newmaxconns) { + this.maxconns = newmaxconns; + if (this.context != null) this.context.maxconns = newmaxconns; + if (this.serverChannel != null) this.serverChannel.maxconns = newmaxconns; + } + + public void changeCharset(final Charset newcharset) { + this.charset = newcharset; + if (this.context != null) this.context.charset = newcharset; + } + + public void changeMaxbody(final int newmaxbody) { + this.maxbody = newmaxbody; + if (this.context != null) this.context.maxbody = newmaxbody; + } + + public void changeReadTimeoutSeconds(final int newReadTimeoutSeconds) { + this.readTimeoutSeconds = newReadTimeoutSeconds; + if (this.context != null) this.context.readTimeoutSeconds = newReadTimeoutSeconds; + } + + public void changeWriteTimeoutSeconds(final int newWriteTimeoutSeconds) { + this.writeTimeoutSeconds = newWriteTimeoutSeconds; + if (this.context != null) this.context.writeTimeoutSeconds = newWriteTimeoutSeconds; + } + + public void changeAliveTimeoutSeconds(final int newAliveTimeoutSeconds) { + this.aliveTimeoutSeconds = newAliveTimeoutSeconds; + if (this.context != null) this.context.aliveTimeoutSeconds = newAliveTimeoutSeconds; + } + + protected abstract C createContext(); + + protected void initContextConfig(Context.ContextConfig contextConfig) { + if (application != null) contextConfig.workExecutor = application.getWorkExecutor(); + contextConfig.serverStartTime = this.serverStartTime; + contextConfig.logger = this.logger; + contextConfig.sslBuilder = this.sslBuilder; + contextConfig.sslContext = this.sslContext; + contextConfig.bufferCapacity = this.bufferCapacity; + contextConfig.maxconns = this.maxconns; + contextConfig.maxbody = this.maxbody; + contextConfig.charset = this.charset; + contextConfig.address = this.address; + contextConfig.prepare = this.prepare; + contextConfig.resourceFactory = this.resourceFactory; + contextConfig.aliveTimeoutSeconds = this.aliveTimeoutSeconds; + contextConfig.readTimeoutSeconds = this.readTimeoutSeconds; + contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds; + } + + //必须在 createContext()之后调用 + protected abstract ObjectPool createBufferPool(LongAdder createCounter, LongAdder cycleCounter, int bufferPoolSize); + + //必须在 createContext()之后调用 + protected abstract ObjectPool createResponsePool(LongAdder createCounter, LongAdder cycleCounter, int responsePoolSize); + + public void shutdown() throws IOException { + long s = System.currentTimeMillis(); + logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdowning"); + try { + this.serverChannel.close(); + } catch (Exception e) { + } + logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdow prepare servlet"); + this.prepare.destroy(this.context, config); + long e = System.currentTimeMillis() - s; + logger.info(this.getClass().getSimpleName() + "-" + this.netprotocol + " shutdown in " + e + " ms"); + } + + public RedkaleClassLoader getServerClassLoader() { + return serverClassLoader; + } + + public void setServerClassLoader(RedkaleClassLoader serverClassLoader) { + this.serverClassLoader = serverClassLoader; + } + + /** + * 判断是否存在Filter + * + * @param 泛型 + * @param filterClass Filter类 + * + * @return boolean + */ + public boolean containsFilter(Class filterClass) { + return this.prepare.containsFilter(filterClass); + } + + /** + * 判断是否存在Filter + * + * @param 泛型 + * @param filterClassName Filter类 + * + * @return boolean + */ + public boolean containsFilter(String filterClassName) { + return this.prepare.containsFilter(filterClassName); + } + + /** + * 判断是否存在Servlet + * + * @param servletClass Servlet类 + * + * @return boolean + */ + public boolean containsServlet(Class servletClass) { + return this.prepare.containsServlet(servletClass); + } + + /** + * 判断是否存在Servlet + * + * @param servletClassName Servlet类 + * + * @return boolean + */ + public boolean containsServlet(String servletClassName) { + return this.prepare.containsServlet(servletClassName); + } + + /** + * 销毁Servlet + * + * @param servlet Servlet + */ + public void destroyServlet(S servlet) { + servlet.destroy(context, this.prepare.getServletConf(servlet)); + } + + //创建数 + public long getCreateConnectionCount() { + return serverChannel == null ? -1 : serverChannel.getCreateConnectionCount(); + } + + //关闭数 + public long getClosedConnectionCount() { + return serverChannel == null ? -1 : serverChannel.getClosedConnectionCount(); + } + + //在线数 + public long getLivingConnectionCount() { + return serverChannel == null ? -1 : serverChannel.getLivingConnectionCount(); + } + + public static URL[] loadLib(final RedkaleClassLoader classLoader, final Logger logger, final String lib) throws Exception { + if (lib == null || lib.isEmpty()) return new URL[0]; + final Set set = new HashSet<>(); + for (String s : lib.split(";")) { + if (s.isEmpty()) continue; + if (s.endsWith("*")) { + File root = new File(s.substring(0, s.length() - 1)); + if (root.isDirectory()) { + File[] lfs = root.listFiles(); + if (lfs == null) throw new RuntimeException("File(" + root + ") cannot listFiles()"); + for (File f : lfs) { + set.add(f.toURI().toURL()); + } + } + } else { + File f = new File(s); + if (f.canRead()) set.add(f.toURI().toURL()); + } + } + if (set.isEmpty()) return new URL[0]; + for (URL url : set) { + classLoader.addURL(url); + } + List list = new ArrayList<>(set); + list.sort((URL o1, URL o2) -> o1.getFile().compareTo(o2.getFile())); + return list.toArray(new URL[list.size()]); + } + +} diff --git a/src/org/redkale/net/Servlet.java b/src/main/java/org/redkale/net/Servlet.java similarity index 96% rename from src/org/redkale/net/Servlet.java rename to src/main/java/org/redkale/net/Servlet.java index 0efcf3537..175c73566 100644 --- a/src/org/redkale/net/Servlet.java +++ b/src/main/java/org/redkale/net/Servlet.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import org.redkale.util.AnyValue; -import java.io.IOException; - -/** - * 协议请求处理类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Context的子类型 - * @param Request的子类型 - * @param

    Response的子类型 - */ -public abstract class Servlet, P extends Response> { - - AnyValue _conf; //当前Servlet的配置 - - //Server执行start时运行此方法 - public void init(C context, AnyValue config) { - } - - public abstract void execute(R request, P response) throws IOException; - - //Server执行shutdown后运行此方法 - public void destroy(C context, AnyValue config) { - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import org.redkale.util.AnyValue; +import java.io.IOException; + +/** + * 协议请求处理类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Context的子类型 + * @param Request的子类型 + * @param

    Response的子类型 + */ +public abstract class Servlet, P extends Response> { + + AnyValue _conf; //当前Servlet的配置 + + //Server执行start时运行此方法 + public void init(C context, AnyValue config) { + } + + public abstract void execute(R request, P response) throws IOException; + + //Server执行shutdown后运行此方法 + public void destroy(C context, AnyValue config) { + } + +} diff --git a/src/org/redkale/net/Transport.java b/src/main/java/org/redkale/net/Transport.java similarity index 94% rename from src/org/redkale/net/Transport.java rename to src/main/java/org/redkale/net/Transport.java index d97b75fc4..fd40674b4 100644 --- a/src/org/redkale/net/Transport.java +++ b/src/main/java/org/redkale/net/Transport.java @@ -1,444 +1,444 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.Supplier; -import java.util.logging.Level; -import javax.net.ssl.SSLContext; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.*; - -/** - * 传输客户端 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class Transport { - - public static final String DEFAULT_NETPROTOCOL = "TCP"; - - protected final AtomicInteger seq = new AtomicInteger(-1); - - protected final TransportFactory factory; - - protected final String name; //即的name属性 - - protected final boolean tcp; - - protected final String netprotocol; - - //传输端的AsyncGroup - protected final AsyncGroup asyncGroup; - - protected final InetSocketAddress clientAddress; - - //不可能为null - protected TransportNode[] transportNodes = new TransportNode[0]; - - protected final SSLContext sslContext; - - //负载均衡策略 - protected final TransportStrategy strategy; - - //连接上限, 为null表示无限制 - protected Semaphore semaphore; - - protected Transport(String name, TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress, - final Collection addresses, final TransportStrategy strategy) { - this(name, DEFAULT_NETPROTOCOL, factory, asyncGroup, sslContext, clientAddress, addresses, strategy); - } - - protected Transport(String name, String netprotocol, final TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress, - final Collection addresses, final TransportStrategy strategy) { - this.name = name; - this.netprotocol = netprotocol; - this.factory = factory; - factory.transportReferences.add(new WeakReference<>(this)); - this.tcp = "TCP".equalsIgnoreCase(netprotocol); - this.asyncGroup = asyncGroup; - this.sslContext = sslContext; - this.clientAddress = clientAddress; - this.strategy = strategy; - updateRemoteAddresses(addresses); - } - - public Semaphore getSemaphore() { - return semaphore; - } - - public void setSemaphore(Semaphore semaphore) { - this.semaphore = semaphore; - } - - public final InetSocketAddress[] updateRemoteAddresses(final Collection addresses) { - final TransportNode[] oldNodes = this.transportNodes; - synchronized (this) { - boolean same = false; - if (this.transportNodes != null && addresses != null && this.transportNodes.length == addresses.size()) { - same = true; - for (TransportNode node : this.transportNodes) { - if (!addresses.contains(node.getAddress())) { - same = false; - break; - } - } - } - if (!same) { - List list = new ArrayList<>(); - if (addresses != null) { - for (InetSocketAddress addr : addresses) { - if (clientAddress != null && clientAddress.equals(addr)) continue; - boolean hasold = false; - for (TransportNode oldAddr : oldNodes) { - if (oldAddr.getAddress().equals(addr)) { - list.add(oldAddr); - hasold = true; - break; - } - } - if (hasold) continue; - list.add(new TransportNode(factory.poolmaxconns, addr)); - } - } - this.transportNodes = list.toArray(new TransportNode[list.size()]); - } - } - InetSocketAddress[] rs = new InetSocketAddress[oldNodes.length]; - for (int i = 0; i < rs.length; i++) { - rs[i] = oldNodes[i].getAddress(); - } - return rs; - } - - public final boolean addRemoteAddresses(final InetSocketAddress addr) { - if (addr == null) return false; - if (clientAddress != null && clientAddress.equals(addr)) return false; - synchronized (this) { - if (this.transportNodes.length == 0) { - this.transportNodes = new TransportNode[]{new TransportNode(factory.poolmaxconns, addr)}; - } else { - for (TransportNode i : this.transportNodes) { - if (addr.equals(i.address)) return false; - } - this.transportNodes = Utility.append(transportNodes, new TransportNode(factory.poolmaxconns, addr)); - } - return true; - } - } - - public final boolean removeRemoteAddresses(InetSocketAddress addr) { - if (addr == null) return false; - synchronized (this) { - this.transportNodes = Utility.remove(transportNodes, new TransportNode(factory.poolmaxconns, addr)); - } - return true; - } - - public String getName() { - return name; - } - - public void close() { - TransportNode[] nodes = this.transportNodes; - if (nodes == null) return; - for (TransportNode node : nodes) { - if (node != null) node.dispose(); - } - } - - public InetSocketAddress getClientAddress() { - return clientAddress; - } - - public TransportNode[] getTransportNodes() { - return transportNodes; - } - - public TransportNode findTransportNode(SocketAddress addr) { - for (TransportNode node : this.transportNodes) { - if (node.address.equals(addr)) return node; - } - return null; - } - - public InetSocketAddress[] getRemoteAddresses() { - InetSocketAddress[] rs = new InetSocketAddress[transportNodes.length]; - for (int i = 0; i < rs.length; i++) { - rs[i] = transportNodes[i].getAddress(); - } - return rs; - } - - @Override - public String toString() { - return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + netprotocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}"; - } - - public String getNetprotocol() { - return netprotocol; - } - - public boolean isTCP() { - return tcp; - } - - protected CompletableFuture pollAsync(TransportNode node, SocketAddress addr, Supplier> func) { - final BlockingQueue queue = node.connQueue; - if (!queue.isEmpty()) { - AsyncConnection conn; - while ((conn = queue.poll()) != null) { - if (conn.isOpen()) { - return CompletableFuture.completedFuture(conn); - } else { - conn.dispose(); - } - } - } - if (semaphore != null && !semaphore.tryAcquire()) { - final CompletableFuture future = Utility.orTimeout(new CompletableFuture<>(), 10, TimeUnit.SECONDS); - future.whenComplete((r, t) -> node.pollQueue.remove(future)); - if (node.pollQueue.offer(future)) return future; - future.completeExceptionally(new IOException("create transport connection error")); - return future; - } - return func.get().thenApply(conn -> { - if (conn != null && semaphore != null) conn.beforeCloseListener((c) -> semaphore.release()); - return conn; - }); - } - - public CompletableFuture pollConnection(SocketAddress addr0) { - if (this.strategy != null) return strategy.pollConnection(addr0, this); - final TransportNode[] nodes = this.transportNodes; - if (addr0 == null && nodes.length == 1) addr0 = nodes[0].address; - final SocketAddress addr = addr0; - final boolean rand = addr == null; //是否随机取地址 - if (rand && nodes.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list"); - try { - if (!tcp) { // UDP - SocketAddress udpaddr = rand ? nodes[0].address : addr; - return asyncGroup.createUDP(udpaddr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds); - } - if (!rand) { //指定地址 - TransportNode node = findTransportNode(addr); - if (node == null) return asyncGroup.createTCP(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds); - return pollAsync(node, addr, () -> asyncGroup.createTCP(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds)); - } - - //---------------------随机取地址------------------------ - int enablecount = 0; - final TransportNode[] newnodes = new TransportNode[nodes.length]; - for (final TransportNode node : nodes) { - if (node.disabletime > 0) continue; - newnodes[enablecount++] = node; - } - final long now = System.currentTimeMillis(); - if (enablecount > 0) { //存在可用的地址 - final TransportNode one = newnodes[Math.abs(seq.incrementAndGet()) % enablecount]; - final BlockingQueue queue = one.connQueue; - if (!queue.isEmpty()) { - AsyncConnection conn; - while ((conn = queue.poll()) != null) { - if (conn.isOpen()) { - return CompletableFuture.completedFuture(conn); - } else { - conn.dispose(); - } - } - } - return pollAsync(one, one.getAddress(), () -> { - return asyncGroup.createTCP(one.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds) - .whenComplete((c, t) -> { - one.disabletime = t == null ? 0 : System.currentTimeMillis(); - }); - }); - } - return pollConnection0(nodes, null, now); - } catch (Exception ex) { - throw new RuntimeException("transport address = " + addr, ex); - } - } - - private CompletableFuture pollConnection0(TransportNode[] nodes, TransportNode exclude, long now) throws IOException { - //从可用/不可用的地址列表中创建连接 - CompletableFuture future = new CompletableFuture(); - for (final TransportNode node : nodes) { - if (node == exclude) continue; - if (future.isDone()) return future; - asyncGroup.createTCP(node.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds) - .whenComplete((c, t) -> { - if (c != null && !future.complete(c)) node.connQueue.offer(c); - node.disabletime = t == null ? 0 : System.currentTimeMillis(); - }); - } - return future; - } - - public void offerConnection(final boolean forceClose, AsyncConnection conn) { - if (this.strategy != null && strategy.offerConnection(forceClose, conn)) return; - if (!forceClose && conn.isTCP()) { - if (conn.isOpen()) { - TransportNode node = findTransportNode(conn.getRemoteAddress()); - if (node == null || !node.connQueue.offer(conn)) conn.dispose(); - } else { - conn.dispose(); - } - } else { - conn.dispose(); - } - } - - public void async(SocketAddress addr, final ByteBuffer buffer, A att, final CompletionHandler handler) { - pollConnection(addr).whenComplete((conn, ex) -> { - if (ex != null) { - factory.getLogger().log(Level.WARNING, Transport.class.getSimpleName() + " async error", ex); - return; - } - conn.write(buffer, buffer, new CompletionHandler() { - - @Override - public void completed(Integer result, ByteBuffer attachment) { - buffer.clear(); - conn.setReadBuffer(buffer); - conn.read(new CompletionHandler() { - - @Override - public void completed(Integer result, ByteBuffer attachment) { - if (handler != null) handler.completed(result, att); - conn.offerBuffer(attachment); - offerConnection(false, conn); - } - - @Override - public void failed(Throwable exc, ByteBuffer attachment) { - conn.offerBuffer(attachment); - offerConnection(true, conn); - } - }); - - } - - @Override - public void failed(Throwable exc, ByteBuffer attachment) { - conn.offerBuffer(attachment); - offerConnection(true, conn); - } - }); - }); - } - - public static class TransportNode { - - protected InetSocketAddress address; - - protected volatile long disabletime; //不可用时的时间, 为0表示可用 - - protected final BlockingQueue connQueue; - - protected final ArrayBlockingQueue> pollQueue; - - protected final ConcurrentHashMap attributes = new ConcurrentHashMap<>(); - - public TransportNode(int poolmaxconns, InetSocketAddress address) { - this.address = address; - this.disabletime = 0; - this.connQueue = new ArrayBlockingQueue<>(poolmaxconns); - this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100); - } - - @ConstructorParameters({"poolmaxconns", "address", "disabletime"}) - public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) { - this.address = address; - this.disabletime = disabletime; - this.connQueue = new LinkedBlockingQueue<>(poolmaxconns); - this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100); - } - - public int getPoolmaxconns() { - return this.connQueue.remainingCapacity() + this.connQueue.size(); - } - - public T setAttribute(String name, T value) { - attributes.put(name, value); - return value; - } - - @SuppressWarnings("unchecked") - public T getAttribute(String name) { - return (T) attributes.get(name); - } - - @SuppressWarnings("unchecked") - public T removeAttribute(String name) { - return (T) attributes.remove(name); - } - - public TransportNode clearAttributes() { - attributes.clear(); - return this; - } - - public ConcurrentHashMap getAttributes() { - return attributes; - } - - public void setAttributes(ConcurrentHashMap map) { - attributes.clear(); - if (map != null) attributes.putAll(map); - } - - public InetSocketAddress getAddress() { - return address; - } - - public long getDisabletime() { - return disabletime; - } - - @ConvertDisabled - public BlockingQueue getConnQueue() { - return connQueue; - } - - public void dispose() { - AsyncConnection conn; - while ((conn = connQueue.poll()) != null) { - conn.dispose(); - } - } - - @Override - public int hashCode() { - return this.address.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final TransportNode other = (TransportNode) obj; - return this.address.equals(other.address); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.Supplier; +import java.util.logging.Level; +import javax.net.ssl.SSLContext; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.*; + +/** + * 传输客户端 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class Transport { + + public static final String DEFAULT_NETPROTOCOL = "TCP"; + + protected final AtomicInteger seq = new AtomicInteger(-1); + + protected final TransportFactory factory; + + protected final String name; //即的name属性 + + protected final boolean tcp; + + protected final String netprotocol; + + //传输端的AsyncGroup + protected final AsyncGroup asyncGroup; + + protected final InetSocketAddress clientAddress; + + //不可能为null + protected TransportNode[] transportNodes = new TransportNode[0]; + + protected final SSLContext sslContext; + + //负载均衡策略 + protected final TransportStrategy strategy; + + //连接上限, 为null表示无限制 + protected Semaphore semaphore; + + protected Transport(String name, TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress, + final Collection addresses, final TransportStrategy strategy) { + this(name, DEFAULT_NETPROTOCOL, factory, asyncGroup, sslContext, clientAddress, addresses, strategy); + } + + protected Transport(String name, String netprotocol, final TransportFactory factory, final AsyncGroup asyncGroup, final SSLContext sslContext, final InetSocketAddress clientAddress, + final Collection addresses, final TransportStrategy strategy) { + this.name = name; + this.netprotocol = netprotocol; + this.factory = factory; + factory.transportReferences.add(new WeakReference<>(this)); + this.tcp = "TCP".equalsIgnoreCase(netprotocol); + this.asyncGroup = asyncGroup; + this.sslContext = sslContext; + this.clientAddress = clientAddress; + this.strategy = strategy; + updateRemoteAddresses(addresses); + } + + public Semaphore getSemaphore() { + return semaphore; + } + + public void setSemaphore(Semaphore semaphore) { + this.semaphore = semaphore; + } + + public final InetSocketAddress[] updateRemoteAddresses(final Collection addresses) { + final TransportNode[] oldNodes = this.transportNodes; + synchronized (this) { + boolean same = false; + if (this.transportNodes != null && addresses != null && this.transportNodes.length == addresses.size()) { + same = true; + for (TransportNode node : this.transportNodes) { + if (!addresses.contains(node.getAddress())) { + same = false; + break; + } + } + } + if (!same) { + List list = new ArrayList<>(); + if (addresses != null) { + for (InetSocketAddress addr : addresses) { + if (clientAddress != null && clientAddress.equals(addr)) continue; + boolean hasold = false; + for (TransportNode oldAddr : oldNodes) { + if (oldAddr.getAddress().equals(addr)) { + list.add(oldAddr); + hasold = true; + break; + } + } + if (hasold) continue; + list.add(new TransportNode(factory.poolmaxconns, addr)); + } + } + this.transportNodes = list.toArray(new TransportNode[list.size()]); + } + } + InetSocketAddress[] rs = new InetSocketAddress[oldNodes.length]; + for (int i = 0; i < rs.length; i++) { + rs[i] = oldNodes[i].getAddress(); + } + return rs; + } + + public final boolean addRemoteAddresses(final InetSocketAddress addr) { + if (addr == null) return false; + if (clientAddress != null && clientAddress.equals(addr)) return false; + synchronized (this) { + if (this.transportNodes.length == 0) { + this.transportNodes = new TransportNode[]{new TransportNode(factory.poolmaxconns, addr)}; + } else { + for (TransportNode i : this.transportNodes) { + if (addr.equals(i.address)) return false; + } + this.transportNodes = Utility.append(transportNodes, new TransportNode(factory.poolmaxconns, addr)); + } + return true; + } + } + + public final boolean removeRemoteAddresses(InetSocketAddress addr) { + if (addr == null) return false; + synchronized (this) { + this.transportNodes = Utility.remove(transportNodes, new TransportNode(factory.poolmaxconns, addr)); + } + return true; + } + + public String getName() { + return name; + } + + public void close() { + TransportNode[] nodes = this.transportNodes; + if (nodes == null) return; + for (TransportNode node : nodes) { + if (node != null) node.dispose(); + } + } + + public InetSocketAddress getClientAddress() { + return clientAddress; + } + + public TransportNode[] getTransportNodes() { + return transportNodes; + } + + public TransportNode findTransportNode(SocketAddress addr) { + for (TransportNode node : this.transportNodes) { + if (node.address.equals(addr)) return node; + } + return null; + } + + public InetSocketAddress[] getRemoteAddresses() { + InetSocketAddress[] rs = new InetSocketAddress[transportNodes.length]; + for (int i = 0; i < rs.length; i++) { + rs[i] = transportNodes[i].getAddress(); + } + return rs; + } + + @Override + public String toString() { + return Transport.class.getSimpleName() + "{name = " + name + ", protocol = " + netprotocol + ", clientAddress = " + clientAddress + ", remoteNodes = " + Arrays.toString(transportNodes) + "}"; + } + + public String getNetprotocol() { + return netprotocol; + } + + public boolean isTCP() { + return tcp; + } + + protected CompletableFuture pollAsync(TransportNode node, SocketAddress addr, Supplier> func) { + final BlockingQueue queue = node.connQueue; + if (!queue.isEmpty()) { + AsyncConnection conn; + while ((conn = queue.poll()) != null) { + if (conn.isOpen()) { + return CompletableFuture.completedFuture(conn); + } else { + conn.dispose(); + } + } + } + if (semaphore != null && !semaphore.tryAcquire()) { + final CompletableFuture future = Utility.orTimeout(new CompletableFuture<>(), 10, TimeUnit.SECONDS); + future.whenComplete((r, t) -> node.pollQueue.remove(future)); + if (node.pollQueue.offer(future)) return future; + future.completeExceptionally(new IOException("create transport connection error")); + return future; + } + return func.get().thenApply(conn -> { + if (conn != null && semaphore != null) conn.beforeCloseListener((c) -> semaphore.release()); + return conn; + }); + } + + public CompletableFuture pollConnection(SocketAddress addr0) { + if (this.strategy != null) return strategy.pollConnection(addr0, this); + final TransportNode[] nodes = this.transportNodes; + if (addr0 == null && nodes.length == 1) addr0 = nodes[0].address; + final SocketAddress addr = addr0; + final boolean rand = addr == null; //是否随机取地址 + if (rand && nodes.length < 1) throw new RuntimeException("Transport (" + this.name + ") have no remoteAddress list"); + try { + if (!tcp) { // UDP + SocketAddress udpaddr = rand ? nodes[0].address : addr; + return asyncGroup.createUDPClient(udpaddr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds); + } + if (!rand) { //指定地址 + TransportNode node = findTransportNode(addr); + if (node == null) return asyncGroup.createTCPClient(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds); + return pollAsync(node, addr, () -> asyncGroup.createTCPClient(addr, factory.readTimeoutSeconds, factory.writeTimeoutSeconds)); + } + + //---------------------随机取地址------------------------ + int enablecount = 0; + final TransportNode[] newnodes = new TransportNode[nodes.length]; + for (final TransportNode node : nodes) { + if (node.disabletime > 0) continue; + newnodes[enablecount++] = node; + } + final long now = System.currentTimeMillis(); + if (enablecount > 0) { //存在可用的地址 + final TransportNode one = newnodes[Math.abs(seq.incrementAndGet()) % enablecount]; + final BlockingQueue queue = one.connQueue; + if (!queue.isEmpty()) { + AsyncConnection conn; + while ((conn = queue.poll()) != null) { + if (conn.isOpen()) { + return CompletableFuture.completedFuture(conn); + } else { + conn.dispose(); + } + } + } + return pollAsync(one, one.getAddress(), () -> { + return asyncGroup.createTCPClient(one.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds) + .whenComplete((c, t) -> { + one.disabletime = t == null ? 0 : System.currentTimeMillis(); + }); + }); + } + return pollConnection0(nodes, null, now); + } catch (Exception ex) { + throw new RuntimeException("transport address = " + addr, ex); + } + } + + private CompletableFuture pollConnection0(TransportNode[] nodes, TransportNode exclude, long now) throws IOException { + //从可用/不可用的地址列表中创建连接 + CompletableFuture future = new CompletableFuture(); + for (final TransportNode node : nodes) { + if (node == exclude) continue; + if (future.isDone()) return future; + asyncGroup.createTCPClient(node.address, factory.readTimeoutSeconds, factory.writeTimeoutSeconds) + .whenComplete((c, t) -> { + if (c != null && !future.complete(c)) node.connQueue.offer(c); + node.disabletime = t == null ? 0 : System.currentTimeMillis(); + }); + } + return future; + } + + public void offerConnection(final boolean forceClose, AsyncConnection conn) { + if (this.strategy != null && strategy.offerConnection(forceClose, conn)) return; + if (!forceClose && conn.isTCP()) { + if (conn.isOpen()) { + TransportNode node = findTransportNode(conn.getRemoteAddress()); + if (node == null || !node.connQueue.offer(conn)) conn.dispose(); + } else { + conn.dispose(); + } + } else { + conn.dispose(); + } + } + + public void async(SocketAddress addr, final ByteBuffer buffer, A att, final CompletionHandler handler) { + pollConnection(addr).whenComplete((conn, ex) -> { + if (ex != null) { + factory.getLogger().log(Level.WARNING, Transport.class.getSimpleName() + " async error", ex); + return; + } + conn.write(buffer, buffer, new CompletionHandler() { + + @Override + public void completed(Integer result, ByteBuffer attachment) { + buffer.clear(); + conn.setReadBuffer(buffer); + conn.read(new CompletionHandler() { + + @Override + public void completed(Integer result, ByteBuffer attachment) { + if (handler != null) handler.completed(result, att); + conn.offerBuffer(attachment); + offerConnection(false, conn); + } + + @Override + public void failed(Throwable exc, ByteBuffer attachment) { + conn.offerBuffer(attachment); + offerConnection(true, conn); + } + }); + + } + + @Override + public void failed(Throwable exc, ByteBuffer attachment) { + conn.offerBuffer(attachment); + offerConnection(true, conn); + } + }); + }); + } + + public static class TransportNode { + + protected InetSocketAddress address; + + protected volatile long disabletime; //不可用时的时间, 为0表示可用 + + protected final BlockingQueue connQueue; + + protected final ArrayBlockingQueue> pollQueue; + + protected final ConcurrentHashMap attributes = new ConcurrentHashMap<>(); + + public TransportNode(int poolmaxconns, InetSocketAddress address) { + this.address = address; + this.disabletime = 0; + this.connQueue = new ArrayBlockingQueue<>(poolmaxconns); + this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100); + } + + @ConstructorParameters({"poolmaxconns", "address", "disabletime"}) + public TransportNode(int poolmaxconns, InetSocketAddress address, long disabletime) { + this.address = address; + this.disabletime = disabletime; + this.connQueue = new LinkedBlockingQueue<>(poolmaxconns); + this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 100); + } + + public int getPoolmaxconns() { + return this.connQueue.remainingCapacity() + this.connQueue.size(); + } + + public T setAttribute(String name, T value) { + attributes.put(name, value); + return value; + } + + @SuppressWarnings("unchecked") + public T getAttribute(String name) { + return (T) attributes.get(name); + } + + @SuppressWarnings("unchecked") + public T removeAttribute(String name) { + return (T) attributes.remove(name); + } + + public TransportNode clearAttributes() { + attributes.clear(); + return this; + } + + public ConcurrentHashMap getAttributes() { + return attributes; + } + + public void setAttributes(ConcurrentHashMap map) { + attributes.clear(); + if (map != null) attributes.putAll(map); + } + + public InetSocketAddress getAddress() { + return address; + } + + public long getDisabletime() { + return disabletime; + } + + @ConvertDisabled + public BlockingQueue getConnQueue() { + return connQueue; + } + + public void dispose() { + AsyncConnection conn; + while ((conn = connQueue.poll()) != null) { + conn.dispose(); + } + } + + @Override + public int hashCode() { + return this.address.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final TransportNode other = (TransportNode) obj; + return this.address.equals(other.address); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/org/redkale/net/TransportFactory.java b/src/main/java/org/redkale/net/TransportFactory.java similarity index 94% rename from src/org/redkale/net/TransportFactory.java rename to src/main/java/org/redkale/net/TransportFactory.java index 523a2907d..b44e7d270 100644 --- a/src/org/redkale/net/TransportFactory.java +++ b/src/main/java/org/redkale/net/TransportFactory.java @@ -1,348 +1,348 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.lang.ref.WeakReference; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import java.util.stream.Collectors; -import javax.net.ssl.SSLContext; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * System.getProperty("net.transport.ping.interval", "30") 心跳周期,默认30秒 - * System.getProperty("net.transport.check.interval", "30") 检查不可用地址周期,默认30秒 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class TransportFactory { - - @Comment("默认TCP读取超时秒数") - public static int DEFAULT_READTIMEOUTSECONDS = 6; - - @Comment("默认TCP写入超时秒数") - public static int DEFAULT_WRITETIMEOUTSECONDS = 6; - - public static final String NAME_POOLMAXCONNS = "poolmaxconns"; - - public static final String NAME_PINGINTERVAL = "pinginterval"; - - public static final String NAME_CHECKINTERVAL = "checkinterval"; - - protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName()); - - //传输端的AsyncGroup - protected final AsyncGroup asyncGroup; - - //每个地址对应的Group名 - protected final Map groupAddrs = new HashMap<>(); - - //协议地址的Group集合 - protected final Map groupInfos = new HashMap<>(); - - protected final List> services = new CopyOnWriteArrayList<>(); - - protected final List> transportReferences = new CopyOnWriteArrayList<>(); - - //连接池大小 - protected int poolmaxconns = Integer.getInteger("net.transport.pool.maxconns", Math.max(100, Runtime.getRuntime().availableProcessors() * 16)); //最少是wsthreads的两倍 - - //检查不可用地址周期, 单位:秒 - protected int checkinterval = Integer.getInteger("net.transport.check.interval", 30); - - //心跳周期, 单位:秒 - protected int pinginterval; - - //TCP读取超时秒数 - protected int readTimeoutSeconds; - - //TCP写入超时秒数 - protected int writeTimeoutSeconds; - - //ping和检查的定时器 - private ScheduledThreadPoolExecutor scheduler; - - protected SSLContext sslContext; - - //ping的内容 - private ByteBuffer pingBuffer; - - //pong的数据长度, 小于0表示不进行判断 - protected int pongLength; - - //是否TCP - protected String netprotocol = "TCP"; - - //负载均衡策略 - protected final TransportStrategy strategy; - - protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, - int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) { - this.asyncGroup = asyncGroup; - this.sslContext = sslContext; - this.netprotocol = netprotocol; - this.readTimeoutSeconds = readTimeoutSeconds; - this.writeTimeoutSeconds = writeTimeoutSeconds; - this.strategy = strategy; - } - - protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, - int readTimeoutSeconds, int writeTimeoutSeconds) { - this(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null); - } - - public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) { - if (conf != null) { - this.poolmaxconns = conf.getIntValue(NAME_POOLMAXCONNS, this.poolmaxconns); - this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, this.pinginterval); - this.checkinterval = conf.getIntValue(NAME_CHECKINTERVAL, this.checkinterval); - if (this.poolmaxconns < 2) this.poolmaxconns = 2; - if (this.pinginterval < 2) this.pinginterval = 2; - if (this.checkinterval < 2) this.checkinterval = 2; - } - this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-" + this.getClass().getSimpleName() + "-Schedule-Thread"); - t.setDaemon(true); - return t; - }); - this.scheduler.scheduleAtFixedRate(() -> { - try { - checks(); - } catch (Throwable t) { - logger.log(Level.SEVERE, "TransportFactory schedule(interval=" + checkinterval + "s) check error", t); - } - }, checkinterval, checkinterval, TimeUnit.SECONDS); - - if (this.pinginterval > 0) { - if (pingBuffer != null) { - this.pingBuffer = pingBuffer.asReadOnlyBuffer(); - this.pongLength = pongLength; - - scheduler.scheduleAtFixedRate(() -> { - pings(); - }, pinginterval, pinginterval, TimeUnit.SECONDS); - } - } - } - - public static TransportFactory create(AsyncGroup asyncGroup, int readTimeoutSeconds, int writeTimeoutSeconds) { - return new TransportFactory(asyncGroup, null, "TCP", readTimeoutSeconds, writeTimeoutSeconds, null); - } - - public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) { - return new TransportFactory(asyncGroup, sslContext, "TCP", readTimeoutSeconds, writeTimeoutSeconds, strategy); - } - - public static TransportFactory create(AsyncGroup asyncGroup, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds) { - return new TransportFactory(asyncGroup, null, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null); - } - - public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) { - return new TransportFactory(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, strategy); - } - - public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection addresses) { - return new Transport(name, "TCP", this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy); - } - - public Transport createTransport(String name, String netprotocol, final InetSocketAddress clientAddress, final Collection addresses) { - return new Transport(name, netprotocol, this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy); - } - - public String findGroupName(InetSocketAddress addr) { - if (addr == null) return null; - return groupAddrs.get(addr); - } - - public TransportGroupInfo findGroupInfo(String group) { - if (group == null) return null; - return groupInfos.get(group); - } - - public boolean addGroupInfo(String groupName, InetSocketAddress... addrs) { - addGroupInfo(new TransportGroupInfo(groupName, addrs)); - return true; - } - - public boolean removeGroupInfo(String groupName, InetSocketAddress addr) { - if (groupName == null || groupName.isEmpty() || addr == null) return false; - if (!groupName.equals(groupAddrs.get(addr))) return false; - TransportGroupInfo group = groupInfos.get(groupName); - if (group == null) return false; - group.removeAddress(addr); - groupAddrs.remove(addr); - return true; - } - - public TransportFactory addGroupInfo(String name, Set addrs) { - addGroupInfo(new TransportGroupInfo(name, addrs)); - return this; - } - - public boolean addGroupInfo(TransportGroupInfo info) { - if (info == null) throw new RuntimeException("TransportGroupInfo can not null"); - if (info.addresses == null) throw new RuntimeException("TransportGroupInfo.addresses can not null"); - if (!checkName(info.name)) throw new RuntimeException("Transport.group.name only 0-9 a-z A-Z _ cannot begin 0-9"); - TransportGroupInfo old = groupInfos.get(info.name); - if (old != null && !old.protocol.equals(info.protocol)) throw new RuntimeException("Transport.group.name repeat but protocol is different"); - for (InetSocketAddress addr : info.addresses) { - if (!groupAddrs.getOrDefault(addr, info.name).equals(info.name)) throw new RuntimeException(addr + " repeat but different group.name"); - } - if (old == null) { - groupInfos.put(info.name, info); - } else { - old.putAddress(info.addresses); - } - for (InetSocketAddress addr : info.addresses) { - groupAddrs.put(addr, info.name); - } - return true; - } - - public Transport loadTransport(InetSocketAddress sncpAddress, final Set groups) { - if (groups == null) return null; - Set addresses = new HashSet<>(); - TransportGroupInfo info = null; - for (String group : groups) { - info = groupInfos.get(group); - if (info == null) continue; - addresses.addAll(info.addresses); - } - if (info == null) { - info = new TransportGroupInfo(netprotocol); - } else { - info.protocol = netprotocol; - } - if (sncpAddress != null) addresses.remove(sncpAddress); - return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.asyncGroup, this.sslContext, sncpAddress, addresses, this.strategy); - } - - public List getGroupInfos() { - return new ArrayList<>(this.groupInfos.values()); - } - - public Logger getLogger() { - return logger; - } - - public void addSncpService(Service service) { - if (service == null) return; - services.add(new WeakReference<>(service)); - } - - public List getServices() { - List rs = new ArrayList<>(); - for (WeakReference ref : services) { - Service service = ref.get(); - if (service != null) rs.add(service); - } - return rs; - } - - public void shutdownNow() { - if (this.scheduler != null) this.scheduler.shutdownNow(); - } - - private void checks() { - List nulllist = new ArrayList<>(); - for (WeakReference ref : transportReferences) { - Transport transport = ref.get(); - if (transport == null) { - nulllist.add(ref); - continue; - } - Transport.TransportNode[] nodes = transport.getTransportNodes(); - for (final Transport.TransportNode node : nodes) { - if (node.disabletime < 1) continue; //可用 - CompletableFuture future = Utility.orTimeout(asyncGroup.createTCP(node.address), 2, TimeUnit.SECONDS); - future.whenComplete((r, t) -> { - node.disabletime = t == null ? 0 : System.currentTimeMillis(); - if (r != null) r.dispose(); - }); - } - } - for (WeakReference ref : nulllist) { - transportReferences.remove(ref); - } - } - - private void pings() { - long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000; - for (WeakReference ref : transportReferences) { - Transport transport = ref.get(); - if (transport == null) continue; - Transport.TransportNode[] nodes = transport.getTransportNodes(); - for (final Transport.TransportNode node : nodes) { - final BlockingQueue queue = node.connQueue; - AsyncConnection conn; - while ((conn = queue.poll()) != null) { - if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作 - queue.offer(conn); - } else { //超过一定时间的连接需要进行ping处理 - ByteBuffer sendBuffer = pingBuffer.duplicate(); - final AsyncConnection localconn = conn; - final BlockingQueue localqueue = queue; - localconn.write(sendBuffer, sendBuffer, new CompletionHandler() { - @Override - public void completed(Integer result, ByteBuffer wbuffer) { - localconn.read(new CompletionHandler() { - int counter = 0; - - @Override - public void completed(Integer result, ByteBuffer pongBuffer) { - if (counter > 3) { - localconn.offerBuffer(pongBuffer); - localconn.dispose(); - return; - } - if (pongLength > 0 && pongBuffer.position() < pongLength) { - counter++; - localconn.setReadBuffer(pongBuffer); - localconn.read(this); - return; - } - localconn.offerBuffer(pongBuffer); - localqueue.offer(localconn); - } - - @Override - public void failed(Throwable exc, ByteBuffer pongBuffer) { - localconn.offerBuffer(pongBuffer); - localconn.dispose(); - } - }); - } - - @Override - public void failed(Throwable exc, ByteBuffer buffer) { - localconn.dispose(); - } - }); - } - } - } - } - } - - private static boolean checkName(String name) { //不能含特殊字符 - if (name.isEmpty()) return false; - if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false; - for (char ch : name.toCharArray()) { - if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 - return false; - } - } - return true; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.lang.ref.WeakReference; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.*; +import java.util.stream.Collectors; +import javax.net.ssl.SSLContext; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * System.getProperty("redkale.net.transport.ping.interval", "30") 心跳周期,默认30秒 + * System.getProperty("redkale.net.transport.check.interval", "30") 检查不可用地址周期,默认30秒 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class TransportFactory { + + @Comment("默认TCP读取超时秒数") + public static int DEFAULT_READTIMEOUTSECONDS = 6; + + @Comment("默认TCP写入超时秒数") + public static int DEFAULT_WRITETIMEOUTSECONDS = 6; + + public static final String NAME_POOLMAXCONNS = "poolmaxconns"; + + public static final String NAME_PINGINTERVAL = "pinginterval"; + + public static final String NAME_CHECKINTERVAL = "checkinterval"; + + protected static final Logger logger = Logger.getLogger(TransportFactory.class.getSimpleName()); + + //传输端的AsyncGroup + protected final AsyncGroup asyncGroup; + + //每个地址对应的Group名 + protected final Map groupAddrs = new HashMap<>(); + + //协议地址的Group集合 + protected final Map groupInfos = new HashMap<>(); + + protected final List> services = new CopyOnWriteArrayList<>(); + + protected final List> transportReferences = new CopyOnWriteArrayList<>(); + + //连接池大小 + protected int poolmaxconns = Integer.getInteger("redkale.net.transport.pool.maxconns", Math.max(100, Utility.cpus() * 16)); //最少是wsthreads的两倍 + + //检查不可用地址周期, 单位:秒 + protected int checkinterval = Integer.getInteger("redkale.net.transport.check.interval", 30); + + //心跳周期, 单位:秒 + protected int pinginterval; + + //TCP读取超时秒数 + protected int readTimeoutSeconds; + + //TCP写入超时秒数 + protected int writeTimeoutSeconds; + + //ping和检查的定时器 + private ScheduledThreadPoolExecutor scheduler; + + protected SSLContext sslContext; + + //ping的内容 + private ByteBuffer pingBuffer; + + //pong的数据长度, 小于0表示不进行判断 + protected int pongLength; + + //是否TCP + protected String netprotocol = "TCP"; + + //负载均衡策略 + protected final TransportStrategy strategy; + + protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, + int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) { + this.asyncGroup = asyncGroup; + this.sslContext = sslContext; + this.netprotocol = netprotocol; + this.readTimeoutSeconds = readTimeoutSeconds; + this.writeTimeoutSeconds = writeTimeoutSeconds; + this.strategy = strategy; + } + + protected TransportFactory(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, + int readTimeoutSeconds, int writeTimeoutSeconds) { + this(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null); + } + + public void init(AnyValue conf, ByteBuffer pingBuffer, int pongLength) { + if (conf != null) { + this.poolmaxconns = conf.getIntValue(NAME_POOLMAXCONNS, this.poolmaxconns); + this.pinginterval = conf.getIntValue(NAME_PINGINTERVAL, this.pinginterval); + this.checkinterval = conf.getIntValue(NAME_CHECKINTERVAL, this.checkinterval); + if (this.poolmaxconns < 2) this.poolmaxconns = 2; + if (this.pinginterval < 2) this.pinginterval = 2; + if (this.checkinterval < 2) this.checkinterval = 2; + } + this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { + final Thread t = new Thread(r, "Redkale-" + this.getClass().getSimpleName() + "-Schedule-Thread"); + t.setDaemon(true); + return t; + }); + this.scheduler.scheduleAtFixedRate(() -> { + try { + checks(); + } catch (Throwable t) { + logger.log(Level.SEVERE, "TransportFactory schedule(interval=" + checkinterval + "s) check error", t); + } + }, checkinterval, checkinterval, TimeUnit.SECONDS); + + if (this.pinginterval > 0) { + if (pingBuffer != null) { + this.pingBuffer = pingBuffer.asReadOnlyBuffer(); + this.pongLength = pongLength; + + scheduler.scheduleAtFixedRate(() -> { + pings(); + }, pinginterval, pinginterval, TimeUnit.SECONDS); + } + } + } + + public static TransportFactory create(AsyncGroup asyncGroup, int readTimeoutSeconds, int writeTimeoutSeconds) { + return new TransportFactory(asyncGroup, null, "TCP", readTimeoutSeconds, writeTimeoutSeconds, null); + } + + public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) { + return new TransportFactory(asyncGroup, sslContext, "TCP", readTimeoutSeconds, writeTimeoutSeconds, strategy); + } + + public static TransportFactory create(AsyncGroup asyncGroup, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds) { + return new TransportFactory(asyncGroup, null, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, null); + } + + public static TransportFactory create(AsyncGroup asyncGroup, SSLContext sslContext, String netprotocol, int readTimeoutSeconds, int writeTimeoutSeconds, final TransportStrategy strategy) { + return new TransportFactory(asyncGroup, sslContext, netprotocol, readTimeoutSeconds, writeTimeoutSeconds, strategy); + } + + public Transport createTransportTCP(String name, final InetSocketAddress clientAddress, final Collection addresses) { + return new Transport(name, "TCP", this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy); + } + + public Transport createTransport(String name, String netprotocol, final InetSocketAddress clientAddress, final Collection addresses) { + return new Transport(name, netprotocol, this, this.asyncGroup, this.sslContext, clientAddress, addresses, strategy); + } + + public String findGroupName(InetSocketAddress addr) { + if (addr == null) return null; + return groupAddrs.get(addr); + } + + public TransportGroupInfo findGroupInfo(String group) { + if (group == null) return null; + return groupInfos.get(group); + } + + public boolean addGroupInfo(String groupName, InetSocketAddress... addrs) { + addGroupInfo(new TransportGroupInfo(groupName, addrs)); + return true; + } + + public boolean removeGroupInfo(String groupName, InetSocketAddress addr) { + if (groupName == null || groupName.isEmpty() || addr == null) return false; + if (!groupName.equals(groupAddrs.get(addr))) return false; + TransportGroupInfo group = groupInfos.get(groupName); + if (group == null) return false; + group.removeAddress(addr); + groupAddrs.remove(addr); + return true; + } + + public TransportFactory addGroupInfo(String name, Set addrs) { + addGroupInfo(new TransportGroupInfo(name, addrs)); + return this; + } + + public boolean addGroupInfo(TransportGroupInfo info) { + if (info == null) throw new RuntimeException("TransportGroupInfo can not null"); + if (info.addresses == null) throw new RuntimeException("TransportGroupInfo.addresses can not null"); + if (!checkName(info.name)) throw new RuntimeException("Transport.group.name only 0-9 a-z A-Z _ cannot begin 0-9"); + TransportGroupInfo old = groupInfos.get(info.name); + if (old != null && !old.protocol.equals(info.protocol)) throw new RuntimeException("Transport.group.name repeat but protocol is different"); + for (InetSocketAddress addr : info.addresses) { + if (!groupAddrs.getOrDefault(addr, info.name).equals(info.name)) throw new RuntimeException(addr + " repeat but different group.name"); + } + if (old == null) { + groupInfos.put(info.name, info); + } else { + old.putAddress(info.addresses); + } + for (InetSocketAddress addr : info.addresses) { + groupAddrs.put(addr, info.name); + } + return true; + } + + public Transport loadTransport(InetSocketAddress sncpAddress, final Set groups) { + if (groups == null) return null; + Set addresses = new HashSet<>(); + TransportGroupInfo info = null; + for (String group : groups) { + info = groupInfos.get(group); + if (info == null) continue; + addresses.addAll(info.addresses); + } + if (info == null) { + info = new TransportGroupInfo(netprotocol); + } else { + info.protocol = netprotocol; + } + if (sncpAddress != null) addresses.remove(sncpAddress); + return new Transport(groups.stream().sorted().collect(Collectors.joining(";")), info.protocol, this, this.asyncGroup, this.sslContext, sncpAddress, addresses, this.strategy); + } + + public List getGroupInfos() { + return new ArrayList<>(this.groupInfos.values()); + } + + public Logger getLogger() { + return logger; + } + + public void addSncpService(Service service) { + if (service == null) return; + services.add(new WeakReference<>(service)); + } + + public List getServices() { + List rs = new ArrayList<>(); + for (WeakReference ref : services) { + Service service = ref.get(); + if (service != null) rs.add(service); + } + return rs; + } + + public void shutdownNow() { + if (this.scheduler != null) this.scheduler.shutdownNow(); + } + + private void checks() { + List nulllist = new ArrayList<>(); + for (WeakReference ref : transportReferences) { + Transport transport = ref.get(); + if (transport == null) { + nulllist.add(ref); + continue; + } + Transport.TransportNode[] nodes = transport.getTransportNodes(); + for (final Transport.TransportNode node : nodes) { + if (node.disabletime < 1) continue; //可用 + CompletableFuture future = Utility.orTimeout(asyncGroup.createTCPClient(node.address), 2, TimeUnit.SECONDS); + future.whenComplete((r, t) -> { + node.disabletime = t == null ? 0 : System.currentTimeMillis(); + if (r != null) r.dispose(); + }); + } + } + for (WeakReference ref : nulllist) { + transportReferences.remove(ref); + } + } + + private void pings() { + long timex = System.currentTimeMillis() - (this.pinginterval < 15 ? this.pinginterval : (this.pinginterval - 3)) * 1000; + for (WeakReference ref : transportReferences) { + Transport transport = ref.get(); + if (transport == null) continue; + Transport.TransportNode[] nodes = transport.getTransportNodes(); + for (final Transport.TransportNode node : nodes) { + final BlockingQueue queue = node.connQueue; + AsyncConnection conn; + while ((conn = queue.poll()) != null) { + if (conn.getLastWriteTime() > timex && false) { //最近几秒内已经进行过IO操作 + queue.offer(conn); + } else { //超过一定时间的连接需要进行ping处理 + ByteBuffer sendBuffer = pingBuffer.duplicate(); + final AsyncConnection localconn = conn; + final BlockingQueue localqueue = queue; + localconn.write(sendBuffer, sendBuffer, new CompletionHandler() { + @Override + public void completed(Integer result, ByteBuffer wbuffer) { + localconn.read(new CompletionHandler() { + int counter = 0; + + @Override + public void completed(Integer result, ByteBuffer pongBuffer) { + if (counter > 3) { + localconn.offerBuffer(pongBuffer); + localconn.dispose(); + return; + } + if (pongLength > 0 && pongBuffer.position() < pongLength) { + counter++; + localconn.setReadBuffer(pongBuffer); + localconn.read(this); + return; + } + localconn.offerBuffer(pongBuffer); + localqueue.offer(localconn); + } + + @Override + public void failed(Throwable exc, ByteBuffer pongBuffer) { + localconn.offerBuffer(pongBuffer); + localconn.dispose(); + } + }); + } + + @Override + public void failed(Throwable exc, ByteBuffer buffer) { + localconn.dispose(); + } + }); + } + } + } + } + } + + private static boolean checkName(String name) { //不能含特殊字符 + if (name.isEmpty()) return false; + if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false; + for (char ch : name.toCharArray()) { + if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 + return false; + } + } + return true; + } +} diff --git a/src/org/redkale/net/TransportGroupInfo.java b/src/main/java/org/redkale/net/TransportGroupInfo.java similarity index 96% rename from src/org/redkale/net/TransportGroupInfo.java rename to src/main/java/org/redkale/net/TransportGroupInfo.java index 21975af1f..7b409570a 100644 --- a/src/org/redkale/net/TransportGroupInfo.java +++ b/src/main/java/org/redkale/net/TransportGroupInfo.java @@ -1,115 +1,115 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.net.InetSocketAddress; -import java.util.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.Utility; - -/** - * 协议地址组合对象, 对应application.xml 中 resources->group 节点信息 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class TransportGroupInfo { - - protected String name; //地址 - - protected String protocol; //协议 取值范围: TCP、UDP - - protected Set addresses; //地址列表, 对应 resources->group->node节点信息 - - public TransportGroupInfo() { - } - - public TransportGroupInfo(String name, InetSocketAddress... addrs) { - this(name, "TCP", Utility.ofSet(addrs)); - } - - public TransportGroupInfo(String name, Set addrs) { - this(name, "TCP", addrs); - } - - public TransportGroupInfo(String name, String protocol, InetSocketAddress... addrs) { - this(name, protocol, Utility.ofSet(addrs)); - } - - public TransportGroupInfo(String name, String protocol, Set addrs) { - Objects.requireNonNull(name, "Transport.group.name can not null"); - this.name = name; - this.protocol = protocol == null ? "TCP" : protocol; - this.addresses = addrs; - } - - public String getName() { - return name; - } - - public void setName(String name) { - Objects.requireNonNull(name, "Transport.group.name can not null"); - this.name = name; - } - - public String getProtocol() { - return protocol; - } - - public void setProtocol(String protocol) { - this.protocol = protocol == null ? "TCP" : protocol; - } - - public Set getAddresses() { - return addresses; - } - - public Set copyAddresses() { - return addresses == null ? null : new LinkedHashSet<>(addresses); - } - - public void setAddresses(Set addresses) { - this.addresses = addresses; - } - - public boolean containsAddress(InetSocketAddress addr) { - synchronized (this) { - if (this.addresses == null) return false; - return this.addresses.contains(addr); - } - } - - public void removeAddress(InetSocketAddress addr) { - if (addr == null) return; - synchronized (this) { - if (this.addresses == null) return; - this.addresses.remove(addr); - } - } - - public void putAddress(InetSocketAddress addr) { - if (addr == null) return; - synchronized (this) { - if (this.addresses == null) this.addresses = new HashSet<>(); - this.addresses.add(addr); - } - } - - public void putAddress(Set addrs) { - if (addrs == null) return; - synchronized (this) { - if (this.addresses == null) this.addresses = new HashSet<>(); - this.addresses.addAll(addrs); - } - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.net.InetSocketAddress; +import java.util.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.Utility; + +/** + * 协议地址组合对象, 对应application.xml 中 resources->group 节点信息 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class TransportGroupInfo { + + protected String name; //地址 + + protected String protocol; //协议 取值范围: TCP、UDP + + protected Set addresses; //地址列表, 对应 resources->group->node节点信息 + + public TransportGroupInfo() { + } + + public TransportGroupInfo(String name, InetSocketAddress... addrs) { + this(name, "TCP", Utility.ofSet(addrs)); + } + + public TransportGroupInfo(String name, Set addrs) { + this(name, "TCP", addrs); + } + + public TransportGroupInfo(String name, String protocol, InetSocketAddress... addrs) { + this(name, protocol, Utility.ofSet(addrs)); + } + + public TransportGroupInfo(String name, String protocol, Set addrs) { + Objects.requireNonNull(name, "Transport.group.name can not null"); + this.name = name; + this.protocol = protocol == null ? "TCP" : protocol; + this.addresses = addrs; + } + + public String getName() { + return name; + } + + public void setName(String name) { + Objects.requireNonNull(name, "Transport.group.name can not null"); + this.name = name; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol == null ? "TCP" : protocol; + } + + public Set getAddresses() { + return addresses; + } + + public Set copyAddresses() { + return addresses == null ? null : new LinkedHashSet<>(addresses); + } + + public void setAddresses(Set addresses) { + this.addresses = addresses; + } + + public boolean containsAddress(InetSocketAddress addr) { + synchronized (this) { + if (this.addresses == null) return false; + return this.addresses.contains(addr); + } + } + + public void removeAddress(InetSocketAddress addr) { + if (addr == null) return; + synchronized (this) { + if (this.addresses == null) return; + this.addresses.remove(addr); + } + } + + public void putAddress(InetSocketAddress addr) { + if (addr == null) return; + synchronized (this) { + if (this.addresses == null) this.addresses = new HashSet<>(); + this.addresses.add(addr); + } + } + + public void putAddress(Set addrs) { + if (addrs == null) return; + synchronized (this) { + if (this.addresses == null) this.addresses = new HashSet<>(); + this.addresses.addAll(addrs); + } + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/org/redkale/net/TransportStrategy.java b/src/main/java/org/redkale/net/TransportStrategy.java similarity index 96% rename from src/org/redkale/net/TransportStrategy.java rename to src/main/java/org/redkale/net/TransportStrategy.java index 1464c410f..e3f99835c 100644 --- a/src/org/redkale/net/TransportStrategy.java +++ b/src/main/java/org/redkale/net/TransportStrategy.java @@ -1,42 +1,42 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.net.SocketAddress; -import java.util.concurrent.CompletableFuture; - -/** - * 远程请求的负载均衡策略 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface TransportStrategy { - - /** - * 创建AsyncConnection - * - * @param addr 服务器地址 - * @param transport Transport - * - * @return AsyncConnection - */ - public CompletableFuture pollConnection(SocketAddress addr, Transport transport); - - /** - * 回收AsyncConnection,返回false表示使用Transport默认的回收实现, 返回true表示自定义回收实现 - * - * @param forceClose 是否强制关闭 - * @param conn AsyncConnection - * - * @return boolean - */ - default boolean offerConnection(final boolean forceClose, AsyncConnection conn) { - return false; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.net.SocketAddress; +import java.util.concurrent.CompletableFuture; + +/** + * 远程请求的负载均衡策略 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface TransportStrategy { + + /** + * 创建AsyncConnection + * + * @param addr 服务器地址 + * @param transport Transport + * + * @return AsyncConnection + */ + public CompletableFuture pollConnection(SocketAddress addr, Transport transport); + + /** + * 回收AsyncConnection,返回false表示使用Transport默认的回收实现, 返回true表示自定义回收实现 + * + * @param forceClose 是否强制关闭 + * @param conn AsyncConnection + * + * @return boolean + */ + default boolean offerConnection(final boolean forceClose, AsyncConnection conn) { + return false; + } +} diff --git a/src/org/redkale/net/WorkThread.java b/src/main/java/org/redkale/net/WorkThread.java similarity index 57% rename from src/org/redkale/net/WorkThread.java rename to src/main/java/org/redkale/net/WorkThread.java index a2b919a8a..0e1c46b42 100644 --- a/src/org/redkale/net/WorkThread.java +++ b/src/main/java/org/redkale/net/WorkThread.java @@ -1,81 +1,115 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.util.concurrent.*; -import org.redkale.util.ThreadHashExecutor; - -/** - * 协议处理的自定义线程类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class WorkThread extends Thread { - - protected Thread localThread; - - protected final ExecutorService workExecutor; - - protected final ThreadHashExecutor hashExecutor; - - public WorkThread(String name, ExecutorService workExecutor, Runnable target) { - super(target); - if (name != null) setName(name); - this.workExecutor = workExecutor; - this.hashExecutor = workExecutor instanceof ThreadHashExecutor ? (ThreadHashExecutor) workExecutor : null; - this.setDaemon(true); - } - - public void runWork(Runnable command) { - if (workExecutor == null) { - command.run(); - } else { - workExecutor.execute(command); - } - } - - public void runAsync(Runnable command) { - if (workExecutor == null) { - ForkJoinPool.commonPool().execute(command); - } else { - workExecutor.execute(command); - } - } - - public void runAsync(int hash, Runnable command) { - if (hashExecutor == null) { - if (workExecutor == null) { - ForkJoinPool.commonPool().execute(command); - } else { - workExecutor.execute(command); - } - } else { - hashExecutor.execute(hash, command); - } - } - - public ExecutorService getWorkExecutor() { - return workExecutor; - } - - @Override - public void run() { - this.localThread = Thread.currentThread(); - super.run(); - } - - public boolean inCurrThread() { - return this.localThread == Thread.currentThread(); - } - - public boolean inCurrThread(Thread thread) { - return this.localThread == thread; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net; + +import java.util.concurrent.*; +import org.redkale.util.ThreadHashExecutor; + +/** + * 协议处理的自定义线程类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class WorkThread extends Thread implements Executor { + + protected final ExecutorService workExecutor; + + protected final ThreadHashExecutor hashExecutor; + + private int index; //WorkThread下标 + + private int threads; //WorkThread个数 + + public WorkThread(String name, int index, int threads, ExecutorService workExecutor, Runnable target) { + super(target); + if (name != null) setName(name); + this.index = index; + this.threads = threads; + this.workExecutor = workExecutor; + this.hashExecutor = workExecutor instanceof ThreadHashExecutor ? (ThreadHashExecutor) workExecutor : null; + this.setDaemon(true); + } + + public static WorkThread currWorkThread() { + Thread t = Thread.currentThread(); + return t instanceof WorkThread ? (WorkThread) t : null; + } + + @Override + public void execute(Runnable command) { + if (workExecutor == null) { + command.run(); + } else { + workExecutor.execute(command); + } + } + + public void execute(Runnable... commands) { + if (workExecutor == null) { + for (Runnable command : commands) { + command.run(); + } + } else { + for (Runnable command : commands) { + workExecutor.execute(command); + } + } + } + + public void runAsync(Runnable command) { + if (workExecutor == null) { + ForkJoinPool.commonPool().execute(command); + } else { + workExecutor.execute(command); + } + } + + public void runAsync(int hash, Runnable command) { + if (hashExecutor == null) { + if (workExecutor == null) { + ForkJoinPool.commonPool().execute(command); + } else { + workExecutor.execute(command); + } + } else { + hashExecutor.execute(hash, command); + } + } + + public ExecutorService getWorkExecutor() { + return workExecutor; + } + + public boolean inCurrThread() { + return this == Thread.currentThread(); + } + + public boolean inCurrThread(Thread thread) { + return this == thread; + } + + /** + * 获取线程池数组下标, 从0开始 + * + * @return int + */ + public int index() { + return index; + } + + /** + * 获取线程池数组大小,不属于任何数组返回0 + * + * @return int + */ + public int threads() { + return threads; + } + +} diff --git a/src/org/redkale/net/client/Client.java b/src/main/java/org/redkale/net/client/Client.java similarity index 56% rename from src/org/redkale/net/client/Client.java rename to src/main/java/org/redkale/net/client/Client.java index 3d029da87..0f7fa6d60 100644 --- a/src/org/redkale/net/client/Client.java +++ b/src/main/java/org/redkale/net/client/Client.java @@ -1,223 +1,300 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.client; - -import java.net.SocketAddress; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import org.redkale.net.*; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.3.0 - * - * @param 请求对象 - * @param

    响应对象 - */ -public abstract class Client { - - protected final AsyncGroup group; //连接构造器 - - protected final boolean tcp; //是否TCP协议 - - protected final SocketAddress address; //连接的地址 - - protected final ConcurrentLinkedDeque> connQueue = new ConcurrentLinkedDeque(); - - protected final Creator> codecCreator; - - protected final ScheduledThreadPoolExecutor timeoutScheduler; - - protected final AtomicLong writeReqCounter = new AtomicLong(); - - protected final AtomicLong pollRespCounter = new AtomicLong(); - - protected ScheduledFuture timeoutFuture; - - protected ClientConnection[] connArray; //连接池 - - protected AtomicInteger[] connResps; //连接当前处理数 - - protected AtomicBoolean[] connFlags; //conns的标记组,当conn不存在或closed状态,标记为false - - protected int connLimit = Runtime.getRuntime().availableProcessors(); //最大连接数 - - protected int maxPipelines = 16; //单个连接最大并行处理数 - - protected int readTimeoutSeconds; - - protected int writeTimeoutSeconds; - - //------------------ 可选项 ------------------ - //PING心跳的请求数据,为null且pingInterval<1表示不需要定时ping - protected R pingRequest; - - //关闭请求的数据, 为null表示直接关闭 - protected R closeRequest; - - //创建连接后进行的登录鉴权操作 - protected Function, CompletableFuture> authenticate; - - protected Client(AsyncGroup group, SocketAddress address, Creator> responseCreator) { - this(group, true, address, Runtime.getRuntime().availableProcessors(), 16, responseCreator, null, null, null); - } - - protected Client(AsyncGroup group, boolean tcp, SocketAddress address, Creator> codecCreator) { - this(group, tcp, address, Runtime.getRuntime().availableProcessors(), 16, codecCreator, null, null, null); - } - - protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator> codecCreator) { - this(group, tcp, address, maxconns, 16, codecCreator, null, null, null); - } - - protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, int maxPipelines, Creator> codecCreator) { - this(group, tcp, address, maxconns, maxPipelines, codecCreator, null, null, null); - } - - protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator> codecCreator, - Function, CompletableFuture> authenticate) { - this(group, tcp, address, maxconns, 16, codecCreator, null, null, authenticate); - } - - protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator> codecCreator, - R closeRequest, Function, CompletableFuture> authenticate) { - this(group, tcp, address, maxconns, 16, codecCreator, null, closeRequest, authenticate); - } - - protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, - int maxPipelines, Creator> codecCreator, R pingRequest, R closeRequest, - Function, CompletableFuture> authenticate) { - if (maxPipelines < 1) throw new IllegalArgumentException("maxPipelines must bigger 0"); - this.group = group; - this.tcp = tcp; - this.address = address; - this.connLimit = maxconns; - this.maxPipelines = maxPipelines; - this.pingRequest = pingRequest; - this.closeRequest = closeRequest; - this.codecCreator = codecCreator; - this.authenticate = authenticate; - this.connArray = new ClientConnection[connLimit]; - this.connFlags = new AtomicBoolean[connLimit]; - this.connResps = new AtomicInteger[connLimit]; - for (int i = 0; i < connFlags.length; i++) { - this.connFlags[i] = new AtomicBoolean(); - this.connResps[i] = new AtomicInteger(); - } - //timeoutScheduler 不仅仅给超时用, 还给write用 - this.timeoutScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-" + Client.this.getClass().getSimpleName() + "-Interval-Thread"); - t.setDaemon(true); - return t; - }); - if (pingRequest != null) { - this.timeoutFuture = this.timeoutScheduler.scheduleAtFixedRate(() -> { - try { - ClientRequest req = pingRequest; - if (req == null) { //可能运行中进行重新赋值 - timeoutFuture.cancel(true); - timeoutFuture = null; - return; - } - long now = System.currentTimeMillis(); - for (ClientConnection conn : this.connArray) { - if (conn == null) continue; - if (now - conn.getLastWriteTime() > 10_000) conn.writeChannel(req); - } - } catch (Throwable t) { - } - }, 10, 10, TimeUnit.SECONDS); - } - } - - public void close() { - this.timeoutScheduler.shutdownNow(); - final R closereq = closeRequest; - for (ClientConnection conn : this.connArray) { - if (conn == null) continue; - if (closereq == null) { - conn.dispose(null); - } else { - try { - conn.writeChannel(closereq).get(100, TimeUnit.MILLISECONDS); - } catch (Exception e) { - } - conn.dispose(null); - } - } - } - - public CompletableFuture

    sendAsync(R request) { - return connect(null).thenCompose(conn -> conn.writeChannel(request)); - } - - public CompletableFuture

    sendAsync(ChannelContext context, R request) { - return connect(context).thenCompose(conn -> conn.writeChannel(request)); - } - - protected CompletableFuture

    writeChannel(ClientConnection conn, R request) { - return conn.writeChannel(request); - } - - protected CompletableFuture connect(ChannelContext context) { - ClientConnection minRunningConn = null; - for (int i = 0; i < this.connArray.length; i++) { - final int index = i; - final ClientConnection conn = this.connArray[index]; - if (conn == null || !conn.isOpen()) { - if (this.connFlags[index].compareAndSet(false, true)) { - CompletableFuture future = group.create(tcp, address, readTimeoutSeconds, writeTimeoutSeconds).thenApply(c -> createClientConnection(index, c)); - return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> { - c.authenticated = true; - this.connArray[index] = c; - return c; - }).whenComplete((r, t) -> { - if (t != null) this.connFlags[index].set(false); - }); - } - } else if (conn.runningCount() < 1) { - return CompletableFuture.completedFuture(conn); - } else if (minRunningConn == null || minRunningConn.runningCount() > conn.runningCount()) { - minRunningConn = conn; - } - } - if (minRunningConn != null && minRunningConn.runningCount() < maxPipelines) return CompletableFuture.completedFuture(minRunningConn); - return waitClientConnection(); - } - - protected CompletableFuture waitClientConnection() { - CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS); - connQueue.offer(rs); - return rs; - } - - protected ClientConnection createClientConnection(final int index, AsyncConnection channel) { - return new ClientConnection(this, index, channel); - } - - public int getReadTimeoutSeconds() { - return readTimeoutSeconds; - } - - public void setReadTimeoutSeconds(int readTimeoutSeconds) { - this.readTimeoutSeconds = readTimeoutSeconds; - } - - public int getWriteTimeoutSeconds() { - return writeTimeoutSeconds; - } - - public void setWriteTimeoutSeconds(int writeTimeoutSeconds) { - this.writeTimeoutSeconds = writeTimeoutSeconds; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.client; + +import java.net.SocketAddress; +import java.util.Queue; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.*; +import org.redkale.net.*; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.3.0 + * + * @param 请求对象 + * @param

    响应对象 + */ +public abstract class Client { + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected final boolean finest = logger.isLoggable(Level.FINEST); + + protected final AsyncGroup group; //连接构造器 + + protected final boolean tcp; //是否TCP协议 + + protected final SocketAddress address; //连接的地址 + + protected final Creator> codecCreator; + + protected final ScheduledThreadPoolExecutor timeoutScheduler; + + protected final LongAdder writeReqCounter = new LongAdder(); + + protected final LongAdder pollRespCounter = new LongAdder(); + + private final AtomicInteger connSeqno = new AtomicInteger(); + + private boolean closed; + + protected ScheduledFuture timeoutFuture; + + protected ClientConnection[] connArray; //连接池 + + protected LongAdder[] connResps; //连接当前处理数 + + protected AtomicBoolean[] connOpens; //conns的标记组,当conn不存在或closed状态,标记为false + + protected final Queue>[] connWaits; //连接等待池 + + protected int connLimit = Utility.cpus(); //最大连接数 + + protected int maxPipelines = 16; //单个连接最大并行处理数 + + protected int readTimeoutSeconds; + + protected int writeTimeoutSeconds; + + protected String connectionContextName; + + //------------------ 可选项 ------------------ + //PING心跳的请求数据,为null且pingInterval<1表示不需要定时ping + protected R pingRequest; + + //关闭请求的数据, 为null表示直接关闭 + protected R closeRequest; + + //创建连接后进行的登录鉴权操作 + protected Function, CompletableFuture> authenticate; + + protected Client(AsyncGroup group, SocketAddress address, Creator> responseCreator) { + this(group, true, address, Utility.cpus(), 16, responseCreator, null, null, null); + } + + protected Client(AsyncGroup group, boolean tcp, SocketAddress address, Creator> codecCreator) { + this(group, tcp, address, Utility.cpus(), 16, codecCreator, null, null, null); + } + + protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator> codecCreator) { + this(group, tcp, address, maxconns, 16, codecCreator, null, null, null); + } + + protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, int maxPipelines, Creator> codecCreator) { + this(group, tcp, address, maxconns, maxPipelines, codecCreator, null, null, null); + } + + protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator> codecCreator, + Function, CompletableFuture> authenticate) { + this(group, tcp, address, maxconns, 16, codecCreator, null, null, authenticate); + } + + protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, Creator> codecCreator, + R closeRequest, Function, CompletableFuture> authenticate) { + this(group, tcp, address, maxconns, 16, codecCreator, null, closeRequest, authenticate); + } + + @SuppressWarnings("OverridableMethodCallInConstructor") + protected Client(AsyncGroup group, boolean tcp, SocketAddress address, int maxconns, + int maxPipelines, Creator> codecCreator, R pingRequest, R closeRequest, + Function, CompletableFuture> authenticate) { + if (maxPipelines < 1) throw new IllegalArgumentException("maxPipelines must bigger 0"); + this.group = group; + this.tcp = tcp; + this.address = address; + this.connLimit = maxconns; + this.maxPipelines = maxPipelines; + this.pingRequest = pingRequest; + this.closeRequest = closeRequest; + this.codecCreator = codecCreator; + this.authenticate = authenticate; + this.connArray = new ClientConnection[connLimit]; + this.connOpens = new AtomicBoolean[connLimit]; + this.connResps = new LongAdder[connLimit]; + this.connWaits = new Queue[connLimit]; + for (int i = 0; i < connOpens.length; i++) { + this.connOpens[i] = new AtomicBoolean(); + this.connResps[i] = new LongAdder(); + this.connWaits[i] = Utility.unsafe() != null ? new MpscGrowableArrayQueue<>(16, 1 << 10) : new ConcurrentLinkedDeque(); + } + //timeoutScheduler 不仅仅给超时用, 还给write用 + this.timeoutScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { + final Thread t = new Thread(r, "Redkale-" + Client.this.getClass().getSimpleName() + "-Interval-Thread"); + t.setDaemon(true); + return t; + }); + if (pingRequest != null && this.timeoutFuture == null) { + this.timeoutFuture = this.timeoutScheduler.scheduleAtFixedRate(() -> { + try { + R req = pingRequest; + if (req == null) { //可能运行中进行重新赋值 + timeoutFuture.cancel(true); + timeoutFuture = null; + return; + } + long now = System.currentTimeMillis(); + for (ClientConnection conn : this.connArray) { + if (conn == null) continue; + if (now - conn.getLastWriteTime() < 10_000) continue; + conn.writeChannel(req).thenAccept(p -> handlePingResult(conn, p)); + } + } catch (Throwable t) { + } + }, pingInterval(), pingInterval(), TimeUnit.SECONDS); + } + } + + protected int pingInterval() { + return 30; + } + + protected void handlePingResult(ClientConnection conn, P result) { + } + + public synchronized void close() { + if (this.closed) return; + this.timeoutScheduler.shutdownNow(); + final R closereq = closeRequest; + for (ClientConnection conn : this.connArray) { + if (conn == null) continue; + if (closereq == null) { + conn.dispose(null); + } else { + try { + conn.writeChannel(closereq).get(1, TimeUnit.SECONDS); + } catch (Exception e) { + } + conn.dispose(null); + } + } + group.close(); + this.closed = true; + } + + public final CompletableFuture

    sendAsync(R request) { + if (request.workThread == null) request.workThread = WorkThread.currWorkThread(); + return connect(null).thenCompose(conn -> writeChannel(conn, request)); + } + + public final CompletableFuture

    sendAsync(ChannelContext context, R request) { + if (request.workThread == null) request.workThread = WorkThread.currWorkThread(); + return connect(context).thenCompose(conn -> writeChannel(conn, request)); + } + + protected CompletableFuture

    writeChannel(ClientConnection conn, R request) { + return conn.writeChannel(request); + } + + protected CompletableFuture connect() { + return connect(null); + } + + protected CompletableFuture connect(final ChannelContext context) { + final boolean cflag = context != null && connectionContextName != null; + if (cflag) { + ClientConnection cc = context.getAttribute(connectionContextName); + if (cc != null && cc.isOpen()) return CompletableFuture.completedFuture(cc); + + } + int connIndex = -1; + final int size = this.connArray.length; + WorkThread workThread = WorkThread.currWorkThread(); + if (workThread != null && workThread.threads() == size) { + connIndex = workThread.index(); + } + if (connIndex >= 0) { + ClientConnection cc = this.connArray[connIndex]; + if (cc != null && cc.isOpen()) { + if (cflag) context.setAttribute(connectionContextName, cc); + return CompletableFuture.completedFuture(cc); + } + final int index = connIndex; + final Queue> waitQueue = this.connWaits[index]; + if (this.connOpens[index].compareAndSet(false, true)) { + CompletableFuture future = group.createClient(tcp, address, readTimeoutSeconds, writeTimeoutSeconds) + .thenApply(c -> createClientConnection(index, c).setMaxPipelines(maxPipelines)); + return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> { + c.authenticated = true; + this.connArray[index] = c; + CompletableFuture f; + if (cflag) context.setAttribute(connectionContextName, c); + while ((f = waitQueue.poll()) != null) { + f.complete(c); + } + return c; + }).whenComplete((r, t) -> { + if (t != null) this.connOpens[index].set(false); + }); + } else { + CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS); + waitQueue.offer(rs); + return rs; + } + } + ClientConnection minRunningConn = null; + for (int i = 0; i < size; i++) { + final int index = i; + final ClientConnection conn = this.connArray[index]; + if (conn == null || !conn.isOpen()) { + if (this.connOpens[index].compareAndSet(false, true)) { + CompletableFuture future = group.createClient(tcp, address, readTimeoutSeconds, writeTimeoutSeconds) + .thenApply(c -> createClientConnection(index, c).setMaxPipelines(maxPipelines)); + return (authenticate == null ? future : authenticate.apply(future)).thenApply(c -> { + c.authenticated = true; + this.connArray[index] = c; + return c; + }).whenComplete((r, t) -> { + if (t != null) this.connOpens[index].set(false); + }); + } + } else if (conn.runningCount() < 1) { + return CompletableFuture.completedFuture(conn); + } else if (minRunningConn == null || minRunningConn.runningCount() > conn.runningCount()) { + minRunningConn = conn; + } + } + if (minRunningConn != null && minRunningConn.runningCount() < maxPipelines) { + ClientConnection minConn = minRunningConn; + return CompletableFuture.completedFuture(minConn); + } + return waitClientConnection(); + } + + protected CompletableFuture waitClientConnection() { + CompletableFuture rs = Utility.orTimeout(new CompletableFuture(), 6, TimeUnit.SECONDS); + connWaits[connSeqno.getAndIncrement() % this.connLimit].offer(rs); + return rs; + } + + protected ClientConnection createClientConnection(final int index, AsyncConnection channel) { + return new ClientConnection(this, index, channel); + } + + public int getReadTimeoutSeconds() { + return readTimeoutSeconds; + } + + public void setReadTimeoutSeconds(int readTimeoutSeconds) { + this.readTimeoutSeconds = readTimeoutSeconds; + } + + public int getWriteTimeoutSeconds() { + return writeTimeoutSeconds; + } + + public void setWriteTimeoutSeconds(int writeTimeoutSeconds) { + this.writeTimeoutSeconds = writeTimeoutSeconds; + } + +} diff --git a/src/org/redkale/net/client/ClientCodec.java b/src/main/java/org/redkale/net/client/ClientCodec.java similarity index 78% rename from src/org/redkale/net/client/ClientCodec.java rename to src/main/java/org/redkale/net/client/ClientCodec.java index 1488a368a..cb651402b 100644 --- a/src/org/redkale/net/client/ClientCodec.java +++ b/src/main/java/org/redkale/net/client/ClientCodec.java @@ -1,57 +1,62 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.client; - -import java.nio.ByteBuffer; -import java.util.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.ByteArray; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.3.0 - * @param ClientRequest - * @param

    响应对象 - */ -public abstract class ClientCodec { - - private final List> results = new ArrayList<>(); - - public ClientCodec() { - } - - //返回true后array会clear - public abstract boolean codecResult(ClientConnection conn, List requests, ByteBuffer buffer, ByteArray array); - - public List> removeResults() { - if (results.isEmpty()) return null; - List> rs = new ArrayList<>(results); - this.results.clear(); - return rs; - } - - public void addResult(P result) { - this.results.add(new ClientResult<>(result)); - } - - public void addResult(Throwable exc) { - this.results.add(new ClientResult<>(exc)); - } - - public void reset() { - this.results.clear(); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.client; + +import java.nio.ByteBuffer; +import java.util.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.ByteArray; + +/** + * 每个ClientConnection绑定一个独立的ClientCodec实例 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.3.0 + * @param ClientRequest + * @param

    响应对象 + */ +public abstract class ClientCodec { + + private final List> results = new ArrayList<>(); + + public ClientCodec() { + } + + //返回true: array会clear, 返回false: buffer会clear + public abstract boolean codecResult(ClientConnection conn, ByteBuffer buffer, ByteArray array); + + protected Queue responseQueue(ClientConnection conn) { + return conn.responseQueue; + } + + public List> removeResults() { + if (results.isEmpty()) return null; + List> rs = new ArrayList<>(results); + this.results.clear(); + return rs; + } + + public void addResult(P result) { + this.results.add(new ClientResult<>(result)); + } + + public void addResult(Throwable exc) { + this.results.add(new ClientResult<>(exc)); + } + + public void reset() { + this.results.clear(); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + +} diff --git a/src/org/redkale/net/client/ClientConnection.java b/src/main/java/org/redkale/net/client/ClientConnection.java similarity index 54% rename from src/org/redkale/net/client/ClientConnection.java rename to src/main/java/org/redkale/net/client/ClientConnection.java index 1358fcc0c..b144bafc8 100644 --- a/src/org/redkale/net/client/ClientConnection.java +++ b/src/main/java/org/redkale/net/client/ClientConnection.java @@ -1,273 +1,322 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.client; - -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.List; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.stream.*; -import org.redkale.net.AsyncConnection; -import org.redkale.util.ByteArray; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.3.0 - * - * @param 请求对象 - * @param

    响应对象 - */ -public class ClientConnection implements Consumer { - - protected final int index; - - protected final Client client; - - protected final AtomicInteger respCounter; - - protected final AsyncConnection channel; - - protected final ByteArray writeArray = new ByteArray(); - - protected final ByteArray readArray = new ByteArray(); - - protected final AtomicBoolean readPending = new AtomicBoolean(); - - protected final AtomicBoolean writePending = new AtomicBoolean(); - - protected final ConcurrentLinkedDeque requestQueue = new ConcurrentLinkedDeque(); - - protected final ConcurrentLinkedDeque responseQueue = new ConcurrentLinkedDeque(); - - protected final CompletionHandler writeHandler = new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - if (writeLastRequest != null && writeLastRequest == client.closeRequest) { - if (closeFuture != null) closeFuture.complete(null); - closeFuture = null; - return; - } - - if (continueWrite()) return; - if (!writePending.compareAndSet(true, false)) { - if (continueWrite()) return; - } - - readChannel(); - } - - @Override - public void failed(Throwable exc, Void attachment) { - dispose(exc); - } - }; - - private boolean continueWrite() { - writeArray.clear(); - int pipelines = client.maxPipelines > 1 ? (client.maxPipelines - responseQueue.size()) : 1; - currPipelineIndex = 0; - for (int i = 0; i < pipelines; i++) { - R r = requestQueue.poll(); - if (r == null) break; - writeLastRequest = r; - r.accept(ClientConnection.this, writeArray); - currPipelineIndex++; - } - if (writeArray.length() > 0) { - channel.write(writeArray, writeHandler); - return true; - } - return false; - } - - protected final CompletionHandler readHandler = new CompletionHandler() { - - ClientCodec codec; - - @Override - public void completed(Integer count, ByteBuffer attachment) { - if (count < 1) { - channel.setReadBuffer(attachment); - dispose(new NonReadableChannelException()); - return; - } - try { - if (codec == null) codec = client.codecCreator.create(); - attachment.flip(); - codecResponse(attachment); - } catch (Exception e) { - channel.setReadBuffer(attachment); - dispose(e); - } - } - - public void codecResponse(ByteBuffer buffer) { - Stream reqstream = responseQueue.stream().map(r -> (R) r.request); - List requests = reqstream.collect(Collectors.toList()); - if (codec.codecResult(ClientConnection.this, requests, buffer, readArray)) { //成功了 - readArray.clear(); - List> results = codec.removeResults(); - if (results != null) { - for (ClientResult

    rs : results) { - ClientFuture respFuture = responseQueue.poll(); - if (respFuture != null) { - respCounter.decrementAndGet(); - if (isAuthenticated()) client.pollRespCounter.incrementAndGet(); - try { - if (respFuture.timeout != null) respFuture.timeout.cancel(true); - if (rs.exc != null) { - respFuture.completeExceptionally(rs.exc); - } else { - respFuture.complete(rs.result); - } - } catch (Throwable t) { - t.printStackTrace(); - } - } - } - } - { - CompletableFuture connFuture = client.connQueue.poll(); - if (connFuture != null) connFuture.complete(ClientConnection.this); - } - if (buffer.hasRemaining()) { - codecResponse(buffer); - } else if (responseQueue.isEmpty()) { //队列都已处理完了 - buffer.clear(); - channel.setReadBuffer(buffer); - if (readPending.compareAndSet(true, false)) { - CompletableFuture connFuture = client.connQueue.poll(); - if (connFuture != null) connFuture.complete(ClientConnection.this); - } else { - channel.read(this); - } - } else { - buffer.clear(); - channel.setReadBuffer(buffer); - channel.read(this); - } - } else { //数据不全, 继续读 - buffer.clear(); - channel.setReadBuffer(buffer); - channel.read(this); - } - } - - @Override - public void failed(Throwable t, ByteBuffer attachment) { - dispose(t); - } - }; - - protected boolean authenticated; - - protected int currPipelineIndex; - - protected ClientFuture closeFuture; - - private R writeLastRequest; - - public ClientConnection(Client client, int index, AsyncConnection channel) { - this.client = client; - this.index = index; - this.respCounter = client.connResps[index]; - this.channel = channel.beforeCloseListener(this); - } - - protected CompletableFuture

    writeChannel(R request) { - ClientFuture respFuture = createClientFuture(request); - if (request == client.closeRequest) { - respFuture.request = null; - closeFuture = respFuture; - } else { - int rts = this.channel.getReadTimeoutSeconds(); - if (rts > 0 && respFuture.request != null) { - respFuture.responseQueue = responseQueue; - respFuture.timeout = client.timeoutScheduler.schedule(respFuture, rts, TimeUnit.SECONDS); - } - } - synchronized (requestQueue) { //保证顺序一致 - responseQueue.offer(respFuture.request == null ? ClientFuture.EMPTY : respFuture); - requestQueue.offer(request); - respCounter.incrementAndGet(); - if (isAuthenticated()) client.writeReqCounter.incrementAndGet(); - } - if (writePending.compareAndSet(false, true)) { - writeArray.clear(); - int pipelines = client.maxPipelines > 1 ? client.maxPipelines : 1; //pipelines必须大于0 - currPipelineIndex = 0; - for (int i = 0; i < pipelines; i++) { - R r = requestQueue.poll(); - if (r == null) break; - r.accept(ClientConnection.this, writeArray); - currPipelineIndex++; - } - channel.write(writeArray, writeHandler); - } - return respFuture; - } - - protected ClientFuture createClientFuture(R request) { - return new ClientFuture(request); - } - - protected void readChannel() { - if (readPending.compareAndSet(false, true)) { - readArray.clear(); - channel.read(readHandler); - } - } - - public boolean isAuthenticated() { - return authenticated; - } - - public AsyncConnection getChannel() { - return channel; - } - - public int currPipelineIndex() { - return currPipelineIndex; - } - - @Override //AsyncConnection.beforeCloseListener - public void accept(AsyncConnection t) { - respCounter.set(0); - client.connFlags[index].set(false); - client.connArray[index] = null; //必须connflags之后 - } - - public void dispose(Throwable exc) { - channel.dispose(); - Throwable e = exc; - CompletableFuture f; - respCounter.set(0); - while ((f = responseQueue.poll()) != null) { - if (e == null) e = new ClosedChannelException(); - f.completeExceptionally(e); - } - } - - public int runningCount() { - return respCounter.get(); - } - - public long getLastWriteTime() { - return channel.getLastWriteTime(); - } - - public boolean isOpen() { - return channel.isOpen(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.client; + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.Level; +import org.redkale.net.*; +import org.redkale.util.*; + +/** + * 注意: 要确保AsyncConnection的读写过程都必须在channel.ioThread中运行 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.3.0 + * + * @param 请求对象 + * @param

    响应对象 + */ +public class ClientConnection implements Consumer { + + protected final int index; //从0开始, connArray的下坐标 + + protected final Client client; + + protected final LongAdder respCounter; + + protected final AsyncConnection channel; + + protected final ByteArray writeArray = new ByteArray(); + + protected final ByteArray readArray = new ByteArray(); + + protected final AtomicBoolean pauseWriting = new AtomicBoolean(); + + protected final AtomicBoolean readPending = new AtomicBoolean(); + + protected final AtomicBoolean writePending = new AtomicBoolean(); + + protected final Queue requestQueue = new ArrayDeque<>(); + + protected final Queue responseQueue = new ArrayDeque<>(); + + protected final CompletionHandler writeHandler = new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + if (writeLastRequest != null && writeLastRequest == client.closeRequest) { + if (closeFuture != null) closeFuture.complete(null); + closeFuture = null; + return; + } + if (continueWrite(false)) return; + writePending.compareAndSet(true, false); + readChannel(); + } + + @Override + public void failed(Throwable exc, Void attachment) { + dispose(exc); + } + }; + + protected int maxPipelines; //最大并行处理数 + + protected ClientConnection setMaxPipelines(int maxPipelines) { + this.maxPipelines = maxPipelines; + return this; + } + + protected ClientConnection resetMaxPipelines() { + this.maxPipelines = client.maxPipelines; + return this; + } + + protected void pauseWriting(boolean flag) { + this.pauseWriting.set(flag); + } + + private boolean continueWrite(boolean must) { + writeArray.clear(); + int pipelines = maxPipelines > 1 ? (maxPipelines - responseQueue.size()) : 1; + if (must && pipelines < 1) pipelines = 1; + int c = 0; + AtomicBoolean pw = this.pauseWriting; + for (int i = 0; i < pipelines; i++) { + if (pw.get()) break; + R r = requestQueue.poll(); + if (r == null) break; + writeLastRequest = r; + r.accept(this, writeArray); + c++; + } + if (c > 0) { //当Client连接Server时先从Server读取数据时,会先发送一个EMPTY的request,这样writeArray.count就会为0 + channel.write(writeArray, writeHandler); + return true; + } + return false; + } + + protected void preComplete(P resp, R req, Throwable exc) { + } + + protected final CompletionHandler readHandler = new CompletionHandler() { + + ClientCodec codec; + + @Override + public void completed(Integer count, ByteBuffer attachment) { + if (count < 1) { + channel.setReadBuffer(attachment); + dispose(new NonReadableChannelException()); + return; + } + try { + if (codec == null) codec = client.codecCreator.create(); + attachment.flip(); + codecResponse(attachment); + } catch (Throwable e) { + channel.setReadBuffer(attachment); + dispose(e); + } + } + + public void codecResponse(ByteBuffer buffer) { + if (codec.codecResult(ClientConnection.this, buffer, readArray)) { //成功了 + readArray.clear(); + List> results = codec.removeResults(); + if (results != null) { + for (ClientResult

    rs : results) { + ClientFuture respFuture = responseQueue.poll(); + if (respFuture != null) { + respCounter.decrement(); + if (isAuthenticated() && client.pollRespCounter != null) client.pollRespCounter.increment(); + try { + if (respFuture.timeout != null) respFuture.timeout.cancel(true); + ClientRequest request = respFuture.request; + //if (client.finest) client.logger.log(Level.FINEST, Utility.nowMillis() + ": " + Thread.currentThread().getName() + ": " + ClientConnection.this + ", 回调处理, req=" + request + ", result=" + rs.result); + preComplete(rs.result, (R) request, rs.exc); + WorkThread workThread = null; + if (request != null) { + workThread = request.workThread; + request.workThread = null; + } + if (rs.exc != null) { + if (workThread == null || workThread == Thread.currentThread() + || workThread.getState() == Thread.State.BLOCKED + || workThread.getState() == Thread.State.WAITING) { + respFuture.completeExceptionally(rs.exc); + } else { + workThread.execute(() -> respFuture.completeExceptionally(rs.exc)); + } + } else { + if (workThread == null || workThread == Thread.currentThread() + || workThread.getState() == Thread.State.BLOCKED + || workThread.getState() == Thread.State.WAITING) { + respFuture.complete(rs.result); + } else { + workThread.execute(() -> respFuture.complete(rs.result)); + } + } + } catch (Throwable t) { + client.logger.log(Level.INFO, "complete result error, request: " + respFuture.request, t); + } + } + } + } + + if (buffer.hasRemaining()) { + codecResponse(buffer); + } else if (responseQueue.isEmpty()) { //队列都已处理完了 + buffer.clear(); + channel.setReadBuffer(buffer); + if (readPending.compareAndSet(true, false)) { + //无消息处理 + } else { + channel.read(this); + } + } else { //还有消息需要读取 + if (!requestQueue.isEmpty() && writePending.compareAndSet(false, true)) { + //先写后读取 + if (!continueWrite(true)) { + writePending.compareAndSet(true, false); + } + } + buffer.clear(); + channel.setReadBuffer(buffer); + channel.read(this); + } + } else { //数据不全, 继续读 + buffer.clear(); + channel.setReadBuffer(buffer); + channel.read(this); + } + } + + @Override + public void failed(Throwable t, ByteBuffer attachment) { + dispose(t); + } + }; + + protected boolean authenticated; + + protected ClientFuture closeFuture; + + private R writeLastRequest; + + @SuppressWarnings("LeakingThisInConstructor") + public ClientConnection(Client client, int index, AsyncConnection channel) { + this.client = client; + this.index = index; + this.respCounter = client.connResps[index]; + this.channel = channel.beforeCloseListener(this); + } + + protected CompletableFuture

    writeChannel(R request) { + ClientFuture respFuture = createClientFuture(request); + if (request == client.closeRequest) { + respFuture.request = null; + closeFuture = respFuture; + } else { + int rts = this.channel.getReadTimeoutSeconds(); + if (rts > 0 && respFuture.request != null) { + respFuture.responseQueue = responseQueue; + respFuture.timeout = client.timeoutScheduler.schedule(respFuture, rts, TimeUnit.SECONDS); + } + } + if (channel.inCurrThread()) { + writeChannelInThread(request, respFuture); + } else { + channel.execute(() -> writeChannelInThread(request, respFuture)); + } + return respFuture; + } + + private void writeChannelInThread(R request, ClientFuture respFuture) { + { //保证顺序一致 + responseQueue.offer(client.closeRequest != null && respFuture.request == client.closeRequest ? ClientFuture.EMPTY : respFuture); + requestQueue.offer(request); + respCounter.increment(); + if (isAuthenticated() && client.writeReqCounter != null) client.writeReqCounter.increment(); + } + if (writePending.compareAndSet(false, true)) { + continueWrite(true); + } + } + + protected ClientFuture createClientFuture(R request) { + return new ClientFuture(request); + } + + protected void readChannel() { + if (readPending.compareAndSet(false, true)) { + readArray.clear(); + channel.read(readHandler); + } + } + + public boolean isAuthenticated() { + return authenticated; + } + + public AsyncConnection getChannel() { + return channel; + } + + @Override //AsyncConnection.beforeCloseListener + public void accept(AsyncConnection t) { + respCounter.reset(); + client.connOpens[index].set(false); + client.connArray[index] = null; //必须connflags之后 + } + + public void dispose(Throwable exc) { + channel.dispose(); + Throwable e = exc; + CompletableFuture f; + respCounter.reset(); + while ((f = responseQueue.poll()) != null) { + if (e == null) e = new ClosedChannelException(); + f.completeExceptionally(e); + } + } + + public int runningCount() { + return respCounter.intValue(); + } + + public long getLastWriteTime() { + return channel.getLastWriteTime(); + } + + public long getLastReadTime() { + return channel.getLastReadTime(); + } + + public boolean isOpen() { + return channel.isOpen(); + } + + @Override + public String toString() { + String s = super.toString(); + int pos = s.lastIndexOf('@'); + if (pos < 1) return s; + int cha = pos + 10 - s.length(); + if (cha < 1) return s; + for (int i = 0; i < cha; i++) s += ' '; + return s; + } +} diff --git a/src/org/redkale/net/client/ClientFuture.java b/src/main/java/org/redkale/net/client/ClientFuture.java similarity index 64% rename from src/org/redkale/net/client/ClientFuture.java rename to src/main/java/org/redkale/net/client/ClientFuture.java index 3bb9992d6..cc230a7be 100644 --- a/src/org/redkale/net/client/ClientFuture.java +++ b/src/main/java/org/redkale/net/client/ClientFuture.java @@ -1,58 +1,72 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.client; - -import java.util.concurrent.*; - -/** - * - * @author zhangjx - * @param 泛型 - */ -public class ClientFuture extends CompletableFuture implements Runnable { - - public static final ClientFuture EMPTY = new ClientFuture() { - @Override - public boolean complete(Object value) { - return true; - } - - @Override - public boolean completeExceptionally(Throwable ex) { - return true; - } - }; - - protected ClientRequest request; - - ScheduledFuture timeout; - - ConcurrentLinkedDeque responseQueue; - - public ClientFuture() { - super(); - } - - public ClientFuture(ClientRequest request) { - super(); - this.request = request; - } - - //@Override //JDK9+ - public ClientFuture newIncompleteFuture() { - return new ClientFuture<>(); - } - - public R getRequest() { - return (R) request; - } - - @Override - public void run() { - if (responseQueue != null) responseQueue.remove(this); - this.completeExceptionally(new TimeoutException()); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.client; + +import java.util.Queue; +import java.util.concurrent.*; +import org.redkale.net.WorkThread; + +/** + * + * @author zhangjx + * @param 泛型 + */ +public class ClientFuture extends CompletableFuture implements Runnable { + + public static final ClientFuture EMPTY = new ClientFuture() { + @Override + public boolean complete(Object value) { + return true; + } + + @Override + public boolean completeExceptionally(Throwable ex) { + return true; + } + }; + + protected ClientRequest request; + + ScheduledFuture timeout; + + Queue responseQueue; + + public ClientFuture() { + super(); + } + + public ClientFuture(ClientRequest request) { + super(); + this.request = request; + } + + @Override //JDK9+ + public ClientFuture newIncompleteFuture() { + return new ClientFuture<>(); + } + + public R getRequest() { + return (R) request; + } + + @Override + public void run() { + if (responseQueue != null) responseQueue.remove(this); + TimeoutException ex = new TimeoutException(); + WorkThread workThread = null; + if (request != null) { + workThread = request.workThread; + request.workThread = null; + } + if (workThread == null || workThread == Thread.currentThread() + || workThread.getState() == Thread.State.BLOCKED + || workThread.getState() == Thread.State.WAITING) { + this.completeExceptionally(ex); + } else { + workThread.execute(() -> completeExceptionally(ex)); + } + } +} diff --git a/src/main/java/org/redkale/net/client/ClientRequest.java b/src/main/java/org/redkale/net/client/ClientRequest.java new file mode 100644 index 000000000..2f51c694c --- /dev/null +++ b/src/main/java/org/redkale/net/client/ClientRequest.java @@ -0,0 +1,44 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.client; + +import java.util.function.*; +import org.redkale.net.WorkThread; +import org.redkale.util.ByteArray; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.3.0 + */ +public abstract class ClientRequest implements BiConsumer { + + protected long createTime = System.currentTimeMillis(); + + WorkThread workThread; + + public long getCreateTime() { + return createTime; + } + + public T currThread(WorkThread thread) { + this.workThread = thread; + return (T) this; + } + + protected void prepare() { + this.createTime = System.currentTimeMillis(); + } + + protected boolean recycle() { + this.createTime = 0; + return true; + } +} diff --git a/src/org/redkale/net/client/ClientResult.java b/src/main/java/org/redkale/net/client/ClientResult.java similarity index 95% rename from src/org/redkale/net/client/ClientResult.java rename to src/main/java/org/redkale/net/client/ClientResult.java index b62a61db6..60096452c 100644 --- a/src/org/redkale/net/client/ClientResult.java +++ b/src/main/java/org/redkale/net/client/ClientResult.java @@ -1,47 +1,47 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.client; - -/** - * - * @author zhangjx - */ -public class ClientResult

    { - - protected P result; - - protected Throwable exc; - - public ClientResult(P result) { - this.result = result; - } - - public ClientResult(Throwable exc) { - this.exc = exc; - } - - public P getResult() { - return result; - } - - public void setResult(P result) { - this.result = result; - } - - public Throwable getExc() { - return exc; - } - - public void setExc(Throwable exc) { - this.exc = exc; - } - - @Override - public String toString() { - if (exc != null) return "{\"exc\":" + exc + "}"; - return "{\"result\":" + result + "}"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.client; + +/** + * + * @author zhangjx + */ +public class ClientResult

    { + + protected P result; + + protected Throwable exc; + + public ClientResult(P result) { + this.result = result; + } + + public ClientResult(Throwable exc) { + this.exc = exc; + } + + public P getResult() { + return result; + } + + public void setResult(P result) { + this.result = result; + } + + public Throwable getExc() { + return exc; + } + + public void setExc(Throwable exc) { + this.exc = exc; + } + + @Override + public String toString() { + if (exc != null) return "{\"exc\":" + exc + "}"; + return "{\"result\":" + result + "}"; + } +} diff --git a/src/org/redkale/net/http/HttpClient.java b/src/main/java/org/redkale/net/http/HttpClient.java similarity index 94% rename from src/org/redkale/net/http/HttpClient.java rename to src/main/java/org/redkale/net/http/HttpClient.java index 1c633f917..a65156b3d 100644 --- a/src/org/redkale/net/http/HttpClient.java +++ b/src/main/java/org/redkale/net/http/HttpClient.java @@ -1,347 +1,347 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.nio.charset.*; -import java.util.Map; -import java.util.concurrent.CompletableFuture; -import org.redkale.net.*; -import static org.redkale.net.http.HttpRequest.parseHeaderName; -import org.redkale.util.*; - -/** - * 简单的HttpClient实现, 存在以下情况不能使用此类:
    - * 1、使用HTTPS;
    - * 2、上传下载文件;
    - * 3、返回超大响应包;
    - * 类似JDK11的 java.net.http.HttpClient
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.3.0 - * - */ -public class HttpClient { - - protected final AsyncGroup asyncGroup; - - protected int readTimeoutSeconds = 6; - - protected int writeTimeoutSeconds = 6; - - protected HttpClient(AsyncGroup asyncGroup) { - this.asyncGroup = asyncGroup; - } - - public static HttpClient create(AsyncGroup asyncGroup) { - return new HttpClient(asyncGroup); - } - - public CompletableFuture> getAsync(String url) { - return sendAsync("GET", url, null, (byte[]) null); - } - - public CompletableFuture> postAsync(String url) { - return sendAsync("POST", url, null, (byte[]) null); - } - - public CompletableFuture> getAsync(String url, String body) { - return sendAsync("GET", url, null, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); - } - - public CompletableFuture> postAsync(String url, String body) { - return sendAsync("POST", url, null, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); - } - - public CompletableFuture> getAsync(String url, byte[] body) { - return sendAsync("GET", url, null, body); - } - - public CompletableFuture> postAsync(String url, byte[] body) { - return sendAsync("POST", url, null, body); - } - - public CompletableFuture> getAsync(String url, Map headers) { - return sendAsync("GET", url, headers, (byte[]) null); - } - - public CompletableFuture> postAsync(String url, Map headers) { - return sendAsync("POST", url, headers, (byte[]) null); - } - - public CompletableFuture> getAsync(String url, Map headers, String body) { - return sendAsync("GET", url, headers, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); - } - - public CompletableFuture> postAsync(String url, Map headers, String body) { - return sendAsync("POST", url, headers, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); - } - - public CompletableFuture> getAsync(String url, Map headers, byte[] body) { - return sendAsync("GET", url, headers, body); - } - - public CompletableFuture> postAsync(String url, Map headers, byte[] body) { - return sendAsync("POST", url, headers, body); - } - - public CompletableFuture> sendAsync(String method, String url, Map headers, byte[] body) { - final URI uri = URI.create(url); - final SocketAddress address = new InetSocketAddress(uri.getHost(), uri.getPort() > 0 ? uri.getPort() : (url.startsWith("https:") ? 443 : 80)); - return asyncGroup.createTCP(address, readTimeoutSeconds, writeTimeoutSeconds).thenCompose(conn -> { - final ByteArray array = new ByteArray(); - int urlpos = url.indexOf("/", url.indexOf("//") + 3); - array.put((method + " " + (urlpos > 0 ? url.substring(urlpos) : "/") + " HTTP/1.1\r\n" - + "Host: " + uri.getHost() + "\r\n" - + "Content-Length: " + (body == null ? 0 : body.length) + "\r\n").getBytes(StandardCharsets.UTF_8)); - if (headers == null || !headers.containsKey("User-Agent")) { - array.put(("User-Agent: redkale-httpclient/" + Redkale.getDotedVersion() + "\r\n").getBytes(StandardCharsets.UTF_8)); - } - if (headers == null || !headers.containsKey("Connection")) { - array.put(("Connection: close\r\n").getBytes(StandardCharsets.UTF_8)); - } - if (headers != null) { - headers.forEach((k, v) -> { - array.put((k + ": " + v + "\r\n").getBytes(StandardCharsets.UTF_8)); - }); - } - array.put((byte) '\r', (byte) '\n'); - if (body != null) array.put(body); - System.out.println(array); - final CompletableFuture> future = new CompletableFuture(); - conn.write(array, new CompletionHandler() { - @Override - public void completed(Integer result, Void attachment) { - conn.read(new ClientReadCompletionHandler(conn, array.clear(), future)); - } - - @Override - public void failed(Throwable exc, Void attachment) { - conn.dispose(); - future.completeExceptionally(exc); - } - }); - return future; - }); - } - - public static void main(String[] args) throws Throwable { - final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); - asyncGroup.start(); - String url = "http://redkale.org"; - HttpClient client = HttpClient.create(asyncGroup); - System.out.println(client.getAsync(url).join()); - } - - protected static class ClientReadCompletionHandler implements CompletionHandler { - - protected static final int READ_STATE_ROUTE = 1; - - protected static final int READ_STATE_HEADER = 2; - - protected static final int READ_STATE_BODY = 3; - - protected static final int READ_STATE_END = 4; - - protected final AsyncConnection conn; - - protected final ByteArray array; - - protected final CompletableFuture> future; - - protected HttpResult responseResult; - - protected int readState = READ_STATE_ROUTE; - - protected int contentLength = -1; - - public ClientReadCompletionHandler(AsyncConnection conn, ByteArray array, CompletableFuture> future) { - this.conn = conn; - this.array = array; - this.future = future; - } - - @Override - public void completed(Integer count, ByteBuffer buffer) { - buffer.flip(); - if (this.readState == READ_STATE_ROUTE) { - if (this.responseResult == null) { - this.responseResult = new HttpResult<>(); - } - int rs = readStatusLine(buffer); - if (rs != 0) { - buffer.clear(); - conn.setReadBuffer(buffer); - conn.read(this); - return; - } - this.readState = READ_STATE_HEADER; - } - if (this.readState == READ_STATE_HEADER) { - int rs = readHeaderLines(buffer); - if (rs != 0) { - buffer.clear(); - conn.setReadBuffer(buffer); - conn.read(this); - return; - } - this.readState = READ_STATE_BODY; - } - if (this.readState == READ_STATE_BODY) { - if (this.contentLength > 0) { - array.put(buffer, Math.min((int) this.contentLength, buffer.remaining())); - int lr = (int) this.contentLength - array.length(); - if (lr == 0) { - this.readState = READ_STATE_END; - } else { - buffer.clear(); - conn.setReadBuffer(buffer); - conn.read(this); - return; - } - } - if (buffer.hasRemaining()) array.put(buffer, buffer.remaining()); - this.readState = READ_STATE_END; - } - this.responseResult.setResult(array.getBytes()); - this.future.complete(this.responseResult); - conn.offerBuffer(buffer); - conn.dispose(); - } - - //解析 HTTP/1.1 200 OK - private int readStatusLine(final ByteBuffer buffer) { - int remain = buffer.remaining(); - ByteArray bytes = array; - for (;;) { - if (remain-- < 1) { - buffer.clear(); - return 1; - } - byte b = buffer.get(); - if (b == '\r') { - if (remain-- < 1) { - buffer.clear(); - buffer.put((byte) '\r'); - return 1; - } - if (buffer.get() != '\n') return -1; - break; - } - bytes.put(b); - } - String value = bytes.toString(null); - int pos = value.indexOf(' '); - this.responseResult.setStatus(Integer.decode(value.substring(pos + 1, value.indexOf(" ", pos + 2)))); - bytes.clear(); - return 0; - } - - //解析Header Connection: keep-alive - private int readHeaderLines(final ByteBuffer buffer) { - int remain = buffer.remaining(); - ByteArray bytes = array; - HttpResult result = responseResult; - for (;;) { - bytes.clear(); - if (remain-- < 2) { - if (remain == 1) { - byte one = buffer.get(); - buffer.clear(); - buffer.put(one); - return 1; - } - buffer.clear(); - return 1; - } - remain--; - byte b1 = buffer.get(); - byte b2 = buffer.get(); - if (b1 == '\r' && b2 == '\n') return 0; - bytes.put(b1, b2); - for (;;) { // name - if (remain-- < 1) { - buffer.clear(); - buffer.put(bytes.content(), 0, bytes.length()); - return 1; - } - byte b = buffer.get(); - if (b == ':') break; - bytes.put(b); - } - String name = parseHeaderName(bytes, null); - bytes.clear(); - boolean first = true; - int space = 0; - for (;;) { // value - if (remain-- < 1) { - buffer.clear(); - buffer.put(name.getBytes()); - buffer.put((byte) ':'); - if (space == 1) { - buffer.put((byte) ' '); - } else if (space > 0) { - for (int i = 0; i < space; i++) buffer.put((byte) ' '); - } - buffer.put(bytes.content(), 0, bytes.length()); - return 1; - } - byte b = buffer.get(); - if (b == '\r') { - if (remain-- < 1) { - buffer.clear(); - buffer.put(name.getBytes()); - buffer.put((byte) ':'); - if (space == 1) { - buffer.put((byte) ' '); - } else if (space > 0) { - for (int i = 0; i < space; i++) buffer.put((byte) ' '); - } - buffer.put(bytes.content(), 0, bytes.length()); - buffer.put((byte) '\r'); - return 1; - } - if (buffer.get() != '\n') return -1; - break; - } - if (first) { - if (b <= ' ') { - space++; - continue; - } - first = false; - } - bytes.put(b); - } - String value; - switch (name) { - case "Content-Length": - case "content-length": - value = bytes.toString(null); - this.contentLength = Integer.decode(value); - result.header(name, value); - break; - default: - value = bytes.toString(null); - result.header(name, value); - } - } - } - - @Override - public void failed(Throwable exc, ByteBuffer attachment) { - conn.offerBuffer(attachment); - conn.dispose(); - future.completeExceptionally(exc); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.CompletionHandler; +import java.nio.charset.*; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import org.redkale.net.*; +import static org.redkale.net.http.HttpRequest.parseHeaderName; +import org.redkale.util.*; + +/** + * 简单的HttpClient实现, 存在以下情况不能使用此类:
    + * 1、使用HTTPS;
    + * 2、上传下载文件;
    + * 3、返回超大响应包;
    + * 类似JDK11的 java.net.http.HttpClient
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.3.0 + * + */ +public class HttpClient { + + protected final AsyncGroup asyncGroup; + + protected int readTimeoutSeconds = 6; + + protected int writeTimeoutSeconds = 6; + + protected HttpClient(AsyncGroup asyncGroup) { + this.asyncGroup = asyncGroup; + } + + public static HttpClient create(AsyncGroup asyncGroup) { + return new HttpClient(asyncGroup); + } + + public CompletableFuture> getAsync(String url) { + return sendAsync("GET", url, null, (byte[]) null); + } + + public CompletableFuture> postAsync(String url) { + return sendAsync("POST", url, null, (byte[]) null); + } + + public CompletableFuture> getAsync(String url, String body) { + return sendAsync("GET", url, null, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); + } + + public CompletableFuture> postAsync(String url, String body) { + return sendAsync("POST", url, null, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); + } + + public CompletableFuture> getAsync(String url, byte[] body) { + return sendAsync("GET", url, null, body); + } + + public CompletableFuture> postAsync(String url, byte[] body) { + return sendAsync("POST", url, null, body); + } + + public CompletableFuture> getAsync(String url, Map headers) { + return sendAsync("GET", url, headers, (byte[]) null); + } + + public CompletableFuture> postAsync(String url, Map headers) { + return sendAsync("POST", url, headers, (byte[]) null); + } + + public CompletableFuture> getAsync(String url, Map headers, String body) { + return sendAsync("GET", url, headers, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); + } + + public CompletableFuture> postAsync(String url, Map headers, String body) { + return sendAsync("POST", url, headers, body == null ? null : body.getBytes(StandardCharsets.UTF_8)); + } + + public CompletableFuture> getAsync(String url, Map headers, byte[] body) { + return sendAsync("GET", url, headers, body); + } + + public CompletableFuture> postAsync(String url, Map headers, byte[] body) { + return sendAsync("POST", url, headers, body); + } + + public CompletableFuture> sendAsync(String method, String url, Map headers, byte[] body) { + final URI uri = URI.create(url); + final SocketAddress address = new InetSocketAddress(uri.getHost(), uri.getPort() > 0 ? uri.getPort() : (url.startsWith("https:") ? 443 : 80)); + return asyncGroup.createTCPClient(address, readTimeoutSeconds, writeTimeoutSeconds).thenCompose(conn -> { + final ByteArray array = new ByteArray(); + int urlpos = url.indexOf("/", url.indexOf("//") + 3); + array.put((method + " " + (urlpos > 0 ? url.substring(urlpos) : "/") + " HTTP/1.1\r\n" + + "Host: " + uri.getHost() + "\r\n" + + "Content-Length: " + (body == null ? 0 : body.length) + "\r\n").getBytes(StandardCharsets.UTF_8)); + if (headers == null || !headers.containsKey("User-Agent")) { + array.put(("User-Agent: redkale-httpclient/" + Redkale.getDotedVersion() + "\r\n").getBytes(StandardCharsets.UTF_8)); + } + if (headers == null || !headers.containsKey("Connection")) { + array.put(("Connection: close\r\n").getBytes(StandardCharsets.UTF_8)); + } + if (headers != null) { + headers.forEach((k, v) -> { + array.put((k + ": " + v + "\r\n").getBytes(StandardCharsets.UTF_8)); + }); + } + array.put((byte) '\r', (byte) '\n'); + if (body != null) array.put(body); + System.out.println(array); + final CompletableFuture> future = new CompletableFuture(); + conn.write(array, new CompletionHandler() { + @Override + public void completed(Integer result, Void attachment) { + conn.read(new ClientReadCompletionHandler(conn, array.clear(), future)); + } + + @Override + public void failed(Throwable exc, Void attachment) { + conn.dispose(); + future.completeExceptionally(exc); + } + }); + return future; + }); + } +// +// public static void main(String[] args) throws Throwable { +// final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); +// asyncGroup.start(); +// String url = "http://redkale.org"; +// HttpClient client = HttpClient.create(asyncGroup); +// System.out.println(client.getAsync(url).join()); +// } + + protected static class ClientReadCompletionHandler implements CompletionHandler { + + protected static final int READ_STATE_ROUTE = 1; + + protected static final int READ_STATE_HEADER = 2; + + protected static final int READ_STATE_BODY = 3; + + protected static final int READ_STATE_END = 4; + + protected final AsyncConnection conn; + + protected final ByteArray array; + + protected final CompletableFuture> future; + + protected HttpResult responseResult; + + protected int readState = READ_STATE_ROUTE; + + protected int contentLength = -1; + + public ClientReadCompletionHandler(AsyncConnection conn, ByteArray array, CompletableFuture> future) { + this.conn = conn; + this.array = array; + this.future = future; + } + + @Override + public void completed(Integer count, ByteBuffer buffer) { + buffer.flip(); + if (this.readState == READ_STATE_ROUTE) { + if (this.responseResult == null) { + this.responseResult = new HttpResult<>(); + } + int rs = readStatusLine(buffer); + if (rs != 0) { + buffer.clear(); + conn.setReadBuffer(buffer); + conn.read(this); + return; + } + this.readState = READ_STATE_HEADER; + } + if (this.readState == READ_STATE_HEADER) { + int rs = readHeaderLines(buffer); + if (rs != 0) { + buffer.clear(); + conn.setReadBuffer(buffer); + conn.read(this); + return; + } + this.readState = READ_STATE_BODY; + } + if (this.readState == READ_STATE_BODY) { + if (this.contentLength > 0) { + array.put(buffer, Math.min((int) this.contentLength, buffer.remaining())); + int lr = (int) this.contentLength - array.length(); + if (lr == 0) { + this.readState = READ_STATE_END; + } else { + buffer.clear(); + conn.setReadBuffer(buffer); + conn.read(this); + return; + } + } + if (buffer.hasRemaining()) array.put(buffer, buffer.remaining()); + this.readState = READ_STATE_END; + } + this.responseResult.setResult(array.getBytes()); + this.future.complete(this.responseResult); + conn.offerBuffer(buffer); + conn.dispose(); + } + + //解析 HTTP/1.1 200 OK + private int readStatusLine(final ByteBuffer buffer) { + int remain = buffer.remaining(); + ByteArray bytes = array; + for (;;) { + if (remain-- < 1) { + buffer.clear(); + return 1; + } + byte b = buffer.get(); + if (b == '\r') { + if (remain-- < 1) { + buffer.clear(); + buffer.put((byte) '\r'); + return 1; + } + if (buffer.get() != '\n') return -1; + break; + } + bytes.put(b); + } + String value = bytes.toString(null); + int pos = value.indexOf(' '); + this.responseResult.setStatus(Integer.decode(value.substring(pos + 1, value.indexOf(" ", pos + 2)))); + bytes.clear(); + return 0; + } + + //解析Header Connection: keep-alive + private int readHeaderLines(final ByteBuffer buffer) { + int remain = buffer.remaining(); + ByteArray bytes = array; + HttpResult result = responseResult; + for (;;) { + bytes.clear(); + if (remain-- < 2) { + if (remain == 1) { + byte one = buffer.get(); + buffer.clear(); + buffer.put(one); + return 1; + } + buffer.clear(); + return 1; + } + remain--; + byte b1 = buffer.get(); + byte b2 = buffer.get(); + if (b1 == '\r' && b2 == '\n') return 0; + bytes.put(b1, b2); + for (;;) { // name + if (remain-- < 1) { + buffer.clear(); + buffer.put(bytes.content(), 0, bytes.length()); + return 1; + } + byte b = buffer.get(); + if (b == ':') break; + bytes.put(b); + } + String name = parseHeaderName(bytes, null); + bytes.clear(); + boolean first = true; + int space = 0; + for (;;) { // value + if (remain-- < 1) { + buffer.clear(); + buffer.put(name.getBytes()); + buffer.put((byte) ':'); + if (space == 1) { + buffer.put((byte) ' '); + } else if (space > 0) { + for (int i = 0; i < space; i++) buffer.put((byte) ' '); + } + buffer.put(bytes.content(), 0, bytes.length()); + return 1; + } + byte b = buffer.get(); + if (b == '\r') { + if (remain-- < 1) { + buffer.clear(); + buffer.put(name.getBytes()); + buffer.put((byte) ':'); + if (space == 1) { + buffer.put((byte) ' '); + } else if (space > 0) { + for (int i = 0; i < space; i++) buffer.put((byte) ' '); + } + buffer.put(bytes.content(), 0, bytes.length()); + buffer.put((byte) '\r'); + return 1; + } + if (buffer.get() != '\n') return -1; + break; + } + if (first) { + if (b <= ' ') { + space++; + continue; + } + first = false; + } + bytes.put(b); + } + String value; + switch (name) { + case "Content-Length": + case "content-length": + value = bytes.toString(null); + this.contentLength = Integer.decode(value); + result.header(name, value); + break; + default: + value = bytes.toString(null); + result.header(name, value); + } + } + } + + @Override + public void failed(Throwable exc, ByteBuffer attachment) { + conn.offerBuffer(attachment); + conn.dispose(); + future.completeExceptionally(exc); + } + + } +} diff --git a/src/org/redkale/net/http/HttpContext.java b/src/main/java/org/redkale/net/http/HttpContext.java similarity index 86% rename from src/org/redkale/net/http/HttpContext.java rename to src/main/java/org/redkale/net/http/HttpContext.java index bd1d66cb0..840be9474 100644 --- a/src/org/redkale/net/http/HttpContext.java +++ b/src/main/java/org/redkale/net/http/HttpContext.java @@ -1,199 +1,206 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import org.redkale.asm.MethodDebugVisitor; -import java.nio.channels.CompletionHandler; -import java.security.*; -import java.util.concurrent.*; -import org.redkale.asm.*; -import static org.redkale.asm.Opcodes.*; -import org.redkale.net.*; -import org.redkale.util.*; - -/** - * HTTP服务的上下文对象 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpContext extends Context { - - protected final SecureRandom random = new SecureRandom(); - - protected final ConcurrentHashMap asyncHandlerCreators = new ConcurrentHashMap<>(); - - protected final String remoteAddrHeader; - - protected boolean lazyHeaders; //存在动态改值 - -// protected RequestURINode[] uriCacheNodes; - public HttpContext(HttpContextConfig config) { - super(config); - this.remoteAddrHeader = config.remoteAddrHeader; - random.setSeed(Math.abs(System.nanoTime())); - } - -// protected RequestURINode[] getUriCacheNodes() { -// return uriCacheNodes; -// } -// -// protected void addRequestURINode(String path) { -// RequestURINode node = new RequestURINode(path); -// synchronized (this) { -// if (this.uriCacheNodes != null) { -// for (int i = 0; i < uriCacheNodes.length; i++) { -// if (uriCacheNodes[i].path.equals(path)) return; -// } -// } -// this.uriCacheNodes = Utility.append(this.uriCacheNodes, node); -// } -// } - protected String createSessionid() { - byte[] bytes = new byte[16]; - random.nextBytes(bytes); - return new String(Utility.binToHex(bytes)); - } - - @SuppressWarnings("unchecked") - protected Creator loadAsyncHandlerCreator(Class handlerClass) { - Creator creator = asyncHandlerCreators.get(handlerClass); - if (creator == null) { - creator = createAsyncHandlerCreator(handlerClass); - asyncHandlerCreators.put(handlerClass, creator); - } - return creator; - } - - @SuppressWarnings("unchecked") - private Creator createAsyncHandlerCreator(Class handlerClass) { - //生成规则与SncpAsyncHandler.Factory 很类似 - //------------------------------------------------------------- - final boolean handlerinterface = handlerClass.isInterface(); - final String cpDesc = Type.getDescriptor(ConstructorParameters.class); - final String handlerClassName = handlerClass.getName().replace('.', '/'); - final String handlerName = CompletionHandler.class.getName().replace('.', '/'); - final String handlerDesc = Type.getDescriptor(CompletionHandler.class); - final String newDynName = handlerClass.getName().replace('.', '/') + "_DyncAsyncHandler_" + (System.currentTimeMillis() % 10000); - - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - FieldVisitor fv; - MethodDebugVisitor mv; - AnnotationVisitor av0; - cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName} : new String[]{handlerName}); - - { //handler 属性 - fv = cw.visitField(ACC_PRIVATE, "handler", handlerDesc, null, null); - fv.visitEnd(); - } - {//构造方法 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "(" + handlerDesc + ")V", null, null)); - //mv.setDebug(true); - { - av0 = mv.visitAnnotation(cpDesc, true); - { - AnnotationVisitor av1 = av0.visitArray("value"); - av1.visit(null, "handler"); - av1.visitEnd(); - } - av0.visitEnd(); - } - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "", "()V", false); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(PUTFIELD, newDynName, "handler", handlerDesc); - mv.visitInsn(RETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - - for (java.lang.reflect.Method method : handlerClass.getMethods()) { // - if ("completed".equals(method.getName()) && method.getParameterCount() == 2) { - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) { - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) { - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null)); - Class returnType = method.getReturnType(); - if (returnType == void.class) { - mv.visitInsn(RETURN); - mv.visitMaxs(0, 1); - } else if (returnType.isPrimitive()) { - mv.visitInsn(ICONST_0); - if (returnType == long.class) { - mv.visitInsn(LRETURN); - mv.visitMaxs(2, 1); - } else if (returnType == float.class) { - mv.visitInsn(FRETURN); - mv.visitMaxs(2, 1); - } else if (returnType == double.class) { - mv.visitInsn(DRETURN); - mv.visitMaxs(2, 1); - } else { - mv.visitInsn(IRETURN); - mv.visitMaxs(1, 1); - } - } else { - mv.visitInsn(ACONST_NULL); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - } - mv.visitEnd(); - } - } - cw.visitEnd(); - byte[] bytes = cw.toByteArray(); - Class newHandlerClazz = (Class) new ClassLoader(handlerClass.getClassLoader()) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - return (Creator) Creator.create(newHandlerClazz); - } - - public static class HttpContextConfig extends ContextConfig { - - public String remoteAddrHeader; - - } - - protected static class RequestURINode { - - public final byte[] bytes; - - public final String path; - - public RequestURINode(String path) { - this.path = path; - this.bytes = path.getBytes(); - } - - @Override - public String toString() { - return "RequestURINode{" + "path=" + path + '}'; - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import org.redkale.asm.MethodDebugVisitor; +import java.nio.channels.CompletionHandler; +import java.security.*; +import java.util.concurrent.*; +import org.redkale.asm.*; +import static org.redkale.asm.Opcodes.*; +import org.redkale.net.*; +import org.redkale.util.*; + +/** + * HTTP服务的上下文对象 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpContext extends Context { + + protected final SecureRandom random = new SecureRandom(); + + protected final ConcurrentHashMap asyncHandlerCreators = new ConcurrentHashMap<>(); + + protected final String remoteAddrHeader; + + protected boolean lazyHeaders; //存在动态改值 + +// protected RequestURINode[] uriCacheNodes; + public HttpContext(HttpContextConfig config) { + super(config); + this.remoteAddrHeader = config.remoteAddrHeader; + random.setSeed(Math.abs(System.nanoTime())); + } + +// protected RequestURINode[] getUriCacheNodes() { +// return uriCacheNodes; +// } +// +// protected void addRequestURINode(String path) { +// RequestURINode node = new RequestURINode(path); +// synchronized (this) { +// if (this.uriCacheNodes != null) { +// for (int i = 0; i < uriCacheNodes.length; i++) { +// if (uriCacheNodes[i].path.equals(path)) return; +// } +// } +// this.uriCacheNodes = Utility.append(this.uriCacheNodes, node); +// } +// } + protected String createSessionid() { + byte[] bytes = new byte[16]; + random.nextBytes(bytes); + return new String(Utility.binToHex(bytes)); + } + + @SuppressWarnings("unchecked") + protected Creator loadAsyncHandlerCreator(Class handlerClass) { + Creator creator = asyncHandlerCreators.get(handlerClass); + if (creator == null) { + creator = createAsyncHandlerCreator(handlerClass); + asyncHandlerCreators.put(handlerClass, creator); + } + return creator; + } + + @SuppressWarnings("unchecked") + private static synchronized Creator createAsyncHandlerCreator(Class handlerClass) { + //生成规则与SncpAsyncHandler.Factory 很类似 + //------------------------------------------------------------- + final boolean handlerinterface = handlerClass.isInterface(); + final String cpDesc = Type.getDescriptor(ConstructorParameters.class); + final String handlerClassName = handlerClass.getName().replace('.', '/'); + final String handlerName = CompletionHandler.class.getName().replace('.', '/'); + final String handlerDesc = Type.getDescriptor(CompletionHandler.class); + final String newDynName = "org/redkaledyn/http/handler/_DynHttpAsyncHandler__" + handlerClass.getName().replace('.', '/').replace('$', '_'); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + Class newHandlerClazz = clz == null ? Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.')) : clz; + return Creator.create(newHandlerClazz); + } catch (Throwable ex) { + } + // ------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + AnnotationVisitor av0; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName} : new String[]{handlerName}); + + { //handler 属性 + fv = cw.visitField(ACC_PRIVATE, "handler", handlerDesc, null, null); + fv.visitEnd(); + } + {//构造方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "(" + handlerDesc + ")V", null, null)); + //mv.setDebug(true); + { + av0 = mv.visitAnnotation(cpDesc, true); + { + AnnotationVisitor av1 = av0.visitArray("value"); + av1.visit(null, "handler"); + av1.visitEnd(); + } + av0.visitEnd(); + } + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "", "()V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, newDynName, "handler", handlerDesc); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + + for (java.lang.reflect.Method method : handlerClass.getMethods()) { // + if ("completed".equals(method.getName()) && method.getParameterCount() == 2) { + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) { + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) { + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null)); + Class returnType = method.getReturnType(); + if (returnType == void.class) { + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + } else if (returnType.isPrimitive()) { + mv.visitInsn(ICONST_0); + if (returnType == long.class) { + mv.visitInsn(LRETURN); + mv.visitMaxs(2, 1); + } else if (returnType == float.class) { + mv.visitInsn(FRETURN); + mv.visitMaxs(2, 1); + } else if (returnType == double.class) { + mv.visitInsn(DRETURN); + mv.visitMaxs(2, 1); + } else { + mv.visitInsn(IRETURN); + mv.visitMaxs(1, 1); + } + } else { + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + } + mv.visitEnd(); + } + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class newClazz = (Class) new ClassLoader(handlerClass.getClassLoader()) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + return (Creator) Creator.create(newClazz); + } + + public static class HttpContextConfig extends ContextConfig { + + public String remoteAddrHeader; + + } + + protected static class RequestURINode { + + public final byte[] bytes; + + public final String path; + + public RequestURINode(String path) { + this.path = path; + this.bytes = path.getBytes(); + } + + @Override + public String toString() { + return "RequestURINode{" + "path=" + path + '}'; + } + + } +} diff --git a/src/org/redkale/net/http/HttpFilter.java b/src/main/java/org/redkale/net/http/HttpFilter.java similarity index 96% rename from src/org/redkale/net/http/HttpFilter.java rename to src/main/java/org/redkale/net/http/HttpFilter.java index 40a72bacd..5a16da850 100644 --- a/src/org/redkale/net/http/HttpFilter.java +++ b/src/main/java/org/redkale/net/http/HttpFilter.java @@ -1,24 +1,24 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import org.redkale.net.Filter; -import org.redkale.util.AnyValue; - -/** - * HTTP 过滤器
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class HttpFilter extends Filter { - - //Server执行start后运行此方法 - public void postStart(HttpContext context, AnyValue config) { - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import org.redkale.net.Filter; +import org.redkale.util.AnyValue; + +/** + * HTTP 过滤器
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class HttpFilter extends Filter { + + //Server执行start后运行此方法 + public void postStart(HttpContext context, AnyValue config) { + } +} diff --git a/src/org/redkale/net/http/HttpMapping.java b/src/main/java/org/redkale/net/http/HttpMapping.java similarity index 78% rename from src/org/redkale/net/http/HttpMapping.java rename to src/main/java/org/redkale/net/http/HttpMapping.java index e12590df3..210e5ced1 100644 --- a/src/org/redkale/net/http/HttpMapping.java +++ b/src/main/java/org/redkale/net/http/HttpMapping.java @@ -1,97 +1,123 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 配合 HttpServlet 使用。 - * 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -public @interface HttpMapping { - - /** - * 操作ID值,鉴权时用到 - * - * @return int - */ - int actionid() default 0; - - /** - * 请求地址 - * - * @return String - */ - String url(); - - /** - * 结果缓存的秒数, 为0表示不缓存
    - * * 当值大于0,将被缓存一段时间(默认值 seconds=15秒)。
    - * 通常情况下需要 auth() == true 才使用,没有标记auth==true方法一般输出的结果与当前用户信息有关。
    - * - * @return int - */ - int cacheseconds() default 0; - - /** - * 是否只接受RPC请求, 默认为false - * - * @return 默认false - */ - boolean rpconly() default false; - - /** - * 是否鉴权,默认需要鉴权
    - * - * @return boolean - */ - boolean auth() default true; - - /** - * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法 - * - * @return String[] - */ - String[] methods() default {}; - - /** - * 是否能被继承, 当 HttpServlet 被继承后该方法是否能被子类继承 - * - * @return boolean - */ - boolean inherited() default true; - - /** - * 输出结果的数据类型 - * - * @return String - */ - String result() default "Object"; - - /** - * 输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合 - * - * @return Class[] - */ - Class[] results() default {}; - - /** - * 备注描述 - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 配合 HttpServlet 使用。 + * 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface HttpMapping { + + /** + * for OpenAPI Specification 3 + * + * @return String + */ + String name() default ""; + + /** + * 操作ID值,鉴权时用到 + * + * @return int + */ + int actionid() default 0; + + /** + * 请求地址 + * + * @return String + */ + String url(); + + /** + * 结果缓存的秒数, 为0表示不缓存
    + * * 当值大于0,将被缓存一段时间(默认值 seconds=15秒)。
    + * 通常情况下需要 auth() == true 才使用,没有标记auth==true方法一般输出的结果与当前用户信息有关。
    + * + * @return int + */ + int cacheseconds() default 0; + + /** + * 是否只接受RPC请求, 默认为false + * + * @return 默认false + */ + boolean rpconly() default false; + + /** + * 是否鉴权,默认需要鉴权
    + * + * @return boolean + */ + boolean auth() default true; + + /** + * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法 + * + * @return String[] + */ + String[] methods() default {}; + + /** + * 是否能被继承, 当 HttpServlet 被继承后该方法是否能被子类继承 + * + * @return boolean + */ + boolean inherited() default true; + + /** + * 输出结果的数据类型 + * + * @return Class + */ + Class result() default void.class; + + /** + * 输出结果的泛型数据类型在HttpServlet里的字段名,且字段类型必须是 java.lang.reflect.Type
    + * 如果输出结果数据类型不是泛型,则值为空 + * + * @since 2.5.0 + * @return String + */ + String resultref() default ""; + + /** + * 输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合 + * + * @deprecated 2.5.0 + * @return Class[] + */ + @Deprecated + Class[] results() default {}; + + /** + * 返回结果的样例 + * for OpenAPI Specification 3.1.0 + * + * @return String + */ + String example() default ""; + + /** + * 备注描述 + * + * @return String + */ + String comment() default ""; +} diff --git a/src/main/java/org/redkale/net/http/HttpParam.java b/src/main/java/org/redkale/net/http/HttpParam.java new file mode 100644 index 000000000..ebf0f4077 --- /dev/null +++ b/src/main/java/org/redkale/net/http/HttpParam.java @@ -0,0 +1,115 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 配合 @HttpMapping 使用。 + * 用于对@HttpMapping方法中参数描述
    + * 从RestService生成过来的HttpMapping,标记为@RestUserid、@RestAddress的参数不会生成HttpParam + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +@Repeatable(HttpParam.HttpParams.class) +public @interface HttpParam { + + /** + * 参数名 + * + * @return String + */ + String name(); + + /** + * 参数的数据类型 + * + * @return Class + */ + Class type(); + + /** + * 参数的泛型数据类型在HttpServlet里的字段名,且字段类型必须是 java.lang.reflect.Type
    + * 如果参数数据类型不是泛型,则值为空 + * + * @since 2.5.0 + * @return String + */ + String typeref() default ""; + + /** + * 备注描述 + * + * @return String + */ + String comment() default ""; + + /** + * 参数来源类型 + * + * @return HttpParameterStyle + */ + HttpParameterStyle style() default HttpParameterStyle.QUERY; + + /** + * 转换数字byte/short/int/long时所用的进制数, 默认10进制 + * + * @return int + */ + int radix() default 10; + + /** + * 参数是否必传, 框架运行中不作验证, only for OpenAPI Specification 3 + * + * @return boolean + */ + boolean required() default true; + + /** + * 是否过期字段, only for OpenAPI Specification 3 + * + * @return boolean + */ + boolean deprecated() default false; + + /** + * for OpenAPI Specification 3 + * + * @return String + */ + String example() default ""; + + /** + * 配合 @HttpParam 使用。 + * 用于对@HttpParam中参数的来源类型 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ + public static enum HttpParameterStyle { + + QUERY, HEADER, COOKIE, BODY; + } + + @Documented + @Target({METHOD}) + @Retention(RUNTIME) + @interface HttpParams { + + HttpParam[] value(); + } + +} diff --git a/src/org/redkale/net/http/HttpPrepareServlet.java b/src/main/java/org/redkale/net/http/HttpPrepareServlet.java similarity index 84% rename from src/org/redkale/net/http/HttpPrepareServlet.java rename to src/main/java/org/redkale/net/http/HttpPrepareServlet.java index 4eb5d8720..c3a19ce0b 100644 --- a/src/org/redkale/net/http/HttpPrepareServlet.java +++ b/src/main/java/org/redkale/net/http/HttpPrepareServlet.java @@ -1,449 +1,471 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import org.redkale.util.AnyValue.DefaultAnyValue; -import java.io.*; -import java.util.*; -import java.util.function.*; -import java.util.logging.*; -import java.util.regex.*; -import org.redkale.net.*; -import org.redkale.net.http.Rest.RestDynSourceType; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * HTTP Servlet的总入口,请求在HttpPrepareServlet中进行分流。
    - * 一个HttpServer只有一个HttpPrepareServlet, 用于管理所有HttpServlet。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpPrepareServlet extends PrepareServlet { - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - protected HttpServlet resourceHttpServlet = new HttpResourceServlet(); - - protected MappingEntry[] regArray = null; //regArray 包含 regWsArray - - protected MappingEntry[] regWsArray = null; - - protected Map wsmappings = new HashMap<>(); //super.mappings 包含 wsmappings - - protected final Map allMapStrings = new HashMap<>(); - - private final Object excludeLock = new Object(); - - protected HttpContext context; - - protected boolean lazyHeaders = true; - - private Map> forbidURIMaps; //禁用的URL的正则表达式, 必须与 forbidURIPredicates 保持一致 - - private BiPredicate[] forbidURIPredicates; //禁用的URL的Predicate, 必须与 forbidURIMaps 保持一致 - - private List removeHttpServlet(final Predicate predicateEntry, final Predicate> predicateFilter) { - List servlets = new ArrayList<>(); - synchronized (allMapStrings) { - List keys = new ArrayList<>(); - if (regArray != null) { - for (MappingEntry me : regArray) { - if (predicateEntry.test(me)) { - servlets.add(me.servlet); - keys.add(me.mapping); - } - } - } - if (regWsArray != null) { - for (MappingEntry me : regWsArray) { - if (predicateEntry.test(me)) { - servlets.add(me.servlet); - keys.add(me.mapping); - } - } - } - Map newwsmappings = new HashMap<>(); - for (Map.Entry en : wsmappings.entrySet()) { - if (predicateFilter.test(en)) { - servlets.add(en.getValue()); - keys.add(en.getKey()); - } else { - newwsmappings.put(en.getKey(), en.getValue()); - } - } - if (newwsmappings.size() != wsmappings.size()) this.wsmappings = newwsmappings; - if (!keys.isEmpty()) { - this.regArray = Utility.remove(this.regArray, predicateEntry); - this.regWsArray = Utility.remove(this.regWsArray, predicateEntry); - for (HttpServlet rs : servlets) { - super.removeServlet(rs); - } - for (String key : keys) { - super.removeMapping(key); - allMapStrings.remove(key); - } - } - } - return servlets; - } - - public HttpServlet removeHttpServlet(final HttpServlet servlet) { - Predicate predicateEntry = (t) -> t.servlet == servlet; - Predicate> predicateFilter = (t) -> t.getValue() == servlet; - removeHttpServlet(predicateEntry, predicateFilter); - return servlet; - } - - public HttpServlet removeHttpServlet(Service service) { - Predicate predicateEntry = (t) -> { - if (!Rest.isRestDyn(t.servlet)) return false; - Service s = Rest.getService(t.servlet); - if (s == service) return true; - if (s != null) return false; - Map map = Rest.getServiceMap(t.servlet); - if (map == null) return false; - boolean rs = map.values().contains(service); - if (rs && map.size() == 1) return true; - if (rs && map.size() > 1) { - String key = null; - for (Map.Entry en : map.entrySet()) { - if (en.getValue() == service) { - key = en.getKey(); - break; - } - } - if (key != null) map.remove(key); - return false; //还有其他Resouce.name 的Service - } - return rs; - }; - Predicate> predicateFilter = null; - List list = removeHttpServlet(predicateEntry, predicateFilter); - return list == null || list.isEmpty() ? null : list.get(0); - } - - @SuppressWarnings("unchecked") - public HttpServlet removeHttpServlet(Class websocketOrServletType) { - Predicate predicateEntry = (t) -> { - Class type = t.servlet.getClass(); - if (type == websocketOrServletType) return true; - RestDynSourceType rdt = (RestDynSourceType) type.getAnnotation(RestDynSourceType.class); - return (rdt != null && rdt.value() == websocketOrServletType); - }; - Predicate> predicateFilter = (t) -> { - Class type = t.getValue().getClass(); - if (type == websocketOrServletType) return true; - RestDynSourceType rdt = (RestDynSourceType) type.getAnnotation(RestDynSourceType.class); - return (rdt != null && rdt.value() == websocketOrServletType); - }; - List list = removeHttpServlet(predicateEntry, predicateFilter); - return list == null || list.isEmpty() ? null : list.get(0); - } - - @SuppressWarnings("unchecked") - public boolean addForbidURIReg(final String urlreg) { - if (urlreg == null || urlreg.isEmpty()) return false; - synchronized (excludeLock) { - if (forbidURIMaps != null && forbidURIMaps.containsKey(urlreg)) return false; - if (forbidURIMaps == null) forbidURIMaps = new HashMap<>(); - String mapping = urlreg; - if (Utility.contains(mapping, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式)) - if (mapping.endsWith("/*")) { - mapping = mapping.substring(0, mapping.length() - 1) + ".*"; - } else { - mapping = mapping + "$"; - } - } - final String reg = mapping; - final boolean begin = mapping.charAt(0) == '^'; - final Predicate regPredicate = Pattern.compile(reg).asPredicate(); - BiPredicate predicate = (prefix, uri) -> { - return begin || prefix.isEmpty() ? regPredicate.test(uri) : uri.matches(prefix + reg); - }; - forbidURIMaps.put(urlreg, predicate); - forbidURIPredicates = Utility.append(forbidURIPredicates, predicate); - return true; - } - } - - @SuppressWarnings("unchecked") - public boolean removeForbidURIReg(final String urlreg) { - if (urlreg == null || urlreg.isEmpty()) return false; - synchronized (excludeLock) { - if (forbidURIMaps == null || forbidURIPredicates == null || !forbidURIMaps.containsKey(urlreg)) return false; - BiPredicate predicate = forbidURIMaps.get(urlreg); - forbidURIMaps.remove(urlreg); - int index = -1; - for (int i = 0; i < forbidURIPredicates.length; i++) { - if (forbidURIPredicates[i] == predicate) { - index = i; - break; - } - } - if (index > -1) { - if (forbidURIPredicates.length == 1) { - forbidURIPredicates = null; - } else { - int newlen = forbidURIPredicates.length - 1; - BiPredicate[] news = new BiPredicate[newlen]; - System.arraycopy(forbidURIPredicates, 0, news, 0, index); - System.arraycopy(forbidURIPredicates, index + 1, news, index, newlen - index); - forbidURIPredicates = news; - } - } - return true; - } - } - - @Override - @SuppressWarnings("unchecked") - public void init(HttpContext context, AnyValue config) { - super.init(context, config); //必须要执行 - this.context = context; - context.lazyHeaders = lazyHeaders; - Collection servlets = getServlets(); - servlets.forEach(s -> { - s.preInit(context, getServletConf(s)); - s.init(context, getServletConf(s)); - }); - { //设置ResourceServlet - AnyValue resConfig = config.getAnyValue("resource-servlet"); - if ((resConfig instanceof DefaultAnyValue) && resConfig.getValue("webroot", "").isEmpty()) { - ((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root")); - } - if (resConfig == null) { //主要用于嵌入式的HttpServer初始化 - DefaultAnyValue dresConfig = new DefaultAnyValue(); - dresConfig.addValue("webroot", config.getValue("root")); - dresConfig.addValue("ranges", config.getValue("ranges")); - dresConfig.addValue("cache", config.getAnyValue("cache")); - AnyValue[] rewrites = config.getAnyValues("rewrite"); - if (rewrites != null) { - for (AnyValue rewrite : rewrites) { - dresConfig.addValue("rewrite", rewrite); - } - } - resConfig = dresConfig; - } - String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName()); - try { - this.resourceHttpServlet = (HttpServlet) Thread.currentThread().getContextClassLoader().loadClass(resServlet).getDeclaredConstructor().newInstance(); - } catch (Throwable e) { - this.resourceHttpServlet = new HttpResourceServlet(); - logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e); - } - { //获取render的suffixs - AnyValue renderConfig = config.getAnyValue("render"); - if (renderConfig != null) { - String[] suffixs = renderConfig.getValue("suffixs", ".htel").toLowerCase().split(";"); - ((HttpResourceServlet) this.resourceHttpServlet).renderSuffixs = suffixs; - } - } - context.getResourceFactory().inject(this.resourceHttpServlet); - this.resourceHttpServlet.init(context, resConfig); - } - } - - @Override - public void execute(HttpRequest request, HttpResponse response) throws IOException { - try { - final String uri = request.getRequestURI(); - HttpServlet servlet; - if (response.isAutoOptions() && "OPTIONS".equals(request.getMethod())) { - response.finish(200, null); - return; - } - if (request.isWebSocket()) { - servlet = wsmappings.get(uri); - if (servlet == null && this.regWsArray != null) { - for (MappingEntry en : regWsArray) { - if (en.predicate.test(uri)) { - servlet = en.servlet; - break; - } - } - } - if (servlet == null) { - response.finish(500, null); - return; - } - } else { - servlet = mappingServlet(uri); - if (servlet == null && this.regArray != null) { - for (MappingEntry en : regArray) { - if (en.predicate.test(uri)) { - servlet = en.servlet; - break; - } - } - } - //找不到匹配的HttpServlet则使用静态资源HttpResourceServlet - if (servlet == null) servlet = this.resourceHttpServlet; - } - boolean forbid = false; - BiPredicate[] forbidUrlPredicates = this.forbidURIPredicates; - if (forbidUrlPredicates != null && forbidUrlPredicates.length > 0) { - for (BiPredicate predicate : forbidUrlPredicates) { - if (predicate != null && predicate.test(servlet._prefix, uri)) { - forbid = true; - break; - } - } - } - if (forbid) { - response.finish(403, response.getHttpCode(403)); - return; - } - servlet.execute(request, response); - } catch (Exception e) { - request.getContext().getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request, e); - response.finish(500, null); - } - } - - /** - * 添加HttpServlet - * - * @param servlet HttpServlet - * @param prefix url前缀 - * @param conf 配置信息 - * @param mappingpaths 匹配规则 - */ - @Override - public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappingpaths) { - if (prefix == null) prefix = ""; - if (mappingpaths.length < 1) { - WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); - if (ws != null) { - mappingpaths = ws.value(); - if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix - } - } - if (lazyHeaders && !Rest.isSimpleRestDyn(servlet)) { - lazyHeaders = false; - if (context != null) context.lazyHeaders = false; //启动后运行过程中执行addServlet - } - synchronized (allMapStrings) { //需要整段锁住 - for (String mappingpath : mappingpaths) { - if (mappingpath == null) continue; - if (!prefix.toString().isEmpty()) mappingpath = prefix + mappingpath; - - if (Utility.contains(mappingpath, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式)) - if (mappingpath.charAt(0) != '^') mappingpath = '^' + mappingpath; - if (mappingpath.endsWith("/*")) { - mappingpath = mappingpath.substring(0, mappingpath.length() - 1) + ".*"; - } else { - mappingpath = mappingpath + "$"; - } - if (regArray == null) { - regArray = new MappingEntry[1]; - regArray[0] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), servlet); - } else { - regArray = Arrays.copyOf(regArray, regArray.length + 1); - regArray[regArray.length - 1] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), servlet); - } - if (servlet instanceof WebSocketServlet) { - if (regWsArray == null) { - regWsArray = new MappingEntry[1]; - regWsArray[0] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), (WebSocketServlet) servlet); - } else { - regWsArray = Arrays.copyOf(regWsArray, regWsArray.length + 1); - regWsArray[regWsArray.length - 1] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), (WebSocketServlet) servlet); - } - } - } else if (mappingpath != null && !mappingpath.isEmpty()) { - if (servlet._actionmap != null && servlet._actionmap.containsKey(mappingpath)) { - //context.addRequestURINode(mappingpath); - putMapping(mappingpath, new HttpServlet.HttpActionServlet(servlet._actionmap.get(mappingpath), servlet)); - } else { - putMapping(mappingpath, servlet); - } - if (servlet instanceof WebSocketServlet) { - Map newmappings = new HashMap<>(wsmappings); - newmappings.put(mappingpath, (WebSocketServlet) servlet); - this.wsmappings = newmappings; - } - } - if (this.allMapStrings.containsKey(mappingpath)) { - Class old = this.allMapStrings.get(mappingpath); - throw new RuntimeException("mapping [" + mappingpath + "] repeat on " + old.getName() + " and " + servlet.getClass().getName()); - } - this.allMapStrings.put(mappingpath, servlet.getClass()); - } - setServletConf(servlet, conf); - servlet._prefix = prefix.toString(); - putServlet(servlet); - } - } - - /** - * 设置静态资源HttpServlet - * - * @param servlet HttpServlet - */ - public void setResourceServlet(HttpServlet servlet) { - if (servlet != null) { - this.resourceHttpServlet = servlet; - } - } - - /** - * 获取静态资源HttpServlet - * - * @return HttpServlet - */ - public HttpServlet getResourceServlet() { - return this.resourceHttpServlet; - } - - public void postStart(HttpContext context, AnyValue config) { - List filters = getFilters(); - synchronized (filters) { - if (!filters.isEmpty()) { - for (Object filter : filters) { - ((HttpFilter) filter).postStart(context, config); - } - } - } - this.resourceHttpServlet.postStart(context, config); - getServlets().forEach(s -> { - s.postStart(context, getServletConf(s)); - }); - } - - @Override - public void destroy(HttpContext context, AnyValue config) { - super.destroy(context, config); //必须要执行 - this.resourceHttpServlet.destroy(context, config); - getServlets().forEach(s -> { - s.destroy(context, getServletConf(s)); - s.postDestroy(context, getServletConf(s)); - }); - this.allMapStrings.clear(); - this.wsmappings.clear(); - this.regArray = null; - this.regWsArray = null; - } - - protected static class MappingEntry { - - public final String mapping; - - public final Predicate predicate; - - public final HttpServlet servlet; - - public MappingEntry(String mapping, Predicate predicate, HttpServlet servlet) { - this.mapping = mapping; - this.predicate = predicate; - this.servlet = servlet; - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import org.redkale.util.AnyValue.DefaultAnyValue; +import java.io.*; +import java.util.*; +import java.util.function.*; +import java.util.logging.*; +import java.util.regex.*; +import java.util.stream.Stream; +import org.redkale.net.*; +import org.redkale.net.http.Rest.RestDynSourceType; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * HTTP Servlet的总入口,请求在HttpPrepareServlet中进行分流。
    + * 一个HttpServer只有一个HttpPrepareServlet, 用于管理所有HttpServlet。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpPrepareServlet extends PrepareServlet { + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected HttpServlet resourceHttpServlet = new HttpResourceServlet(); + + protected MappingEntry[] regxArray = null; //regxArray 包含 regxWsArray + + protected MappingEntry[] regxWsArray = null; + + protected Map wsmappings = new HashMap<>(); //super.mappings 包含 wsmappings + + protected final Map allMapStrings = new HashMap<>(); + + private final Object excludeLock = new Object(); + + protected HttpContext context; + + protected boolean lazyHeaders = true; + + private Map> forbidURIMaps; //禁用的URL的正则表达式, 必须与 forbidURIPredicates 保持一致 + + private BiPredicate[] forbidURIPredicates; //禁用的URL的Predicate, 必须与 forbidURIMaps 保持一致 + + private List removeHttpServlet(final Predicate predicateEntry, final Predicate> predicateFilter) { + List servlets = new ArrayList<>(); + synchronized (allMapStrings) { + List keys = new ArrayList<>(); + if (regxArray != null) { + for (MappingEntry me : regxArray) { + if (predicateEntry.test(me)) { + servlets.add(me.servlet); + keys.add(me.mapping); + } + } + } + if (regxWsArray != null) { + for (MappingEntry me : regxWsArray) { + if (predicateEntry.test(me)) { + servlets.add(me.servlet); + keys.add(me.mapping); + } + } + } + Map newwsmappings = new HashMap<>(); + for (Map.Entry en : wsmappings.entrySet()) { + if (predicateFilter.test(en)) { + servlets.add(en.getValue()); + keys.add(en.getKey()); + } else { + newwsmappings.put(en.getKey(), en.getValue()); + } + } + if (newwsmappings.size() != wsmappings.size()) this.wsmappings = newwsmappings; + if (!keys.isEmpty()) { + this.regxArray = Utility.remove(this.regxArray, predicateEntry); + this.regxWsArray = Utility.remove(this.regxWsArray, predicateEntry); + for (HttpServlet rs : servlets) { + super.removeServlet(rs); + } + for (String key : keys) { + super.removeMapping(key); + allMapStrings.remove(key); + } + } + } + return servlets; + } + + public HttpServlet removeHttpServlet(final HttpServlet servlet) { + Predicate predicateEntry = (t) -> t.servlet == servlet; + Predicate> predicateFilter = (t) -> t.getValue() == servlet; + removeHttpServlet(predicateEntry, predicateFilter); + return servlet; + } + + public HttpServlet removeHttpServlet(Service service) { + Predicate predicateEntry = (t) -> { + if (!Rest.isRestDyn(t.servlet)) return false; + Service s = Rest.getService(t.servlet); + if (s == service) return true; + if (s != null) return false; + Map map = Rest.getServiceMap(t.servlet); + if (map == null) return false; + boolean rs = map.values().contains(service); + if (rs && map.size() == 1) return true; + if (rs && map.size() > 1) { + String key = null; + for (Map.Entry en : map.entrySet()) { + if (en.getValue() == service) { + key = en.getKey(); + break; + } + } + if (key != null) map.remove(key); + return false; //还有其他Resouce.name 的Service + } + return rs; + }; + Predicate> predicateFilter = null; + List list = removeHttpServlet(predicateEntry, predicateFilter); + return list == null || list.isEmpty() ? null : list.get(0); + } + + @SuppressWarnings("unchecked") + public HttpServlet removeHttpServlet(Class websocketOrServletType) { + Predicate predicateEntry = (t) -> { + Class type = t.servlet.getClass(); + if (type == websocketOrServletType) return true; + RestDynSourceType rdt = (RestDynSourceType) type.getAnnotation(RestDynSourceType.class); + return (rdt != null && rdt.value() == websocketOrServletType); + }; + Predicate> predicateFilter = (t) -> { + Class type = t.getValue().getClass(); + if (type == websocketOrServletType) return true; + RestDynSourceType rdt = (RestDynSourceType) type.getAnnotation(RestDynSourceType.class); + return (rdt != null && rdt.value() == websocketOrServletType); + }; + List list = removeHttpServlet(predicateEntry, predicateFilter); + return list == null || list.isEmpty() ? null : list.get(0); + } + + @SuppressWarnings("unchecked") + public boolean addForbidURIReg(final String urlreg) { + if (urlreg == null || urlreg.isEmpty()) return false; + synchronized (excludeLock) { + if (forbidURIMaps != null && forbidURIMaps.containsKey(urlreg)) return false; + if (forbidURIMaps == null) forbidURIMaps = new HashMap<>(); + String mapping = urlreg; + if (Utility.contains(mapping, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式)) + if (mapping.endsWith("/*")) { + mapping = mapping.substring(0, mapping.length() - 1) + ".*"; + } else { + mapping = mapping + "$"; + } + } + final String reg = mapping; + final boolean begin = mapping.charAt(0) == '^'; + final Predicate regPredicate = Pattern.compile(reg).asPredicate(); + BiPredicate predicate = (prefix, uri) -> { + return begin || prefix.isEmpty() ? regPredicate.test(uri) : uri.matches(prefix + reg); + }; + forbidURIMaps.put(urlreg, predicate); + forbidURIPredicates = Utility.append(forbidURIPredicates, predicate); + return true; + } + } + + @SuppressWarnings("unchecked") + public boolean removeForbidURIReg(final String urlreg) { + if (urlreg == null || urlreg.isEmpty()) return false; + synchronized (excludeLock) { + if (forbidURIMaps == null || forbidURIPredicates == null || !forbidURIMaps.containsKey(urlreg)) return false; + BiPredicate predicate = forbidURIMaps.get(urlreg); + forbidURIMaps.remove(urlreg); + int index = -1; + for (int i = 0; i < forbidURIPredicates.length; i++) { + if (forbidURIPredicates[i] == predicate) { + index = i; + break; + } + } + if (index > -1) { + if (forbidURIPredicates.length == 1) { + forbidURIPredicates = null; + } else { + int newlen = forbidURIPredicates.length - 1; + BiPredicate[] news = new BiPredicate[newlen]; + System.arraycopy(forbidURIPredicates, 0, news, 0, index); + System.arraycopy(forbidURIPredicates, index + 1, news, index, newlen - index); + forbidURIPredicates = news; + } + } + return true; + } + } + + @Override + @SuppressWarnings("unchecked") + public void init(HttpContext context, AnyValue config) { + super.init(context, config); //必须要执行 + this.context = context; + context.lazyHeaders = lazyHeaders; + Collection servlets = getServlets(); + servlets.forEach(s -> { + s.preInit(application, context, getServletConf(s)); + if (application == null || !application.isCompileMode()) s.init(context, getServletConf(s)); + }); + { //设置ResourceServlet + AnyValue resConfig = config.getAnyValue("resource-servlet"); + if ((resConfig instanceof DefaultAnyValue) && resConfig.getValue("webroot", "").isEmpty()) { + ((DefaultAnyValue) resConfig).addValue("webroot", config.getValue("root")); + } + if (resConfig == null) { //主要用于嵌入式的HttpServer初始化 + DefaultAnyValue dresConfig = new DefaultAnyValue(); + dresConfig.addValue("webroot", config.getValue("root")); + dresConfig.addValue("ranges", config.getValue("ranges")); + dresConfig.addValue("cache", config.getAnyValue("cache")); + AnyValue[] rewrites = config.getAnyValues("rewrite"); + if (rewrites != null) { + for (AnyValue rewrite : rewrites) { + dresConfig.addValue("rewrite", rewrite); + } + } + resConfig = dresConfig; + } + String resServlet = resConfig.getValue("servlet", HttpResourceServlet.class.getName()); + try { + Class resClazz = Thread.currentThread().getContextClassLoader().loadClass(resServlet); + RedkaleClassLoader.putReflectionDeclaredConstructors(resClazz, resClazz.getName()); + this.resourceHttpServlet = (HttpServlet) resClazz.getDeclaredConstructor().newInstance(); + } catch (Throwable e) { + this.resourceHttpServlet = new HttpResourceServlet(); + logger.log(Level.WARNING, "init HttpResourceSerlvet(" + resServlet + ") error", e); + } + { //获取render的suffixs + AnyValue renderConfig = config.getAnyValue("render"); + if (renderConfig != null) { + String[] suffixs = renderConfig.getValue("suffixs", ".htel").toLowerCase().split(";"); + ((HttpResourceServlet) this.resourceHttpServlet).renderSuffixs = suffixs; + } + } + context.getResourceFactory().inject(this.resourceHttpServlet); + if (application == null || !application.isCompileMode()) this.resourceHttpServlet.init(context, resConfig); + } + } + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + try { + final String uri = request.getRequestURI(); + HttpServlet servlet; + if (response.isAutoOptions() && "OPTIONS".equals(request.getMethod())) { + response.finish(200, null); + return; + } + if (request.isWebSocket()) { + servlet = wsmappings.get(uri); + if (servlet == null && this.regxWsArray != null) { + for (MappingEntry en : regxWsArray) { + if (en.predicate.test(uri)) { + servlet = en.servlet; + break; + } + } + } + if (servlet == null) { + response.finish(500, null); + return; + } + } else { + servlet = mappingServlet(uri); + if (servlet == null && this.regxArray != null) { + for (MappingEntry en : regxArray) { + if (en.predicate.test(uri)) { + servlet = en.servlet; + break; + } + } + } + //找不到匹配的HttpServlet则使用静态资源HttpResourceServlet + if (servlet == null) servlet = this.resourceHttpServlet; + } + boolean forbid = false; + BiPredicate[] forbidUrlPredicates = this.forbidURIPredicates; + if (forbidUrlPredicates != null && forbidUrlPredicates.length > 0) { + for (BiPredicate predicate : forbidUrlPredicates) { + if (predicate != null && predicate.test(servlet._prefix, uri)) { + forbid = true; + break; + } + } + } + if (forbid) { + response.finish(403, response.getHttpCode(403)); + return; + } + servlet.execute(request, response); + } catch (Exception e) { + request.getContext().getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request, e); + response.finish(500, null); + } + } + + /** + * 添加HttpServlet + * + * @param servlet HttpServlet + * @param prefix url前缀 + * @param conf 配置信息 + * @param mappingpaths 匹配规则 + */ + @Override + public void addServlet(HttpServlet servlet, Object prefix, AnyValue conf, String... mappingpaths) { + if (prefix == null) prefix = ""; + if (mappingpaths.length < 1) { + WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); + if (ws != null) { + mappingpaths = ws.value(); + if (!ws.repair()) prefix = "";//被设置为不自动追加前缀则清空prefix + } + } + if (lazyHeaders && !Rest.isSimpleRestDyn(servlet)) { + lazyHeaders = false; + if (context != null) context.lazyHeaders = false; //启动后运行过程中执行addServlet + } + synchronized (allMapStrings) { //需要整段锁住 + for (String mappingpath : mappingpaths) { + if (mappingpath == null) continue; + if (!prefix.toString().isEmpty()) mappingpath = prefix + mappingpath; + + if (Utility.contains(mappingpath, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\')) { //是否是正则表达式)) + if (mappingpath.charAt(0) != '^') mappingpath = '^' + mappingpath; + if (mappingpath.endsWith("/*")) { + mappingpath = mappingpath.substring(0, mappingpath.length() - 1) + ".*"; + } else { + mappingpath = mappingpath + "$"; + } + if (regxArray == null) { + regxArray = new MappingEntry[1]; + regxArray[0] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), servlet); + } else { + regxArray = Arrays.copyOf(regxArray, regxArray.length + 1); + regxArray[regxArray.length - 1] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), servlet); + Arrays.sort(regxArray); + } + if (servlet instanceof WebSocketServlet) { + if (regxWsArray == null) { + regxWsArray = new MappingEntry[1]; + regxWsArray[0] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), (WebSocketServlet) servlet); + } else { + regxWsArray = Arrays.copyOf(regxWsArray, regxWsArray.length + 1); + regxWsArray[regxWsArray.length - 1] = new MappingEntry(mappingpath, Pattern.compile(mappingpath).asPredicate(), (WebSocketServlet) servlet); + Arrays.sort(regxWsArray); + } + } + } else if (mappingpath != null && !mappingpath.isEmpty()) { + if (servlet._actionmap != null && servlet._actionmap.containsKey(mappingpath)) { + //context.addRequestURINode(mappingpath); + putMapping(mappingpath, new HttpServlet.HttpActionServlet(servlet._actionmap.get(mappingpath), servlet)); + } else { + putMapping(mappingpath, servlet); + } + if (servlet instanceof WebSocketServlet) { + Map newmappings = new HashMap<>(wsmappings); + newmappings.put(mappingpath, (WebSocketServlet) servlet); + this.wsmappings = newmappings; + } + } + if (this.allMapStrings.containsKey(mappingpath)) { + Class old = this.allMapStrings.get(mappingpath); + throw new RuntimeException("mapping [" + mappingpath + "] repeat on " + old.getName() + " and " + servlet.getClass().getName()); + } + this.allMapStrings.put(mappingpath, servlet.getClass()); + } + setServletConf(servlet, conf); + servlet._prefix = prefix.toString(); + putServlet(servlet); + } + } + + /** + * 设置静态资源HttpServlet + * + * @param servlet HttpServlet + */ + public void setResourceServlet(HttpServlet servlet) { + if (servlet != null) { + this.resourceHttpServlet = servlet; + } + } + + /** + * 获取静态资源HttpServlet + * + * @return HttpServlet + */ + public HttpServlet getResourceServlet() { + return this.resourceHttpServlet; + } + + public void postStart(HttpContext context, AnyValue config) { + List filters = getFilters(); + synchronized (filters) { + if (!filters.isEmpty()) { + for (Object filter : filters) { + ((HttpFilter) filter).postStart(context, config); + } + } + } + this.resourceHttpServlet.postStart(context, config); + getServlets().forEach(s -> { + s.postStart(context, getServletConf(s)); + }); + } + + public HttpServlet findServletByTopic(String topic) { + return filterServlets(x -> x._reqtopic != null && x._reqtopic.equals(topic)).findFirst().orElse(null); + } + + public Stream filterServletsByMmcTopic(String mmctopic) { + return filterServlets(x -> x._mmctopic != null && x._mmctopic.equals(mmctopic)); + } + + public Stream filterServlets(Predicate predicate) { + return predicate == null ? servletStream() : servletStream().filter(predicate); + } + + @Override + public void destroy(HttpContext context, AnyValue config) { + super.destroy(context, config); //必须要执行 + this.resourceHttpServlet.destroy(context, config); + getServlets().forEach(s -> { + s.destroy(context, getServletConf(s)); + s.postDestroy(application, context, getServletConf(s)); + }); + this.allMapStrings.clear(); + this.wsmappings.clear(); + this.regxArray = null; + this.regxWsArray = null; + } + + protected static class MappingEntry implements Comparable { + + public final String mapping; + + public final Predicate predicate; + + public final HttpServlet servlet; + + public MappingEntry(String mapping, Predicate predicate, HttpServlet servlet) { + this.mapping = mapping; + this.predicate = predicate; + this.servlet = servlet; + } + + @Override + public int compareTo(MappingEntry o) { + return o.mapping.compareTo(this.mapping); + } + + } +} diff --git a/src/org/redkale/net/http/HttpRender.java b/src/main/java/org/redkale/net/http/HttpRender.java similarity index 96% rename from src/org/redkale/net/http/HttpRender.java rename to src/main/java/org/redkale/net/http/HttpRender.java index 4c0886601..01fa1de93 100644 --- a/src/org/redkale/net/http/HttpRender.java +++ b/src/main/java/org/redkale/net/http/HttpRender.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import org.redkale.convert.Convert; -import org.redkale.util.AnyValue; - -/** - * HTTP输出引擎的基类
    - * HttpRender主要是给HttpResponse.finish(Object obj)提供指定数据类型的输出策略。
    - *

    - * HttpResponse.finish(Object obj)内置对如下数据类型进行了特殊处理:
    - *      CharSequence/String
    - *      byte[]
    - *      File
    - *      RetResult
    - *      HttpResult
    - * 
    - *

    - * 如果对其他数据类型有特殊输出的需求,则需要自定义HttpRender。 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface HttpRender { - - public void init(HttpContext context, AnyValue config); - - public void renderTo(HttpRequest request, HttpResponse response, Convert convert, HttpScope scope); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import org.redkale.convert.Convert; +import org.redkale.util.AnyValue; + +/** + * HTTP输出引擎的基类
    + * HttpRender主要是给HttpResponse.finish(Object obj)提供指定数据类型的输出策略。
    + *

    + * HttpResponse.finish(Object obj)内置对如下数据类型进行了特殊处理:
    + *      CharSequence/String
    + *      byte[]
    + *      File
    + *      RetResult
    + *      HttpResult
    + * 
    + *

    + * 如果对其他数据类型有特殊输出的需求,则需要自定义HttpRender。 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface HttpRender { + + public void init(HttpContext context, AnyValue config); + + public void renderTo(HttpRequest request, HttpResponse response, Convert convert, HttpScope scope); + +} diff --git a/src/org/redkale/net/http/HttpRequest.java b/src/main/java/org/redkale/net/http/HttpRequest.java similarity index 95% rename from src/org/redkale/net/http/HttpRequest.java rename to src/main/java/org/redkale/net/http/HttpRequest.java index ce059fd32..99c26d62e 100644 --- a/src/org/redkale/net/http/HttpRequest.java +++ b/src/main/java/org/redkale/net/http/HttpRequest.java @@ -1,2466 +1,2520 @@ -/* - * To change this license headers, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.*; -import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.charset.*; -import java.util.*; -import java.util.function.Supplier; -import java.util.logging.Level; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.*; -import org.redkale.util.*; - -/** - * Http请求包 与javax.servlet.http.HttpServletRequest 基本类似。
    - * 同时提供json的解析接口: public Object getJsonParameter(Type type, String name)
    - * Redkale提倡带简单的参数的GET请求采用类似REST风格, 因此提供了 getRequstURIPath 系列接口。
    - * 例如简单的翻页查询
    - * /pipes/user/query/offset:0/limit:20
    - * 获取页号: int offset = request.getRequstURIPath("offset:", 0);
    - * 获取行数: int limit = request.getRequstURIPath("limit:", 10);
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpRequest extends Request { - - private static final boolean pipelineSameHeaders = Boolean.getBoolean("http.request.pipeline.sameheaders"); - - protected static final Serializable CURRUSERID_NIL = new Serializable() { - }; - - protected static final int READ_STATE_ROUTE = 1; - - protected static final int READ_STATE_HEADER = 2; - - protected static final int READ_STATE_BODY = 3; - - protected static final int READ_STATE_END = 4; - - protected static final byte[] EMPTY_BYTES = new byte[0]; - - protected static final String KEY_GET = "GET"; - - protected static final String KEY_POST = "POST"; - - protected static final String KEY_HTTP_1_1 = "HTTP/1.1"; - - protected static final String KEY_COOKIE = "Cookie"; - - protected static final String KEY_CONNECTION = "Connection"; - - protected static final String KEY_CONTENT_TYPE = "Content-Type"; - - protected static final String KEY_ACCEPT = "Accept"; - - protected static final String KEY_HOST = "Host"; - - public static final String SESSIONID_NAME = "JSESSIONID"; - - //---------- header 相关参数 开始 ---------- - protected int headerLength; - - protected int headerHalfLen; - - protected String contentType; - - protected long contentLength = -1; - - protected String host; - - @Comment("原始的cookie字符串,解析后值赋给HttpCookie[] cookies") - protected String cookie; - - protected HttpCookie[] cookies; - - private boolean maybews = false; //是否可能是WebSocket - - protected boolean rpc; - - protected int readState = READ_STATE_ROUTE; - - // @since 2.1.0 - protected Serializable currentUserid = CURRUSERID_NIL; - - protected Supplier currentUserSupplier; - - protected boolean frombody; - - protected ConvertType reqConvertType; - - protected Convert reqConvert; - - protected ConvertType respConvertType; - - protected Convert respConvert; - - protected final Map headers = new HashMap<>(); - //---------- header 相关参数 结束 ---------- - - @Comment("Method GET/POST/...") - protected String method; - - protected boolean getmethod; - - protected String protocol; - - protected String requestURI; - - protected byte[] queryBytes; - - protected String newsessionid; - - protected final Map params = new HashMap<>(); - - protected boolean boundary = false; - - protected int moduleid; - - protected int actionid; - - protected Annotation[] annotations; - - protected String remoteAddr; - - private String lastRequestURIString; - - private byte[] lastRequestURIBytes; - - private final ByteArray array; - - private byte[] headerBytes; - - private boolean headerParsed = false; - - private boolean bodyParsed = false; - - private final String remoteAddrHeader; - - HttpServlet.ActionEntry actionEntry; //仅供HttpServlet传递Entry使用 - - public HttpRequest(HttpContext context) { - this(context, new ByteArray()); - } - - protected HttpRequest(HttpContext context, ByteArray array) { - super(context); - this.array = array; - this.remoteAddrHeader = context.remoteAddrHeader; - } - - @SuppressWarnings("OverridableMethodCallInConstructor") - protected HttpRequest(HttpContext context, HttpSimpleRequest req) { - super(context); - this.array = new ByteArray(); - this.remoteAddrHeader = null; - if (req != null) initSimpleRequest(req); - } - - protected HttpRequest initSimpleRequest(HttpSimpleRequest req) { - if (req != null) { - this.rpc = req.rpc; - if (req.getBody() != null) this.array.put(req.getBody()); - if (req.getHeaders() != null) this.headers.putAll(req.getHeaders()); - this.frombody = req.isFrombody(); - this.reqConvertType = req.getReqConvertType(); - this.reqConvert = req.getReqConvertType() == null ? null : ConvertFactory.findConvert(req.getReqConvertType()); - this.respConvertType = req.getRespConvertType(); - this.respConvert = req.getRespConvertType() == null ? null : ConvertFactory.findConvert(req.getRespConvertType()); - if (req.getParams() != null) this.params.putAll(req.getParams()); - this.hashid = req.getHashid(); - if (req.getCurrentUserid() != 0) this.currentUserid = req.getCurrentUserid(); - this.contentType = req.getContentType(); - this.remoteAddr = req.getRemoteAddr(); - this.requestURI = req.getRequestURI(); - this.method = "POST"; - if (req.getSessionid() != null && !req.getSessionid().isEmpty()) { - this.cookies = new HttpCookie[]{new HttpCookie(SESSIONID_NAME, req.getSessionid())}; - } - } - return this; - } - - public HttpSimpleRequest createSimpleRequest(String prefix) { - HttpSimpleRequest req = new HttpSimpleRequest(); - req.setBody(array.length() == 0 ? null : array.getBytes()); - if (!getHeaders().isEmpty()) { - if (headers.containsKey(Rest.REST_HEADER_RPC_NAME) - || headers.containsKey(Rest.REST_HEADER_CURRUSERID_NAME)) { //外部request不能包含RPC的header信息 - req.setHeaders(new HashMap<>(headers)); - req.removeHeader(Rest.REST_HEADER_RPC_NAME); - req.removeHeader(Rest.REST_HEADER_CURRUSERID_NAME); - } else { - req.setHeaders(headers); - } - } - parseBody(); - req.setParams(params.isEmpty() ? null : params); - req.setRemoteAddr(getRemoteAddr()); - req.setContentType(getContentType()); - req.setPath(prefix); - String uri = this.requestURI; - if (prefix != null && !prefix.isEmpty() && uri.startsWith(prefix)) { - uri = uri.substring(prefix.length()); - } - req.setHashid(this.hashid); - req.setRequestURI(uri); - req.setSessionid(getSessionid(false)); - req.setRpc(this.rpc); - return req; - } - - protected boolean isWebSocket() { - return maybews && "Upgrade".equalsIgnoreCase(getHeader("Connection")) && "GET".equalsIgnoreCase(method); - } - - protected void setPipelineOver(boolean pipelineOver) { - this.pipelineOver = pipelineOver; - } - - protected void setKeepAlive(boolean keepAlive) { - this.keepAlive = keepAlive; - } - - protected boolean isKeepAlive() { - return this.keepAlive; - } - - protected AsyncConnection getChannel() { - return this.channel; - } - - protected int getPipelineIndex() { - return this.pipelineIndex; - } - - protected int getPipelineCount() { - return this.pipelineCount; - } - - protected ConvertType getRespConvertType() { - return this.respConvertType; - } - - protected Convert getRespConvert() { - return this.respConvert == null ? this.jsonConvert : this.respConvert; - } - - @Override - protected int readHeader(final ByteBuffer buffer, final Request last) { - ByteArray bytes = array; - if (this.readState == READ_STATE_ROUTE) { - int rs = readMethodLine(buffer); - if (rs != 0) return rs; - this.readState = READ_STATE_HEADER; - } - if (this.readState == READ_STATE_HEADER) { - if (last != null && ((HttpRequest) last).headerLength > 0) { - final HttpRequest httplast = (HttpRequest) last; - int bufremain = buffer.remaining(); - int remainhalf = httplast.headerLength - this.headerHalfLen; - if (remainhalf > bufremain) { - bytes.put(buffer); - this.headerHalfLen += bufremain; - buffer.clear(); - return 1; - } - buffer.position(buffer.position() + remainhalf); - this.contentType = httplast.contentType; - this.contentLength = httplast.contentLength; - this.host = httplast.host; - this.cookie = httplast.cookie; - this.cookies = httplast.cookies; - this.keepAlive = httplast.keepAlive; - this.maybews = httplast.maybews; - this.rpc = httplast.rpc; - this.hashid = httplast.hashid; - this.currentUserid = httplast.currentUserid; - this.frombody = httplast.frombody; - this.reqConvertType = httplast.reqConvertType; - this.reqConvert = httplast.reqConvert; - this.respConvertType = httplast.respConvertType; - this.respConvert = httplast.respConvert; - this.headerLength = httplast.headerLength; - this.headerHalfLen = httplast.headerLength; - this.headerBytes = httplast.headerBytes; - this.headerParsed = httplast.headerParsed; - this.headers.putAll(httplast.headers); - } else if (context.lazyHeaders && getmethod) { //非GET必须要读header,会有Content-Length - int rs = loadHeaderBytes(buffer); - if (rs != 0) return rs; - this.headerParsed = false; - } else { - int startpos = buffer.position(); - int rs = readHeaderLines(buffer, bytes); - if (rs != 0) { - this.headerHalfLen = bytes.length(); - return rs; - } - this.headerParsed = true; - this.headerLength = buffer.position() - startpos + this.headerHalfLen; - this.headerHalfLen = this.headerLength; - } - bytes.clear(); - this.readState = READ_STATE_BODY; - } - if (this.contentType != null && this.contentType.contains("boundary=")) this.boundary = true; - if (this.boundary) this.keepAlive = false; //文件上传必须设置keepAlive为false,因为文件过大时用户不一定会skip掉多余的数据 - if (this.readState == READ_STATE_BODY) { - if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) { - if (this.contentLength > context.getMaxbody()) return -1; - bytes.put(buffer, Math.min((int) this.contentLength, buffer.remaining())); - int lr = (int) this.contentLength - bytes.length(); - if (lr == 0) this.readState = READ_STATE_END; - return lr > 0 ? lr : 0; - } - if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) bytes.put(buffer, buffer.remaining()); //文件上传、HTTP1.0或Connection:close - this.readState = READ_STATE_END; - } - //暂不考虑是keep-alive且存在body却没有指定Content-Length的情况 - return 0; - } - - private int loadHeaderBytes(final ByteBuffer buffer) { - ByteArray bytes = array; - int remain = buffer.remaining(); - byte b1, b2, b3, b4; - for (;;) { - if (remain-- < 4) { //bytes不存放\r\n\r\n这4个字节 - bytes.put(buffer); - buffer.clear(); - if (bytes.length() > 0) { - byte rn1 = 0, rn2 = 0, rn3 = 0; - byte b = bytes.getLastByte(); - if (b == '\r' || b == '\n') { - rn3 = b; - bytes.backCount(); - if (bytes.length() > 0) { - b = bytes.getLastByte(); - if (b == '\r' || b == '\n') { - rn2 = b; - bytes.backCount(); - if (bytes.length() > 0) { - b = bytes.getLastByte(); - if (b == '\r' || b == '\n') { - rn1 = b; - bytes.backCount(); - } - } - } - } - } - if (rn1 != 0) buffer.put(rn1); - if (rn2 != 0) buffer.put(rn2); - if (rn3 != 0) buffer.put(rn3); - } - return 1; - } - b1 = buffer.get(); - bytes.put(b1); - if (b1 == '\r') { - remain--; - b2 = buffer.get(); - bytes.put(b2); - if (b2 == '\n') { - remain--; - b3 = buffer.get(); - bytes.put(b3); - if (b3 == '\r') { - remain--; - b4 = buffer.get(); - bytes.put(b4); - if (b4 == '\n') { - this.headerBytes = Utility.append(this.headerBytes, bytes.content(), 0, bytes.length()); - this.headerLength = this.headerBytes.length; - this.headerHalfLen = this.headerLength; - bytes.clear(); - return 0; - } - } - } - } - } - } - -// @Override -// protected int readBody(ByteBuffer buffer, int length) { -// int len = buffer.remaining(); -// array.put(buffer, len); -// return len; -// } - //解析 GET /xxx HTTP/1.1 - private int readMethodLine(final ByteBuffer buffer) { - Charset charset = this.context.getCharset(); - int remain = buffer.remaining(); - int size; - ByteArray bytes = array; - //读method - if (this.method == null) { - for (;;) { - if (remain-- < 1) { - buffer.clear(); - return 1; - } - byte b = buffer.get(); - if (b == ' ') break; - bytes.put(b); - } - size = bytes.length(); - if (size == 3 && bytes.get(0) == 'G' && bytes.get(1) == 'E' && bytes.get(2) == 'T') { - this.method = KEY_GET; - this.getmethod = true; - } else if (size == 4 && bytes.get(0) == 'P' && bytes.get(1) == 'O' && bytes.get(2) == 'S' && bytes.get(3) == 'T') { - this.method = KEY_POST; - this.getmethod = false; - } else { - this.method = bytes.toString(charset); - this.getmethod = false; - } - bytes.clear(); - } - //读uri - if (this.requestURI == null) { - int qst = -1;//?的位置 - boolean decodeable = false; - for (;;) { - if (remain-- < 1) { - buffer.clear(); - return 1; - } - byte b = buffer.get(); - if (b == ' ') break; - if (b == '?' && qst < 0) { - qst = bytes.length(); - } else if (!decodeable && (b == '+' || b == '%')) { - decodeable = true; - } - bytes.put(b); - } - size = bytes.length(); - if (qst > 0) { - this.requestURI = decodeable ? bytes.toDecodeString(0, qst, charset) : bytes.toString(0, qst, charset); - this.queryBytes = bytes.getBytes(qst + 1, size - qst - 1); - this.lastRequestURIString = null; - this.lastRequestURIBytes = null; - try { - addParameter(bytes, qst + 1, size - qst - 1); - } catch (Exception e) { - this.context.getLogger().log(Level.WARNING, "HttpRequest.addParameter error: " + bytes.toString(), e); - } - } else { - if (decodeable) { - this.requestURI = bytes.toDecodeString(charset); - this.lastRequestURIString = null; - this.lastRequestURIBytes = null; - } else if (context.lazyHeaders) { - byte[] lastURIBytes = lastRequestURIBytes; - if (lastURIBytes != null && lastURIBytes.length == size && bytes.equal(lastURIBytes)) { - this.requestURI = this.lastRequestURIString; - } else { - this.requestURI = bytes.toString(charset); - this.lastRequestURIString = this.requestURI; - this.lastRequestURIBytes = bytes.getBytes(); - } - } else { - this.requestURI = bytes.toString(charset); - } - this.queryBytes = EMPTY_BYTES; - } - bytes.clear(); - } - //读protocol - for (;;) { - if (remain-- < 1) { - this.params.clear(); - buffer.clear(); - return 1; - } - byte b = buffer.get(); - if (b == '\r') { - if (remain-- < 1) { - this.params.clear(); - buffer.clear(); - buffer.put((byte) '\r'); - return 1; - } - if (buffer.get() != '\n') return -1; - break; - } - bytes.put(b); - } - size = bytes.length(); - if (size == 8 && bytes.get(0) == 'H' && bytes.get(5) == '1' && bytes.get(7) == '1') { - this.protocol = KEY_HTTP_1_1; - } else { - this.protocol = bytes.toString(charset); - } - bytes.clear(); - return 0; - } - - //解析Header Connection: keep-alive - private int readHeaderLines(final ByteBuffer buffer, ByteArray bytes) { - Charset charset = this.context.getCharset(); - int remain = buffer.remaining(); - for (;;) { - bytes.clear(); - if (remain-- < 2) { - if (remain == 1) { - byte one = buffer.get(); - buffer.clear(); - buffer.put(one); - return 1; - } - buffer.clear(); - return 1; - } - remain--; - byte b1 = buffer.get(); - byte b2 = buffer.get(); - if (b1 == '\r' && b2 == '\n') return 0; - bytes.put(b1, b2); - for (;;) { // name - if (remain-- < 1) { - buffer.clear(); - buffer.put(bytes.content(), 0, bytes.length()); - return 1; - } - byte b = buffer.get(); - if (b == ':') break; - bytes.put(b); - } - String name = parseHeaderName(bytes, charset); - bytes.clear(); - boolean first = true; - int space = 0; - for (;;) { // value - if (remain-- < 1) { - buffer.clear(); - buffer.put(name.getBytes()); - buffer.put((byte) ':'); - if (space == 1) { - buffer.put((byte) ' '); - } else if (space > 0) { - for (int i = 0; i < space; i++) buffer.put((byte) ' '); - } - buffer.put(bytes.content(), 0, bytes.length()); - return 1; - } - byte b = buffer.get(); - if (b == '\r') { - if (remain-- < 1) { - buffer.clear(); - buffer.put(name.getBytes()); - buffer.put((byte) ':'); - if (space == 1) { - buffer.put((byte) ' '); - } else if (space > 0) { - for (int i = 0; i < space; i++) buffer.put((byte) ' '); - } - buffer.put(bytes.content(), 0, bytes.length()); - buffer.put((byte) '\r'); - return 1; - } - if (buffer.get() != '\n') return -1; - break; - } - if (first) { - if (b <= ' ') { - space++; - continue; - } - first = false; - } - bytes.put(b); - } - String value; - int vallen = bytes.length(); - switch (name) { - case "Content-Type": - case "content-type": - value = bytes.toString(charset); - this.contentType = value; - break; - case "Content-Length": - case "content-length": - value = bytes.toString(charset); - this.contentLength = Long.decode(value); - break; - case "Host": - case "host": - value = bytes.toString(charset); - this.host = value; - break; - case "Cookie": - case "cookie": - value = bytes.toString(charset); - if (this.cookie == null || this.cookie.isEmpty()) { - this.cookie = value; - } else { - this.cookie += ";" + value; - } - break; - case "Connection": - case "connection": - if (vallen > 0) { - if (bytes.get(0) == 'c' && vallen == 5 - && bytes.get(1) == 'l' && bytes.get(2) == 'o' - && bytes.get(3) == 's' && bytes.get(4) == 'e') { - value = "close"; - this.setKeepAlive(false); - } else if (bytes.get(0) == 'k' && vallen == 10 - && bytes.get(1) == 'e' && bytes.get(2) == 'e' - && bytes.get(3) == 'p' && bytes.get(4) == '-' - && bytes.get(5) == 'a' && bytes.get(6) == 'l' - && bytes.get(7) == 'i' && bytes.get(8) == 'v' - && bytes.get(9) == 'e') { - value = "keep-alive"; - //if (context.getAliveTimeoutSeconds() >= 0) { - this.setKeepAlive(true); - //} - } else { - value = bytes.toString(charset); - this.setKeepAlive(true); - } - } else { - value = ""; - } - headers.put("Connection", value); - break; - case "Upgrade": - case "upgrade": - value = bytes.toString(charset); - this.maybews = "websocket".equalsIgnoreCase(value); - headers.put("Upgrade", value); - break; - case "user-agent": - value = bytes.toString(charset); - headers.put("User-Agent", value); - break; - case Rest.REST_HEADER_RPC_NAME: - value = bytes.toString(charset); - this.rpc = "true".equalsIgnoreCase(value); - headers.put(name, value); - break; - case Rest.REST_HEADER_CURRUSERID_NAME: - value = bytes.toString(charset); - this.hashid = value.hashCode(); - this.currentUserid = value; - headers.put(name, value); - break; - case Rest.REST_HEADER_PARAM_FROM_BODY: - value = bytes.toString(charset); - this.frombody = "true".equalsIgnoreCase(value); - headers.put(name, value); - break; - case Rest.REST_HEADER_REQ_CONVERT_TYPE: - value = bytes.toString(charset); - reqConvertType = ConvertType.valueOf(value); - reqConvert = ConvertFactory.findConvert(reqConvertType); - headers.put(name, value); - break; - case Rest.REST_HEADER_RESP_CONVERT_TYPE: - value = bytes.toString(charset); - respConvertType = ConvertType.valueOf(value); - respConvert = ConvertFactory.findConvert(respConvertType); - headers.put(name, value); - break; - default: - value = bytes.toString(charset); - headers.put(name, value); - } - } - } - - private void parseHeader() { - if (headerParsed) return; - headerParsed = true; - if (headerBytes == null) return; - if (array.isEmpty()) { - readHeaderLines(ByteBuffer.wrap(headerBytes), array); - array.clear(); - } else { //array存有body数据 - readHeaderLines(ByteBuffer.wrap(headerBytes), new ByteArray()); - } - } - - static String parseHeaderName(ByteArray bytes, Charset charset) { - final int size = bytes.length(); - final byte[] bs = bytes.content(); - final byte first = bs[0]; - if (first == 'H' && size == 4) { //Host - if (bs[1] == 'o' && bs[2] == 's' && bs[3] == 't') return KEY_HOST; - } else if (first == 'A' && size == 6) { //Accept - if (bs[1] == 'c' && bs[2] == 'c' && bs[3] == 'e' - && bs[4] == 'p' && bs[5] == 't') return KEY_ACCEPT; - } else if (first == 'C') { - if (size == 10) { //Connection - if (bs[1] == 'o' && bs[2] == 'n' && bs[3] == 'n' - && bs[4] == 'e' && bs[5] == 'c' && bs[6] == 't' - && bs[7] == 'i' && bs[8] == 'o' && bs[9] == 'n') return KEY_CONNECTION; - } else if (size == 12) { //Content-Type - if (bs[1] == 'o' && bs[2] == 'n' && bs[3] == 't' - && bs[4] == 'e' && bs[5] == 'n' && bs[6] == 't' - && bs[7] == '-' && bs[8] == 'T' && bs[9] == 'y' - && bs[10] == 'p' && bs[11] == 'e') return KEY_CONTENT_TYPE; - } else if (size == 6) { //Cookie - if (bs[1] == 'o' && bs[2] == 'o' && bs[3] == 'k' - && bs[4] == 'i' && bs[5] == 'e') return KEY_COOKIE; - } - } - return bytes.toString(charset); - } - - @Override - protected HttpRequest copyHeader() { - if (!pipelineSameHeaders || !context.lazyHeaders) return null; - HttpRequest req = new HttpRequest(context, this.array); - req.headerLength = this.headerLength; - req.headerBytes = this.headerBytes; - req.headerParsed = this.headerParsed; - req.contentType = this.contentType; - req.contentLength = this.contentLength; - req.host = this.host; - req.cookie = this.cookie; - req.cookies = this.cookies; - req.keepAlive = this.keepAlive; - req.maybews = this.maybews; - req.rpc = this.rpc; - req.hashid = this.hashid; - req.currentUserid = this.currentUserid; - req.currentUserSupplier = this.currentUserSupplier; - req.frombody = this.frombody; - req.reqConvertType = this.reqConvertType; - req.reqConvert = this.reqConvert; - req.respConvert = this.respConvert; - req.respConvertType = this.respConvertType; - req.headers.putAll(this.headers); - return req; - } - - @Override - protected void prepare() { - this.keepAlive = true; //默认HTTP/1.1 - } - - @Override - protected void recycle() { - //header - this.headerLength = 0; - this.headerHalfLen = 0; - this.headerBytes = null; - this.headerParsed = false; - this.contentType = null; - this.contentLength = -1; - this.host = null; - this.cookie = null; - this.cookies = null; - this.maybews = false; - this.rpc = false; - this.readState = READ_STATE_ROUTE; - this.currentUserid = CURRUSERID_NIL; - this.currentUserSupplier = null; - this.frombody = false; - this.reqConvertType = null; - this.reqConvert = null; - this.respConvert = jsonConvert; - this.respConvertType = null; - this.headers.clear(); - //其他 - this.newsessionid = null; - this.method = null; - this.getmethod = false; - this.protocol = null; - this.requestURI = null; - this.queryBytes = null; - this.boundary = false; - this.bodyParsed = false; - this.moduleid = 0; - this.actionid = 0; - this.annotations = null; - this.remoteAddr = null; - this.params.clear(); - this.array.clear(); - //内部 - this.actionEntry = null; - super.recycle(); - } - - protected void skipBodyParse() { - this.bodyParsed = true; - } - - private void parseBody() { - if (this.boundary || bodyParsed) return; - bodyParsed = true; - if (this.contentType != null && this.contentType.toLowerCase().contains("x-www-form-urlencoded")) { - addParameter(array, 0, array.length()); - } - } - - private void addParameter(final ByteArray array, final int offset, final int len) { - if (len < 1) return; - Charset charset = this.context.getCharset(); - int limit = offset + len; - int keypos = array.find(offset, limit, '='); - int valpos = array.find(offset, limit, '&'); - if (keypos <= 0 || (valpos >= 0 && valpos < keypos)) { - if (valpos > 0) addParameter(array, valpos + 1, limit - valpos - 1); - return; - } - String name = array.toDecodeString(offset, keypos - offset, charset); - if (name.charAt(0) == '<') return; //内容可能是xml格式; 如: = 0) { - addParameter(array, valpos + 1, limit - valpos - 1); - } - } - - @Override - protected T setProperty(String name, T value) { - return super.setProperty(name, value); - } - - @Override - @SuppressWarnings("unchecked") - protected T getProperty(String name) { - return super.getProperty(name); - } - - @Override - protected T removeProperty(String name) { - return super.removeProperty(name); - } - - /** - * 设置当前用户ID, 通常在HttpServlet.preExecute方法里设置currentUserid
    - * 数据类型只能是int、long、String、JavaBean - * - * @param 泛型 - * @param userid 用户ID - * - * @return HttpRequest - * - * @since 2.1.0 - */ - public HttpRequest setCurrentUserid(T userid) { - this.currentUserid = userid; - return this; - } - - /** - * 获取当前用户ID的int值
    - * - * @return 用户ID - * - * @since 2.4.0 - */ - @SuppressWarnings("unchecked") - public int currentIntUserid() { - if (currentUserid == CURRUSERID_NIL || currentUserid == null) return 0; - if (this.currentUserid instanceof Number) return ((Number) this.currentUserid).intValue(); - return Integer.parseInt(this.currentUserid.toString()); - } - - /** - * 获取当前用户ID
    - * - * @param 数据类型只能是int、long、String、JavaBean - * @param type 类型 - * - * @return 用户ID - * - * @since 2.1.0 - */ - @SuppressWarnings("unchecked") - public T currentUserid(Class type) { - if (currentUserid == CURRUSERID_NIL || currentUserid == null) { - if (type == int.class) return (T) (Integer) (int) 0; - if (type == long.class) return (T) (Long) (long) 0; - return null; - } - if (type == int.class) { - if (this.currentUserid instanceof Number) return (T) (Integer) ((Number) this.currentUserid).intValue(); - return (T) (Integer) Integer.parseInt(this.currentUserid.toString()); - } - if (type == long.class) { - if (this.currentUserid instanceof Number) return (T) (Long) ((Number) this.currentUserid).longValue(); - return (T) (Long) Long.parseLong(this.currentUserid.toString()); - } - if (type == String.class) return (T) this.currentUserid.toString(); - if (this.currentUserid instanceof CharSequence) return JsonConvert.root().convertFrom(type, this.currentUserid.toString()); - return (T) this.currentUserid; - } - - /** - * 建议使用 setCurrentUserid, 通过userid从Service或缓存中获取用户信息
    - * 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser
    - * 数据类型由@HttpUserType指定 - * - * @param supplier currentUser对象方法 - * - * @since 2.4.0 - * - * @return HttpRequest - */ - public HttpRequest setCurrentUserSupplier(Supplier supplier) { - this.currentUserSupplier = supplier; - return this; - } - - /** - * 建议使用 currentUserid, 通过userid从Service或缓存中获取用户信息
    - * 获取当前用户信息
    - * 数据类型由@HttpUserType指定 - * - * @param @HttpUserType指定的用户信息类型 - * - * @return 用户信息 - */ - @SuppressWarnings("unchecked") - public T currentUser() { - Supplier supplier = this.currentUserSupplier; - return (T) (supplier == null ? null : supplier.get()); - } - - /** - * 获取模块ID,来自@HttpServlet.moduleid() - * - * @return 模块ID - */ - @ConvertDisabled - public int getModuleid() { - return this.moduleid; - } - - /** - * 获取操作ID,来自@HttpMapping.actionid() - * - * @return 模块ID - */ - @ConvertDisabled - public int getActionid() { - return this.actionid; - } - - /** - * 获取当前操作Method上的注解集合 - * - * @return Annotation[] - */ - @ConvertDisabled - public Annotation[] getAnnotations() { - if (this.annotations == null) return new Annotation[0]; - Annotation[] newanns = new Annotation[this.annotations.length]; - System.arraycopy(this.annotations, 0, newanns, 0, newanns.length); - return newanns; - } - - /** - * 获取当前操作Method上的注解 - * - * @param 注解泛型 - * @param annotationClass 注解类型 - * - * @return Annotation - */ - public T getAnnotation(Class annotationClass) { - if (this.annotations == null) return null; - for (Annotation ann : this.annotations) { - if (ann.getClass() == annotationClass) return (T) ann; - } - return null; - } - - /** - * 获取当前操作Method上的注解集合 - * - * @param 注解泛型 - * @param annotationClass 注解类型 - * - * @return Annotation[] - */ - public T[] getAnnotationsByType(Class annotationClass) { - if (this.annotations == null) return (T[]) Array.newInstance(annotationClass, 0); - T[] news = (T[]) Array.newInstance(annotationClass, this.annotations.length); - int index = 0; - for (Annotation ann : this.annotations) { - if (ann.getClass() == annotationClass) { - news[index++] = (T) ann; - } - } - if (index < 1) return (T[]) Array.newInstance(annotationClass, 0); - return Arrays.copyOf(news, index); - } - - /** - * 获取客户端地址IP - * - * @return 地址 - */ - @ConvertDisabled - public SocketAddress getRemoteAddress() { - return this.channel == null || !this.channel.isOpen() ? null : this.channel.getRemoteAddress(); - } - - /** - * 获取客户端地址IP, 与getRemoteAddress() 的区别在于:本方法优先取header中指定为RemoteAddress名的值,没有则返回getRemoteAddress()的getHostAddress()。
    - * 本方法适用于服务前端有如nginx的代理服务器进行中转,通过 getRemoteAddress()是获取不到客户端的真实IP。 - * - * @return 地址 - */ - public String getRemoteAddr() { - if (this.remoteAddr != null) return this.remoteAddr; - parseHeader(); - if (remoteAddrHeader != null) { - String val = getHeader(remoteAddrHeader); - if (val != null) { - this.remoteAddr = val; - return val; - } - } - SocketAddress addr = getRemoteAddress(); - if (addr == null) return ""; - if (addr instanceof InetSocketAddress) { - this.remoteAddr = ((InetSocketAddress) addr).getAddress().getHostAddress(); - return this.remoteAddr; - } - this.remoteAddr = String.valueOf(addr); - return this.remoteAddr; - } - - /** - * 获取请求内容指定的编码字符串 - * - * @param charset 编码 - * - * @return 内容 - */ - public String getBody(final Charset charset) { - return charset == null ? array.toString() : array.toString(charset); - } - - /** - * 获取请求内容的UTF-8编码字符串 - * - * @return 内容 - */ - @ConvertDisabled - public String getBodyUTF8() { - return array.toString(StandardCharsets.UTF_8); - } - - /** - * 获取请求内容的JavaBean对象 - * - * @param 泛型 - * @param type 类型 - * - * @return 内容 - */ - public T getBodyJson(java.lang.reflect.Type type) { - if (array == null || array.isEmpty()) return null; - Convert convert = this.reqConvert; - if (convert == null) convert = context.getJsonConvert(); - if (type == byte[].class) return (T) array.getBytes(); - return (T) convert.convertFrom(type, array.content()); - } - - /** - * 获取请求内容的JavaBean对象 - * - * @param 泛型 - * @param convert Convert - * @param type 类型 - * - * @return 内容 - */ - public T getBodyJson(Convert convert, java.lang.reflect.Type type) { - if (array.isEmpty()) return null; - if (type == byte[].class) return (T) array.getBytes(); - return (T) convert.convertFrom(type, array.content()); - } - - /** - * 获取请求内容的byte[] - * - * @return 内容 - */ - public byte[] getBody() { - return array.length() == 0 ? null : array.getBytes(); - } - - /** - * 直接获取body对象 - * - * @return body对象 - */ - @ConvertDisabled - protected ByteArray getDirectBody() { - return array; - } - - @Override - public String toString() { - parseBody(); - return this.getClass().getSimpleName() + "{\r\n method: " + this.method + ", \r\n requestURI: " + this.requestURI - + (this.frombody ? (", \r\n frombody: " + this.frombody) : "") - + (this.reqConvertType != null ? (", \r\n reqConvertType: " + this.reqConvertType) : "") - + (this.respConvertType != null ? (", \r\n respConvertType: " + this.respConvertType) : "") - + ", \r\n currentUserid: " + (this.currentUserid == CURRUSERID_NIL ? null : this.currentUserid) + ", \r\n remoteAddr: " + this.getRemoteAddr() - + ", \r\n cookies: " + this.cookie + ", \r\n contentType: " + this.contentType - + ", \r\n protocol: " + this.protocol + ", \r\n host: " + this.host - + ", \r\n contentLength: " + this.contentLength + ", \r\n bodyLength: " + this.array.length() - + (this.boundary || this.array.isEmpty() ? "" : (", \r\n bodyContent: " + (this.respConvertType == null || this.respConvertType == ConvertType.JSON ? this.getBodyUTF8() : Arrays.toString(getBody())))) - + ", \r\n params: " + toMapString(this.params, 4) + ", \r\n header: " + toMapString(this.headers, 4) + "\r\n}"; //this.headers.toString(4) - } - - private static CharSequence toMapString(Map map, int indent) { - char[] chars = new char[indent]; - Arrays.fill(chars, ' '); - final String space = new String(chars); - StringBuilder sb = new StringBuilder(); - sb.append("{\r\n"); - for (Map.Entry en : map.entrySet()) { - sb.append(space).append(" '").append(en.getKey()).append("': '").append(en.getValue()).append("',\r\n"); - } - sb.append(space).append('}'); - return sb; - } - - /** - * 获取文件上传对象 - * - * @return 文件上传对象 - */ - @ConvertDisabled - public final MultiContext getMultiContext() { - final InputStream in = newInputStream(); - return new MultiContext(context.getCharset(), this.getContentType(), this.params, - new BufferedInputStream(in, Math.max(array.length(), 8192)) { - { - array.copyTo(this.buf); - this.count = array.length(); - } - }, null); - } - - /** - * 是否上传文件请求 - * - * @return boolean - */ - public final boolean isMultipart() { - return boundary; - } - - /** - * 获取文件上传信息列表 - * - * @return 文件上传对象集合 - * - * @throws IOException IO异常 - */ - @ConvertDisabled - public final Iterable multiParts() throws IOException { - return getMultiContext().parts(); - } - - /** - * 获取sessionid - * - * @param create 无sessionid是否自动创建 - * - * @return sessionid - */ - @ConvertDisabled - public String getSessionid(boolean create) { - String sessionid = getCookie(SESSIONID_NAME, null); - if (create && (sessionid == null || sessionid.isEmpty())) { - sessionid = context.createSessionid(); - this.newsessionid = sessionid; - } - return sessionid; - } - - /** - * 更新sessionid - * - * @return 新的sessionid值 - */ - public String changeSessionid() { - this.newsessionid = context.createSessionid(); - return newsessionid; - } - - /** - * 指定值更新sessionid - * - * @param newsessionid 新sessionid值 - * - * @return 新的sessionid值 - */ - public String changeSessionid(String newsessionid) { - this.newsessionid = newsessionid == null ? context.createSessionid() : newsessionid.trim(); - return newsessionid; - } - - /** - * 使sessionid失效 - */ - public void invalidateSession() { - this.newsessionid = ""; //为空表示删除sessionid - } - - /** - * 获取所有Cookie对象 - * - * @return cookie对象数组 - */ - public HttpCookie[] getCookies() { - parseHeader(); - if (this.cookies == null) this.cookies = parseCookies(this.cookie); - return this.cookies.length == 0 ? null : this.cookies; - } - - /** - * 获取Cookie值 - * - * @param name cookie名 - * - * @return cookie值 - */ - public String getCookie(String name) { - return getCookie(name, null); - } - - /** - * 获取Cookie值, 没有返回默认值 - * - * @param name cookie名 - * @param dfvalue 默认cookie值 - * - * @return cookie值 - */ - public String getCookie(String name, String dfvalue) { - HttpCookie[] cs = getCookies(); - if (cs == null) return dfvalue; - for (HttpCookie c : cs) { - if (name.equals(c.getName())) return c.getValue(); - } - return dfvalue; - } - - private static HttpCookie[] parseCookies(String cookiestr) { - if (cookiestr == null || cookiestr.isEmpty()) return new HttpCookie[0]; - String str = cookiestr.replaceAll("(^;)|(;$)", "").replaceAll(";+", ";"); - if (str.isEmpty()) return new HttpCookie[0]; - String[] strs = str.split(";"); - HttpCookie[] cookies = new HttpCookie[strs.length]; - for (int i = 0; i < strs.length; i++) { - String s = strs[i]; - int pos = s.indexOf('='); - String v = (pos < 0 ? "" : s.substring(pos + 1)); - if (v.indexOf('"') == 0 && v.lastIndexOf('"') == v.length() - 1) v = v.substring(1, v.length() - 1); - cookies[i] = new HttpCookie((pos < 0 ? s : s.substring(0, pos)), v); - } - return cookies; - } - - /** - * 获取协议名 http、https、ws、wss等 - * - * @return protocol - */ - public String getProtocol() { - return protocol; - } - - /** - * 获取请求方法 GET、POST等 - * - * @return method - */ - public String getMethod() { - return method; - } - - /** - * 获取Content-Type的header值 - * - * @return contentType - */ - public String getContentType() { - return contentType; - } - - /** - * 获取请求内容的长度, 为-1表示内容长度不确定 - * - * @return 内容长度 - */ - public long getContentLength() { - return contentLength; - } - - /** - * 获取Host的Header值 - * - * @return Host - */ - public String getHost() { - return host; - } - - /** - * 获取请求的URL - * - * @return 请求的URL - */ - public String getRequestURI() { - return requestURI; - } - - /** - * 获取请求参数的byte[] - * - * @return byte[] - */ - public byte[] getQueryBytes() { - return queryBytes; - } - - /** - * 截取getRequestURI最后的一个/后面的部分 - * - * @return String - */ - @ConvertDisabled - public String getRequstURILastPath() { - if (requestURI == null) return ""; - return requestURI.substring(requestURI.lastIndexOf('/') + 1); - } - - /** - * 获取请求URL最后的一个/后面的部分的short值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: short type = request.getRequstURILastPath((short)0); //type = 2 - * - * @param defvalue 默认short值 - * - * @return short值 - */ - public short getRequstURILastPath(short defvalue) { - String val = getRequstURILastPath(); - if (val.isEmpty()) return defvalue; - try { - return Short.parseShort(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL最后的一个/后面的部分的short值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: short type = request.getRequstURILastPath(16, (short)0); //type = 2 - * - * @param radix 进制数 - * @param defvalue 默认short值 - * - * @return short值 - */ - public short getRequstURILastPath(int radix, short defvalue) { - String val = getRequstURILastPath(); - if (val.isEmpty()) return defvalue; - try { - return Short.parseShort(val, radix); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL最后的一个/后面的部分的int值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: int type = request.getRequstURILastPath(0); //type = 2 - * - * @param defvalue 默认int值 - * - * @return int值 - */ - public int getRequstURILastPath(int defvalue) { - String val = getRequstURILastPath(); - try { - return val.isEmpty() ? defvalue : Integer.parseInt(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL最后的一个/后面的部分的int值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: int type = request.getRequstURILastPath(16, 0); //type = 2 - * - * @param radix 进制数 - * @param defvalue 默认int值 - * - * @return int值 - */ - public int getRequstURILastPath(int radix, int defvalue) { - String val = getRequstURILastPath(); - try { - return val.isEmpty() ? defvalue : Integer.parseInt(val, radix); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL最后的一个/后面的部分的float值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: float type = request.getRequstURILastPath(0.0f); //type = 2.0f - * - * @param defvalue 默认float值 - * - * @return float值 - */ - public float getRequstURILastPath(float defvalue) { - String val = getRequstURILastPath(); - try { - return val.isEmpty() ? defvalue : Float.parseFloat(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL最后的一个/后面的部分的int值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: long type = request.getRequstURILastPath(0L); //type = 2 - * - * @param defvalue 默认long值 - * - * @return long值 - */ - public long getRequstURILastPath(long defvalue) { - String val = getRequstURILastPath(); - try { - return val.isEmpty() ? defvalue : Long.parseLong(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL最后的一个/后面的部分的int值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: long type = request.getRequstURILastPath(16, 0L); //type = 2 - * - * @param radix 进制数 - * @param defvalue 默认long值 - * - * @return long值 - */ - public long getRequstURILastPath(int radix, long defvalue) { - String val = getRequstURILastPath(); - try { - return val.isEmpty() ? defvalue : Long.parseLong(val, radix); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL最后的一个/后面的部分的double值
    - * 例如请求URL /pipes/user/query/2
    - * 获取type参数: double type = request.getRequstURILastPath(0.0); //type = 2.0 - * - * @param defvalue 默认double值 - * - * @return double值 - */ - public double getRequstURILastPath(double defvalue) { - String val = getRequstURILastPath(); - try { - return val.isEmpty() ? defvalue : Double.parseDouble(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * - * 从prefix之后截取getRequestURI再对"/"进行分隔 - *

    - * @param prefix 前缀 - * - * @return String[] - */ - public String[] getRequstURIPaths(String prefix) { - if (requestURI == null || prefix == null) return new String[0]; - return requestURI.substring(requestURI.indexOf(prefix) + prefix.length() + (prefix.endsWith("/") ? 0 : 1)).split("/"); - } - - /** - * 获取请求URL分段中含prefix段的值
    - * 例如请求URL /pipes/user/query/name:hello
    - * 获取name参数: String name = request.getRequstURIPath("name:", "none"); - * - * @param prefix prefix段前缀 - * @param defvalue 默认值 - * - * @return prefix截断后的值 - */ - public String getRequstURIPath(String prefix, String defvalue) { - if (requestURI == null || prefix == null || prefix.isEmpty()) return defvalue; - int pos = requestURI.indexOf(prefix); - if (pos < 0) return defvalue; - String sub = requestURI.substring(pos + prefix.length()); - pos = sub.indexOf('/'); - return pos < 0 ? sub : sub.substring(0, pos); - } - - /** - * 获取请求URL分段中含prefix段的short值
    - * 例如请求URL /pipes/user/query/type:10
    - * 获取type参数: short type = request.getRequstURIPath("type:", (short)0); - * - * @param prefix prefix段前缀 - * @param defvalue 默认short值 - * - * @return short值 - */ - public short getRequstURIPath(String prefix, short defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Short.parseShort(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL分段中含prefix段的short值
    - * 例如请求URL /pipes/user/query/type:a
    - * 获取type参数: short type = request.getRequstURIPath(16, "type:", (short)0); //type = 10 - * - * @param radix 进制数 - * @param prefix prefix段前缀 - * @param defvalue 默认short值 - * - * @return short值 - */ - public short getRequstURIPath(int radix, String prefix, short defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Short.parseShort(val, radix); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL分段中含prefix段的int值
    - * 例如请求URL /pipes/user/query/offset:0/limit:50
    - * 获取offset参数: int offset = request.getRequstURIPath("offset:", 0);
    - * 获取limit参数: int limit = request.getRequstURIPath("limit:", 20);
    - * - * @param prefix prefix段前缀 - * @param defvalue 默认int值 - * - * @return int值 - */ - public int getRequstURIPath(String prefix, int defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Integer.parseInt(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL分段中含prefix段的int值
    - * 例如请求URL /pipes/user/query/offset:0/limit:50
    - * 获取offset参数: int offset = request.getRequstURIPath("offset:", 0);
    - * 获取limit参数: int limit = request.getRequstURIPath(16, "limit:", 20); // limit = 16
    - * - * @param radix 进制数 - * @param prefix prefix段前缀 - * @param defvalue 默认int值 - * - * @return int值 - */ - public int getRequstURIPath(int radix, String prefix, int defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Integer.parseInt(val, radix); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL分段中含prefix段的float值
    - * 例如请求URL /pipes/user/query/point:40.0
    - * 获取time参数: float point = request.getRequstURIPath("point:", 0.0f); - * - * @param prefix prefix段前缀 - * @param defvalue 默认float值 - * - * @return float值 - */ - public float getRequstURIPath(String prefix, float defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Float.parseFloat(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL分段中含prefix段的long值
    - * 例如请求URL /pipes/user/query/time:1453104341363/id:40
    - * 获取time参数: long time = request.getRequstURIPath("time:", 0L); - * - * @param prefix prefix段前缀 - * @param defvalue 默认long值 - * - * @return long值 - */ - public long getRequstURIPath(String prefix, long defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Long.parseLong(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL分段中含prefix段的long值
    - * 例如请求URL /pipes/user/query/time:1453104341363/id:40
    - * 获取time参数: long time = request.getRequstURIPath(16, "time:", 0L); - * - * @param radix 进制数 - * @param prefix prefix段前缀 - * @param defvalue 默认long值 - * - * @return long值 - */ - public long getRequstURIPath(int radix, String prefix, long defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Long.parseLong(val, radix); - } catch (NumberFormatException e) { - return defvalue; - } - } - - /** - * 获取请求URL分段中含prefix段的double值
    - * 例如请求URL /pipes/user/query/point:40.0
    - * 获取time参数: double point = request.getRequstURIPath("point:", 0.0); - * - * @param prefix prefix段前缀 - * @param defvalue 默认double值 - * - * @return double值 - */ - public double getRequstURIPath(String prefix, double defvalue) { - String val = getRequstURIPath(prefix, null); - try { - return val == null ? defvalue : Double.parseDouble(val); - } catch (NumberFormatException e) { - return defvalue; - } - } - - //------------------------------------------------------------------------------ - /** - * 获取请求Header总对象 - * - * @return AnyValue - */ - public Map getHeaders() { - parseHeader(); - return headers; - } - - /** - * 将请求Header转换成Map - * - * @param map Map - * - * @return Map - */ - @ConvertDisabled - public Map getHeadersToMap(Map map) { - parseHeader(); - if (map == null) map = new LinkedHashMap<>(); - final Map map0 = map; - headers.forEach((k, v) -> map0.put(k, v)); - return map0; - } - - /** - * 获取所有的header名 - * - * @return header名数组 - */ - @ConvertDisabled - public String[] getHeaderNames() { - parseHeader(); - Set names = headers.keySet(); - return names.toArray(new String[names.size()]); - } - - /** - * 获取指定的header值 - * - * @param name header名 - * - * @return header值 - */ - public String getHeader(String name) { - parseHeader(); - return headers.get(name); - } - - /** - * 获取指定的header值, 没有返回默认值 - * - * @param name header名 - * @param defaultValue 默认值 - * - * @return header值 - */ - public String getHeader(String name, String defaultValue) { - parseHeader(); - return headers.getOrDefault(name, defaultValue); - } - - /** - * 获取指定的header的json值 - * - * @param 泛型 - * @param type 反序列化的类名 - * @param name header名 - * - * @return header值 - */ - public T getJsonHeader(java.lang.reflect.Type type, String name) { - String v = getHeader(name); - return v == null || v.isEmpty() ? null : jsonConvert.convertFrom(type, v); - } - - /** - * 获取指定的header的json值 - * - * @param 泛型 - * @param convert JsonConvert对象 - * @param type 反序列化的类名 - * @param name header名 - * - * @return header值 - */ - public T getJsonHeader(JsonConvert convert, java.lang.reflect.Type type, String name) { - String v = getHeader(name); - return v == null || v.isEmpty() ? null : convert.convertFrom(type, v); - } - - /** - * 获取指定的header的boolean值, 没有返回默认boolean值 - * - * @param name header名 - * @param defaultValue 默认boolean值 - * - * @return header值 - */ - public boolean getBooleanHeader(String name, boolean defaultValue) { - //return headers.getBoolValue(name, defaultValue); - parseHeader(); - String value = headers.get(name); - return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value); - } - - /** - * 获取指定的header的short值, 没有返回默认short值 - * - * @param name header名 - * @param defaultValue 默认short值 - * - * @return header值 - */ - public short getShortHeader(String name, short defaultValue) { - //return headers.getShortValue(name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Short.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的header的short值, 没有返回默认short值 - * - * @param radix 进制数 - * @param name header名 - * @param defaultValue 默认short值 - * - * @return header值 - */ - public short getShortHeader(int radix, String name, short defaultValue) { - //return headers.getShortValue(name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的header的short值, 没有返回默认short值 - * - * @param name header名 - * @param defaultValue 默认short值 - * - * @return header值 - */ - public short getShortHeader(String name, int defaultValue) { - //return headers.getShortValue(name, (short) defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return (short) defaultValue; - try { - return Short.decode(value); - } catch (NumberFormatException e) { - return (short) defaultValue; - } - } - - /** - * 获取指定的header的short值, 没有返回默认short值 - * - * @param radix 进制数 - * @param name header名 - * @param defaultValue 默认short值 - * - * @return header值 - */ - public short getShortHeader(int radix, String name, int defaultValue) { - //return headers.getShortValue(radix, name, (short) defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return (short) defaultValue; - try { - return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); - } catch (NumberFormatException e) { - return (short) defaultValue; - } - } - - /** - * 获取指定的header的int值, 没有返回默认int值 - * - * @param name header名 - * @param defaultValue 默认int值 - * - * @return header值 - */ - public int getIntHeader(String name, int defaultValue) { - //return headers.getIntValue(name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的header的int值, 没有返回默认int值 - * - * @param radix 进制数 - * @param name header名 - * @param defaultValue 默认int值 - * - * @return header值 - */ - public int getIntHeader(int radix, String name, int defaultValue) { - //return headers.getIntValue(radix, name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的header的long值, 没有返回默认long值 - * - * @param name header名 - * @param defaultValue 默认long值 - * - * @return header值 - */ - public long getLongHeader(String name, long defaultValue) { - //return headers.getLongValue(name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Long.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的header的long值, 没有返回默认long值 - * - * @param radix 进制数 - * @param name header名 - * @param defaultValue 默认long值 - * - * @return header值 - */ - public long getLongHeader(int radix, String name, long defaultValue) { - //return headers.getLongValue(radix, name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的header的float值, 没有返回默认float值 - * - * @param name header名 - * @param defaultValue 默认float值 - * - * @return header值 - */ - public float getFloatHeader(String name, float defaultValue) { - //return headers.getFloatValue(name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Float.parseFloat(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的header的double值, 没有返回默认double值 - * - * @param name header名 - * @param defaultValue 默认double值 - * - * @return header值 - */ - public double getDoubleHeader(String name, double defaultValue) { - //return headers.getDoubleValue(name, defaultValue); - parseHeader(); - String value = headers.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Double.parseDouble(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - //------------------------------------------------------------------------------ - /** - * 获取请求参数总对象 - * - * @return AnyValue - */ - public Map getParameters() { - parseBody(); - return params; - } - - /** - * 将请求参数转换成Map - * - * @param map Map - * - * @return Map - */ - @ConvertDisabled - public Map getParametersToMap(Map map) { - if (map == null) map = new LinkedHashMap<>(); - final Map map0 = map; - getParameters().forEach((k, v) -> map0.put(k, v)); - return map0; - } - - /** - * 将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx
    - * 不会返回null,没有参数返回空字符串 - * - * - * @return String - */ - @ConvertDisabled - public String getParametersToString() { - return getParametersToString(null); - } - - /** - * 将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx
    - * 不会返回null,没有参数返回空字符串 - * - * @param prefix 拼接前缀, 如果无参数,返回的字符串不会含有拼接前缀 - * - * @return String - */ - public String getParametersToString(String prefix) { - byte[] rbs = queryBytes; - if (rbs == null || rbs.length < 1) return ""; - Charset charset = this.context.getCharset(); - String str = charset == null ? new String(rbs, StandardCharsets.UTF_8) : new String(rbs, charset); - return (prefix == null) ? str : (prefix + str); - } - - /** - * 获取所有参数名 - * - * @return 参数名数组 - */ - @ConvertDisabled - public String[] getParameterNames() { - parseBody(); - Set names = params.keySet(); - return names.toArray(new String[names.size()]); - } - - /** - * 获取指定的参数值 - * - * @param name 参数名 - * - * @return 参数值 - */ - public String getParameter(String name) { - if (this.frombody) { - if (array.isEmpty()) return null; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (String) convert.convertFrom(String.class, array.content()); - } - parseBody(); - return params.get(name); - } - - /** - * 获取指定的参数值, 没有返回默认值 - * - * @param name 参数名 - * @param defaultValue 默认值 - * - * @return 参数值 - */ - public String getParameter(String name, String defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (String) convert.convertFrom(String.class, array.content()); - } - parseBody(); - return params.getOrDefault(name, defaultValue); - } - - /** - * 获取指定的参数json值 - * - * @param 泛型 - * @param type 反序列化的类名 - * @param name 参数名 - * - * @return 参数值 - */ - public T getJsonParameter(java.lang.reflect.Type type, String name) { - if (this.frombody) { - if (array.isEmpty()) return null; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - if (type == byte[].class) return (T) array.getBytes(); - return (T) convert.convertFrom(type, array.content()); - } - String v = getParameter(name); - return v == null || v.isEmpty() ? null : jsonConvert.convertFrom(type, v); - } - - /** - * 获取指定的参数json值 - * - * @param 泛型 - * @param convert JsonConvert对象 - * @param type 反序列化的类名 - * @param name 参数名 - * - * @return 参数值 - */ - public T getJsonParameter(JsonConvert convert, java.lang.reflect.Type type, String name) { - String v = getParameter(name); - return v == null || v.isEmpty() ? null : convert.convertFrom(type, v); - } - - /** - * 获取指定的参数boolean值, 没有返回默认boolean值 - * - * @param name 参数名 - * @param defaultValue 默认boolean值 - * - * @return 参数值 - */ - public boolean getBooleanParameter(String name, boolean defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (boolean) convert.convertFrom(boolean.class, array.content()); - } - parseBody(); - String value = params.get(name); - return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value); - } - - /** - * 获取指定的参数short值, 没有返回默认short值 - * - * @param name 参数名 - * @param defaultValue 默认short值 - * - * @return 参数值 - */ - public short getShortParameter(String name, short defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (short) convert.convertFrom(short.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Short.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的参数short值, 没有返回默认short值 - * - * @param radix 进制数 - * @param name 参数名 - * @param defaultValue 默认short值 - * - * @return 参数值 - */ - public short getShortParameter(int radix, String name, short defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return (short) defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (short) convert.convertFrom(short.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的参数short值, 没有返回默认short值 - * - * @param name 参数名 - * @param defaultValue 默认short值 - * - * @return 参数值 - */ - public short getShortParameter(String name, int defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return (short) defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (short) convert.convertFrom(short.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return (short) defaultValue; - try { - return Short.decode(value); - } catch (NumberFormatException e) { - return (short) defaultValue; - } - } - - /** - * 获取指定的参数int值, 没有返回默认int值 - * - * @param name 参数名 - * @param defaultValue 默认int值 - * - * @return 参数值 - */ - public int getIntParameter(String name, int defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (int) convert.convertFrom(int.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Integer.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的参数int值, 没有返回默认int值 - * - * @param radix 进制数 - * @param name 参数名 - * @param defaultValue 默认int值 - * - * @return 参数值 - */ - public int getIntParameter(int radix, String name, int defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (int) convert.convertFrom(int.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的参数long值, 没有返回默认long值 - * - * @param name 参数名 - * @param defaultValue 默认long值 - * - * @return 参数值 - */ - public long getLongParameter(String name, long defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (long) convert.convertFrom(long.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Long.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的参数long值, 没有返回默认long值 - * - * @param radix 进制数 - * @param name 参数名 - * @param defaultValue 默认long值 - * - * @return 参数值 - */ - public long getLongParameter(int radix, String name, long defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (long) convert.convertFrom(long.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的参数float值, 没有返回默认float值 - * - * @param name 参数名 - * @param defaultValue 默认float值 - * - * @return 参数值 - */ - public float getFloatParameter(String name, float defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (float) convert.convertFrom(float.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Float.parseFloat(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取指定的参数double值, 没有返回默认double值 - * - * @param name 参数名 - * @param defaultValue 默认double值 - * - * @return 参数值 - */ - public double getDoubleParameter(String name, double defaultValue) { - if (this.frombody) { - if (array.isEmpty()) return defaultValue; - Convert convert = this.reqConvert; - if (convert == null) convert = jsonConvert; - return (double) convert.convertFrom(double.class, array.content()); - } - parseBody(); - String value = params.get(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Double.parseDouble(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - /** - * 获取翻页对象 同 getFlipper("flipper", false, 0); - * - * @return Flipper翻页对象 - */ - public org.redkale.source.Flipper getFlipper() { - return getFlipper(false, 0); - } - - /** - * 获取翻页对象 同 getFlipper("flipper", needcreate, 0); - * - * @param needcreate 无参数时是否创建新Flipper对象 - * - * @return Flipper翻页对象 - */ - public org.redkale.source.Flipper getFlipper(boolean needcreate) { - return getFlipper(needcreate, 0); - } - - /** - * 获取翻页对象 同 getFlipper("flipper", false, maxLimit); - * - * @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT - * - * @return Flipper翻页对象 - */ - public org.redkale.source.Flipper getFlipper(int maxLimit) { - return getFlipper(false, maxLimit); - } - - /** - * 获取翻页对象 同 getFlipper("flipper", needcreate, maxLimit) - * - * @param needcreate 无参数时是否创建新Flipper对象 - * @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT - * - * @return Flipper翻页对象 - */ - public org.redkale.source.Flipper getFlipper(boolean needcreate, int maxLimit) { - return getFlipper("flipper", needcreate, maxLimit); - } - - /** - * 获取翻页对象 https://redkale.org/pipes/users/list/offset:0/limit:20/sort:createtime%20ASC
    - * https://redkale.org/pipes/users/list?flipper={'offset':0,'limit':20, 'sort':'createtime ASC'}
    - * 以上两种接口都可以获取到翻页对象 - * - * - * @param name Flipper对象的参数名,默认为 "flipper" - * @param needcreate 无参数时是否创建新Flipper对象 - * @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT - * - * @return Flipper翻页对象 - */ - public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit) { - org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name); - if (flipper == null) { - if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT; - int limit = getRequstURIPath("limit:", 0); - int offset = getRequstURIPath("offset:", 0); - String sort = getRequstURIPath("sort:", ""); - if (limit > 0) { - if (limit > maxLimit) limit = maxLimit; - flipper = new org.redkale.source.Flipper(limit, offset, sort); - } - } else if (flipper.getLimit() < 1 || (maxLimit > 0 && flipper.getLimit() > maxLimit)) { - flipper.setLimit(maxLimit); - } - if (flipper != null || !needcreate) return flipper; - if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT; - return new org.redkale.source.Flipper(maxLimit); - } -} +/* + * To change this license headers, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.*; +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.charset.*; +import java.util.*; +import java.util.function.Supplier; +import java.util.logging.Level; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.net.*; +import org.redkale.util.*; + +/** + * Http请求包 与javax.servlet.http.HttpServletRequest 基本类似。
    + * 同时提供json的解析接口: public Object getJsonParameter(Type type, String name)
    + * Redkale提倡带简单的参数的GET请求采用类似REST风格, 因此提供了 getRequstURIPath 系列接口。
    + * 例如简单的翻页查询
    + * /pipes/user/query/offset:0/limit:20
    + * 获取页号: int offset = request.getRequstURIPath("offset:", 0);
    + * 获取行数: int limit = request.getRequstURIPath("limit:", 10);
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpRequest extends Request { + + private static final boolean pipelineSameHeaders = Boolean.getBoolean("redkale.http.request.pipeline.sameheaders"); + + protected static final Serializable CURRUSERID_NIL = new Serializable() { + }; + + protected static final int READ_STATE_ROUTE = 1; + + protected static final int READ_STATE_HEADER = 2; + + protected static final int READ_STATE_BODY = 3; + + protected static final int READ_STATE_END = 4; + + protected static final byte[] EMPTY_BYTES = new byte[0]; + + protected static final String KEY_GET = "GET"; + + protected static final String KEY_POST = "POST"; + + protected static final String KEY_HTTP_1_1 = "HTTP/1.1"; + + protected static final String KEY_COOKIE = "Cookie"; + + protected static final String KEY_CONNECTION = "Connection"; + + protected static final String KEY_CONTENT_TYPE = "Content-Type"; + + protected static final String KEY_ACCEPT = "Accept"; + + protected static final String KEY_HOST = "Host"; + + public static final String SESSIONID_NAME = "JSESSIONID"; + + //---------- header 相关参数 开始 ---------- + protected int headerLength; + + protected int headerHalfLen; + + protected String contentType; + + protected long contentLength = -1; + + protected String host; + + @Comment("原始的cookie字符串,解析后值赋给HttpCookie[] cookies") + protected String cookie; + + protected HttpCookie[] cookies; + + private boolean maybews = false; //是否可能是WebSocket + + protected boolean rpc; + + protected int readState = READ_STATE_ROUTE; + + // @since 2.1.0 + protected Serializable currentUserid = CURRUSERID_NIL; + + protected Supplier currentUserSupplier; + + protected boolean frombody; + + protected ConvertType reqConvertType; + + protected Convert reqConvert; + + protected ConvertType respConvertType; + + protected Convert respConvert; + + protected final Map headers = new HashMap<>(); + //---------- header 相关参数 结束 ---------- + + @Comment("Method GET/POST/...") + protected String method; + + protected boolean getmethod; + + protected String protocol; + + protected String requestURI; + + protected byte[] queryBytes; + + protected String newsessionid; + + protected final Map params = new HashMap<>(); + + protected boolean boundary = false; + + protected int moduleid; + + protected int actionid; + + protected Annotation[] annotations; + + protected String remoteAddr; + + private String lastRequestURIString; + + private byte[] lastRequestURIBytes; + + private final ByteArray array; + + private byte[] headerBytes; + + private boolean headerParsed = false; + + private boolean bodyParsed = false; + + private final String remoteAddrHeader; + + HttpServlet.ActionEntry actionEntry; //仅供HttpServlet传递Entry使用 + + public HttpRequest(HttpContext context) { + this(context, new ByteArray()); + } + + protected HttpRequest(HttpContext context, ByteArray array) { + super(context); + this.array = array; + this.remoteAddrHeader = context.remoteAddrHeader; + } + + @SuppressWarnings("OverridableMethodCallInConstructor") + protected HttpRequest(HttpContext context, HttpSimpleRequest req) { + super(context); + this.array = new ByteArray(); + this.remoteAddrHeader = null; + if (req != null) initSimpleRequest(req, true); + } + + protected HttpRequest initSimpleRequest(HttpSimpleRequest req, boolean needPath) { + if (req != null) { + this.rpc = req.rpc; + if (req.getBody() != null) this.array.put(req.getBody()); + if (req.getHeaders() != null) this.headers.putAll(req.getHeaders()); + this.frombody = req.isFrombody(); + this.reqConvertType = req.getReqConvertType(); + this.reqConvert = req.getReqConvertType() == null ? null : ConvertFactory.findConvert(req.getReqConvertType()); + this.respConvertType = req.getRespConvertType(); + this.respConvert = req.getRespConvertType() == null ? null : ConvertFactory.findConvert(req.getRespConvertType()); + if (req.getParams() != null) this.params.putAll(req.getParams()); + this.hashid = req.getHashid(); + if (req.getCurrentUserid() != null) this.currentUserid = req.getCurrentUserid(); + this.contentType = req.getContentType(); + this.remoteAddr = req.getRemoteAddr(); + if (needPath) { + this.requestURI = (req.getPath() == null || req.getPath().isEmpty()) ? req.getRequestURI() : (req.getPath() + req.getRequestURI()); + } else { + this.requestURI = req.getRequestURI(); + } + this.method = "POST"; + if (req.getSessionid() != null && !req.getSessionid().isEmpty()) { + this.cookies = new HttpCookie[]{new HttpCookie(SESSIONID_NAME, req.getSessionid())}; + } + } + return this; + } + + public HttpSimpleRequest createSimpleRequest(String prefix) { + HttpSimpleRequest req = new HttpSimpleRequest(); + req.setBody(array.length() == 0 ? null : array.getBytes()); + if (!getHeaders().isEmpty()) { + if (headers.containsKey(Rest.REST_HEADER_RPC_NAME) + || headers.containsKey(Rest.REST_HEADER_CURRUSERID_NAME)) { //外部request不能包含RPC的header信息 + req.setHeaders(new HashMap<>(headers)); + req.removeHeader(Rest.REST_HEADER_RPC_NAME); + req.removeHeader(Rest.REST_HEADER_CURRUSERID_NAME); + } else { + req.setHeaders(headers); + } + } + parseBody(); + req.setParams(params.isEmpty() ? null : params); + req.setRemoteAddr(getRemoteAddr()); + req.setContentType(getContentType()); + req.setPath(prefix); + String uri = this.requestURI; + if (prefix != null && !prefix.isEmpty() && uri.startsWith(prefix)) { + uri = uri.substring(prefix.length()); + } + req.setHashid(this.hashid); + req.setRequestURI(uri); + req.setSessionid(getSessionid(false)); + req.setRpc(this.rpc); + return req; + } + + protected boolean isWebSocket() { + return maybews && "Upgrade".equalsIgnoreCase(getHeader("Connection")) && "GET".equalsIgnoreCase(method); + } + + protected void setPipelineOver(boolean pipelineOver) { + this.pipelineOver = pipelineOver; + } + + protected void setKeepAlive(boolean keepAlive) { + this.keepAlive = keepAlive; + } + + protected boolean isKeepAlive() { + return this.keepAlive; + } + + protected AsyncConnection getChannel() { + return this.channel; + } + + protected int getPipelineIndex() { + return this.pipelineIndex; + } + + protected int getPipelineCount() { + return this.pipelineCount; + } + + protected ConvertType getRespConvertType() { + return this.respConvertType; + } + + protected Convert getRespConvert() { + return this.respConvert == null ? this.jsonConvert : this.respConvert; + } + + @Override + protected int readHeader(final ByteBuffer buffer, final Request last) { + ByteArray bytes = array; + if (this.readState == READ_STATE_ROUTE) { + int rs = readMethodLine(buffer); + if (rs != 0) return rs; + this.readState = READ_STATE_HEADER; + } + if (this.readState == READ_STATE_HEADER) { + if (last != null && ((HttpRequest) last).headerLength > 0) { + final HttpRequest httplast = (HttpRequest) last; + int bufremain = buffer.remaining(); + int remainhalf = httplast.headerLength - this.headerHalfLen; + if (remainhalf > bufremain) { + bytes.put(buffer); + this.headerHalfLen += bufremain; + buffer.clear(); + return 1; + } + buffer.position(buffer.position() + remainhalf); + this.contentType = httplast.contentType; + this.contentLength = httplast.contentLength; + this.host = httplast.host; + this.cookie = httplast.cookie; + this.cookies = httplast.cookies; + this.keepAlive = httplast.keepAlive; + this.maybews = httplast.maybews; + this.rpc = httplast.rpc; + this.hashid = httplast.hashid; + this.currentUserid = httplast.currentUserid; + this.frombody = httplast.frombody; + this.reqConvertType = httplast.reqConvertType; + this.reqConvert = httplast.reqConvert; + this.respConvertType = httplast.respConvertType; + this.respConvert = httplast.respConvert; + this.headerLength = httplast.headerLength; + this.headerHalfLen = httplast.headerLength; + this.headerBytes = httplast.headerBytes; + this.headerParsed = httplast.headerParsed; + this.headers.putAll(httplast.headers); + } else if (context.lazyHeaders && getmethod) { //非GET必须要读header,会有Content-Length + int rs = loadHeaderBytes(buffer); + if (rs != 0) return rs; + this.headerParsed = false; + } else { + int startpos = buffer.position(); + int rs = readHeaderLines(buffer, bytes); + if (rs != 0) { + this.headerHalfLen = bytes.length(); + return rs; + } + this.headerParsed = true; + this.headerLength = buffer.position() - startpos + this.headerHalfLen; + this.headerHalfLen = this.headerLength; + } + bytes.clear(); + this.readState = READ_STATE_BODY; + } + if (this.contentType != null && this.contentType.contains("boundary=")) this.boundary = true; + if (this.boundary) this.keepAlive = false; //文件上传必须设置keepAlive为false,因为文件过大时用户不一定会skip掉多余的数据 + if (this.readState == READ_STATE_BODY) { + if (this.contentLength > 0 && (this.contentType == null || !this.boundary)) { + if (this.contentLength > context.getMaxbody()) return -1; + bytes.put(buffer, Math.min((int) this.contentLength, buffer.remaining())); + int lr = (int) this.contentLength - bytes.length(); + if (lr == 0) { + this.readState = READ_STATE_END; + if (bytes.isEmpty()) this.bodyParsed = true; //no body data + } + return lr > 0 ? lr : 0; + } + if (buffer.hasRemaining() && (this.boundary || !this.keepAlive)) bytes.put(buffer, buffer.remaining()); //文件上传、HTTP1.0或Connection:close + this.readState = READ_STATE_END; + if (bytes.isEmpty()) this.bodyParsed = true; //no body data + } + //暂不考虑是keep-alive且存在body却没有指定Content-Length的情况 + return 0; + } + + private int loadHeaderBytes(final ByteBuffer buffer) { + ByteArray bytes = array; + int remain = buffer.remaining(); + byte b1, b2, b3, b4; + for (;;) { + if (remain-- < 4) { //bytes不存放\r\n\r\n这4个字节 + bytes.put(buffer); + buffer.clear(); + if (bytes.length() > 0) { + byte rn1 = 0, rn2 = 0, rn3 = 0; + byte b = bytes.getLastByte(); + if (b == '\r' || b == '\n') { + rn3 = b; + bytes.backCount(); + if (bytes.length() > 0) { + b = bytes.getLastByte(); + if (b == '\r' || b == '\n') { + rn2 = b; + bytes.backCount(); + if (bytes.length() > 0) { + b = bytes.getLastByte(); + if (b == '\r' || b == '\n') { + rn1 = b; + bytes.backCount(); + } + } + } + } + } + if (rn1 != 0) buffer.put(rn1); + if (rn2 != 0) buffer.put(rn2); + if (rn3 != 0) buffer.put(rn3); + } + return 1; + } + b1 = buffer.get(); + bytes.put(b1); + if (b1 == '\r') { + remain--; + b2 = buffer.get(); + bytes.put(b2); + if (b2 == '\n') { + remain--; + b3 = buffer.get(); + bytes.put(b3); + if (b3 == '\r') { + remain--; + b4 = buffer.get(); + bytes.put(b4); + if (b4 == '\n') { + this.headerBytes = Utility.append(this.headerBytes, bytes.content(), 0, bytes.length()); + this.headerLength = this.headerBytes.length; + this.headerHalfLen = this.headerLength; + bytes.clear(); + return 0; + } + } + } + } + } + } + +// @Override +// protected int readBody(ByteBuffer buffer, int length) { +// int len = buffer.remaining(); +// array.put(buffer, len); +// return len; +// } + //解析 GET /xxx HTTP/1.1 + private int readMethodLine(final ByteBuffer buffer) { + Charset charset = this.context.getCharset(); + int remain = buffer.remaining(); + int size; + ByteArray bytes = array; + //读method + if (this.method == null) { + for (;;) { + if (remain-- < 1) { + buffer.clear(); + return 1; + } + byte b = buffer.get(); + if (b == ' ') break; + bytes.put(b); + } + size = bytes.length(); + if (size == 3 && bytes.get(0) == 'G' && bytes.get(1) == 'E' && bytes.get(2) == 'T') { + this.method = KEY_GET; + this.getmethod = true; + } else if (size == 4 && bytes.get(0) == 'P' && bytes.get(1) == 'O' && bytes.get(2) == 'S' && bytes.get(3) == 'T') { + this.method = KEY_POST; + this.getmethod = false; + } else { + this.method = bytes.toString(charset); + this.getmethod = false; + } + bytes.clear(); + } + //读uri + if (this.requestURI == null) { + int qst = -1;//?的位置 + boolean decodeable = false; + for (;;) { + if (remain-- < 1) { + buffer.clear(); + return 1; + } + byte b = buffer.get(); + if (b == ' ') break; + if (b == '?' && qst < 0) { + qst = bytes.length(); + } else if (!decodeable && (b == '+' || b == '%')) { + decodeable = true; + } + bytes.put(b); + } + size = bytes.length(); + if (qst > 0) { + this.requestURI = decodeable ? toDecodeString(bytes, 0, qst, charset) : bytes.toString(0, qst, charset); + this.queryBytes = bytes.getBytes(qst + 1, size - qst - 1); + this.lastRequestURIString = null; + this.lastRequestURIBytes = null; + try { + addParameter(bytes, qst + 1, size - qst - 1); + } catch (Exception e) { + this.context.getLogger().log(Level.WARNING, "HttpRequest.addParameter error: " + bytes.toString(), e); + } + } else { + if (decodeable) { + this.requestURI = toDecodeString(bytes, 0, bytes.length(), charset); + this.lastRequestURIString = null; + this.lastRequestURIBytes = null; + } else if (context.lazyHeaders) { + byte[] lastURIBytes = lastRequestURIBytes; + if (lastURIBytes != null && lastURIBytes.length == size && bytes.equal(lastURIBytes)) { + this.requestURI = this.lastRequestURIString; + } else { + this.requestURI = bytes.toString(charset); + this.lastRequestURIString = this.requestURI; + this.lastRequestURIBytes = bytes.getBytes(); + } + } else { + this.requestURI = bytes.toString(charset); + } + this.queryBytes = EMPTY_BYTES; + } + bytes.clear(); + } + //读protocol + for (;;) { + if (remain-- < 1) { + this.params.clear(); + buffer.clear(); + return 1; + } + byte b = buffer.get(); + if (b == '\r') { + if (remain-- < 1) { + this.params.clear(); + buffer.clear(); + buffer.put((byte) '\r'); + return 1; + } + if (buffer.get() != '\n') return -1; + break; + } + bytes.put(b); + } + size = bytes.length(); + if (size == 8 && bytes.get(0) == 'H' && bytes.get(5) == '1' && bytes.get(7) == '1') { + this.protocol = KEY_HTTP_1_1; + } else { + this.protocol = bytes.toString(charset); + } + bytes.clear(); + return 0; + } + + //解析Header Connection: keep-alive + private int readHeaderLines(final ByteBuffer buffer, ByteArray bytes) { + Charset charset = this.context.getCharset(); + int remain = buffer.remaining(); + for (;;) { + bytes.clear(); + if (remain-- < 2) { + if (remain == 1) { + byte one = buffer.get(); + buffer.clear(); + buffer.put(one); + return 1; + } + buffer.clear(); + return 1; + } + remain--; + byte b1 = buffer.get(); + byte b2 = buffer.get(); + if (b1 == '\r' && b2 == '\n') return 0; + bytes.put(b1, b2); + for (;;) { // name + if (remain-- < 1) { + buffer.clear(); + buffer.put(bytes.content(), 0, bytes.length()); + return 1; + } + byte b = buffer.get(); + if (b == ':') break; + bytes.put(b); + } + String name = parseHeaderName(bytes, charset); + bytes.clear(); + boolean first = true; + int space = 0; + for (;;) { // value + if (remain-- < 1) { + buffer.clear(); + buffer.put(name.getBytes()); + buffer.put((byte) ':'); + if (space == 1) { + buffer.put((byte) ' '); + } else if (space > 0) { + for (int i = 0; i < space; i++) buffer.put((byte) ' '); + } + buffer.put(bytes.content(), 0, bytes.length()); + return 1; + } + byte b = buffer.get(); + if (b == '\r') { + if (remain-- < 1) { + buffer.clear(); + buffer.put(name.getBytes()); + buffer.put((byte) ':'); + if (space == 1) { + buffer.put((byte) ' '); + } else if (space > 0) { + for (int i = 0; i < space; i++) buffer.put((byte) ' '); + } + buffer.put(bytes.content(), 0, bytes.length()); + buffer.put((byte) '\r'); + return 1; + } + if (buffer.get() != '\n') return -1; + break; + } + if (first) { + if (b <= ' ') { + space++; + continue; + } + first = false; + } + bytes.put(b); + } + String value; + int vallen = bytes.length(); + switch (name) { + case "Content-Type": + case "content-type": + value = bytes.toString(charset); + this.contentType = value; + break; + case "Content-Length": + case "content-length": + value = bytes.toString(charset); + this.contentLength = Long.decode(value); + break; + case "Host": + case "host": + value = bytes.toString(charset); + this.host = value; + break; + case "Cookie": + case "cookie": + value = bytes.toString(charset); + if (this.cookie == null || this.cookie.isEmpty()) { + this.cookie = value; + } else { + this.cookie += ";" + value; + } + break; + case "Connection": + case "connection": + if (vallen > 0) { + if (bytes.get(0) == 'c' && vallen == 5 + && bytes.get(1) == 'l' && bytes.get(2) == 'o' + && bytes.get(3) == 's' && bytes.get(4) == 'e') { + value = "close"; + this.setKeepAlive(false); + } else if (bytes.get(0) == 'k' && vallen == 10 + && bytes.get(1) == 'e' && bytes.get(2) == 'e' + && bytes.get(3) == 'p' && bytes.get(4) == '-' + && bytes.get(5) == 'a' && bytes.get(6) == 'l' + && bytes.get(7) == 'i' && bytes.get(8) == 'v' + && bytes.get(9) == 'e') { + value = "keep-alive"; + //if (context.getAliveTimeoutSeconds() >= 0) { + this.setKeepAlive(true); + //} + } else { + value = bytes.toString(charset); + this.setKeepAlive(true); + } + } else { + value = ""; + } + headers.put("Connection", value); + break; + case "Upgrade": + case "upgrade": + value = bytes.toString(charset); + this.maybews = "websocket".equalsIgnoreCase(value); + headers.put("Upgrade", value); + break; + case "user-agent": + value = bytes.toString(charset); + headers.put("User-Agent", value); + break; + case Rest.REST_HEADER_RPC_NAME: + value = bytes.toString(charset); + this.rpc = "true".equalsIgnoreCase(value); + headers.put(name, value); + break; + case Rest.REST_HEADER_CURRUSERID_NAME: + value = bytes.toString(charset); + this.hashid = value.hashCode(); + this.currentUserid = value; + headers.put(name, value); + break; + case Rest.REST_HEADER_PARAM_FROM_BODY: + value = bytes.toString(charset); + this.frombody = "true".equalsIgnoreCase(value); + headers.put(name, value); + break; + case Rest.REST_HEADER_REQ_CONVERT_TYPE: + value = bytes.toString(charset); + reqConvertType = ConvertType.valueOf(value); + reqConvert = ConvertFactory.findConvert(reqConvertType); + headers.put(name, value); + break; + case Rest.REST_HEADER_RESP_CONVERT_TYPE: + value = bytes.toString(charset); + respConvertType = ConvertType.valueOf(value); + respConvert = ConvertFactory.findConvert(respConvertType); + headers.put(name, value); + break; + default: + value = bytes.toString(charset); + headers.put(name, value); + } + } + } + + private void parseHeader() { + if (headerParsed) return; + headerParsed = true; + if (headerBytes == null) return; + if (array.isEmpty()) { + readHeaderLines(ByteBuffer.wrap(headerBytes), array); + array.clear(); + } else { //array存有body数据 + readHeaderLines(ByteBuffer.wrap(headerBytes), new ByteArray()); + } + } + + static String parseHeaderName(ByteArray bytes, Charset charset) { + final int size = bytes.length(); + final byte[] bs = bytes.content(); + final byte first = bs[0]; + if (first == 'H' && size == 4) { //Host + if (bs[1] == 'o' && bs[2] == 's' && bs[3] == 't') return KEY_HOST; + } else if (first == 'A' && size == 6) { //Accept + if (bs[1] == 'c' && bs[2] == 'c' && bs[3] == 'e' + && bs[4] == 'p' && bs[5] == 't') return KEY_ACCEPT; + } else if (first == 'C') { + if (size == 10) { //Connection + if (bs[1] == 'o' && bs[2] == 'n' && bs[3] == 'n' + && bs[4] == 'e' && bs[5] == 'c' && bs[6] == 't' + && bs[7] == 'i' && bs[8] == 'o' && bs[9] == 'n') return KEY_CONNECTION; + } else if (size == 12) { //Content-Type + if (bs[1] == 'o' && bs[2] == 'n' && bs[3] == 't' + && bs[4] == 'e' && bs[5] == 'n' && bs[6] == 't' + && bs[7] == '-' && bs[8] == 'T' && bs[9] == 'y' + && bs[10] == 'p' && bs[11] == 'e') return KEY_CONTENT_TYPE; + } else if (size == 6) { //Cookie + if (bs[1] == 'o' && bs[2] == 'o' && bs[3] == 'k' + && bs[4] == 'i' && bs[5] == 'e') return KEY_COOKIE; + } + } + return bytes.toString(charset); + } + + @Override + protected HttpRequest copyHeader() { + if (!pipelineSameHeaders || !context.lazyHeaders) return null; + HttpRequest req = new HttpRequest(context, this.array); + req.headerLength = this.headerLength; + req.headerBytes = this.headerBytes; + req.headerParsed = this.headerParsed; + req.contentType = this.contentType; + req.contentLength = this.contentLength; + req.host = this.host; + req.cookie = this.cookie; + req.cookies = this.cookies; + req.keepAlive = this.keepAlive; + req.maybews = this.maybews; + req.rpc = this.rpc; + req.hashid = this.hashid; + req.currentUserid = this.currentUserid; + req.currentUserSupplier = this.currentUserSupplier; + req.frombody = this.frombody; + req.reqConvertType = this.reqConvertType; + req.reqConvert = this.reqConvert; + req.respConvert = this.respConvert; + req.respConvertType = this.respConvertType; + req.headers.putAll(this.headers); + return req; + } + + @Override + protected void prepare() { + this.keepAlive = true; //默认HTTP/1.1 + } + + @Override + protected void recycle() { + //header + this.headerLength = 0; + this.headerHalfLen = 0; + this.headerBytes = null; + this.headerParsed = false; + this.contentType = null; + this.contentLength = -1; + this.host = null; + this.cookie = null; + this.cookies = null; + this.maybews = false; + this.rpc = false; + this.readState = READ_STATE_ROUTE; + this.currentUserid = CURRUSERID_NIL; + this.currentUserSupplier = null; + this.frombody = false; + this.reqConvertType = null; + this.reqConvert = null; + this.respConvert = jsonConvert; + this.respConvertType = null; + this.headers.clear(); + //其他 + this.newsessionid = null; + this.method = null; + this.getmethod = false; + this.protocol = null; + this.requestURI = null; + this.queryBytes = null; + this.boundary = false; + this.bodyParsed = false; + this.moduleid = 0; + this.actionid = 0; + this.annotations = null; + this.remoteAddr = null; + this.params.clear(); + this.array.clear(); + //内部 + this.actionEntry = null; + super.recycle(); + } + + protected void skipBodyParse() { + this.bodyParsed = true; + } + + private void parseBody() { + if (this.boundary || bodyParsed) return; + bodyParsed = true; + if (this.contentType != null && this.contentType.toLowerCase().contains("x-www-form-urlencoded")) { + addParameter(array, 0, array.length()); + } + } + + private void addParameter(final ByteArray array, final int offset, final int len) { + if (len < 1) return; + Charset charset = this.context.getCharset(); + int limit = offset + len; + int keypos = array.find(offset, limit, '='); + int valpos = array.find(offset, limit, '&'); + if (keypos <= 0 || (valpos >= 0 && valpos < keypos)) { + if (valpos > 0) addParameter(array, valpos + 1, limit - valpos - 1); + return; + } + String name = toDecodeString(array, offset, keypos - offset, charset); + if (name.charAt(0) == '<') return; //内容可能是xml格式; 如: = 0) { + addParameter(array, valpos + 1, limit - valpos - 1); + } + } + + protected static String toDecodeString(ByteArray array, int offset, int len, final Charset charset) { + byte[] content = array.content(); + int start = offset; + final int end = offset + len; + boolean flag = false; //是否需要转义 + byte[] bs = content; + for (int i = offset; i < end; i++) { + if (content[i] == '+' || content[i] == '%') { + flag = true; + break; + } + } + if (flag) { + int index = 0; + bs = new byte[len]; + for (int i = offset; i < end; i++) { + switch (content[i]) { + case '+': + bs[index] = ' '; + break; + case '%': + bs[index] = (byte) ((hexBit(content[++i]) * 16 + hexBit(content[++i]))); + break; + default: + bs[index] = content[i]; + break; + } + index++; + } + start = 0; + len = index; + } + if (charset == null) return new String(bs, start, len, StandardCharsets.UTF_8); + return new String(bs, start, len, charset); + } + + private static int hexBit(byte b) { + if ('0' <= b && '9' >= b) return b - '0'; + if ('a' <= b && 'z' >= b) return b - 'a' + 10; + if ('A' <= b && 'Z' >= b) return b - 'A' + 10; + return b; + } + + @Override + protected T setProperty(String name, T value) { + return super.setProperty(name, value); + } + + @Override + @SuppressWarnings("unchecked") + protected T getProperty(String name) { + return super.getProperty(name); + } + + @Override + protected T removeProperty(String name) { + return super.removeProperty(name); + } + + /** + * 设置当前用户ID, 通常在HttpServlet.preExecute方法里设置currentUserid
    + * 数据类型只能是int、long、String、JavaBean + * + * @param 泛型 + * @param userid 用户ID + * + * @return HttpRequest + * + * @since 2.1.0 + */ + public HttpRequest setCurrentUserid(T userid) { + this.currentUserid = userid; + return this; + } + + /** + * 获取当前用户ID的int值
    + * + * @return 用户ID + * + * @since 2.4.0 + */ + @SuppressWarnings("unchecked") + public int currentIntUserid() { + if (currentUserid == CURRUSERID_NIL || currentUserid == null) return 0; + if (this.currentUserid instanceof Number) return ((Number) this.currentUserid).intValue(); + String uid = this.currentUserid.toString(); + return uid.isEmpty() ? 0 : Integer.parseInt(uid); + } + + /** + * 获取当前用户ID
    + * + * @param 数据类型只能是int、long、String、JavaBean + * @param type 类型 + * + * @return 用户ID + * + * @since 2.1.0 + */ + @SuppressWarnings("unchecked") + public T currentUserid(Class type) { + if (currentUserid == CURRUSERID_NIL || currentUserid == null) { + if (type == int.class) return (T) (Integer) (int) 0; + if (type == long.class) return (T) (Long) (long) 0; + return null; + } + if (type == int.class) { + if (this.currentUserid instanceof Number) return (T) (Integer) ((Number) this.currentUserid).intValue(); + String uid = this.currentUserid.toString(); + return (T) (Integer) (uid.isEmpty() ? 0 : Integer.parseInt(uid)); + } + if (type == long.class) { + if (this.currentUserid instanceof Number) return (T) (Long) ((Number) this.currentUserid).longValue(); + String uid = this.currentUserid.toString(); + return (T) (Long) (uid.isEmpty() ? 0L : Long.parseLong(uid)); + } + if (type == String.class) return (T) this.currentUserid.toString(); + if (this.currentUserid instanceof CharSequence) return JsonConvert.root().convertFrom(type, this.currentUserid.toString()); + return (T) this.currentUserid; + } + + /** + * 建议使用 setCurrentUserid, 通过userid从Service或缓存中获取用户信息
    + * 设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser
    + * 数据类型由@HttpUserType指定 + * + * @param supplier currentUser对象方法 + * + * @since 2.4.0 + * + * @return HttpRequest + */ + public HttpRequest setCurrentUserSupplier(Supplier supplier) { + this.currentUserSupplier = supplier; + return this; + } + + /** + * 建议使用 currentUserid, 通过userid从Service或缓存中获取用户信息
    + * 获取当前用户信息
    + * 数据类型由@HttpUserType指定 + * + * @param @HttpUserType指定的用户信息类型 + * + * @return 用户信息 + */ + @SuppressWarnings("unchecked") + public T currentUser() { + Supplier supplier = this.currentUserSupplier; + return (T) (supplier == null ? null : supplier.get()); + } + + /** + * 获取模块ID,来自@HttpServlet.moduleid() + * + * @return 模块ID + */ + @ConvertDisabled + public int getModuleid() { + return this.moduleid; + } + + /** + * 获取操作ID,来自@HttpMapping.actionid() + * + * @return 模块ID + */ + @ConvertDisabled + public int getActionid() { + return this.actionid; + } + + /** + * 获取当前操作Method上的注解集合 + * + * @return Annotation[] + */ + @ConvertDisabled + public Annotation[] getAnnotations() { + if (this.annotations == null) return new Annotation[0]; + Annotation[] newanns = new Annotation[this.annotations.length]; + System.arraycopy(this.annotations, 0, newanns, 0, newanns.length); + return newanns; + } + + /** + * 获取当前操作Method上的注解 + * + * @param 注解泛型 + * @param annotationClass 注解类型 + * + * @return Annotation + */ + public T getAnnotation(Class annotationClass) { + if (this.annotations == null) return null; + for (Annotation ann : this.annotations) { + if (ann.getClass() == annotationClass) return (T) ann; + } + return null; + } + + /** + * 获取当前操作Method上的注解集合 + * + * @param 注解泛型 + * @param annotationClass 注解类型 + * + * @return Annotation[] + */ + public T[] getAnnotationsByType(Class annotationClass) { + if (this.annotations == null) return (T[]) Array.newInstance(annotationClass, 0); + T[] news = (T[]) Array.newInstance(annotationClass, this.annotations.length); + int index = 0; + for (Annotation ann : this.annotations) { + if (ann.getClass() == annotationClass) { + news[index++] = (T) ann; + } + } + if (index < 1) return (T[]) Array.newInstance(annotationClass, 0); + return Arrays.copyOf(news, index); + } + + /** + * 获取客户端地址IP + * + * @return 地址 + */ + @ConvertDisabled + public SocketAddress getRemoteAddress() { + return this.channel == null || !this.channel.isOpen() ? null : this.channel.getRemoteAddress(); + } + + /** + * 获取客户端地址IP, 与getRemoteAddress() 的区别在于:本方法优先取header中指定为RemoteAddress名的值,没有则返回getRemoteAddress()的getHostAddress()。
    + * 本方法适用于服务前端有如nginx的代理服务器进行中转,通过 getRemoteAddress()是获取不到客户端的真实IP。 + * + * @return 地址 + */ + public String getRemoteAddr() { + if (this.remoteAddr != null) return this.remoteAddr; + parseHeader(); + if (remoteAddrHeader != null) { + String val = getHeader(remoteAddrHeader); + if (val != null) { + this.remoteAddr = val; + return val; + } + } + SocketAddress addr = getRemoteAddress(); + if (addr == null) return ""; + if (addr instanceof InetSocketAddress) { + this.remoteAddr = ((InetSocketAddress) addr).getAddress().getHostAddress(); + return this.remoteAddr; + } + this.remoteAddr = String.valueOf(addr); + return this.remoteAddr; + } + + /** + * 获取请求内容指定的编码字符串 + * + * @param charset 编码 + * + * @return 内容 + */ + public String getBody(final Charset charset) { + return charset == null ? array.toString() : array.toString(charset); + } + + /** + * 获取请求内容的UTF-8编码字符串 + * + * @return 内容 + */ + @ConvertDisabled + public String getBodyUTF8() { + return array.toString(StandardCharsets.UTF_8); + } + + /** + * 获取请求内容的JavaBean对象 + * + * @param 泛型 + * @param type 类型 + * + * @return 内容 + */ + public T getBodyJson(java.lang.reflect.Type type) { + if (array == null || array.isEmpty()) return null; + Convert convert = this.reqConvert; + if (convert == null) convert = context.getJsonConvert(); + if (type == byte[].class) return (T) array.getBytes(); + return (T) convert.convertFrom(type, array.content()); + } + + /** + * 获取请求内容的JavaBean对象 + * + * @param 泛型 + * @param convert Convert + * @param type 类型 + * + * @return 内容 + */ + public T getBodyJson(Convert convert, java.lang.reflect.Type type) { + if (array.isEmpty()) return null; + if (type == byte[].class) return (T) array.getBytes(); + return (T) convert.convertFrom(type, array.content()); + } + + /** + * 获取请求内容的byte[] + * + * @return 内容 + */ + public byte[] getBody() { + return array.length() == 0 ? null : array.getBytes(); + } + + /** + * 直接获取body对象 + * + * @return body对象 + */ + @ConvertDisabled + protected ByteArray getDirectBody() { + return array; + } + + @Override + public String toString() { + parseBody(); + return this.getClass().getSimpleName() + "{\r\n method: " + this.method + ", \r\n requestURI: " + this.requestURI + + (this.frombody ? (", \r\n frombody: " + this.frombody) : "") + + (this.reqConvertType != null ? (", \r\n reqConvertType: " + this.reqConvertType) : "") + + (this.respConvertType != null ? (", \r\n respConvertType: " + this.respConvertType) : "") + + ", \r\n currentUserid: " + (this.currentUserid == CURRUSERID_NIL ? null : this.currentUserid) + ", \r\n remoteAddr: " + this.getRemoteAddr() + + ", \r\n cookies: " + this.cookie + ", \r\n contentType: " + this.contentType + + ", \r\n protocol: " + this.protocol + ", \r\n host: " + this.host + + ", \r\n contentLength: " + this.contentLength + ", \r\n bodyLength: " + this.array.length() + + (this.boundary || this.array.isEmpty() ? "" : (", \r\n bodyContent: " + (this.respConvertType == null || this.respConvertType == ConvertType.JSON ? this.getBodyUTF8() : Arrays.toString(getBody())))) + + ", \r\n params: " + toMapString(this.params, 4) + ", \r\n header: " + toMapString(this.headers, 4) + "\r\n}"; //this.headers.toString(4) + } + + private static CharSequence toMapString(Map map, int indent) { + char[] chars = new char[indent]; + Arrays.fill(chars, ' '); + final String space = new String(chars); + StringBuilder sb = new StringBuilder(); + sb.append("{\r\n"); + for (Map.Entry en : map.entrySet()) { + sb.append(space).append(" '").append(en.getKey()).append("': '").append(en.getValue()).append("',\r\n"); + } + sb.append(space).append('}'); + return sb; + } + + /** + * 获取文件上传对象 + * + * @return 文件上传对象 + */ + @ConvertDisabled + public final MultiContext getMultiContext() { + final InputStream in = newInputStream(); + return new MultiContext(context.getCharset(), this.getContentType(), this.params, + new BufferedInputStream(in, Math.max(array.length(), 8192)) { + { + array.copyTo(this.buf); + this.count = array.length(); + } + }, null); + } + + /** + * 是否上传文件请求 + * + * @return boolean + */ + public final boolean isMultipart() { + return boundary; + } + + /** + * 获取文件上传信息列表 + * + * @return 文件上传对象集合 + * + * @throws IOException IO异常 + */ + @ConvertDisabled + public final Iterable multiParts() throws IOException { + return getMultiContext().parts(); + } + + /** + * 获取sessionid + * + * @param create 无sessionid是否自动创建 + * + * @return sessionid + */ + @ConvertDisabled + public String getSessionid(boolean create) { + String sessionid = getCookie(SESSIONID_NAME, null); + if (create && (sessionid == null || sessionid.isEmpty())) { + sessionid = context.createSessionid(); + this.newsessionid = sessionid; + } + return sessionid; + } + + /** + * 更新sessionid + * + * @return 新的sessionid值 + */ + public String changeSessionid() { + this.newsessionid = context.createSessionid(); + return newsessionid; + } + + /** + * 指定值更新sessionid + * + * @param newsessionid 新sessionid值 + * + * @return 新的sessionid值 + */ + public String changeSessionid(String newsessionid) { + this.newsessionid = newsessionid == null ? context.createSessionid() : newsessionid.trim(); + return newsessionid; + } + + /** + * 使sessionid失效 + */ + public void invalidateSession() { + this.newsessionid = ""; //为空表示删除sessionid + } + + /** + * 获取所有Cookie对象 + * + * @return cookie对象数组 + */ + public HttpCookie[] getCookies() { + parseHeader(); + if (this.cookies == null) this.cookies = parseCookies(this.cookie); + return this.cookies.length == 0 ? null : this.cookies; + } + + /** + * 获取Cookie值 + * + * @param name cookie名 + * + * @return cookie值 + */ + public String getCookie(String name) { + return getCookie(name, null); + } + + /** + * 获取Cookie值, 没有返回默认值 + * + * @param name cookie名 + * @param dfvalue 默认cookie值 + * + * @return cookie值 + */ + public String getCookie(String name, String dfvalue) { + HttpCookie[] cs = getCookies(); + if (cs == null) return dfvalue; + for (HttpCookie c : cs) { + if (name.equals(c.getName())) return c.getValue(); + } + return dfvalue; + } + + private static HttpCookie[] parseCookies(String cookiestr) { + if (cookiestr == null || cookiestr.isEmpty()) return new HttpCookie[0]; + String str = cookiestr.replaceAll("(^;)|(;$)", "").replaceAll(";+", ";"); + if (str.isEmpty()) return new HttpCookie[0]; + String[] strs = str.split(";"); + HttpCookie[] cookies = new HttpCookie[strs.length]; + for (int i = 0; i < strs.length; i++) { + String s = strs[i]; + int pos = s.indexOf('='); + String v = (pos < 0 ? "" : s.substring(pos + 1)); + if (v.indexOf('"') == 0 && v.lastIndexOf('"') == v.length() - 1) v = v.substring(1, v.length() - 1); + cookies[i] = new HttpCookie((pos < 0 ? s : s.substring(0, pos)), v); + } + return cookies; + } + + /** + * 获取协议名 http、https、ws、wss等 + * + * @return protocol + */ + public String getProtocol() { + return protocol; + } + + /** + * 获取请求方法 GET、POST等 + * + * @return method + */ + public String getMethod() { + return method; + } + + /** + * 获取Content-Type的header值 + * + * @return contentType + */ + public String getContentType() { + return contentType; + } + + /** + * 获取请求内容的长度, 为-1表示内容长度不确定 + * + * @return 内容长度 + */ + public long getContentLength() { + return contentLength; + } + + /** + * 获取Host的Header值 + * + * @return Host + */ + public String getHost() { + return host; + } + + /** + * 获取请求的URL + * + * @return 请求的URL + */ + public String getRequestURI() { + return requestURI; + } + + /** + * 获取请求参数的byte[] + * + * @return byte[] + */ + public byte[] getQueryBytes() { + return queryBytes; + } + + /** + * 截取getRequestURI最后的一个/后面的部分 + * + * @return String + */ + @ConvertDisabled + public String getRequstURILastPath() { + if (requestURI == null) return ""; + return requestURI.substring(requestURI.lastIndexOf('/') + 1); + } + + /** + * 获取请求URL最后的一个/后面的部分的short值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: short type = request.getRequstURILastPath((short)0); //type = 2 + * + * @param defvalue 默认short值 + * + * @return short值 + */ + public short getRequstURILastPath(short defvalue) { + String val = getRequstURILastPath(); + if (val.isEmpty()) return defvalue; + try { + return Short.parseShort(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL最后的一个/后面的部分的short值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: short type = request.getRequstURILastPath(16, (short)0); //type = 2 + * + * @param radix 进制数 + * @param defvalue 默认short值 + * + * @return short值 + */ + public short getRequstURILastPath(int radix, short defvalue) { + String val = getRequstURILastPath(); + if (val.isEmpty()) return defvalue; + try { + return Short.parseShort(val, radix); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL最后的一个/后面的部分的int值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: int type = request.getRequstURILastPath(0); //type = 2 + * + * @param defvalue 默认int值 + * + * @return int值 + */ + public int getRequstURILastPath(int defvalue) { + String val = getRequstURILastPath(); + try { + return val.isEmpty() ? defvalue : Integer.parseInt(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL最后的一个/后面的部分的int值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: int type = request.getRequstURILastPath(16, 0); //type = 2 + * + * @param radix 进制数 + * @param defvalue 默认int值 + * + * @return int值 + */ + public int getRequstURILastPath(int radix, int defvalue) { + String val = getRequstURILastPath(); + try { + return val.isEmpty() ? defvalue : Integer.parseInt(val, radix); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL最后的一个/后面的部分的float值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: float type = request.getRequstURILastPath(0.0f); //type = 2.0f + * + * @param defvalue 默认float值 + * + * @return float值 + */ + public float getRequstURILastPath(float defvalue) { + String val = getRequstURILastPath(); + try { + return val.isEmpty() ? defvalue : Float.parseFloat(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL最后的一个/后面的部分的int值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: long type = request.getRequstURILastPath(0L); //type = 2 + * + * @param defvalue 默认long值 + * + * @return long值 + */ + public long getRequstURILastPath(long defvalue) { + String val = getRequstURILastPath(); + try { + return val.isEmpty() ? defvalue : Long.parseLong(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL最后的一个/后面的部分的int值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: long type = request.getRequstURILastPath(16, 0L); //type = 2 + * + * @param radix 进制数 + * @param defvalue 默认long值 + * + * @return long值 + */ + public long getRequstURILastPath(int radix, long defvalue) { + String val = getRequstURILastPath(); + try { + return val.isEmpty() ? defvalue : Long.parseLong(val, radix); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL最后的一个/后面的部分的double值
    + * 例如请求URL /pipes/user/query/2
    + * 获取type参数: double type = request.getRequstURILastPath(0.0); //type = 2.0 + * + * @param defvalue 默认double值 + * + * @return double值 + */ + public double getRequstURILastPath(double defvalue) { + String val = getRequstURILastPath(); + try { + return val.isEmpty() ? defvalue : Double.parseDouble(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * + * 从prefix之后截取getRequestURI再对"/"进行分隔 + *

    + * @param prefix 前缀 + * + * @return String[] + */ + public String[] getRequstURIPaths(String prefix) { + if (requestURI == null || prefix == null) return new String[0]; + return requestURI.substring(requestURI.indexOf(prefix) + prefix.length() + (prefix.endsWith("/") ? 0 : 1)).split("/"); + } + + /** + * 获取请求URL分段中含prefix段的值
    + * 例如请求URL /pipes/user/query/name:hello
    + * 获取name参数: String name = request.getRequstURIPath("name:", "none"); + * + * @param prefix prefix段前缀 + * @param defvalue 默认值 + * + * @return prefix截断后的值 + */ + public String getRequstURIPath(String prefix, String defvalue) { + if (requestURI == null || prefix == null || prefix.isEmpty()) return defvalue; + int pos = requestURI.indexOf(prefix); + if (pos < 0) return defvalue; + String sub = requestURI.substring(pos + prefix.length()); + pos = sub.indexOf('/'); + return pos < 0 ? sub : sub.substring(0, pos); + } + + /** + * 获取请求URL分段中含prefix段的short值
    + * 例如请求URL /pipes/user/query/type:10
    + * 获取type参数: short type = request.getRequstURIPath("type:", (short)0); + * + * @param prefix prefix段前缀 + * @param defvalue 默认short值 + * + * @return short值 + */ + public short getRequstURIPath(String prefix, short defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Short.parseShort(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL分段中含prefix段的short值
    + * 例如请求URL /pipes/user/query/type:a
    + * 获取type参数: short type = request.getRequstURIPath(16, "type:", (short)0); //type = 10 + * + * @param radix 进制数 + * @param prefix prefix段前缀 + * @param defvalue 默认short值 + * + * @return short值 + */ + public short getRequstURIPath(int radix, String prefix, short defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Short.parseShort(val, radix); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL分段中含prefix段的int值
    + * 例如请求URL /pipes/user/query/offset:0/limit:50
    + * 获取offset参数: int offset = request.getRequstURIPath("offset:", 0);
    + * 获取limit参数: int limit = request.getRequstURIPath("limit:", 20);
    + * + * @param prefix prefix段前缀 + * @param defvalue 默认int值 + * + * @return int值 + */ + public int getRequstURIPath(String prefix, int defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Integer.parseInt(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL分段中含prefix段的int值
    + * 例如请求URL /pipes/user/query/offset:0/limit:50
    + * 获取offset参数: int offset = request.getRequstURIPath("offset:", 0);
    + * 获取limit参数: int limit = request.getRequstURIPath(16, "limit:", 20); // limit = 16
    + * + * @param radix 进制数 + * @param prefix prefix段前缀 + * @param defvalue 默认int值 + * + * @return int值 + */ + public int getRequstURIPath(int radix, String prefix, int defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Integer.parseInt(val, radix); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL分段中含prefix段的float值
    + * 例如请求URL /pipes/user/query/point:40.0
    + * 获取time参数: float point = request.getRequstURIPath("point:", 0.0f); + * + * @param prefix prefix段前缀 + * @param defvalue 默认float值 + * + * @return float值 + */ + public float getRequstURIPath(String prefix, float defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Float.parseFloat(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL分段中含prefix段的long值
    + * 例如请求URL /pipes/user/query/time:1453104341363/id:40
    + * 获取time参数: long time = request.getRequstURIPath("time:", 0L); + * + * @param prefix prefix段前缀 + * @param defvalue 默认long值 + * + * @return long值 + */ + public long getRequstURIPath(String prefix, long defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Long.parseLong(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL分段中含prefix段的long值
    + * 例如请求URL /pipes/user/query/time:1453104341363/id:40
    + * 获取time参数: long time = request.getRequstURIPath(16, "time:", 0L); + * + * @param radix 进制数 + * @param prefix prefix段前缀 + * @param defvalue 默认long值 + * + * @return long值 + */ + public long getRequstURIPath(int radix, String prefix, long defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Long.parseLong(val, radix); + } catch (NumberFormatException e) { + return defvalue; + } + } + + /** + * 获取请求URL分段中含prefix段的double值
    + * 例如请求URL /pipes/user/query/point:40.0
    + * 获取time参数: double point = request.getRequstURIPath("point:", 0.0); + * + * @param prefix prefix段前缀 + * @param defvalue 默认double值 + * + * @return double值 + */ + public double getRequstURIPath(String prefix, double defvalue) { + String val = getRequstURIPath(prefix, null); + try { + return val == null ? defvalue : Double.parseDouble(val); + } catch (NumberFormatException e) { + return defvalue; + } + } + + //------------------------------------------------------------------------------ + /** + * 获取请求Header总对象 + * + * @return AnyValue + */ + public Map getHeaders() { + parseHeader(); + return headers; + } + + /** + * 将请求Header转换成Map + * + * @param map Map + * + * @return Map + */ + @ConvertDisabled + public Map getHeadersToMap(Map map) { + parseHeader(); + if (map == null) map = new LinkedHashMap<>(); + final Map map0 = map; + headers.forEach((k, v) -> map0.put(k, v)); + return map0; + } + + /** + * 获取所有的header名 + * + * @return header名数组 + */ + @ConvertDisabled + public String[] getHeaderNames() { + parseHeader(); + Set names = headers.keySet(); + return names.toArray(new String[names.size()]); + } + + /** + * 获取指定的header值 + * + * @param name header名 + * + * @return header值 + */ + public String getHeader(String name) { + parseHeader(); + return headers.get(name); + } + + /** + * 获取指定的header值, 没有返回默认值 + * + * @param name header名 + * @param defaultValue 默认值 + * + * @return header值 + */ + public String getHeader(String name, String defaultValue) { + parseHeader(); + return headers.getOrDefault(name, defaultValue); + } + + /** + * 获取指定的header的json值 + * + * @param 泛型 + * @param type 反序列化的类名 + * @param name header名 + * + * @return header值 + */ + public T getJsonHeader(java.lang.reflect.Type type, String name) { + String v = getHeader(name); + return v == null || v.isEmpty() ? null : jsonConvert.convertFrom(type, v); + } + + /** + * 获取指定的header的json值 + * + * @param 泛型 + * @param convert JsonConvert对象 + * @param type 反序列化的类名 + * @param name header名 + * + * @return header值 + */ + public T getJsonHeader(JsonConvert convert, java.lang.reflect.Type type, String name) { + String v = getHeader(name); + return v == null || v.isEmpty() ? null : convert.convertFrom(type, v); + } + + /** + * 获取指定的header的boolean值, 没有返回默认boolean值 + * + * @param name header名 + * @param defaultValue 默认boolean值 + * + * @return header值 + */ + public boolean getBooleanHeader(String name, boolean defaultValue) { + //return headers.getBoolValue(name, defaultValue); + parseHeader(); + String value = headers.get(name); + return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value); + } + + /** + * 获取指定的header的short值, 没有返回默认short值 + * + * @param name header名 + * @param defaultValue 默认short值 + * + * @return header值 + */ + public short getShortHeader(String name, short defaultValue) { + //return headers.getShortValue(name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Short.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的header的short值, 没有返回默认short值 + * + * @param radix 进制数 + * @param name header名 + * @param defaultValue 默认short值 + * + * @return header值 + */ + public short getShortHeader(int radix, String name, short defaultValue) { + //return headers.getShortValue(name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的header的short值, 没有返回默认short值 + * + * @param name header名 + * @param defaultValue 默认short值 + * + * @return header值 + */ + public short getShortHeader(String name, int defaultValue) { + //return headers.getShortValue(name, (short) defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return (short) defaultValue; + try { + return Short.decode(value); + } catch (NumberFormatException e) { + return (short) defaultValue; + } + } + + /** + * 获取指定的header的short值, 没有返回默认short值 + * + * @param radix 进制数 + * @param name header名 + * @param defaultValue 默认short值 + * + * @return header值 + */ + public short getShortHeader(int radix, String name, int defaultValue) { + //return headers.getShortValue(radix, name, (short) defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return (short) defaultValue; + try { + return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); + } catch (NumberFormatException e) { + return (short) defaultValue; + } + } + + /** + * 获取指定的header的int值, 没有返回默认int值 + * + * @param name header名 + * @param defaultValue 默认int值 + * + * @return header值 + */ + public int getIntHeader(String name, int defaultValue) { + //return headers.getIntValue(name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的header的int值, 没有返回默认int值 + * + * @param radix 进制数 + * @param name header名 + * @param defaultValue 默认int值 + * + * @return header值 + */ + public int getIntHeader(int radix, String name, int defaultValue) { + //return headers.getIntValue(radix, name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的header的long值, 没有返回默认long值 + * + * @param name header名 + * @param defaultValue 默认long值 + * + * @return header值 + */ + public long getLongHeader(String name, long defaultValue) { + //return headers.getLongValue(name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Long.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的header的long值, 没有返回默认long值 + * + * @param radix 进制数 + * @param name header名 + * @param defaultValue 默认long值 + * + * @return header值 + */ + public long getLongHeader(int radix, String name, long defaultValue) { + //return headers.getLongValue(radix, name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的header的float值, 没有返回默认float值 + * + * @param name header名 + * @param defaultValue 默认float值 + * + * @return header值 + */ + public float getFloatHeader(String name, float defaultValue) { + //return headers.getFloatValue(name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的header的double值, 没有返回默认double值 + * + * @param name header名 + * @param defaultValue 默认double值 + * + * @return header值 + */ + public double getDoubleHeader(String name, double defaultValue) { + //return headers.getDoubleValue(name, defaultValue); + parseHeader(); + String value = headers.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + //------------------------------------------------------------------------------ + /** + * 获取请求参数总对象 + * + * @return AnyValue + */ + public Map getParameters() { + parseBody(); + return params; + } + + /** + * 将请求参数转换成Map + * + * @param map Map + * + * @return Map + */ + @ConvertDisabled + public Map getParametersToMap(Map map) { + if (map == null) map = new LinkedHashMap<>(); + final Map map0 = map; + getParameters().forEach((k, v) -> map0.put(k, v)); + return map0; + } + + /** + * 将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx
    + * 不会返回null,没有参数返回空字符串 + * + * + * @return String + */ + @ConvertDisabled + public String getParametersToString() { + return getParametersToString(null); + } + + /** + * 将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx
    + * 不会返回null,没有参数返回空字符串 + * + * @param prefix 拼接前缀, 如果无参数,返回的字符串不会含有拼接前缀 + * + * @return String + */ + public String getParametersToString(String prefix) { + byte[] rbs = queryBytes; + if (rbs == null || rbs.length < 1) return ""; + Charset charset = this.context.getCharset(); + String str = charset == null ? new String(rbs, StandardCharsets.UTF_8) : new String(rbs, charset); + return (prefix == null) ? str : (prefix + str); + } + + /** + * 获取所有参数名 + * + * @return 参数名数组 + */ + @ConvertDisabled + public String[] getParameterNames() { + parseBody(); + Set names = params.keySet(); + return names.toArray(new String[names.size()]); + } + + /** + * 获取指定的参数值 + * + * @param name 参数名 + * + * @return 参数值 + */ + public String getParameter(String name) { + if (this.frombody) { + if (array.isEmpty()) return null; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (String) convert.convertFrom(String.class, array.content()); + } + parseBody(); + return params.get(name); + } + + /** + * 获取指定的参数值, 没有返回默认值 + * + * @param name 参数名 + * @param defaultValue 默认值 + * + * @return 参数值 + */ + public String getParameter(String name, String defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (String) convert.convertFrom(String.class, array.content()); + } + parseBody(); + return params.getOrDefault(name, defaultValue); + } + + /** + * 获取指定的参数json值 + * + * @param 泛型 + * @param type 反序列化的类名 + * @param name 参数名 + * + * @return 参数值 + */ + public T getJsonParameter(java.lang.reflect.Type type, String name) { + if (this.frombody) { + if (array.isEmpty()) return null; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + if (type == byte[].class) return (T) array.getBytes(); + return (T) convert.convertFrom(type, array.content()); + } + String v = getParameter(name); + return v == null || v.isEmpty() ? null : jsonConvert.convertFrom(type, v); + } + + /** + * 获取指定的参数json值 + * + * @param 泛型 + * @param convert JsonConvert对象 + * @param type 反序列化的类名 + * @param name 参数名 + * + * @return 参数值 + */ + public T getJsonParameter(JsonConvert convert, java.lang.reflect.Type type, String name) { + String v = getParameter(name); + return v == null || v.isEmpty() ? null : convert.convertFrom(type, v); + } + + /** + * 获取指定的参数boolean值, 没有返回默认boolean值 + * + * @param name 参数名 + * @param defaultValue 默认boolean值 + * + * @return 参数值 + */ + public boolean getBooleanParameter(String name, boolean defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (boolean) convert.convertFrom(boolean.class, array.content()); + } + parseBody(); + String value = params.get(name); + return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value); + } + + /** + * 获取指定的参数short值, 没有返回默认short值 + * + * @param name 参数名 + * @param defaultValue 默认short值 + * + * @return 参数值 + */ + public short getShortParameter(String name, short defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (short) convert.convertFrom(short.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Short.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的参数short值, 没有返回默认short值 + * + * @param radix 进制数 + * @param name 参数名 + * @param defaultValue 默认short值 + * + * @return 参数值 + */ + public short getShortParameter(int radix, String name, short defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return (short) defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (short) convert.convertFrom(short.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的参数short值, 没有返回默认short值 + * + * @param name 参数名 + * @param defaultValue 默认short值 + * + * @return 参数值 + */ + public short getShortParameter(String name, int defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return (short) defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (short) convert.convertFrom(short.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return (short) defaultValue; + try { + return Short.decode(value); + } catch (NumberFormatException e) { + return (short) defaultValue; + } + } + + /** + * 获取指定的参数int值, 没有返回默认int值 + * + * @param name 参数名 + * @param defaultValue 默认int值 + * + * @return 参数值 + */ + public int getIntParameter(String name, int defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (int) convert.convertFrom(int.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Integer.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的参数int值, 没有返回默认int值 + * + * @param radix 进制数 + * @param name 参数名 + * @param defaultValue 默认int值 + * + * @return 参数值 + */ + public int getIntParameter(int radix, String name, int defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (int) convert.convertFrom(int.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的参数long值, 没有返回默认long值 + * + * @param name 参数名 + * @param defaultValue 默认long值 + * + * @return 参数值 + */ + public long getLongParameter(String name, long defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (long) convert.convertFrom(long.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Long.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的参数long值, 没有返回默认long值 + * + * @param radix 进制数 + * @param name 参数名 + * @param defaultValue 默认long值 + * + * @return 参数值 + */ + public long getLongParameter(int radix, String name, long defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (long) convert.convertFrom(long.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的参数float值, 没有返回默认float值 + * + * @param name 参数名 + * @param defaultValue 默认float值 + * + * @return 参数值 + */ + public float getFloatParameter(String name, float defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (float) convert.convertFrom(float.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取指定的参数double值, 没有返回默认double值 + * + * @param name 参数名 + * @param defaultValue 默认double值 + * + * @return 参数值 + */ + public double getDoubleParameter(String name, double defaultValue) { + if (this.frombody) { + if (array.isEmpty()) return defaultValue; + Convert convert = this.reqConvert; + if (convert == null) convert = jsonConvert; + return (double) convert.convertFrom(double.class, array.content()); + } + parseBody(); + String value = params.get(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * 获取翻页对象 同 getFlipper("flipper", false, 0); + * + * @return Flipper翻页对象 + */ + public org.redkale.source.Flipper getFlipper() { + return getFlipper(false, 0); + } + + /** + * 获取翻页对象 同 getFlipper("flipper", needcreate, 0); + * + * @param needcreate 无参数时是否创建新Flipper对象 + * + * @return Flipper翻页对象 + */ + public org.redkale.source.Flipper getFlipper(boolean needcreate) { + return getFlipper(needcreate, 0); + } + + /** + * 获取翻页对象 同 getFlipper("flipper", false, maxLimit); + * + * @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT + * + * @return Flipper翻页对象 + */ + public org.redkale.source.Flipper getFlipper(int maxLimit) { + return getFlipper(false, maxLimit); + } + + /** + * 获取翻页对象 同 getFlipper("flipper", needcreate, maxLimit) + * + * @param needcreate 无参数时是否创建新Flipper对象 + * @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT + * + * @return Flipper翻页对象 + */ + public org.redkale.source.Flipper getFlipper(boolean needcreate, int maxLimit) { + return getFlipper("flipper", needcreate, maxLimit); + } + + /** + * 获取翻页对象 https://redkale.org/pipes/users/list/offset:0/limit:20/sort:createtime%20ASC
    + * https://redkale.org/pipes/users/list?flipper={'offset':0,'limit':20, 'sort':'createtime ASC'}
    + * 以上两种接口都可以获取到翻页对象 + * + * + * @param name Flipper对象的参数名,默认为 "flipper" + * @param needcreate 无参数时是否创建新Flipper对象 + * @param maxLimit 最大行数, 小于1则值为Flipper.DEFAULT_LIMIT + * + * @return Flipper翻页对象 + */ + public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit) { + org.redkale.source.Flipper flipper = getJsonParameter(org.redkale.source.Flipper.class, name); + if (flipper == null) { + if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT; + int limit = getRequstURIPath("limit:", 0); + int offset = getRequstURIPath("offset:", 0); + String sort = getRequstURIPath("sort:", ""); + if (limit > 0) { + if (limit > maxLimit) limit = maxLimit; + flipper = new org.redkale.source.Flipper(limit, offset, sort); + } + } else if (flipper.getLimit() < 1 || (maxLimit > 0 && flipper.getLimit() > maxLimit)) { + flipper.setLimit(maxLimit); + } + if (flipper != null || !needcreate) return flipper; + if (maxLimit < 1) maxLimit = org.redkale.source.Flipper.DEFAULT_LIMIT; + return new org.redkale.source.Flipper(maxLimit); + } +} diff --git a/src/org/redkale/net/http/HttpResourceServlet.java b/src/main/java/org/redkale/net/http/HttpResourceServlet.java similarity index 97% rename from src/org/redkale/net/http/HttpResourceServlet.java rename to src/main/java/org/redkale/net/http/HttpResourceServlet.java index abb904472..e2ffd8917 100644 --- a/src/org/redkale/net/http/HttpResourceServlet.java +++ b/src/main/java/org/redkale/net/http/HttpResourceServlet.java @@ -1,333 +1,333 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.*; -import static java.nio.file.StandardWatchEventKinds.*; -import java.nio.file.*; -import java.util.AbstractMap.SimpleEntry; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.LongAdder; -import java.util.logging.*; -import java.util.regex.*; -import org.redkale.util.*; - -/** - * 静态资源HttpServlet - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpResourceServlet extends HttpServlet { - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - protected class WatchThread extends Thread { - - protected final File root; - - protected final WatchService watcher; - - public WatchThread(File root) throws IOException { - this.root = root; - this.setName("Redkale-HttpResourceServlet-Watch-Thread"); - this.setDaemon(true); - this.watcher = this.root.toPath().getFileSystem().newWatchService(); - } - - @Override - public void run() { - try { - final String rootstr = root.getCanonicalPath(); - while (!this.isInterrupted()) { - final WatchKey key = watcher.take(); - final Path parent = keymaps.get(key); - if (parent == null) { - key.cancel(); - continue; - } - key.pollEvents().stream().forEach((event) -> { - try { - Path path = parent.resolve((Path) event.context()); - final String uri = path.toString().substring(rootstr.length()).replace('\\', '/'); - //logger.log(Level.FINEST, "file(" + uri + ") happen " + event.kind() + " event"); - if (event.kind() == ENTRY_DELETE) { - FileEntry en = files.remove(uri); - if (en != null) en.remove(); - } else if (event.kind() == ENTRY_MODIFY) { - FileEntry en = files.get(uri); - if (en != null && en.file != null) { - long d; //等待update file完毕 - for (;;) { - d = en.file.lastModified(); - Thread.sleep(2000L); - if (d == en.file.lastModified()) break; - } - en.update(); - } - } - } catch (Exception ex) { - logger.log(Level.FINE, event.context() + " occur erroneous", ex); - } - }); - key.reset(); - } - } catch (Exception e) { - } - } - } - - protected final LongAdder cachedLength = new LongAdder(); - - //缓存总大小, 默认0 - protected long cachelimit = 0 * 1024 * 1024L; - - //最大可缓存的文件大小, 大于该值的文件将不被缓存 - protected long cachelengthmax = 1 * 1024 * 1024; - - //是否监控缓存文件的变化, 默认不监控 - protected boolean watch = false; - - protected File root = new File("./root/"); - - protected String indexHtml = "index.html"; - - protected final ConcurrentHashMap files = new ConcurrentHashMap<>(); - - protected final ConcurrentHashMap keymaps = new ConcurrentHashMap<>(); - - protected SimpleEntry[] locationRewrites; - - protected WatchThread watchThread; - - protected String[] renderSuffixs; - - @Override - public void init(HttpContext context, AnyValue config) { - if (config != null) { - String rootstr = config.getValue("webroot", "root"); - this.indexHtml = config.getValue("index", "index.html"); - if (rootstr.indexOf(':') < 0 && rootstr.indexOf('/') != 0 && System.getProperty("APP_HOME") != null) { - rootstr = new File(System.getProperty("APP_HOME"), rootstr).getPath(); - } - try { - this.root = new File(rootstr).getCanonicalFile(); - } catch (IOException ioe) { - this.root = new File(rootstr); - } - AnyValue cacheconf = config.getAnyValue("cache"); - if (cacheconf != null) { - this.cachelimit = parseLenth(cacheconf.getValue("limit"), 0 * 1024 * 1024L); - this.cachelengthmax = parseLenth(cacheconf.getValue("lengthmax"), 1 * 1024 * 1024L); - this.watch = cacheconf.getBoolValue("watch", false); - } - List> locations = new ArrayList<>(); - for (AnyValue av : config.getAnyValues("rewrite")) { - if ("location".equals(av.getValue("type"))) { - String m = av.getValue("match"); - String f = av.getValue("forward"); - if (m != null && f != null) { - locations.add(new SimpleEntry<>(Pattern.compile(m), f)); - } - } - } - this.locationRewrites = locations.isEmpty() ? null : locations.toArray(new SimpleEntry[locations.size()]); - } - if (this.cachelimit < 1) return; //不缓存不需要开启WatchThread监听 - if (this.root != null && this.watch) { - try { - this.watchThread = new WatchThread(this.root); - this.watchThread.start(); - } catch (IOException ex) { - logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " start watch-thread error", ex); - } - } - } - - @Override - public void destroy(HttpContext context, AnyValue config) { - if (this.watchThread != null) { - try { - this.watchThread.watcher.close(); - } catch (IOException ex) { - logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " close watch-thread error", ex); - } - if (this.watchThread.isAlive()) this.watchThread.interrupt(); - } - } - - public void setRoot(String rootstr) { - if (rootstr == null) return; - try { - this.root = new File(rootstr).getCanonicalFile(); - } catch (IOException ioe) { - this.root = new File(rootstr); - } - } - - public void setRoot(File file) { - if (file == null) return; - try { - this.root = file.getCanonicalFile(); - } catch (IOException ioe) { - this.root = file; - } - } - - protected static long parseLenth(String value, long defValue) { - if (value == null) return defValue; - value = value.toUpperCase().replace("B", ""); - if (value.endsWith("G")) return Long.decode(value.replace("G", "")) * 1024 * 1024 * 1024; - if (value.endsWith("M")) return Long.decode(value.replace("M", "")) * 1024 * 1024; - if (value.endsWith("K")) return Long.decode(value.replace("K", "")) * 1024; - return Long.decode(value); - } - - @Override - public void execute(HttpRequest request, HttpResponse response) throws IOException { - String uri = request.getRequestURI(); - if (uri.contains("../")) { - if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "Not found resource (404) be " + uri + ", request = " + request); - response.finish404(); - return; - } - if (locationRewrites != null) { - for (SimpleEntry entry : locationRewrites) { - Matcher matcher = entry.getKey().matcher(uri); - if (matcher.find()) { - StringBuffer sb = new StringBuffer(uri.length()); - matcher.appendReplacement(sb, entry.getValue()); - matcher.appendTail(sb); - uri = sb.toString(); - break; - } - } - } - if (uri.length() == 0 || uri.equals("/")) { - uri = this.indexHtml.indexOf('/') == 0 ? this.indexHtml : ("/" + this.indexHtml); - } - //跳过模板引擎的后缀文件 - if (renderSuffixs != null) { - String suri = uri.toLowerCase(); - for (String suffix : renderSuffixs) { - if (suri.endsWith(suffix)) { - response.finish404(); - return; - } - } - } - //System.out.println(request); - FileEntry entry; - if (watchThread == null && files.isEmpty()) { - entry = createFileEntry(uri); - } else { //有缓存 - entry = files.computeIfAbsent(uri, x -> createFileEntry(x)); - } - if (entry == null) { - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "Not found resource (404), url = " + request.getRequestURI()); - response.finish404(); - } else { - //file = null 表示资源内容在内存而不是在File中 - //file = null 时必须传 filename - response.finishFile(entry.file == null ? entry.filename : null, entry.file, entry.content); - } - } - - protected FileEntry createFileEntry(String uri) { - File rfile = new File(root, uri); - File file = rfile; - if (file.isDirectory()) file = new File(rfile, this.indexHtml); - if (file.isDirectory()) file = new File(rfile, "index.html"); - if (!file.isFile() || !file.canRead()) return null; - FileEntry en = new FileEntry(this, file); - if (watchThread == null) return en; - try { - Path p = file.getParentFile().toPath(); - keymaps.put(p.register(watchThread.watcher, ENTRY_MODIFY, ENTRY_DELETE), p); - } catch (IOException e) { - logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " watch FileEntry(" + uri + ") erroneous", e); - } - return en; - } - - protected static class FileEntry { - - protected final String filename; - - protected final File file; //如果所有资源文件打包成zip文件则file=null - - protected final HttpResourceServlet servlet; - - protected ByteArray content; - - @SuppressWarnings("OverridableMethodCallInConstructor") - public FileEntry(final HttpResourceServlet servlet, File file) { - this.servlet = servlet; - this.file = file; - this.filename = file.getName(); - update(); - } - - public FileEntry(final HttpResourceServlet servlet, String filename, ByteArray content) { - this.servlet = servlet; - this.file = null; - this.filename = filename; - this.content = content; - this.servlet.cachedLength.add(this.content.length()); - } - - public FileEntry(final HttpResourceServlet servlet, String filename, InputStream in) throws IOException { - ByteArray out = new ByteArray(); - byte[] bytes = new byte[10240]; - int pos; - while ((pos = in.read(bytes)) != -1) { - out.put(bytes, 0, pos); - } - this.servlet = servlet; - this.file = null; - this.filename = filename; - this.content = out; - this.servlet.cachedLength.add(this.content.length()); - } - - public void update() { - if (this.file == null) return; - if (this.content != null) { - this.servlet.cachedLength.add(0L - this.content.length()); - this.content = null; - } - long length = this.file.length(); - if (length > this.servlet.cachelengthmax) return; - if (this.servlet.cachedLength.longValue() + length > this.servlet.cachelimit) return; //超过缓存总容量 - try { - FileInputStream in = new FileInputStream(file); - ByteArray out = new ByteArray((int) file.length()); - byte[] bytes = new byte[10240]; - int pos; - while ((pos = in.read(bytes)) != -1) { - out.put(bytes, 0, pos); - } - in.close(); - this.content = out; - this.servlet.cachedLength.add(this.content.length()); - } catch (Exception e) { - this.servlet.logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " update FileEntry(" + file + ") erroneous", e); - } - } - - public void remove() { - if (this.content != null) this.servlet.cachedLength.add(0L - this.content.length()); - } - - public long getCachedLength() { - return this.content == null ? 0L : this.content.length(); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.*; +import static java.nio.file.StandardWatchEventKinds.*; +import java.nio.file.*; +import java.util.AbstractMap.SimpleEntry; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.LongAdder; +import java.util.logging.*; +import java.util.regex.*; +import org.redkale.util.*; + +/** + * 静态资源HttpServlet + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpResourceServlet extends HttpServlet { + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected class WatchThread extends Thread { + + protected final File root; + + protected final WatchService watcher; + + public WatchThread(File root) throws IOException { + this.root = root; + this.setName("Redkale-HttpResourceServlet-Watch-Thread"); + this.setDaemon(true); + this.watcher = this.root.toPath().getFileSystem().newWatchService(); + } + + @Override + public void run() { + try { + final String rootstr = root.getCanonicalPath(); + while (!this.isInterrupted()) { + final WatchKey key = watcher.take(); + final Path parent = keymaps.get(key); + if (parent == null) { + key.cancel(); + continue; + } + key.pollEvents().stream().forEach((event) -> { + try { + Path path = parent.resolve((Path) event.context()); + final String uri = path.toString().substring(rootstr.length()).replace('\\', '/'); + //logger.log(Level.FINEST, "file(" + uri + ") happen " + event.kind() + " event"); + if (event.kind() == ENTRY_DELETE) { + FileEntry en = files.remove(uri); + if (en != null) en.remove(); + } else if (event.kind() == ENTRY_MODIFY) { + FileEntry en = files.get(uri); + if (en != null && en.file != null) { + long d; //等待update file完毕 + for (;;) { + d = en.file.lastModified(); + Thread.sleep(2000L); + if (d == en.file.lastModified()) break; + } + en.update(); + } + } + } catch (Exception ex) { + logger.log(Level.FINE, event.context() + " occur erroneous", ex); + } + }); + key.reset(); + } + } catch (Exception e) { + } + } + } + + protected final LongAdder cachedLength = new LongAdder(); + + //缓存总大小, 默认0 + protected long cachelimit = 0 * 1024 * 1024L; + + //最大可缓存的文件大小, 大于该值的文件将不被缓存 + protected long cachelengthmax = 1 * 1024 * 1024; + + //是否监控缓存文件的变化, 默认不监控 + protected boolean watch = false; + + protected File root = new File("./root/"); + + protected String indexHtml = "index.html"; + + protected final ConcurrentHashMap files = new ConcurrentHashMap<>(); + + protected final ConcurrentHashMap keymaps = new ConcurrentHashMap<>(); + + protected SimpleEntry[] locationRewrites; + + protected WatchThread watchThread; + + protected String[] renderSuffixs; + + @Override + public void init(HttpContext context, AnyValue config) { + if (config != null) { + String rootstr = config.getValue("webroot", "root"); + this.indexHtml = config.getValue("index", "index.html"); + if (rootstr.indexOf(':') < 0 && rootstr.indexOf('/') != 0 && System.getProperty("APP_HOME") != null) { + rootstr = new File(System.getProperty("APP_HOME"), rootstr).getPath(); + } + try { + this.root = new File(rootstr).getCanonicalFile(); + } catch (IOException ioe) { + this.root = new File(rootstr); + } + AnyValue cacheconf = config.getAnyValue("cache"); + if (cacheconf != null) { + this.cachelimit = parseLenth(cacheconf.getValue("limit"), 0 * 1024 * 1024L); + this.cachelengthmax = parseLenth(cacheconf.getValue("lengthmax"), 1 * 1024 * 1024L); + this.watch = cacheconf.getBoolValue("watch", false); + } + List> locations = new ArrayList<>(); + for (AnyValue av : config.getAnyValues("rewrite")) { + if ("location".equals(av.getValue("type"))) { + String m = av.getValue("match"); + String f = av.getValue("forward"); + if (m != null && f != null) { + locations.add(new SimpleEntry<>(Pattern.compile(m), f)); + } + } + } + this.locationRewrites = locations.isEmpty() ? null : locations.toArray(new SimpleEntry[locations.size()]); + } + if (this.cachelimit < 1) return; //不缓存不需要开启WatchThread监听 + if (this.root != null && this.watch) { + try { + this.watchThread = new WatchThread(this.root); + this.watchThread.start(); + } catch (IOException ex) { + logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " start watch-thread error", ex); + } + } + } + + @Override + public void destroy(HttpContext context, AnyValue config) { + if (this.watchThread != null) { + try { + this.watchThread.watcher.close(); + } catch (IOException ex) { + logger.log(Level.WARNING, HttpResourceServlet.class.getSimpleName() + " close watch-thread error", ex); + } + if (this.watchThread.isAlive()) this.watchThread.interrupt(); + } + } + + public void setRoot(String rootstr) { + if (rootstr == null) return; + try { + this.root = new File(rootstr).getCanonicalFile(); + } catch (IOException ioe) { + this.root = new File(rootstr); + } + } + + public void setRoot(File file) { + if (file == null) return; + try { + this.root = file.getCanonicalFile(); + } catch (IOException ioe) { + this.root = file; + } + } + + protected static long parseLenth(String value, long defValue) { + if (value == null) return defValue; + value = value.toUpperCase().replace("B", ""); + if (value.endsWith("G")) return Long.decode(value.replace("G", "")) * 1024 * 1024 * 1024; + if (value.endsWith("M")) return Long.decode(value.replace("M", "")) * 1024 * 1024; + if (value.endsWith("K")) return Long.decode(value.replace("K", "")) * 1024; + return Long.decode(value); + } + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + String uri = request.getRequestURI(); + if (uri.contains("../")) { + if (logger.isLoggable(Level.FINEST)) logger.log(Level.FINEST, "Not found resource (404) be " + uri + ", request = " + request); + response.finish404(); + return; + } + if (locationRewrites != null) { + for (SimpleEntry entry : locationRewrites) { + Matcher matcher = entry.getKey().matcher(uri); + if (matcher.find()) { + StringBuffer sb = new StringBuffer(uri.length()); + matcher.appendReplacement(sb, entry.getValue()); + matcher.appendTail(sb); + uri = sb.toString(); + break; + } + } + } + if (uri.length() == 0 || uri.equals("/")) { + uri = this.indexHtml.indexOf('/') == 0 ? this.indexHtml : ("/" + this.indexHtml); + } + //跳过模板引擎的后缀文件 + if (renderSuffixs != null) { + String suri = uri.toLowerCase(); + for (String suffix : renderSuffixs) { + if (suri.endsWith(suffix)) { + response.finish404(); + return; + } + } + } + //System.out.println(request); + FileEntry entry; + if (watchThread == null && files.isEmpty()) { + entry = createFileEntry(uri); + } else { //有缓存 + entry = files.computeIfAbsent(uri, x -> createFileEntry(x)); + } + if (entry == null) { + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "Not found resource (404), url = " + request.getRequestURI()); + response.finish404(); + } else { + //file = null 表示资源内容在内存而不是在File中 + //file = null 时必须传 filename + response.finishFile(entry.file == null ? entry.filename : null, entry.file, entry.content); + } + } + + protected FileEntry createFileEntry(String uri) { + File rfile = new File(root, uri); + File file = rfile; + if (file.isDirectory()) file = new File(rfile, this.indexHtml); + if (file.isDirectory()) file = new File(rfile, "index.html"); + if (!file.isFile() || !file.canRead()) return null; + FileEntry en = new FileEntry(this, file); + if (watchThread == null) return en; + try { + Path p = file.getParentFile().toPath(); + keymaps.put(p.register(watchThread.watcher, ENTRY_MODIFY, ENTRY_DELETE), p); + } catch (IOException e) { + logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " watch FileEntry(" + uri + ") erroneous", e); + } + return en; + } + + protected static class FileEntry { + + protected final String filename; + + protected final File file; //如果所有资源文件打包成zip文件则file=null + + protected final HttpResourceServlet servlet; + + protected ByteArray content; + + @SuppressWarnings("OverridableMethodCallInConstructor") + public FileEntry(final HttpResourceServlet servlet, File file) { + this.servlet = servlet; + this.file = file; + this.filename = file.getName(); + update(); + } + + public FileEntry(final HttpResourceServlet servlet, String filename, ByteArray content) { + this.servlet = servlet; + this.file = null; + this.filename = filename; + this.content = content; + this.servlet.cachedLength.add(this.content.length()); + } + + public FileEntry(final HttpResourceServlet servlet, String filename, InputStream in) throws IOException { + ByteArray out = new ByteArray(); + byte[] bytes = new byte[10240]; + int pos; + while ((pos = in.read(bytes)) != -1) { + out.put(bytes, 0, pos); + } + this.servlet = servlet; + this.file = null; + this.filename = filename; + this.content = out; + this.servlet.cachedLength.add(this.content.length()); + } + + public void update() { + if (this.file == null) return; + if (this.content != null) { + this.servlet.cachedLength.add(0L - this.content.length()); + this.content = null; + } + long length = this.file.length(); + if (length > this.servlet.cachelengthmax) return; + if (this.servlet.cachedLength.longValue() + length > this.servlet.cachelimit) return; //超过缓存总容量 + try { + FileInputStream in = new FileInputStream(file); + ByteArray out = new ByteArray((int) file.length()); + byte[] bytes = new byte[10240]; + int pos; + while ((pos = in.read(bytes)) != -1) { + out.put(bytes, 0, pos); + } + in.close(); + this.content = out; + this.servlet.cachedLength.add(this.content.length()); + } catch (Exception e) { + this.servlet.logger.log(Level.INFO, HttpResourceServlet.class.getSimpleName() + " update FileEntry(" + file + ") erroneous", e); + } + } + + public void remove() { + if (this.content != null) this.servlet.cachedLength.add(0L - this.content.length()); + } + + public long getCachedLength() { + return this.content == null ? 0L : this.content.length(); + } + + } +} diff --git a/src/org/redkale/net/http/HttpResponse.java b/src/main/java/org/redkale/net/http/HttpResponse.java similarity index 75% rename from src/org/redkale/net/http/HttpResponse.java rename to src/main/java/org/redkale/net/http/HttpResponse.java index 2bbdbdde2..48ea6b8eb 100644 --- a/src/org/redkale/net/http/HttpResponse.java +++ b/src/main/java/org/redkale/net/http/HttpResponse.java @@ -1,1334 +1,1579 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.*; -import java.lang.reflect.Type; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.nio.file.*; -import java.time.ZoneId; -import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.*; -import java.util.logging.*; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.*; -import org.redkale.util.AnyValue.DefaultAnyValue; -import org.redkale.util.AnyValue.Entry; -import org.redkale.util.*; -import static org.redkale.util.Utility.append; - -/** - * Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。
    - * 同时提供发送json的系列接口: public void finishJson(Type type, Object obj)
    - * Redkale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpResponse extends Response { - - protected static final byte[] bytes304 = "HTTP/1.1 304 Not Modified\r\nContent-Length:0\r\n\r\n".getBytes(); - - protected static final byte[] bytes404 = "HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes(); - - protected static final byte[] status200Bytes = "HTTP/1.1 200 OK\r\n".getBytes(); - - protected static final byte[] LINE = new byte[]{'\r', '\n'}; - - protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes(); - - protected static final byte[] connectCloseBytes = "none".equalsIgnoreCase(System.getProperty("http.response.header.connection")) ? new byte[0] : "Connection: close\r\n".getBytes(); - - protected static final byte[] connectAliveBytes = "none".equalsIgnoreCase(System.getProperty("http.response.header.connection")) ? new byte[0] : "Connection: keep-alive\r\n".getBytes(); - - protected static final String contentTypeHtmlUTF8 = "text/html; charset=utf-8"; - - private static final int cacheMaxContentLength = 1000; - - private static final byte[] status200_server_live_Bytes = append(append(status200Bytes, serverNameBytes), connectAliveBytes); - - private static final byte[] status200_server_close_Bytes = append(append(status200Bytes, serverNameBytes), connectCloseBytes); - - private static final ZoneId ZONE_GMT = ZoneId.of("GMT"); - - private static final OpenOption[] options = new OpenOption[]{StandardOpenOption.READ}; - - private static final Map httpCodes = new HashMap<>(); - - private static final byte[][] contentLengthArray = new byte[cacheMaxContentLength][]; - - static { - - httpCodes.put(100, "Continue"); - httpCodes.put(101, "Switching Protocols"); - - httpCodes.put(200, "OK"); - httpCodes.put(201, "Created"); - httpCodes.put(202, "Accepted"); - httpCodes.put(203, "Non-Authoritative Information"); - httpCodes.put(204, "No Content"); - httpCodes.put(205, "Reset Content"); - httpCodes.put(206, "Partial Content"); - - httpCodes.put(300, "Multiple Choices"); - httpCodes.put(301, "Moved Permanently"); - httpCodes.put(302, "Found"); - httpCodes.put(303, "See Other"); - httpCodes.put(304, "Not Modified"); - httpCodes.put(305, "Use Proxy"); - httpCodes.put(307, "Temporary Redirect"); - - httpCodes.put(400, "Bad Request"); - httpCodes.put(401, "Unauthorized"); - httpCodes.put(402, "Payment Required"); - httpCodes.put(403, "Forbidden"); - httpCodes.put(404, "Not Found"); - httpCodes.put(405, "Method Not Allowed"); - httpCodes.put(406, "Not Acceptable"); - httpCodes.put(407, "Proxy Authentication Required"); - httpCodes.put(408, "Request Timeout"); - httpCodes.put(409, "Conflict"); - httpCodes.put(410, "Gone"); - httpCodes.put(411, "Length Required"); - httpCodes.put(412, "Precondition Failed"); - httpCodes.put(413, "Request Entity Too Large"); - httpCodes.put(414, "Request URI Too Long"); - httpCodes.put(415, "Unsupported Media Type"); - httpCodes.put(416, "Requested Range Not Satisfiable"); - httpCodes.put(417, "Expectation Failed"); - - httpCodes.put(500, "Internal Server Error"); - httpCodes.put(501, "Not Implemented"); - httpCodes.put(502, "Bad Gateway"); - httpCodes.put(503, "Service Unavailable"); - httpCodes.put(504, "Gateway Timeout"); - httpCodes.put(505, "HTTP Version Not Supported"); - - for (int i = 0; i < cacheMaxContentLength; i++) { - contentLengthArray[i] = ("Content-Length: " + i + "\r\n").getBytes(); - } - } - - private int status = 200; - - private String contentType = ""; - - private long contentLength = -1; - - private HttpCookie[] cookies; - - private boolean respHeadContainsConnection; - - private int headWritedSize = -1; //0表示跳过header,正数表示header的字节长度。 - - private BiConsumer cacheHandler; - - private BiFunction retResultHandler; - - //------------------------------------------------ - private final String plainContentType; - - private final byte[] plainContentTypeBytes; - - private final String jsonContentType; - - private final byte[] jsonContentTypeBytes; - - private final DefaultAnyValue header = new DefaultAnyValue(); - - private final String[][] defaultAddHeaders; - - private final String[][] defaultSetHeaders; - - private final boolean autoOptions; - - private final Supplier dateSupplier; - - private final HttpCookie defaultCookie; - - private final HttpRender httpRender; - - private final ByteArray headerArray = new ByteArray(); - - private final byte[][] plainLiveContentLengthArray; - - private final byte[][] jsonLiveContentLengthArray; - - private final byte[][] plainCloseContentLengthArray; - - private final byte[][] jsonCloseContentLengthArray; - - protected final CompletionHandler pipelineWriteHandler = new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - finish(); - } - - @Override - public void failed(Throwable exc, Void attachment) { - finish(true); - } - }; - - @SuppressWarnings("Convert2Lambda") - protected final ConvertBytesHandler convertHandler = new ConvertBytesHandler() { - @Override - public void completed(byte[] bs, int offset, int length, Consumer callback, A attachment) { - finish(bs, offset, length, callback, attachment); - } - }; - - public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) { - super(context, request); - this.defaultAddHeaders = config == null ? null : config.defaultAddHeaders; - this.defaultSetHeaders = config == null ? null : config.defaultSetHeaders; - this.defaultCookie = config == null ? null : config.defaultCookie; - this.autoOptions = config == null ? false : config.autoOptions; - this.dateSupplier = config == null ? null : config.dateSupplier; - this.httpRender = config == null ? null : config.httpRender; - - this.plainContentType = config == null ? "text/plain; charset=utf-8" : config.plainContentType; - this.jsonContentType = config == null ? "application/json; charset=utf-8" : config.jsonContentType; - this.plainContentTypeBytes = config == null ? ("Content-Type: " + this.plainContentType + "\r\n").getBytes() : config.plainContentTypeBytes; - this.jsonContentTypeBytes = config == null ? ("Content-Type: " + this.jsonContentType + "\r\n").getBytes() : config.jsonContentTypeBytes; - this.plainLiveContentLengthArray = config == null ? null : config.plainLiveContentLengthArray; - this.plainCloseContentLengthArray = config == null ? null : config.plainCloseContentLengthArray; - this.jsonLiveContentLengthArray = config == null ? null : config.jsonLiveContentLengthArray; - this.jsonCloseContentLengthArray = config == null ? null : config.jsonCloseContentLengthArray; - this.contentType = this.plainContentType; - } - - @Override - protected AsyncConnection removeChannel() { - return super.removeChannel(); - } - - protected AsyncConnection getChannel() { - return channel; - } - - @Override - protected void prepare() { - super.prepare(); - } - - @Override - protected boolean recycle() { - this.status = 200; - this.contentLength = -1; - this.contentType = null; - this.cookies = null; - this.headWritedSize = -1; - //this.headBuffer = null; - this.header.clear(); - this.headerArray.clear(); - this.cacheHandler = null; - this.retResultHandler = null; - this.respHeadContainsConnection = false; - return super.recycle(); - } - -// protected Supplier getBodyBufferSupplier() { -// return bodyBufferSupplier; -// } - @Override - protected void init(AsyncConnection channel) { - super.init(channel); - } - - /** - * 获取状态码对应的状态描述 - * - * @param status 状态码 - * - * @return 状态描述 - */ - protected String getHttpCode(int status) { - return httpCodes.get(status); - } - - protected HttpRequest getRequest() { - return request; - } - - protected String getHttpCode(int status, String defValue) { - String v = httpCodes.get(status); - return v == null ? defValue : v; - } - - @Override - @SuppressWarnings("unchecked") - protected void thenEvent(Servlet servlet) { - this.servlet = servlet; - } - - protected boolean isAutoOptions() { - return this.autoOptions; - } - - /** - * 增加Cookie值 - * - * @param cookies cookie - * - * @return HttpResponse - */ - public HttpResponse addCookie(HttpCookie... cookies) { - this.cookies = Utility.append(this.cookies, cookies); - return this; - } - - /** - * 增加Cookie值 - * - * @param cookies cookie - * - * @return HttpResponse - */ - public HttpResponse addCookie(Collection cookies) { - this.cookies = Utility.append(this.cookies, cookies); - return this; - } - - /** - * 创建CompletionHandler实例 - * - * @return CompletionHandler - */ - public CompletionHandler createAsyncHandler() { - return Utility.createAsyncHandler((v, a) -> { - finish(v); - }, (t, a) -> { - context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t); - finish(500, null); - }); - } - - /** - * 创建CompletionHandler子类的实例
    - * - * 传入的CompletionHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。 - * - * @param 泛型 - * @param handlerClass CompletionHandler子类 - * - * @return CompletionHandler - */ - @SuppressWarnings("unchecked") - public H createAsyncHandler(Class handlerClass) { - if (handlerClass == null || handlerClass == CompletionHandler.class) return (H) createAsyncHandler(); - return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler()); - } - - /** - * 将对象以JSON格式输出 - * - * @param obj 输出对象 - */ - public void finishJson(final Object obj) { - this.contentType = this.jsonContentType; - if (this.recycleListener != null) this.output = obj; - request.getRespConvert().convertToBytes(obj, convertHandler); - } - - /** - * 将对象以JSON格式输出 - * - * @param convert 指定的JsonConvert - * @param obj 输出对象 - */ - public void finishJson(final JsonConvert convert, final Object obj) { - this.contentType = this.jsonContentType; - if (this.recycleListener != null) this.output = obj; - convert.convertToBytes(obj, convertHandler); - } - - /** - * 将对象以JSON格式输出 - * - * @param type 指定的类型 - * @param obj 输出对象 - */ - public void finishJson(final Type type, final Object obj) { - this.contentType = this.jsonContentType; - if (this.recycleListener != null) this.output = obj; - request.getRespConvert().convertToBytes(type, obj, convertHandler); - } - - /** - * 将对象以JSON格式输出 - * - * @param convert 指定的JsonConvert - * @param type 指定的类型 - * @param obj 输出对象 - */ - public void finishJson(final JsonConvert convert, final Type type, final Object obj) { - this.contentType = this.jsonContentType; - if (this.recycleListener != null) this.output = obj; - convert.convertToBytes(type, obj, convertHandler); - } - - /** - * 将对象以JSON格式输出 - * - * @param objs 输出对象 - */ -// @Deprecated //@since 2.3.0 -// void finishJson(final Object... objs) { -// this.contentType = this.jsonContentType; -// if (this.recycleListener != null) this.output = objs; -// request.getRespConvert().convertToBytes(objs, convertHandler); -// } - /** - * 将RetResult对象以JSON格式输出 - * - * @param ret RetResult输出对象 - */ - public void finishJson(org.redkale.service.RetResult ret) { - this.contentType = this.jsonContentType; - if (this.retResultHandler != null) { - ret = this.retResultHandler.apply(this.request, ret); - } - if (this.recycleListener != null) this.output = ret; - if (ret != null && !ret.isSuccess()) { - this.header.addValue("retcode", String.valueOf(ret.getRetcode())); - this.header.addValue("retinfo", ret.getRetinfo()); - } - Convert convert = ret == null ? null : ret.convert(); - if (convert == null) convert = request.getRespConvert(); - convert.convertToBytes(ret, convertHandler); - } - - /** - * 将RetResult对象以JSON格式输出 - * - * @param convert 指定的JsonConvert - * @param ret RetResult输出对象 - */ - public void finishJson(final JsonConvert convert, org.redkale.service.RetResult ret) { - this.contentType = this.jsonContentType; - if (this.retResultHandler != null) { - ret = this.retResultHandler.apply(this.request, ret); - } - if (this.recycleListener != null) this.output = ret; - if (ret != null && !ret.isSuccess()) { - this.header.addValue("retcode", String.valueOf(ret.getRetcode())); - this.header.addValue("retinfo", ret.getRetinfo()); - } - convert.convertToBytes(ret, convertHandler); - } - - /** - * 将HttpResult对象输出 - * - * @param result HttpResult输出对象 - */ - public void finish(HttpResult result) { - finish(request.getRespConvert(), result); - } - - /** - * 将HttpResult对象输出 - * - * @param convert 指定的Convert - * @param result HttpResult输出对象 - */ - public void finish(final Convert convert, HttpResult result) { - if (result.getContentType() != null) setContentType(result.getContentType()); - addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus()); - if (result.getResult() == null) { - finish(""); - } else if (result.getResult() instanceof CharSequence) { - finish(result.getResult().toString()); - } else { - Convert cc = result.convert(); - if (cc == null) cc = convert; - finish(cc, result.getResult()); - } - } - - /** - * 将HttpScope对象输出 - * - * @param result HttpScope输出对象 - */ - public void finish(HttpScope result) { - finish(request.getRespConvert(), result); - } - - /** - * 将HttpScope对象输出 - * - * @param convert 指定的Convert - * @param result HttpScope输出对象 - */ - public void finish(final Convert convert, HttpScope result) { - if (result == null) { - finish("null"); - return; - } - if (httpRender != null) { - setContentType(contentTypeHtmlUTF8); - if (result.getHeaders() != null) addHeader(result.getHeaders()); - if (result.getCookies() != null) addCookie(result.getCookies()); - httpRender.renderTo(this.request, this, convert, result); - return; - } - finish(""); - } - - /** - * 将CompletableFuture的结果对象以JSON格式输出 - * - * @param future 输出对象的句柄 - */ - public void finishJson(final CompletableFuture future) { - finish(request.getRespConvert(), (Type) null, future); - } - - /** - * 将CompletableFuture的结果对象以JSON格式输出 - * - * @param convert 指定的JsonConvert - * @param future 输出对象的句柄 - */ - @SuppressWarnings("unchecked") - public void finishJson(final JsonConvert convert, final CompletableFuture future) { - finish(convert, (Type) null, future); - } - - /** - * 将CompletableFuture的结果对象以JSON格式输出 - * - * @param convert 指定的JsonConvert - * @param type 指定的类型 - * @param future 输出对象的句柄 - */ - @SuppressWarnings("unchecked") - public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future) { - finish(convert, type, future); - } - - /** - * 将结果对象输出 - * - * @param obj 输出对象 - */ - @SuppressWarnings("unchecked") - public void finish(final Object obj) { - finish(request.getRespConvert(), (Type) null, obj); - } - - /** - * 将结果对象输出 - * - * @param convert 指定的Convert - * @param obj 输出对象 - */ - @SuppressWarnings("unchecked") - public void finish(final Convert convert, final Object obj) { - finish(convert, (Type) null, obj); - } - - /** - * 将结果对象输出 - * - * @param convert 指定的Convert - * @param type 指定的类型 - * @param obj 输出对象 - */ - @SuppressWarnings("unchecked") - public void finish(final Convert convert, final Type type, Object obj) { - //以下if条件会被Rest类第1900行左右的地方用到 - if (obj == null) { - finish("null"); - } else if (obj instanceof CompletableFuture) { - ((CompletableFuture) obj).whenComplete((v, e) -> { - if (e != null) { - context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletableFuture", (Throwable) e); - finish(500, null); - return; - } - finish(convert, type, v); - }); - } else if (obj instanceof CharSequence) { - finish((String) obj.toString()); - } else if (obj instanceof byte[]) { - finish((byte[]) obj); - } else if (obj instanceof File) { - try { - finish((File) obj); - } catch (IOException e) { - context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, force to close channel. request = " + getRequest(), e); - finish(500, null); - } - } else if (obj instanceof org.redkale.service.RetResult) { - finishJson((org.redkale.service.RetResult) obj); - } else if (obj instanceof HttpResult) { - finish(convert, (HttpResult) obj); - } else if (obj instanceof HttpScope) { - finish(convert, (HttpScope) obj); - } else { - if (convert instanceof JsonConvert) { - this.contentType = this.jsonContentType; - } else if (convert instanceof TextConvert) { - this.contentType = this.plainContentType; - } - if (this.recycleListener != null) this.output = obj; - if (obj instanceof org.redkale.service.RetResult) { - org.redkale.service.RetResult ret = (org.redkale.service.RetResult) obj; - if (this.retResultHandler != null) { - ret = this.retResultHandler.apply(this.request, ret); - obj = ret; - } - if (!ret.isSuccess()) { - this.header.addValue("retcode", String.valueOf(ret.getRetcode())).addValue("retinfo", ret.getRetinfo()); - } - } - //this.channel == null为虚拟的HttpResponse - if (type == null) { - convert.convertToBytes(obj, convertHandler); - } else { - convert.convertToBytes(type, obj, convertHandler); - } - } - } - - /** - * 将指定字符串以响应结果输出 - * - * @param obj 输出内容 - */ - public void finish(String obj) { - finish(200, obj); - } - - /** - * 以指定响应码附带内容输出 - * - * @param status 响应码 - * @param message 输出内容 - */ - public void finish(int status, String message) { - if (isClosed()) return; - this.status = status; - if (status != 200) super.refuseAlive(); - final byte[] val = message == null ? HttpRequest.EMPTY_BYTES : (context.getCharset() == null ? Utility.encodeUTF8(message) : message.getBytes(context.getCharset())); - finish(false, null, val, 0, val.length, null, null); - } - - @Override - public void finish(boolean kill, final byte[] bs, int offset, int length) { - finish(false, null, bs, offset, length, null, null); - } - - public
    void finish(final byte[] bs, int offset, int length, Consumer callback, A attachment) { - finish(false, null, bs, offset, length, callback, attachment); - } - - /** - * 将指定byte[]按响应结果输出 - * - * @param contentType ContentType - * @param bs 输出内容 - */ - public void finish(final String contentType, final byte[] bs) { - finish(false, contentType, bs, 0, bs == null ? 0 : bs.length, null, null); - } - - /** - * 将指定byte[]按响应结果输出 - * - * @param kill kill - * @param contentType ContentType - * @param bs 输出内容 - * @param offset 偏移量 - * @param length 长度 - */ - protected void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) { - finish(kill, contentType, bs, offset, length, null, null); - } - - /** - * 将指定byte[]按响应结果输出 - * - * @param kill kill - * @param contentType ContentType - * @param bs 输出内容 - * @param offset 偏移量 - * @param length 长度 - * @param callback Consumer - * @param attachment ConvertWriter - * @param A - */ - protected void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length, Consumer callback, A attachment) { - if (isClosed()) return; //避免重复关闭 - if (this.headWritedSize < 0) { - if (contentType != null) this.contentType = contentType; - this.contentLength = length; - createHeader(); - } - ByteArray data = headerArray; - data.put(bs, offset, length); - if (callback != null) callback.accept(attachment); - if (cacheHandler != null) cacheHandler.accept(this, data.getBytes()); - - int pipelineIndex = request.getPipelineIndex(); - if (pipelineIndex > 0) { - boolean over = this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data); - if (over) { - request.setPipelineOver(true); - this.channel.flushPipelineData(this.pipelineWriteHandler); - } else { - removeChannel(); - this.responseConsumer.accept(this); - } - } else { - if (this.channel.hasPipelineData()) { - this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data); - this.channel.flushPipelineData(this.pipelineWriteHandler); - } else { - //不能用finish(boolean kill, final ByteTuple array) 否则会调this.finish - super.finish(false, data.content(), 0, data.length()); - } - } - -// ByteArray data = headerArray; -// int pipelineIndex = request.getPipelineIndex(); -// if (pipelineIndex > 0) { -// boolean over = this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data.content(), 0, data.length(), bs, offset, length); -// if (callback != null) callback.accept(attachment); -// if (cacheHandler != null) cacheHandler.accept(this, Utility.append(data.getBytes(), bs, offset, length)); -// -// if (over) { -// request.setPipelineOver(true); -// this.channel.flushPipelineData(this.pipelineWriteHandler); -// } else { -// removeChannel(); -// this.responseConsumer.accept(this); -// } -// } else { -// if (this.channel.hasPipelineData()) { -// this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data.content(), 0, data.length(), bs, offset, length); -// if (callback != null) callback.accept(attachment); -// if (cacheHandler != null) cacheHandler.accept(this, Utility.append(data.getBytes(), bs, offset, length)); -// this.channel.flushPipelineData(this.pipelineWriteHandler); -// } else { -// //不能用finish(boolean kill, final ByteTuple array) 否则会调this.finish -// super.finish(false, data.content(), 0, data.length(), bs, offset, length, callback, attachment); -// } -// } - } - - /** - * 以304状态码输出 - */ - public void finish304() { - skipHeader(); - super.finish(false, bytes304); - } - - /** - * 以404状态码输出 - */ - public void finish404() { - skipHeader(); - super.finish(false, bytes404); - } - - //Header大小 - protected void createHeader() { - if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket() - && (this.contentType == null || this.contentType == this.jsonContentType || this.contentType == this.plainContentType) - && (this.contentLength >= 0 && this.contentLength < jsonLiveContentLengthArray.length)) { - byte[][] lengthArray = this.plainLiveContentLengthArray; - if (this.request.isKeepAlive()) { - if (this.contentType == this.jsonContentType) { - lengthArray = this.jsonLiveContentLengthArray; - } - } else { - if (this.contentType == this.jsonContentType) { - lengthArray = this.jsonCloseContentLengthArray; - } else { - lengthArray = this.plainCloseContentLengthArray; - } - } - headerArray.put(lengthArray[(int) this.contentLength]); - } else { - if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()) { - if (this.request.isKeepAlive()) { - headerArray.put(status200_server_live_Bytes); - } else { - headerArray.put(status200_server_close_Bytes); - } - } else { - if (this.status == 200) { - headerArray.put(status200Bytes); - } else { - headerArray.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes()); - } - headerArray.put(serverNameBytes); - if (!this.respHeadContainsConnection) { - headerArray.put(this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes); - } - } - if (!this.request.isWebSocket()) { - if (this.contentType == this.jsonContentType) { - headerArray.put(this.jsonContentTypeBytes); - } else if (this.contentType == null || this.contentType == this.plainContentType) { - headerArray.put(this.plainContentTypeBytes); - } else { - headerArray.put(("Content-Type: " + this.contentType + "\r\n").getBytes()); - } - } - if (this.contentLength >= 0) { - if (this.contentLength < contentLengthArray.length) { - headerArray.put(contentLengthArray[(int) this.contentLength]); - } else { - headerArray.put(("Content-Length: " + this.contentLength + "\r\n").getBytes()); - } - } - } - if (dateSupplier != null) headerArray.put(dateSupplier.get()); - - if (this.defaultAddHeaders != null) { - for (String[] headers : this.defaultAddHeaders) { - if (headers.length > 3) { - String v = request.getParameter(headers[2]); - if (v != null) this.header.addValue(headers[0], v); - } else if (headers.length > 2) { - String v = request.getHeader(headers[2]); - if (v != null) this.header.addValue(headers[0], v); - } else { - this.header.addValue(headers[0], headers[1]); - } - } - } - if (this.defaultSetHeaders != null) { - for (String[] headers : this.defaultSetHeaders) { - if (headers.length > 3) { - String v = request.getParameter(headers[2]); - if (v != null) this.header.setValue(headers[0], v); - } else if (headers.length > 2) { - String v = request.getHeader(headers[2]); - if (v != null) this.header.setValue(headers[0], v); - } else { - this.header.setValue(headers[0], headers[1]); - } - } - } - for (Entry en : this.header.getStringEntrys()) { - headerArray.put((en.name + ": " + en.getValue() + "\r\n").getBytes()); - } - if (request.newsessionid != null) { - String domain = defaultCookie == null ? null : defaultCookie.getDomain(); - if (domain == null || domain.isEmpty()) { - domain = ""; - } else { - domain = "Domain=" + domain + "; "; - } - String path = defaultCookie == null ? null : defaultCookie.getPath(); - if (path == null || path.isEmpty()) path = "/"; - if (request.newsessionid.isEmpty()) { - headerArray.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=/; Max-Age=0; HttpOnly\r\n").getBytes()); - } else { - headerArray.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; " + domain + "Path=/; HttpOnly\r\n").getBytes()); - } - } - if (this.cookies != null) { - for (HttpCookie cookie : this.cookies) { - if (cookie == null) continue; - if (defaultCookie != null) { - if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain()); - if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath()); - } - headerArray.put(("Set-Cookie: " + cookieString(cookie) + "\r\n").getBytes()); - } - } - headerArray.put(LINE); - this.headWritedSize = headerArray.length(); - } - - private CharSequence cookieString(HttpCookie cookie) { - StringBuilder sb = new StringBuilder(); - sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1"); - if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain()); - if (cookie.getPath() != null) sb.append("; Path=").append(cookie.getPath()); - if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist()); - if (cookie.getMaxAge() > 0) { - sb.append("; Max-Age=").append(cookie.getMaxAge()); - sb.append("; Expires=").append(RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(ZONE_GMT).plusSeconds(cookie.getMaxAge()))); - } - if (cookie.getSecure()) sb.append("; Secure"); - if (cookie.isHttpOnly()) sb.append("; HttpOnly"); - return sb; - } - - /** - * 异步输出指定内容 - * - * @param 泛型 - * @param buffer 输出内容 - * @param handler 异步回调函数 - */ - protected void sendBody(ByteBuffer buffer, CompletionHandler handler) { - if (this.headWritedSize < 0) { - if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining(); - createHeader(); - if (buffer == null) { - super.send(headerArray, handler); - } else { - ByteBuffer headbuf = channel.pollWriteBuffer(); - headbuf.put(headerArray.content(), 0, headerArray.length()); - headbuf.flip(); - super.send(new ByteBuffer[]{headbuf, buffer}, null, handler); - } - } else { - super.send(buffer, null, handler); - } - } - - /** - * 将指定文件按响应结果输出 - * - * @param file 输出文件 - * - * @throws IOException IO异常 - */ - public void finish(File file) throws IOException { - finishFile(null, file, null); - } - - /** - * 将文件按指定文件名输出 - * - * @param filename 输出文件名 - * @param file 输出文件 - * - * @throws IOException IO异常 - */ - public void finish(final String filename, File file) throws IOException { - finishFile(filename, file, null); - } - - /** - * 将指定文件句柄或文件内容按响应结果输出,若fileBody不为null则只输出fileBody内容 - * - * @param file 输出文件 - * @param fileBody 文件内容, 没有则输出file - * - * @throws IOException IO异常 - */ - protected void finishFile(final File file, ByteArray fileBody) throws IOException { - finishFile(null, file, fileBody); - } - - /** - * 将指定文件句柄或文件内容按指定文件名输出,若fileBody不为null则只输出fileBody内容 - * file 与 fileBody 不能同时为空 - * file 与 filename 也不能同时为空 - * - * @param filename 输出文件名 - * @param file 输出文件 - * @param fileBody 文件内容, 没有则输出file - * - * @throws IOException IO异常 - */ - protected void finishFile(final String filename, final File file, ByteArray fileBody) throws IOException { - if ((file == null || !file.isFile() || !file.canRead()) && fileBody == null) { - finish404(); - return; - } - final long length = file == null ? fileBody.length() : file.length(); - final String match = request.getHeader("If-None-Match"); - final String etag = (file == null ? 0L : file.lastModified()) + "-" + length; - if (match != null && etag.equals(match)) { - //finish304(); - //return; - } - this.contentLength = length; - if (filename != null && !filename.isEmpty() && file != null) { - if (this.header.getValue("Content-Disposition") == null) { - addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); - } - } - this.contentType = MimeType.getByFilename(filename == null || filename.isEmpty() ? file.getName() : filename); - if (this.contentType == null) this.contentType = "application/octet-stream"; - String range = request.getHeader("Range"); - if (range != null && (!range.startsWith("bytes=") || range.indexOf(',') >= 0)) range = null; - long start = -1; - long len = -1; - if (range != null) { - range = range.substring("bytes=".length()); - int pos = range.indexOf('-'); - start = pos == 0 ? 0 : Integer.parseInt(range.substring(0, pos)); - long end = (pos == range.length() - 1) ? -1 : Long.parseLong(range.substring(pos + 1)); - long clen = end > 0 ? (end - start + 1) : (length - start); - this.status = 206; - addHeader("Accept-Ranges", "bytes"); - addHeader("Content-Range", "bytes " + start + "-" + (end > 0 ? end : length - 1) + "/" + length); - this.contentLength = clen; - len = end > 0 ? clen : end; - } - this.addHeader("ETag", etag); - createHeader(); - ByteArray data = headerArray; - if (fileBody == null) { - if (this.recycleListener != null) this.output = file; - finishFile(data, file, start, len); - } else { //一般HttpResourceServlet缓存file内容时fileBody不为空 - if (start >= 0) data.put(fileBody, (int) start, (int) ((len > 0) ? len : fileBody.length() - start)); - super.finish(false, data.content(), 0, data.length()); - } - } - - //offset、length 为 -1 表示输出整个文件 - private void finishFile(ByteArray headerData, File file, long offset, long length) throws IOException { - //this.channel.write(headerData, new TransferFileHandler(file, offset, length)); - final Logger logger = context.getLogger(); - this.channel.write(headerData, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - FileChannel fileChannel = null; - try { - fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ); - WritableByteChannel writeChannel = channel.writableByteChannel(); - long remain = length > 0 ? length : file.length(); - long start = offset < 0 ? 0 : offset; - while (remain > 0) { - long c = fileChannel.transferTo(start, remain, writeChannel); - start += c; - remain -= c; - } - fileChannel.close(); - finish(); - } catch (Exception e) { - if (fileChannel != null) { - try { - fileChannel.close(); - } catch (IOException ie) { - } - } - failed(e, attachment); - } - } - - @Override - public void failed(Throwable exc, Void attachment) { - if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "finishFile error", exc); - finish(true); - } - }); - } - - /** - * 跳过header的输出 - * 通常应用场景是,调用者的输出内容里已经包含了HTTP的响应头信息,因此需要调用此方法避免重复输出HTTP响应头信息。 - * - * @return HttpResponse - */ - public HttpResponse skipHeader() { - this.headWritedSize = 0; - return this; - } - - protected DefaultAnyValue duplicateHeader() { - return this.header.duplicate(); - } - - /** - * 设置Header值 - * - * @param name header名 - * @param value header值 - * - * @return HttpResponse - */ - public HttpResponse setHeader(String name, Object value) { - this.header.setValue(name, String.valueOf(value)); - if ("Connection".equalsIgnoreCase(name)) this.respHeadContainsConnection = true; - return this; - } - - /** - * 添加Header值 - * - * @param name header名 - * @param value header值 - * - * @return HttpResponse - */ - public HttpResponse addHeader(String name, Object value) { - this.header.addValue(name, String.valueOf(value)); - if ("Connection".equalsIgnoreCase(name)) this.respHeadContainsConnection = true; - return this; - } - - /** - * 添加Header值 - * - * @param map header值 - * - * @return HttpResponse - */ - public HttpResponse addHeader(Map map) { - if (map == null || map.isEmpty()) return this; - for (Map.Entry en : map.entrySet()) { - this.header.addValue(en.getKey(), String.valueOf(en.getValue())); - if (!respHeadContainsConnection && "Connection".equalsIgnoreCase(en.getKey())) { - this.respHeadContainsConnection = true; - } - } - return this; - } - - /** - * 设置状态码 - * - * @param status 状态码 - * - * @return HttpResponse - */ - public HttpResponse setStatus(int status) { - this.status = status; - return this; - } - - /** - * 获取状态码 - * - * @return 状态码 - */ - public int getStatus() { - return this.status; - } - - /** - * 获取 ContentType - * - * @return ContentType - */ - public String getContentType() { - return contentType; - } - - /** - * 设置 ContentType - * - * @param contentType ContentType - * - * @return HttpResponse - */ - public HttpResponse setContentType(String contentType) { - this.contentType = contentType; - return this; - } - - /** - * 获取内容长度 - * - * @return 内容长度 - */ - public long getContentLength() { - return contentLength; - } - - /** - * 设置内容长度 - * - * @param contentLength 内容长度 - * - * @return HttpResponse - */ - public HttpResponse setContentLength(long contentLength) { - this.contentLength = contentLength; - return this; - } - - /** - * 获取输出时的拦截器 - * - * @return 拦截器 - */ - protected BiConsumer getCacheHandler() { - return cacheHandler; - } - - /** - * 设置输出时的拦截器 - * - * @param cacheHandler 拦截器 - */ - protected void setCacheHandler(BiConsumer cacheHandler) { - this.cacheHandler = cacheHandler; - } - - /** - * 获取输出RetResult时的拦截器 - * - * @return 拦截器 - */ - protected BiFunction getRetResultHandler() { - return retResultHandler; - } - - /** - * 设置输出RetResult时的拦截器 - * - * @param retResultHandler 拦截器 - */ - public void retResultHandler(BiFunction retResultHandler) { - this.retResultHandler = retResultHandler; - } - -// protected final class TransferFileHandler implements CompletionHandler { -// -// private final File file; -// -// private final AsynchronousFileChannel filechannel; -// -// private final long max; //需要读取的字节数, -1表示读到文件结尾 -// -// private long count;//读取文件的字节数 -// -// private long readpos = 0; -// -// private boolean hdwrite = true; //写入Header -// -// private boolean read = false; -// -// public TransferFileHandler(File file) throws IOException { -// this.file = file; -// this.filechannel = AsynchronousFileChannel.open(file.toPath(), options); -// this.readpos = 0; -// this.max = file.length(); -// } -// -// public TransferFileHandler(File file, long offset, long len) throws IOException { -// this.file = file; -// this.filechannel = AsynchronousFileChannel.open(file.toPath(), options); -// this.readpos = offset <= 0 ? 0 : offset; -// this.max = len <= 0 ? file.length() : len; -// } -// -// @Override -// public void completed(Integer result, Void attachment) { -// //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", readpos = " + readpos + ", count = " + count + ", " + (hdwrite ? "正在写Header" : (read ? "准备读" : "准备写"))); -// if (result < 0 || count >= max) { -// failed(null, attachment); -// return; -// } -// if (hdwrite && attachment.hasRemaining()) { //Header还没写完 -// channel.write(attachment, attachment, this); -// return; -// } -// if (hdwrite) { -// //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------Header写入完毕, 准备读取文件."); -// hdwrite = false; -// read = true; -// result = 0; -// } -// if (read) { -// count += result; -// } else { -// readpos += result; -// } -// if (read && attachment.hasRemaining()) { //Buffer还没写完 -// channel.write(attachment, attachment, this); -// return; -// } -// -// if (read) { -// read = false; -// attachment.clear(); -// filechannel.read(attachment, readpos, attachment, this); -// } else { -// read = true; -// if (count > max) { -// attachment.limit((int) (attachment.position() + max - count)); -// } -// attachment.flip(); -// if (attachment.hasRemaining()) { -// channel.write(attachment, attachment, this); -// } else { -// failed(null, attachment); -// } -// } -// } -// -// @Override -// public void failed(Throwable exc, Void attachment) { -// finish(true); -// try { -// filechannel.close(); -// } catch (IOException e) { -// } -// } -// -// } - public static class HttpResponseConfig { - - public String plainContentType; - - public String jsonContentType; - - public byte[] plainContentTypeBytes; - - public byte[] jsonContentTypeBytes; - - public String[][] defaultAddHeaders; - - public String[][] defaultSetHeaders; - - public HttpCookie defaultCookie; - - public boolean autoOptions; - - public Supplier dateSupplier; - - public HttpRender httpRender; - - public final byte[][] plainLiveContentLengthArray = new byte[cacheMaxContentLength][]; - - public final byte[][] jsonLiveContentLengthArray = new byte[cacheMaxContentLength][]; - - public final byte[][] plainCloseContentLengthArray = new byte[cacheMaxContentLength][]; - - public final byte[][] jsonCloseContentLengthArray = new byte[cacheMaxContentLength][]; - - public HttpResponseConfig init(AnyValue config) { - if (this.plainContentTypeBytes == null) { - String plainct = plainContentType == null || plainContentType.isEmpty() ? "text/plain; charset=utf-8" : plainContentType; - String jsonct = jsonContentType == null || jsonContentType.isEmpty() ? "application/json; charset=utf-8" : jsonContentType; - this.plainContentType = plainct; - this.jsonContentType = jsonct; - this.plainContentTypeBytes = ("Content-Type: " + plainct + "\r\n").getBytes(); - this.jsonContentTypeBytes = ("Content-Type: " + jsonct + "\r\n").getBytes(); - for (int i = 0; i < cacheMaxContentLength; i++) { - byte[] lenbytes = ("Content-Length: " + i + "\r\n").getBytes(); - plainLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, plainContentTypeBytes), lenbytes); - plainCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, plainContentTypeBytes), lenbytes); - jsonLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, jsonContentTypeBytes), lenbytes); - jsonCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, jsonContentTypeBytes), lenbytes); - } - } - return this; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.*; +import java.lang.reflect.Type; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.nio.file.*; +import java.time.ZoneId; +import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.logging.*; +import org.redkale.convert.*; +import org.redkale.convert.json.*; +import org.redkale.net.*; +import org.redkale.util.AnyValue.DefaultAnyValue; +import org.redkale.util.AnyValue.Entry; +import org.redkale.util.*; +import static org.redkale.util.Utility.append; + +/** + * Http响应包 与javax.servlet.http.HttpServletResponse 基本类似。
    + * 同时提供发送json的系列接口: public void finishJson(Type type, Object obj)
    + * Redkale提倡http+json的接口风格, 所以主要输出的数据格式为json, 同时提供异步接口。
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpResponse extends Response { + + protected static final byte[] bytes304 = "HTTP/1.1 304 Not Modified\r\nContent-Length:0\r\n\r\n".getBytes(); + + protected static final byte[] bytes404 = "HTTP/1.1 404 Not Found\r\nContent-Length:0\r\n\r\n".getBytes(); + + protected static final byte[] bytes500 = "HTTP/1.1 500 Internal Server Error\r\nContent-Length:0\r\n\r\n".getBytes(); + + protected static final byte[] bytes504 = "HTTP/1.1 504 Gateway Timeout\r\nContent-Length:0\r\n\r\n".getBytes(); + + protected static final byte[] status200Bytes = "HTTP/1.1 200 OK\r\n".getBytes(); + + protected static final byte[] LINE = new byte[]{'\r', '\n'}; + + protected static final byte[] serverNameBytes = ("Server: " + System.getProperty("redkale.http.response.header.server", "redkale" + "/" + Redkale.getDotedVersion()) + "\r\n").getBytes(); + + protected static final byte[] connectCloseBytes = "none".equalsIgnoreCase(System.getProperty("redkale.http.response.header.connection")) ? new byte[0] : "Connection: close\r\n".getBytes(); + + protected static final byte[] connectAliveBytes = "none".equalsIgnoreCase(System.getProperty("redkale.http.response.header.connection")) ? new byte[0] : "Connection: keep-alive\r\n".getBytes(); + + protected static final String contentTypeHtmlUTF8 = "text/html; charset=utf-8"; + + private static final int cacheMaxContentLength = 1000; + + private static final byte[] status200_server_live_Bytes = append(append(status200Bytes, serverNameBytes), connectAliveBytes); + + private static final byte[] status200_server_close_Bytes = append(append(status200Bytes, serverNameBytes), connectCloseBytes); + + private static final ZoneId ZONE_GMT = ZoneId.of("GMT"); + + private static final OpenOption[] options = new OpenOption[]{StandardOpenOption.READ}; + + private static final Map httpCodes = new HashMap<>(); + + private static final byte[][] contentLengthArray = new byte[cacheMaxContentLength][]; + + private static final JsonConvert jsonRootConvert = JsonConvert.root(); + + static { + + httpCodes.put(100, "Continue"); + httpCodes.put(101, "Switching Protocols"); + + httpCodes.put(200, "OK"); + httpCodes.put(201, "Created"); + httpCodes.put(202, "Accepted"); + httpCodes.put(203, "Non-Authoritative Information"); + httpCodes.put(204, "No Content"); + httpCodes.put(205, "Reset Content"); + httpCodes.put(206, "Partial Content"); + + httpCodes.put(300, "Multiple Choices"); + httpCodes.put(301, "Moved Permanently"); + httpCodes.put(302, "Found"); + httpCodes.put(303, "See Other"); + httpCodes.put(304, "Not Modified"); + httpCodes.put(305, "Use Proxy"); + httpCodes.put(307, "Temporary Redirect"); + + httpCodes.put(400, "Bad Request"); + httpCodes.put(401, "Unauthorized"); + httpCodes.put(402, "Payment Required"); + httpCodes.put(403, "Forbidden"); + httpCodes.put(404, "Not Found"); + httpCodes.put(405, "Method Not Allowed"); + httpCodes.put(406, "Not Acceptable"); + httpCodes.put(407, "Proxy Authentication Required"); + httpCodes.put(408, "Request Timeout"); + httpCodes.put(409, "Conflict"); + httpCodes.put(410, "Gone"); + httpCodes.put(411, "Length Required"); + httpCodes.put(412, "Precondition Failed"); + httpCodes.put(413, "Request Entity Too Large"); + httpCodes.put(414, "Request URI Too Long"); + httpCodes.put(415, "Unsupported Media Type"); + httpCodes.put(416, "Requested Range Not Satisfiable"); + httpCodes.put(417, "Expectation Failed"); + + httpCodes.put(500, "Internal Server Error"); + httpCodes.put(501, "Not Implemented"); + httpCodes.put(502, "Bad Gateway"); + httpCodes.put(503, "Service Unavailable"); + httpCodes.put(504, "Gateway Timeout"); + httpCodes.put(505, "HTTP Version Not Supported"); + + for (int i = 0; i < cacheMaxContentLength; i++) { + contentLengthArray[i] = ("Content-Length: " + i + "\r\n").getBytes(); + } + } + + private int status = 200; + + private String contentType = ""; + + private long contentLength = -1; + + private HttpCookie[] cookies; + + private boolean respHeadContainsConnection; + + private int headWritedSize = -1; //0表示跳过header,正数表示header的字节长度。 + + private BiConsumer cacheHandler; + + private BiFunction retResultHandler; + + //------------------------------------------------ + private final String plainContentType; + + private final byte[] plainContentTypeBytes; + + private final String jsonContentType; + + private final byte[] jsonContentTypeBytes; + + private final DefaultAnyValue header = new DefaultAnyValue(); + + private final String[][] defaultAddHeaders; + + private final String[][] defaultSetHeaders; + + private final boolean autoOptions; + + private final Supplier dateSupplier; + + private final HttpCookie defaultCookie; + + private final HttpRender httpRender; + + private final ByteArray headerArray = new ByteArray(); + + private final byte[][] plainLiveContentLengthArray; + + private final byte[][] jsonLiveContentLengthArray; + + private final byte[][] plainCloseContentLengthArray; + + private final byte[][] jsonCloseContentLengthArray; + + private final JsonBytesWriter jsonWriter = new JsonBytesWriter(); + + protected final CompletionHandler pipelineWriteHandler = new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + finish(); + } + + @Override + public void failed(Throwable exc, Void attachment) { + finish(true); + } + }; + + @SuppressWarnings("Convert2Lambda") + protected final ConvertBytesHandler convertHandler = new ConvertBytesHandler() { + @Override + public void completed(byte[] bs, int offset, int length, Consumer callback, A attachment) { + finish(bs, offset, length, callback, attachment); + } + }; + + public HttpResponse(HttpContext context, HttpRequest request, HttpResponseConfig config) { + super(context, request); + this.defaultAddHeaders = config == null ? null : config.defaultAddHeaders; + this.defaultSetHeaders = config == null ? null : config.defaultSetHeaders; + this.defaultCookie = config == null ? null : config.defaultCookie; + this.autoOptions = config == null ? false : config.autoOptions; + this.dateSupplier = config == null ? null : config.dateSupplier; + this.httpRender = config == null ? null : config.httpRender; + + this.plainContentType = config == null ? "text/plain; charset=utf-8" : config.plainContentType; + this.jsonContentType = config == null ? "application/json; charset=utf-8" : config.jsonContentType; + this.plainContentTypeBytes = config == null ? ("Content-Type: " + this.plainContentType + "\r\n").getBytes() : config.plainContentTypeBytes; + this.jsonContentTypeBytes = config == null ? ("Content-Type: " + this.jsonContentType + "\r\n").getBytes() : config.jsonContentTypeBytes; + this.plainLiveContentLengthArray = config == null ? null : config.plainLiveContentLengthArray; + this.plainCloseContentLengthArray = config == null ? null : config.plainCloseContentLengthArray; + this.jsonLiveContentLengthArray = config == null ? null : config.jsonLiveContentLengthArray; + this.jsonCloseContentLengthArray = config == null ? null : config.jsonCloseContentLengthArray; + this.contentType = this.plainContentType; + } + + @Override + protected AsyncConnection removeChannel() { + return super.removeChannel(); + } + + protected AsyncConnection getChannel() { + return channel; + } + + @Override + protected void prepare() { + super.prepare(); + } + + @Override + protected boolean recycle() { + this.status = 200; + this.contentLength = -1; + this.contentType = null; + this.cookies = null; + this.headWritedSize = -1; + //this.headBuffer = null; + this.header.clear(); + this.headerArray.clear(); + this.cacheHandler = null; + this.retResultHandler = null; + this.respHeadContainsConnection = false; + this.jsonWriter.recycle(); + return super.recycle(); + } + +// protected Supplier getBodyBufferSupplier() { +// return bodyBufferSupplier; +// } + @Override + protected void init(AsyncConnection channel) { + super.init(channel); + } + + /** + * 获取状态码对应的状态描述 + * + * @param status 状态码 + * + * @return 状态描述 + */ + protected String getHttpCode(int status) { + return httpCodes.get(status); + } + + protected HttpRequest getRequest() { + return request; + } + + protected String getHttpCode(int status, String defValue) { + String v = httpCodes.get(status); + return v == null ? defValue : v; + } + + @Override + @SuppressWarnings("unchecked") + protected void thenEvent(Servlet servlet) { + this.servlet = servlet; + } + + protected boolean isAutoOptions() { + return this.autoOptions; + } + + /** + * 增加Cookie值 + * + * @param cookies cookie + * + * @return HttpResponse + */ + public HttpResponse addCookie(HttpCookie... cookies) { + this.cookies = Utility.append(this.cookies, cookies); + return this; + } + + /** + * 增加Cookie值 + * + * @param cookies cookie + * + * @return HttpResponse + */ + public HttpResponse addCookie(Collection cookies) { + this.cookies = Utility.append(this.cookies, cookies); + return this; + } + + /** + * 创建CompletionHandler实例 + * + * @return CompletionHandler + */ + public CompletionHandler createAsyncHandler() { + return Utility.createAsyncHandler((v, a) -> { + finish(v); + }, (t, a) -> { + context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletionHandler", (Throwable) t); + finish(500, null); + }); + } + + /** + * 创建CompletionHandler子类的实例
    + * + * 传入的CompletionHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。 + * + * @param 泛型 + * @param handlerClass CompletionHandler子类 + * + * @return CompletionHandler + */ + @SuppressWarnings("unchecked") + public H createAsyncHandler(Class handlerClass) { + if (handlerClass == null || handlerClass == CompletionHandler.class) return (H) createAsyncHandler(); + return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler()); + } + + /** + * 将对象以JSON格式输出 + * + * @param obj 输出对象 + */ + public void finishJson(final Object obj) { + this.contentType = this.jsonContentType; + if (this.recycleListener != null) this.output = obj; + Convert c = request.getRespConvert(); + if (c == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + c.convertTo(writer.clear(), obj); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + c.convertToBytes(obj, convertHandler); + } + } + + /** + * 将对象以JSON格式输出 + * + * @param convert 指定的JsonConvert + * @param obj 输出对象 + */ + public void finishJson(final JsonConvert convert, final Object obj) { + this.contentType = this.jsonContentType; + if (this.recycleListener != null) this.output = obj; + if (convert == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + convert.convertTo(writer.clear(), obj); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + convert.convertToBytes(obj, convertHandler); + } + } + + /** + * 将对象以JSON格式输出 + * + * @param type 指定的类型 + * @param obj 输出对象 + */ + public void finishJson(final Type type, final Object obj) { + this.contentType = this.jsonContentType; + if (this.recycleListener != null) this.output = obj; + Convert c = request.getRespConvert(); + if (c == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + c.convertTo(writer.clear(), type, obj); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + c.convertToBytes(type, obj, convertHandler); + } + } + + /** + * 将对象以JSON格式输出 + * + * @param convert 指定的JsonConvert + * @param type 指定的类型 + * @param obj 输出对象 + */ + public void finishJson(final JsonConvert convert, final Type type, final Object obj) { + this.contentType = this.jsonContentType; + if (this.recycleListener != null) this.output = obj; + if (convert == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + convert.convertTo(writer.clear(), type, obj); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + convert.convertToBytes(type, obj, convertHandler); + } + } + + /** + * 将对象以JSON格式输出 + * + * @param objs 输出对象 + */ +// @Deprecated //@since 2.3.0 +// void finishJson(final Object... objs) { +// this.contentType = this.jsonContentType; +// if (this.recycleListener != null) this.output = objs; +// request.getRespConvert().convertToBytes(objs, convertHandler); +// } + /** + * 将RetResult对象以JSON格式输出 + * + * @param type 指定的RetResult泛型类型 + * @param ret RetResult输出对象 + */ + @Deprecated //@since 2.5.0 + public void finishJson(Type type, org.redkale.service.RetResult ret) { + this.contentType = this.jsonContentType; + if (this.retResultHandler != null) { + ret = this.retResultHandler.apply(this.request, ret); + } + if (this.recycleListener != null) this.output = ret; + if (ret != null && !ret.isSuccess()) { + this.header.addValue("retcode", String.valueOf(ret.getRetcode())); + this.header.addValue("retinfo", ret.getRetinfo()); + } + Convert convert = ret == null ? null : ret.convert(); + if (convert == null) convert = request.getRespConvert(); + if (convert == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + convert.convertTo(writer.clear(), type, ret); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + convert.convertToBytes(type, ret, convertHandler); + } + } + + /** + * 将RetResult对象以JSON格式输出 + * + * @param convert 指定的JsonConvert + * @param type 指定的RetResult泛型类型 + * @param ret RetResult输出对象 + */ + @Deprecated //@since 2.5.0 + public void finishJson(final JsonConvert convert, Type type, org.redkale.service.RetResult ret) { + this.contentType = this.jsonContentType; + if (this.retResultHandler != null) { + ret = this.retResultHandler.apply(this.request, ret); + } + if (this.recycleListener != null) this.output = ret; + if (ret != null && !ret.isSuccess()) { + this.header.addValue("retcode", String.valueOf(ret.getRetcode())); + this.header.addValue("retinfo", ret.getRetinfo()); + } + if (convert == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + convert.convertTo(writer.clear(), type, ret); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + convert.convertToBytes(type, ret, convertHandler); + } + } + + /** + * 将CompletableFuture的结果对象以JSON格式输出 + * + * @param convert 指定的JsonConvert + * @param valueType 指定CompletableFuture.value的泛型类型 + * @param future 输出对象的句柄 + */ + @Deprecated //@since 2.5.0 + @SuppressWarnings("unchecked") + public void finishJson(final JsonConvert convert, final Type valueType, final CompletableFuture future) { + finish(convert, valueType, future); + } + + /** + * 将RetResult对象输出 + * + * @param type 指定的RetResult泛型类型 + * @param ret RetResult输出对象 + */ + @SuppressWarnings("null") + public void finish(Type type, org.redkale.service.RetResult ret) { + if (this.retResultHandler != null) { + ret = this.retResultHandler.apply(this.request, ret); + } + if (this.recycleListener != null) this.output = ret; + if (ret != null && !ret.isSuccess()) { + this.header.addValue("retcode", String.valueOf(ret.getRetcode())); + this.header.addValue("retinfo", ret.getRetinfo()); + } + Convert cc = ret == null ? null : ret.convert(); + if (cc == null) cc = request.getRespConvert(); + if (cc instanceof JsonConvert) { + this.contentType = this.jsonContentType; + } else if (cc instanceof TextConvert) { + this.contentType = this.plainContentType; + } + if (cc == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + cc.convertTo(writer.clear(), type, ret); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + cc.convertToBytes(type, ret, convertHandler); + } + } + + /** + * 将RetResult对象输出 + * + * @param convert 指定的Convert + * @param type 指定的RetResult泛型类型 + * @param ret RetResult输出对象 + */ + @SuppressWarnings("null") + public void finish(final Convert convert, Type type, org.redkale.service.RetResult ret) { + if (this.retResultHandler != null) { + ret = this.retResultHandler.apply(this.request, ret); + } + if (this.recycleListener != null) this.output = ret; + if (ret != null && !ret.isSuccess()) { + this.header.addValue("retcode", String.valueOf(ret.getRetcode())); + this.header.addValue("retinfo", ret.getRetinfo()); + } + Convert cc = convert; + if (cc == null && ret != null) cc = ret.convert(); + if (cc == null) cc = request.getRespConvert(); + if (cc instanceof JsonConvert) { + this.contentType = this.jsonContentType; + } else if (cc instanceof TextConvert) { + this.contentType = this.plainContentType; + } + if (cc == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + cc.convertTo(writer.clear(), type, ret); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + cc.convertToBytes(type, ret, convertHandler); + } + } + + /** + * 将HttpResult对象输出 + * + * @param resultType HttpResult.result的泛型类型 + * @param result HttpResult输出对象 + */ + public void finish(Type resultType, HttpResult result) { + finish(request.getRespConvert(), resultType, result); + } + + /** + * 将HttpResult对象输出 + * + * @param convert 指定的Convert + * @param resultType HttpResult.result的泛型类型 + * @param result HttpResult输出对象 + */ + public void finish(final Convert convert, Type resultType, HttpResult result) { + if (result.getContentType() != null) setContentType(result.getContentType()); + addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus()); + Object val = result.getResult(); + if (val == null) { + finish(""); + } else if (val instanceof CharSequence) { + finish(val.toString()); + } else { + Convert cc = result.convert(); + if (cc == null) cc = convert; + finish(cc, resultType, val); + } + } + + /** + * 将CompletionStage对象输出 + * + * @param valueType CompletionFuture.value的泛型类型 + * @param future CompletionStage输出对象 + */ + public void finish(Type valueType, CompletionStage future) { + finish(request.getRespConvert(), valueType, future); + } + + /** + * 将CompletionStage对象输出 + * + * @param convert 指定的Convert + * @param valueType CompletionFuture.value的泛型类型 + * @param future CompletionStage输出对象 + */ + public void finish(final Convert convert, Type valueType, CompletionStage future) { + future.whenComplete((v, e) -> { + if (e != null) { + context.getLogger().log(Level.WARNING, "Servlet occur, force to close channel. request = " + request + ", result is CompletionStage", (Throwable) e); + if (e instanceof TimeoutException) { + finish504(); + } else { + finish500(); + } + return; + } + finish(convert, valueType, v); + }); + } + + /** + * 将Flow.Publisher对象输出 + * + * @param 泛型 + * @param valueType Publisher的泛型类型 + * @param publisher Publisher输出对象 + */ + public void finishPublisher(Type valueType, Flow.Publisher publisher) { + finishPublisher(request.getRespConvert(), valueType, publisher); + } + + /** + * 将Flow.Publisher对象输出 + * + * @param 泛型 + * @param convert 指定的Convert + * @param valueType Publisher的泛型类型 + * @param publisher Publisher输出对象 + */ + public void finishPublisher(final Convert convert, Type valueType, Flow.Publisher publisher) { + finish(convert, valueType, (CompletionStage) Flows.createMonoFuture(publisher)); + } + + /** + * 将第三方类Flow.Publisher对象(如: Mono/Flux)输出 + * + * @param valueType Publisher的泛型类型 + * @param publisher Publisher输出对象 + */ + public void finishPublisher(Type valueType, Object publisher) { + finishPublisher(request.getRespConvert(), valueType, publisher); + } + + /** + * 将第三方类Flow.Publisher对象(如: Mono/Flux)输出 + * + * @param convert 指定的Convert + * @param valueType Publisher的泛型类型 + * @param publisher Publisher输出对象 + */ + public void finishPublisher(final Convert convert, Type valueType, Object publisher) { + finish(convert, valueType, (CompletionStage) Flows.maybePublisherToFuture(publisher)); + } + + /** + * 将HttpScope对象输出 + * + * @param result HttpScope输出对象 + */ + public void finish(HttpScope result) { + finish(request.getRespConvert(), result); + } + + /** + * 将HttpScope对象输出 + * + * @param convert 指定的Convert + * @param result HttpScope输出对象 + */ + public void finish(final Convert convert, HttpScope result) { + if (result == null) { + finish("null"); + return; + } + if (httpRender != null) { + setContentType(contentTypeHtmlUTF8); + if (result.getHeaders() != null) addHeader(result.getHeaders()); + if (result.getCookies() != null) addCookie(result.getCookies()); + httpRender.renderTo(this.request, this, convert, result); + return; + } + finish(""); + } + + /** + * 将结果对象输出 + * + * @param obj 输出对象 + */ + @SuppressWarnings("unchecked") + public void finish(final Object obj) { + finish(request.getRespConvert(), (Type) null, obj); + } + + /** + * 将结果对象输出 + * + * @param convert 指定的Convert + * @param obj 输出对象 + */ + @SuppressWarnings("unchecked") + public void finish(final Convert convert, final Object obj) { + finish(convert, (Type) null, obj); + } + + /** + * 将结果对象输出 + * + * @param type 指定的类型, 不一定是obj的数据类型,必然obj为CompletableFuture, type应该为Future的元素类型 + * @param obj 输出对象 + */ + @SuppressWarnings("unchecked") + public void finish(final Type type, Object obj) { + finish((Convert) null, type, obj); + } + + /** + * 将结果对象输出 + * + * @param convert 指定的Convert + * @param type 指定的类型, 不一定是obj的数据类型,必然obj为CompletionStage, type应该为Future的元素类型 + * @param obj 输出对象 + */ + @SuppressWarnings({"unchecked", "null"}) + public void finish(final Convert convert, final Type type, Object obj) { + Object val = obj; + //以下if条件会被Rest类第2440行左右的地方用到 + if (val == null) { + Convert cc = convert; + if (cc == null) cc = request.getRespConvert(); + if (cc instanceof JsonConvert) { + this.contentType = this.jsonContentType; + } else if (cc instanceof TextConvert) { + this.contentType = this.plainContentType; + } + finish("null"); + } else if (val instanceof CompletionStage) { + finish(convert, val == obj ? type : null, (CompletionStage) val); + } else if (val instanceof CharSequence) { + finish((String) val.toString()); + } else if (val instanceof byte[]) { + finish((byte[]) val); + } else if (val instanceof File) { + try { + finish((File) val); + } catch (IOException e) { + context.getLogger().log(Level.WARNING, "HttpServlet finish File occur, force to close channel. request = " + getRequest(), e); + finish(500, null); + } + } else if (val instanceof org.redkale.service.RetResult) { + finish(convert, type, (org.redkale.service.RetResult) val); + } else if (val instanceof HttpResult) { + finish(convert, type, (HttpResult) val); + } else if (val instanceof HttpScope) { + finish(convert, (HttpScope) val); + } else { + Convert cc = convert; + if (cc == null) cc = request.getRespConvert(); + if (cc instanceof JsonConvert) { + this.contentType = this.jsonContentType; + } else if (cc instanceof TextConvert) { + this.contentType = this.plainContentType; + } + if (this.recycleListener != null) this.output = val; + //this.channel == null为虚拟的HttpResponse + if (type == null) { + if (cc == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + cc.convertTo(writer.clear(), val); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + cc.convertToBytes(val, convertHandler); + } + } else { + if (cc == jsonRootConvert) { + JsonBytesWriter writer = jsonWriter; + cc.convertTo(writer.clear(), type, val); + finish(false, (String) null, writer.content(), writer.offset(), writer.length(), null, null); + } else { + cc.convertToBytes(type, val, convertHandler); + } + } + } + } + + /** + * 将指定字符串以响应结果输出 + * + * @param obj 输出内容 + */ + public void finish(String obj) { + finish(200, obj); + } + + /** + * 以指定响应码附带内容输出 + * + * @param status 响应码 + * @param message 输出内容 + */ + public void finish(int status, String message) { + if (isClosed()) return; + this.status = status; + //if (status != 200) super.refuseAlive(); + final byte[] val = message == null ? HttpRequest.EMPTY_BYTES : (context.getCharset() == null ? Utility.encodeUTF8(message) : message.getBytes(context.getCharset())); + finish(false, null, val, 0, val.length, null, null); + } + + @Override + public void finish(boolean kill, final byte[] bs, int offset, int length) { + finish(false, null, bs, offset, length, null, null); + } + + public
    void finish(final byte[] bs, int offset, int length, Consumer callback, A attachment) { + finish(false, null, bs, offset, length, callback, attachment); + } + + /** + * 将指定byte[]按响应结果输出 + * + * @param contentType ContentType + * @param bs 输出内容 + */ + public void finish(final String contentType, final byte[] bs) { + finish(false, contentType, bs, 0, bs == null ? 0 : bs.length, null, null); + } + + /** + * 将ByteTuple按响应结果输出 + * + * @param contentType ContentType + * @param array 输出内容 + */ + public void finish(final String contentType, final ByteTuple array) { + finish(false, contentType, array.content(), array.offset(), array.length(), null, null); + } + + /** + * 将指定byte[]按响应结果输出 + * + * @param kill kill + * @param contentType ContentType + * @param bs 输出内容 + * @param offset 偏移量 + * @param length 长度 + */ + protected void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length) { + finish(kill, contentType, bs, offset, length, null, null); + } + + /** + * 将指定byte[]按响应结果输出 + * + * @param kill kill + * @param contentType ContentType + * @param bs 输出内容 + * @param offset 偏移量 + * @param length 长度 + * @param callback Consumer + * @param attachment ConvertWriter + * @param A + */ + protected void finish(boolean kill, final String contentType, final byte[] bs, int offset, int length, Consumer callback, A attachment) { + if (isClosed()) return; //避免重复关闭 + if (this.headWritedSize < 0) { + if (contentType != null) this.contentType = contentType; + this.contentLength = length; + createHeader(); + } + ByteArray data = headerArray; + data.put(bs, offset, length); + if (callback != null) callback.accept(attachment); + if (cacheHandler != null) cacheHandler.accept(this, data.getBytes()); + + int pipelineIndex = request.getPipelineIndex(); + if (pipelineIndex > 0) { + boolean over = this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data); + if (over) { + request.setPipelineOver(true); + this.channel.flushPipelineData(this.pipelineWriteHandler); + } else { + removeChannel(); + this.responseConsumer.accept(this); + } + } else { + if (this.channel.hasPipelineData()) { + this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data); + this.channel.flushPipelineData(this.pipelineWriteHandler); + } else { + //不能用finish(boolean kill, final ByteTuple array) 否则会调this.finish + super.finish(false, data.content(), 0, data.length()); + } + } + +// ByteArray data = headerArray; +// int pipelineIndex = request.getPipelineIndex(); +// if (pipelineIndex > 0) { +// boolean over = this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data.content(), 0, data.length(), bs, offset, length); +// if (callback != null) callback.accept(attachment); +// if (cacheHandler != null) cacheHandler.accept(this, Utility.append(data.getBytes(), bs, offset, length)); +// +// if (over) { +// request.setPipelineOver(true); +// this.channel.flushPipelineData(this.pipelineWriteHandler); +// } else { +// removeChannel(); +// this.responseConsumer.accept(this); +// } +// } else { +// if (this.channel.hasPipelineData()) { +// this.channel.writePipelineData(pipelineIndex, request.getPipelineCount(), data.content(), 0, data.length(), bs, offset, length); +// if (callback != null) callback.accept(attachment); +// if (cacheHandler != null) cacheHandler.accept(this, Utility.append(data.getBytes(), bs, offset, length)); +// this.channel.flushPipelineData(this.pipelineWriteHandler); +// } else { +// //不能用finish(boolean kill, final ByteTuple array) 否则会调this.finish +// super.finish(false, data.content(), 0, data.length(), bs, offset, length, callback, attachment); +// } +// } + } + + /** + * 以304状态码输出 + */ + public void finish304() { + skipHeader(); + super.finish(false, bytes304); + } + + /** + * 以404状态码输出 + */ + public void finish404() { + skipHeader(); + super.finish(false, bytes404); + } + + /** + * 以500状态码输出 + */ + public void finish500() { + skipHeader(); + super.finish(false, bytes500); + } + + /** + * 以504状态码输出 + */ + public void finish504() { + skipHeader(); + super.finish(false, bytes504); + } + + //Header大小 + protected void createHeader() { + if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket() + && (this.contentType == null || this.contentType == this.jsonContentType || this.contentType == this.plainContentType) + && (this.contentLength >= 0 && this.contentLength < jsonLiveContentLengthArray.length)) { + byte[][] lengthArray = this.plainLiveContentLengthArray; + if (this.request.isKeepAlive()) { + if (this.contentType == this.jsonContentType) { + lengthArray = this.jsonLiveContentLengthArray; + } + } else { + if (this.contentType == this.jsonContentType) { + lengthArray = this.jsonCloseContentLengthArray; + } else { + lengthArray = this.plainCloseContentLengthArray; + } + } + headerArray.put(lengthArray[(int) this.contentLength]); + } else { + if (this.status == 200 && !this.respHeadContainsConnection && !this.request.isWebSocket()) { + if (this.request.isKeepAlive()) { + headerArray.put(status200_server_live_Bytes); + } else { + headerArray.put(status200_server_close_Bytes); + } + } else { + if (this.status == 200) { + headerArray.put(status200Bytes); + } else { + headerArray.put(("HTTP/1.1 " + this.status + " " + httpCodes.get(this.status) + "\r\n").getBytes()); + } + headerArray.put(serverNameBytes); + if (!this.respHeadContainsConnection) { + byte[] bs = this.request.isKeepAlive() ? connectAliveBytes : connectCloseBytes; + if (bs.length > 0) headerArray.put(bs); + } + } + if (!this.request.isWebSocket()) { + if (this.contentType == this.jsonContentType) { + headerArray.put(this.jsonContentTypeBytes); + } else if (this.contentType == null || this.contentType == this.plainContentType) { + headerArray.put(this.plainContentTypeBytes); + } else { + headerArray.put(("Content-Type: " + this.contentType + "\r\n").getBytes()); + } + } + if (this.contentLength >= 0) { + if (this.contentLength < contentLengthArray.length) { + headerArray.put(contentLengthArray[(int) this.contentLength]); + } else { + headerArray.put(("Content-Length: " + this.contentLength + "\r\n").getBytes()); + } + } + } + if (dateSupplier != null) headerArray.put(dateSupplier.get()); + + if (this.defaultAddHeaders != null) { + for (String[] headers : this.defaultAddHeaders) { + if (headers.length > 3) { + String v = request.getParameter(headers[2]); + if (v != null) this.header.addValue(headers[0], v); + } else if (headers.length > 2) { + String v = request.getHeader(headers[2]); + if (v != null) this.header.addValue(headers[0], v); + } else { + this.header.addValue(headers[0], headers[1]); + } + } + } + if (this.defaultSetHeaders != null) { + for (String[] headers : this.defaultSetHeaders) { + if (headers.length > 3) { + String v = request.getParameter(headers[2]); + if (v != null) this.header.setValue(headers[0], v); + } else if (headers.length > 2) { + String v = request.getHeader(headers[2]); + if (v != null) this.header.setValue(headers[0], v); + } else { + this.header.setValue(headers[0], headers[1]); + } + } + } + for (Entry en : this.header.getStringEntrys()) { + headerArray.put((en.name + ": " + en.getValue() + "\r\n").getBytes()); + } + if (request.newsessionid != null) { + String domain = defaultCookie == null ? null : defaultCookie.getDomain(); + if (domain == null || domain.isEmpty()) { + domain = ""; + } else { + domain = "Domain=" + domain + "; "; + } + String path = defaultCookie == null ? null : defaultCookie.getPath(); + if (path == null || path.isEmpty()) path = "/"; + if (request.newsessionid.isEmpty()) { + headerArray.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=; " + domain + "Path=/; Max-Age=0; HttpOnly\r\n").getBytes()); + } else { + headerArray.put(("Set-Cookie: " + HttpRequest.SESSIONID_NAME + "=" + request.newsessionid + "; " + domain + "Path=/; HttpOnly\r\n").getBytes()); + } + } + if (this.cookies != null) { + for (HttpCookie cookie : this.cookies) { + if (cookie == null) continue; + if (defaultCookie != null) { + if (defaultCookie.getDomain() != null && cookie.getDomain() == null) cookie.setDomain(defaultCookie.getDomain()); + if (defaultCookie.getPath() != null && cookie.getPath() == null) cookie.setPath(defaultCookie.getPath()); + } + headerArray.put(("Set-Cookie: " + cookieString(cookie) + "\r\n").getBytes()); + } + } + headerArray.put(LINE); + this.headWritedSize = headerArray.length(); + } + + private CharSequence cookieString(HttpCookie cookie) { + StringBuilder sb = new StringBuilder(); + sb.append(cookie.getName()).append("=").append(cookie.getValue()).append("; Version=1"); + if (cookie.getDomain() != null) sb.append("; Domain=").append(cookie.getDomain()); + if (cookie.getPath() != null) sb.append("; Path=").append(cookie.getPath()); + if (cookie.getPortlist() != null) sb.append("; Port=").append(cookie.getPortlist()); + if (cookie.getMaxAge() > 0) { + sb.append("; Max-Age=").append(cookie.getMaxAge()); + sb.append("; Expires=").append(RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(ZONE_GMT).plusSeconds(cookie.getMaxAge()))); + } + if (cookie.getSecure()) sb.append("; Secure"); + if (cookie.isHttpOnly()) sb.append("; HttpOnly"); + return sb; + } + + /** + * 异步输出指定内容 + * + * @param 泛型 + * @param buffer 输出内容 + * @param handler 异步回调函数 + */ + protected void sendBody(ByteBuffer buffer, CompletionHandler handler) { + if (this.headWritedSize < 0) { + if (this.contentLength < 0) this.contentLength = buffer == null ? 0 : buffer.remaining(); + createHeader(); + if (buffer == null) { + super.send(headerArray, handler); + } else { + ByteBuffer headbuf = channel.pollWriteBuffer(); + headbuf.put(headerArray.content(), 0, headerArray.length()); + headbuf.flip(); + super.send(new ByteBuffer[]{headbuf, buffer}, null, handler); + } + } else { + super.send(buffer, null, handler); + } + } + + /** + * 将指定文件按响应结果输出 + * + * @param file 输出文件 + * + * @throws IOException IO异常 + */ + public void finish(File file) throws IOException { + finishFile(null, file, null); + } + + /** + * 将文件按指定文件名输出 + * + * @param filename 输出文件名 + * @param file 输出文件 + * + * @throws IOException IO异常 + */ + public void finish(final String filename, File file) throws IOException { + finishFile(filename, file, null); + } + + /** + * 将指定文件句柄或文件内容按响应结果输出,若fileBody不为null则只输出fileBody内容 + * + * @param file 输出文件 + * @param fileBody 文件内容, 没有则输出file + * + * @throws IOException IO异常 + */ + protected void finishFile(final File file, ByteArray fileBody) throws IOException { + finishFile(null, file, fileBody); + } + + /** + * 将指定文件句柄或文件内容按指定文件名输出,若fileBody不为null则只输出fileBody内容 + * file 与 fileBody 不能同时为空 + * file 与 filename 也不能同时为空 + * + * @param filename 输出文件名 + * @param file 输出文件 + * @param fileBody 文件内容, 没有则输出file + * + * @throws IOException IO异常 + */ + protected void finishFile(final String filename, final File file, ByteArray fileBody) throws IOException { + if ((file == null || !file.isFile() || !file.canRead()) && fileBody == null) { + finish404(); + return; + } + final long length = file == null ? fileBody.length() : file.length(); + final String match = request.getHeader("If-None-Match"); + final String etag = (file == null ? 0L : file.lastModified()) + "-" + length; + if (match != null && etag.equals(match)) { + //finish304(); + //return; + } + this.contentLength = length; + if (filename != null && !filename.isEmpty() && file != null) { + if (this.header.getValue("Content-Disposition") == null) { + addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8")); + } + } + this.contentType = MimeType.getByFilename(filename == null || filename.isEmpty() ? file.getName() : filename); + if (this.contentType == null) this.contentType = "application/octet-stream"; + String range = request.getHeader("Range"); + if (range != null && (!range.startsWith("bytes=") || range.indexOf(',') >= 0)) range = null; + long start = -1; + long len = -1; + if (range != null) { + range = range.substring("bytes=".length()); + int pos = range.indexOf('-'); + start = pos == 0 ? 0 : Integer.parseInt(range.substring(0, pos)); + long end = (pos == range.length() - 1) ? -1 : Long.parseLong(range.substring(pos + 1)); + long clen = end > 0 ? (end - start + 1) : (length - start); + this.status = 206; + addHeader("Accept-Ranges", "bytes"); + addHeader("Content-Range", "bytes " + start + "-" + (end > 0 ? end : length - 1) + "/" + length); + this.contentLength = clen; + len = end > 0 ? clen : end; + } + this.addHeader("ETag", etag); + createHeader(); + ByteArray data = headerArray; + if (fileBody == null) { + if (this.recycleListener != null) this.output = file; + finishFile(data, file, start, len); + } else { //一般HttpResourceServlet缓存file内容时fileBody不为空 + if (start >= 0) data.put(fileBody, (int) start, (int) ((len > 0) ? len : fileBody.length() - start)); + super.finish(false, data.content(), 0, data.length()); + } + } + + //offset、length 为 -1 表示输出整个文件 + private void finishFile(ByteArray headerData, File file, long offset, long length) throws IOException { + //this.channel.write(headerData, new TransferFileHandler(file, offset, length)); + final Logger logger = context.getLogger(); + this.channel.write(headerData, new CompletionHandler() { + + FileChannel fileChannel; + + long limit; + + long sends; + + ByteBuffer buffer; + + @Override + public void completed(Integer result, Void attachment) { + try { + if (fileChannel != null && sends >= limit) { + if (buffer != null) channel.offerBuffer(buffer); + try { + fileChannel.close(); + } catch (IOException ie) { + } + finish(); + return; + } + if (fileChannel == null) { + fileChannel = FileChannel.open(file.toPath(), StandardOpenOption.READ); + if (offset > 0) fileChannel = fileChannel.position(offset); + limit = length > 0 ? length : (file.length() - (offset > 0 ? offset : 0)); + sends = 0; + buffer = channel.ssl() ? channel.pollWriteSSLBuffer() : channel.pollWriteBuffer(); + } + + buffer.clear(); + int len = fileChannel.read(buffer); + if (len < 1) throw new IOException("read " + file + " error: " + len); + buffer.flip(); + if (sends + len > limit) { + buffer.limit((int) (len - limit + sends)); + sends = limit; + } else { + sends += len; + } + channel.write(buffer, attachment, this); + } catch (Exception e) { + if (fileChannel != null) { + try { + fileChannel.close(); + } catch (IOException ie) { + } + } + failed(e, attachment); + } + } + + @Override + public void failed(Throwable exc, Void attachment) { + if (buffer != null) channel.offerBuffer(buffer); + if (logger.isLoggable(Level.FINER)) logger.log(Level.FINER, "finishFile error", exc); + finish(true); + } + }); + } + + /** + * 跳过header的输出 + * 通常应用场景是,调用者的输出内容里已经包含了HTTP的响应头信息,因此需要调用此方法避免重复输出HTTP响应头信息。 + * + * @return HttpResponse + */ + public HttpResponse skipHeader() { + this.headWritedSize = 0; + return this; + } + + protected DefaultAnyValue duplicateHeader() { + return this.header.duplicate(); + } + + /** + * 设置Header值 + * + * @param name header名 + * @param value header值 + * + * @return HttpResponse + */ + public HttpResponse setHeader(String name, Object value) { + this.header.setValue(name, String.valueOf(value)); + if ("Connection".equalsIgnoreCase(name)) this.respHeadContainsConnection = true; + return this; + } + + /** + * 添加Header值 + * + * @param name header名 + * @param value header值 + * + * @return HttpResponse + */ + public HttpResponse addHeader(String name, Object value) { + this.header.addValue(name, String.valueOf(value)); + if ("Connection".equalsIgnoreCase(name)) this.respHeadContainsConnection = true; + return this; + } + + /** + * 添加Header值 + * + * @param map header值 + * + * @return HttpResponse + */ + public HttpResponse addHeader(Map map) { + if (map == null || map.isEmpty()) return this; + for (Map.Entry en : map.entrySet()) { + this.header.addValue(en.getKey(), String.valueOf(en.getValue())); + if (!respHeadContainsConnection && "Connection".equalsIgnoreCase(en.getKey())) { + this.respHeadContainsConnection = true; + } + } + return this; + } + + /** + * 设置状态码 + * + * @param status 状态码 + * + * @return HttpResponse + */ + public HttpResponse setStatus(int status) { + this.status = status; + return this; + } + + /** + * 获取状态码 + * + * @return 状态码 + */ + public int getStatus() { + return this.status; + } + + /** + * 获取 ContentType + * + * @return ContentType + */ + public String getContentType() { + return contentType; + } + + /** + * 设置 ContentType + * + * @param contentType ContentType + * + * @return HttpResponse + */ + public HttpResponse setContentType(String contentType) { + this.contentType = contentType; + return this; + } + + /** + * 获取内容长度 + * + * @return 内容长度 + */ + public long getContentLength() { + return contentLength; + } + + /** + * 设置内容长度 + * + * @param contentLength 内容长度 + * + * @return HttpResponse + */ + public HttpResponse setContentLength(long contentLength) { + this.contentLength = contentLength; + return this; + } + + /** + * 获取输出时的拦截器 + * + * @return 拦截器 + */ + protected BiConsumer getCacheHandler() { + return cacheHandler; + } + + /** + * 设置输出时的拦截器 + * + * @param cacheHandler 拦截器 + */ + protected void setCacheHandler(BiConsumer cacheHandler) { + this.cacheHandler = cacheHandler; + } + + /** + * 获取输出RetResult时的拦截器 + * + * @return 拦截器 + */ + protected BiFunction getRetResultHandler() { + return retResultHandler; + } + + /** + * 设置输出RetResult时的拦截器 + * + * @param retResultHandler 拦截器 + */ + public void retResultHandler(BiFunction retResultHandler) { + this.retResultHandler = retResultHandler; + } + +// protected final class TransferFileHandler implements CompletionHandler { +// +// private final File file; +// +// private final AsynchronousFileChannel filechannel; +// +// private final long max; //需要读取的字节数, -1表示读到文件结尾 +// +// private long count;//读取文件的字节数 +// +// private long readpos = 0; +// +// private boolean hdwrite = true; //写入Header +// +// private boolean read = false; +// +// public TransferFileHandler(File file) throws IOException { +// this.file = file; +// this.filechannel = AsynchronousFileChannel.open(file.toPath(), options); +// this.readpos = 0; +// this.max = file.length(); +// } +// +// public TransferFileHandler(File file, long offset, long len) throws IOException { +// this.file = file; +// this.filechannel = AsynchronousFileChannel.open(file.toPath(), options); +// this.readpos = offset <= 0 ? 0 : offset; +// this.max = len <= 0 ? file.length() : len; +// } +// +// @Override +// public void completed(Integer result, Void attachment) { +// //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------result: " + result + ", max = " + max + ", readpos = " + readpos + ", count = " + count + ", " + (hdwrite ? "正在写Header" : (read ? "准备读" : "准备写"))); +// if (result < 0 || count >= max) { +// failed(null, attachment); +// return; +// } +// if (hdwrite && attachment.hasRemaining()) { //Header还没写完 +// channel.write(attachment, attachment, this); +// return; +// } +// if (hdwrite) { +// //(Utility.now() + "---" + Thread.currentThread().getName() + "-----------" + file + "-------------------Header写入完毕, 准备读取文件."); +// hdwrite = false; +// read = true; +// result = 0; +// } +// if (read) { +// count += result; +// } else { +// readpos += result; +// } +// if (read && attachment.hasRemaining()) { //Buffer还没写完 +// channel.write(attachment, attachment, this); +// return; +// } +// +// if (read) { +// read = false; +// attachment.clear(); +// filechannel.read(attachment, readpos, attachment, this); +// } else { +// read = true; +// if (count > max) { +// attachment.limit((int) (attachment.position() + max - count)); +// } +// attachment.flip(); +// if (attachment.hasRemaining()) { +// channel.write(attachment, attachment, this); +// } else { +// failed(null, attachment); +// } +// } +// } +// +// @Override +// public void failed(Throwable exc, Void attachment) { +// finish(true); +// try { +// filechannel.close(); +// } catch (IOException e) { +// } +// } +// +// } + public static class HttpResponseConfig { + + public String plainContentType; + + public String jsonContentType; + + public byte[] plainContentTypeBytes; + + public byte[] jsonContentTypeBytes; + + public String[][] defaultAddHeaders; + + public String[][] defaultSetHeaders; + + public HttpCookie defaultCookie; + + public boolean autoOptions; + + public Supplier dateSupplier; + + public HttpRender httpRender; + + public final byte[][] plainLiveContentLengthArray = new byte[cacheMaxContentLength][]; + + public final byte[][] jsonLiveContentLengthArray = new byte[cacheMaxContentLength][]; + + public final byte[][] plainCloseContentLengthArray = new byte[cacheMaxContentLength][]; + + public final byte[][] jsonCloseContentLengthArray = new byte[cacheMaxContentLength][]; + + public HttpResponseConfig init(AnyValue config) { + if (this.plainContentTypeBytes == null) { + String plainct = plainContentType == null || plainContentType.isEmpty() ? "text/plain; charset=utf-8" : plainContentType; + String jsonct = jsonContentType == null || jsonContentType.isEmpty() ? "application/json; charset=utf-8" : jsonContentType; + this.plainContentType = plainct; + this.jsonContentType = jsonct; + this.plainContentTypeBytes = ("Content-Type: " + plainct + "\r\n").getBytes(); + this.jsonContentTypeBytes = ("Content-Type: " + jsonct + "\r\n").getBytes(); + for (int i = 0; i < cacheMaxContentLength; i++) { + byte[] lenbytes = ("Content-Length: " + i + "\r\n").getBytes(); + plainLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, plainContentTypeBytes), lenbytes); + plainCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, plainContentTypeBytes), lenbytes); + jsonLiveContentLengthArray[i] = append(append(status200_server_live_Bytes, jsonContentTypeBytes), lenbytes); + jsonCloseContentLengthArray[i] = append(append(status200_server_close_Bytes, jsonContentTypeBytes), lenbytes); + } + } + return this; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/org/redkale/net/http/HttpResult.java b/src/main/java/org/redkale/net/http/HttpResult.java similarity index 96% rename from src/org/redkale/net/http/HttpResult.java rename to src/main/java/org/redkale/net/http/HttpResult.java index 9a2a2c973..30daf2ce4 100644 --- a/src/org/redkale/net/http/HttpResult.java +++ b/src/main/java/org/redkale/net/http/HttpResult.java @@ -1,177 +1,177 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.Serializable; -import java.net.HttpCookie; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 结果对象的类型 - */ -public class HttpResult { - - public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME; - - @ConvertColumn(index = 1) - protected int status = 200; //不设置则为 200 - - @ConvertColumn(index = 2) - protected String contentType; - - @ConvertColumn(index = 3) - protected Map headers; - - @ConvertColumn(index = 4) - protected List cookies; - - @ConvertColumn(index = 5) - protected T result; - - protected Convert convert; - - public HttpResult() { - } - - public HttpResult(Convert convert, T result) { - this.convert = convert; - this.result = result; - } - - public HttpResult(T result) { - this.result = result; - } - - public HttpResult(String contentType, T result) { - this.contentType = contentType; - this.result = result; - } - - public HttpResult header(String name, Serializable value) { - if (this.headers == null) this.headers = new HashMap<>(); - this.headers.put(name, String.valueOf(value)); - return this; - } - - public HttpResult cookie(String name, Serializable value) { - return cookie(new HttpCookie(name, String.valueOf(value))); - } - - public HttpResult cookie(String name, Serializable value, boolean httpOnly) { - HttpCookie c = new HttpCookie(name, String.valueOf(value)); - c.setHttpOnly(httpOnly); - return cookie(c); - } - - public HttpResult cookie(HttpCookie cookie) { - if (this.cookies == null) this.cookies = new ArrayList<>(); - this.cookies.add(cookie); - return this; - } - - public HttpResult contentType(String contentType) { - this.contentType = contentType; - return this; - } - - public HttpResult result(T result) { - this.result = result; - return this; - } - - public HttpResult status(int status) { - this.status = status; - return this; - } - - public Convert convert() { - return convert; - } - - public HttpResult convert(Convert convert) { - this.convert = convert; - return this; - } - - public String getHeader(String name) { - return headers == null ? null : headers.get(name); - } - - public String getHeader(String name, String dfvalue) { - return headers == null ? dfvalue : headers.getOrDefault(name, dfvalue); - } - - public CompletableFuture> toFuture() { - return CompletableFuture.completedFuture(this); - } - - public CompletableFuture toAnyFuture() { - return CompletableFuture.completedFuture(this); - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public List getCookies() { - return cookies; - } - - public void setCookies(List cookies) { - this.cookies = cookies; - } - - public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public T getResult() { - return result; - } - - public void setResult(T result) { - this.result = result; - } - - public int getStatus() { - return status; - } - - public void setStatus(int status) { - this.status = status; - } - - @Override - public String toString() { - if (this.result instanceof byte[]) { - HttpResult tmp = new HttpResult(); - tmp.contentType = this.contentType; - tmp.cookies = this.cookies; - tmp.headers = this.headers; - tmp.status = this.status; - tmp.result = new String((byte[]) this.result, StandardCharsets.UTF_8); - return JsonConvert.root().convertTo(tmp); - } - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.Serializable; +import java.net.HttpCookie; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 结果对象的类型 + */ +public class HttpResult { + + public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME; + + @ConvertColumn(index = 1) + protected int status = 200; //不设置则为 200 + + @ConvertColumn(index = 2) + protected String contentType; + + @ConvertColumn(index = 3) + protected Map headers; + + @ConvertColumn(index = 4) + protected List cookies; + + @ConvertColumn(index = 5) + protected T result; + + protected Convert convert; + + public HttpResult() { + } + + public HttpResult(Convert convert, T result) { + this.convert = convert; + this.result = result; + } + + public HttpResult(T result) { + this.result = result; + } + + public HttpResult(String contentType, T result) { + this.contentType = contentType; + this.result = result; + } + + public HttpResult header(String name, Serializable value) { + if (this.headers == null) this.headers = new HashMap<>(); + this.headers.put(name, String.valueOf(value)); + return this; + } + + public HttpResult cookie(String name, Serializable value) { + return cookie(new HttpCookie(name, String.valueOf(value))); + } + + public HttpResult cookie(String name, Serializable value, boolean httpOnly) { + HttpCookie c = new HttpCookie(name, String.valueOf(value)); + c.setHttpOnly(httpOnly); + return cookie(c); + } + + public HttpResult cookie(HttpCookie cookie) { + if (this.cookies == null) this.cookies = new ArrayList<>(); + this.cookies.add(cookie); + return this; + } + + public HttpResult contentType(String contentType) { + this.contentType = contentType; + return this; + } + + public HttpResult result(T result) { + this.result = result; + return this; + } + + public HttpResult status(int status) { + this.status = status; + return this; + } + + public Convert convert() { + return convert; + } + + public HttpResult convert(Convert convert) { + this.convert = convert; + return this; + } + + public String getHeader(String name) { + return headers == null ? null : headers.get(name); + } + + public String getHeader(String name, String dfvalue) { + return headers == null ? dfvalue : headers.getOrDefault(name, dfvalue); + } + + public CompletableFuture> toFuture() { + return CompletableFuture.completedFuture(this); + } + + public CompletableFuture toAnyFuture() { + return CompletableFuture.completedFuture(this); + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public List getCookies() { + return cookies; + } + + public void setCookies(List cookies) { + this.cookies = cookies; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + @Override + public String toString() { + if (this.result instanceof byte[]) { + HttpResult tmp = new HttpResult(); + tmp.contentType = this.contentType; + tmp.cookies = this.cookies; + tmp.headers = this.headers; + tmp.status = this.status; + tmp.result = new String((byte[]) this.result, StandardCharsets.UTF_8); + return JsonConvert.root().convertTo(tmp); + } + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/org/redkale/net/http/HttpScope.java b/src/main/java/org/redkale/net/http/HttpScope.java similarity index 77% rename from src/org/redkale/net/http/HttpScope.java rename to src/main/java/org/redkale/net/http/HttpScope.java index b9b43d6c0..91ec5061c 100644 --- a/src/org/redkale/net/http/HttpScope.java +++ b/src/main/java/org/redkale/net/http/HttpScope.java @@ -1,207 +1,249 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this referid file, choose Tools | Templates - * and open the referid in the editor. - */ -package org.redkale.net.http; - -import java.io.Serializable; -import java.net.HttpCookie; -import java.util.*; -import java.util.function.*; -import javax.persistence.Transient; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; - -/** - * HTTP输出引擎的对象域
    - * 输出引擎的核心类, 业务开发人员只有通过本类对象才能调用到输出引擎功能。
    - *

    - * HttpServlet调用:
    - *

    - *    @HttpMapping(url = "/hello.html", auth = false)
    - *    public void hello(HttpRequest req, HttpResponse resp) throws IOException {
    - *        resp.finish(HttpScope.refer("/hello.html").attr("content", "哈哈"));
    - *    }
    - * 
    - *

    - * RestService调用:
    - *

    - *    @RestMapping(name = "hello.html", auth = false)
    - *    public HttpScope hello() {
    - *       return HttpScope.refer("hello.html").attr("content", "哈哈");
    - *    }
    - * 
    - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpScope { - - public static final Object NIL = new Object(); - - @ConvertColumn(index = 1) - protected String referid; - - @ConvertColumn(index = 2) - protected Map attributes; - - //@since 2.4.0 - @Transient - protected Function attrFunction; - - //@since 2.4.0 - @ConvertColumn(index = 3) - protected Map headers; - - //@since 2.4.0 - @ConvertColumn(index = 4) - protected List cookies; - - public static HttpScope refer(String template) { - HttpScope rs = new HttpScope(); - rs.setReferid(template); - return rs; - } - - public HttpScope attrFunc(Function attrFunction) { - this.attrFunction = attrFunction; - return this; - } - - public HttpScope appendAttrFunc(final String key, Supplier supplier) { - if (supplier == null) return this; - return appendAttrFunc(k -> k.equals(key) ? supplier.get() : null); - } - - public HttpScope appendAttrFunc(final Function attrFunc) { - if (attrFunc == null) return this; - final Function old = this.attrFunction; - if (old == null) { - this.attrFunction = attrFunc; - } else { - this.attrFunction = key -> { - Object r = old.apply(key); - return r == null ? attrFunc.apply(key) : r; - }; - } - return this; - } - - public HttpScope attr(Map map) { - if (map == null) return this; - if (this.attributes == null) this.attributes = new LinkedHashMap<>(); - this.attributes.putAll(map); - return this; - } - - public HttpScope attr(String name, Object value) { - if (name == null || value == null) return this; - if (this.attributes == null) this.attributes = new LinkedHashMap<>(); - this.attributes.put(name, value); - return this; - } - - @SuppressWarnings("unchecked") - public T find(String name) { - return this.attributes == null ? null : (T) this.attributes.get(name); - } - - @SuppressWarnings("unchecked") - public T find(HttpScope parent, String name) { - T rs = this.attributes == null ? null : (T) this.attributes.get(name); - if (rs != null) return rs; - return parent == null ? null : parent.find(name); - } - - public void forEach(BiConsumer action) { - if (this.attributes == null) return; - this.attributes.forEach(action); - } - - public HttpScope header(String name, Serializable value) { - if (this.headers == null) this.headers = new HashMap<>(); - this.headers.put(name, String.valueOf(value)); - return this; - } - - public HttpScope cookie(String name, Serializable value) { - return cookie(new HttpCookie(name, String.valueOf(value))); - } - - public HttpScope cookie(String name, Serializable value, boolean httpOnly) { - HttpCookie c = new HttpCookie(name, String.valueOf(value)); - c.setHttpOnly(httpOnly); - return cookie(c); - } - - public HttpScope cookie(HttpCookie cookie) { - if (this.cookies == null) this.cookies = new ArrayList<>(); - this.cookies.add(cookie); - return this; - } - - public String getHeader(String name) { - return headers == null ? null : headers.get(name); - } - - public String getHeader(String name, String dfvalue) { - return headers == null ? null : headers.getOrDefault(name, dfvalue); - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public List getCookies() { - return cookies; - } - - public void setCookies(List cookies) { - this.cookies = cookies; - } - - public String getReferid() { - return referid; - } - - public void setReferid(String referid) { - this.referid = referid; - } - - public Map getAttributes() { - final Function attrFunc = this.attrFunction; - if (attrFunc != null) { - if (this.attributes == null) this.attributes = new LinkedHashMap<>(); - return new LinkedHashMap(this.attributes) { - @Override - public Object get(Object key) { - if (containsKey(key)) { - return super.get(key); - } else { - Object val = attrFunc.apply(key.toString()); - if (val == NIL) return null; - put(key.toString(), val); - return val; - } - } - }; - } - return this.attributes; - } - - @ConvertDisabled(type = ConvertType.JSON) - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this referid file, choose Tools | Templates + * and open the referid in the editor. + */ +package org.redkale.net.http; + +import java.io.Serializable; +import java.net.HttpCookie; +import java.util.*; +import java.util.function.*; +import javax.persistence.Transient; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; + +/** + * HTTP输出引擎的对象域
    + * 输出引擎的核心类, 业务开发人员只有通过本类对象才能调用到输出引擎功能。
    + *

    + * HttpServlet调用:
    + *

    + *    @HttpMapping(url = "/hello.html", auth = false)
    + *    public void hello(HttpRequest req, HttpResponse resp) throws IOException {
    + *        resp.finish(HttpScope.refer("/hello.html").attr("content", "哈哈"));
    + *    }
    + * 
    + *

    + * RestService调用:
    + *

    + *    @RestMapping(name = "hello.html", auth = false)
    + *    public HttpScope hello() {
    + *       return HttpScope.refer("hello.html").attr("content", "哈哈");
    + *    }
    + * 
    + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpScope { + + public static final Object NIL = new Object(); + + @ConvertColumn(index = 1) + protected String referid; + + @ConvertColumn(index = 2) + protected Map attributes; + + //@since 2.4.0 + @Transient + protected Function attrFunction; + + //@since 2.4.0 + @ConvertColumn(index = 3) + protected Map headers; + + //@since 2.4.0 + @ConvertColumn(index = 4) + protected List cookies; + + public static HttpScope refer(String template) { + HttpScope rs = new HttpScope(); + rs.setReferid(template); + return rs; + } + + public static HttpScope create(String template, String name, Object value) { + HttpScope rs = new HttpScope(); + rs.setReferid(template); + rs.attr(name, value); + return rs; + } + + public static HttpScope create(String template, String name1, Object value1, String name2, Object value2) { + HttpScope rs = new HttpScope(); + rs.setReferid(template); + rs.attr(name1, value1).attr(name2, value2); + return rs; + } + + public static HttpScope create(String template, String name1, Object value1, String name2, Object value2, String name3, Object value3) { + HttpScope rs = new HttpScope(); + rs.setReferid(template); + rs.attr(name1, value1).attr(name2, value2).attr(name3, value3); + return rs; + } + + public static HttpScope create(String template, String name1, Object value1, String name2, Object value2, String name3, Object value3, String name4, Object value4) { + HttpScope rs = new HttpScope(); + rs.setReferid(template); + rs.attr(name1, value1).attr(name2, value2).attr(name3, value3).attr(name4, value4); + return rs; + } + + public static HttpScope create(String template, String name1, Object value1, String name2, Object value2, String name3, Object value3, String name4, Object value4, String name5, Object value5) { + HttpScope rs = new HttpScope(); + rs.setReferid(template); + rs.attr(name1, value1).attr(name2, value2).attr(name3, value3).attr(name4, value4).attr(name5, value5); + return rs; + } + + public static HttpScope create(String template, Function attrFunction) { + HttpScope rs = new HttpScope(); + rs.setReferid(template); + rs.attrFunction = attrFunction; + return rs; + } + + public HttpScope attrFunc(Function attrFunction) { + this.attrFunction = attrFunction; + return this; + } + + public HttpScope appendAttrFunc(final String key, Supplier supplier) { + if (supplier == null) return this; + return appendAttrFunc(k -> k.equals(key) ? supplier.get() : null); + } + + public HttpScope appendAttrFunc(final Function attrFunc) { + if (attrFunc == null) return this; + final Function old = this.attrFunction; + if (old == null) { + this.attrFunction = attrFunc; + } else { + this.attrFunction = key -> { + Object r = old.apply(key); + return r == null ? attrFunc.apply(key) : r; + }; + } + return this; + } + + public HttpScope attr(Map map) { + if (map == null) return this; + if (this.attributes == null) this.attributes = new LinkedHashMap<>(); + this.attributes.putAll(map); + return this; + } + + public HttpScope attr(String name, Object value) { + if (name == null || value == null) return this; + if (this.attributes == null) this.attributes = new LinkedHashMap<>(); + this.attributes.put(name, value); + return this; + } + + @SuppressWarnings("unchecked") + public T find(String name) { + return this.attributes == null ? null : (T) this.attributes.get(name); + } + + @SuppressWarnings("unchecked") + public T find(HttpScope parent, String name) { + T rs = this.attributes == null ? null : (T) this.attributes.get(name); + if (rs != null) return rs; + return parent == null ? null : parent.find(name); + } + + public void forEach(BiConsumer action) { + if (this.attributes == null) return; + this.attributes.forEach(action); + } + + public HttpScope header(String name, Serializable value) { + if (this.headers == null) this.headers = new HashMap<>(); + this.headers.put(name, String.valueOf(value)); + return this; + } + + public HttpScope cookie(String name, Serializable value) { + return cookie(new HttpCookie(name, String.valueOf(value))); + } + + public HttpScope cookie(String name, Serializable value, boolean httpOnly) { + HttpCookie c = new HttpCookie(name, String.valueOf(value)); + c.setHttpOnly(httpOnly); + return cookie(c); + } + + public HttpScope cookie(HttpCookie cookie) { + if (this.cookies == null) this.cookies = new ArrayList<>(); + this.cookies.add(cookie); + return this; + } + + public String getHeader(String name) { + return headers == null ? null : headers.get(name); + } + + public String getHeader(String name, String dfvalue) { + return headers == null ? null : headers.getOrDefault(name, dfvalue); + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public List getCookies() { + return cookies; + } + + public void setCookies(List cookies) { + this.cookies = cookies; + } + + public String getReferid() { + return referid; + } + + public void setReferid(String referid) { + this.referid = referid; + } + + public Map getAttributes() { + final Function attrFunc = this.attrFunction; + if (attrFunc != null) { + if (this.attributes == null) this.attributes = new LinkedHashMap<>(); + return new LinkedHashMap(this.attributes) { + @Override + public Object get(Object key) { + if (containsKey(key)) { + return super.get(key); + } else { + Object val = attrFunc.apply(key.toString()); + if (val == NIL) return null; + put(key.toString(), val); + return val; + } + } + }; + } + return this.attributes; + } + + @ConvertDisabled(type = ConvertType.JSON) + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/org/redkale/net/http/HttpServer.java b/src/main/java/org/redkale/net/http/HttpServer.java similarity index 89% rename from src/org/redkale/net/http/HttpServer.java rename to src/main/java/org/redkale/net/http/HttpServer.java index 382777430..b75e6e58d 100644 --- a/src/org/redkale/net/http/HttpServer.java +++ b/src/main/java/org/redkale/net/http/HttpServer.java @@ -1,488 +1,489 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.reflect.Field; -import java.net.HttpCookie; -import java.nio.ByteBuffer; -import java.text.*; -import java.time.ZoneId; -import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Supplier; -import java.util.logging.Level; -import org.redkale.boot.Application; -import org.redkale.mq.MessageAgent; -import org.redkale.net.*; -import org.redkale.net.http.HttpContext.HttpContextConfig; -import org.redkale.net.http.HttpResponse.HttpResponseConfig; -import org.redkale.net.sncp.Sncp; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * Http服务器 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpServer extends Server { - - private ScheduledThreadPoolExecutor dateScheduler; - - private byte[] currDateBytes; - - private HttpResponseConfig respConfig; - - public HttpServer() { - this(null, System.currentTimeMillis(), ResourceFactory.root()); - } - - public HttpServer(ResourceFactory resourceFactory) { - this(null, System.currentTimeMillis(), resourceFactory); - } - - public HttpServer(Application application, long serverStartTime, ResourceFactory resourceFactory) { - super(application, serverStartTime, "TCP", resourceFactory, new HttpPrepareServlet()); - } - - @Override - public void init(AnyValue config) throws Exception { - super.init(config); - } - - @Override - protected void postStart() { - ((HttpPrepareServlet) this.prepare).postStart(this.context, config); - } - - @Override - public void destroy(final AnyValue config) throws Exception { - super.destroy(config); - if (this.dateScheduler != null) { - this.dateScheduler.shutdownNow(); - this.dateScheduler = null; - } - } - - public List getHttpServlets() { - return this.prepare.getServlets(); - } - - public List getHttpFilters() { - return this.prepare.getFilters(); - } - - /** - * 获取静态资源HttpServlet - * - * @return HttpServlet - */ - public HttpResourceServlet getResourceServlet() { - return (HttpResourceServlet) ((HttpPrepareServlet) this.prepare).resourceHttpServlet; - } - - /** - * 删除HttpServlet - * - * @param service Service - * - * @return HttpServlet - */ - public HttpServlet removeHttpServlet(Service service) { - return ((HttpPrepareServlet) this.prepare).removeHttpServlet(service); - } - - /** - * 删除HttpServlet - * - * @param 泛型 - * @param websocketOrServletType Class - * - * @return HttpServlet - */ - public HttpServlet removeHttpServlet(Class websocketOrServletType) { - return ((HttpPrepareServlet) this.prepare).removeHttpServlet(websocketOrServletType); - } - - /** - * 屏蔽请求URL的正则表达式 - * - * @param urlreg 正则表达式 - * - * @return 是否成功 - */ - public boolean addForbidURIReg(final String urlreg) { - return ((HttpPrepareServlet) this.prepare).addForbidURIReg(urlreg); - } - - /** - * 删除屏蔽请求URL的正则表达式 - * - * @param urlreg 正则表达式 - * - * @return 是否成功 - */ - public boolean removeForbidURIReg(final String urlreg) { - return ((HttpPrepareServlet) this.prepare).removeForbidURIReg(urlreg); - } - - /** - * 删除HttpFilter - * - * @param 泛型 - * @param filterClass HttpFilter类 - * - * @return HttpFilter - */ - public T removeHttpFilter(Class filterClass) { - return (T) this.prepare.removeFilter(filterClass); - } - - /** - * 添加HttpFilter - * - * @param filter HttpFilter - * @param conf AnyValue - * - * @return HttpServer - */ - public HttpServer addHttpFilter(HttpFilter filter, AnyValue conf) { - this.prepare.addFilter(filter, conf); - return this; - } - - /** - * 添加HttpServlet - * - * @param prefix url前缀 - * @param servlet HttpServlet - * @param mappings 匹配规则 - * - * @return HttpServer - */ - public HttpServer addHttpServlet(String prefix, HttpServlet servlet, String... mappings) { - this.prepare.addServlet(servlet, prefix, null, mappings); - return this; - } - - /** - * 添加HttpServlet - * - * @param servlet HttpServlet - * @param mappings 匹配规则 - * - * @return HttpServer - */ - public HttpServer addHttpServlet(HttpServlet servlet, String... mappings) { - this.prepare.addServlet(servlet, null, null, mappings); - return this; - } - - /** - * 添加HttpServlet - * - * @param prefix url前缀 - * @param servlet HttpServlet - * @param conf 配置信息 - * @param mappings 匹配规则 - * - * @return HttpServer - */ - public HttpServer addHttpServlet(HttpServlet servlet, final String prefix, AnyValue conf, String... mappings) { - this.prepare.addServlet(servlet, prefix, conf, mappings); - return this; - } - - /** - * 添加WebSocketServlet - * - * @param WebSocket - * @param HttpServlet - * @param classLoader ClassLoader - * @param webSocketType WebSocket的类型 - * @param messageAgent MessageAgent - * @param prefix url前缀 - * @param conf 配置信息 - * - * @return RestServlet - */ - public T addRestWebSocketServlet(final ClassLoader classLoader, final Class webSocketType, MessageAgent messageAgent, final String prefix, final AnyValue conf) { - T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType, messageAgent); - if (servlet != null) this.prepare.addServlet(servlet, prefix, conf); - return servlet; - } - - /** - * 添加RestServlet - * - * @param Service - * @param HttpServlet - * @param classLoader ClassLoader - * @param service Service对象 - * @param userType 用户数据类型 - * @param baseServletType RestServlet基类 - * @param prefix url前缀 - * - * @return RestServlet - */ - public T addRestServlet(final ClassLoader classLoader, final S service, final Class userType, final Class baseServletType, final String prefix) { - return addRestServlet(classLoader, null, service, userType, baseServletType, prefix); - } - - /** - * 添加RestServlet - * - * @param Service - * @param HttpServlet - * @param classLoader ClassLoader - * @param name 资源名 - * @param service Service对象 - * @param userType 用户数据类型 - * @param baseServletType RestServlet基类 - * @param prefix url前缀 - * - * @return RestServlet - */ - @SuppressWarnings("unchecked") - public T addRestServlet(final ClassLoader classLoader, final String name, final S service, final Class userType, final Class baseServletType, final String prefix) { - T servlet = null; - final boolean sncp = Sncp.isSncpDyn(service); - final String resname = name == null ? (sncp ? Sncp.getResourceName(service) : "") : name; - final Class serviceType = Sncp.getServiceType(service); - if (name != null) { - for (final HttpServlet item : ((HttpPrepareServlet) this.prepare).getServlets()) { - if (!(item instanceof HttpServlet)) continue; - if (item.getClass().getAnnotation(Rest.RestDyn.class) == null) continue; - try { - Field field = item.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); - if (serviceType.equals(field.getType())) { - servlet = (T) item; - break; - } - } catch (NoSuchFieldException | SecurityException e) { - logger.log(Level.SEVERE, "serviceType = " + serviceType + ", servletClass = " + item.getClass(), e); - } - } - } - final boolean first = servlet == null; - if (servlet == null) servlet = Rest.createRestServlet(classLoader, userType, baseServletType, serviceType); - if (servlet == null) return null; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null - try { //若提供动态变更Service服务功能,则改Rest服务无法做出相应更新 - Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); - field.setAccessible(true); - - Field mapfield = servlet.getClass().getDeclaredField(Rest.REST_SERVICEMAP_FIELD_NAME); - mapfield.setAccessible(true); - - Service firstService = (Service) field.get(servlet); - if (resname.isEmpty()) { - field.set(servlet, service); - firstService = service; - } - Map map = (Map) mapfield.get(servlet); - if (map == null && !resname.isEmpty()) map = new HashMap(); - if (map != null) { - map.put(resname, service); - if (firstService != null) map.put("", firstService); - } - mapfield.set(servlet, map); - } catch (Exception e) { - throw new RuntimeException(serviceType + " generate rest servlet error", e); - } - if (first) this.prepare.addServlet(servlet, prefix, sncp ? Sncp.getConf(service) : null); - return servlet; - } - - @Override - @SuppressWarnings("unchecked") - protected HttpContext createContext(Application application) { - final int port = this.address.getPort(); - //this.bufferCapacity = Math.max(this.bufferCapacity, 16 * 1024 + 16); //兼容 HTTP 2.0; - this.bufferCapacity = Math.max(this.bufferCapacity, 1024); - final List defaultAddHeaders = new ArrayList<>(); - final List defaultSetHeaders = new ArrayList<>(); - boolean autoOptions = false; - int datePeriod = 0; - String plainContentType = null; - String jsonContentType = null; - HttpCookie defaultCookie = null; - String remoteAddrHeader = null; - - if (config != null) { - AnyValue reqs = config.getAnyValue("request"); - if (reqs != null) { - AnyValue raddr = reqs.getAnyValue("remoteaddr"); - remoteAddrHeader = raddr == null ? null : raddr.getValue("value"); - if (remoteAddrHeader != null) { - if (remoteAddrHeader.startsWith("request.headers.")) { - remoteAddrHeader = remoteAddrHeader.substring("request.headers.".length()); - } else { - remoteAddrHeader = null; - } - } - } - - AnyValue resps = config.getAnyValue("response"); - if (resps != null) { - AnyValue contenttypes = resps.getAnyValue("contenttype"); - if (contenttypes != null) { - plainContentType = contenttypes.getValue("plain"); - jsonContentType = contenttypes.getValue("json"); - } - AnyValue[] addHeaders = resps.getAnyValues("addheader"); - if (addHeaders.length > 0) { - for (AnyValue addHeader : addHeaders) { - String val = addHeader.getValue("value"); - if (val == null) continue; - if (val.startsWith("request.parameters.")) { - defaultAddHeaders.add(new String[]{addHeader.getValue("name"), val, val.substring("request.parameters.".length()), null}); - } else if (val.startsWith("request.headers.")) { - defaultAddHeaders.add(new String[]{addHeader.getValue("name"), val, val.substring("request.headers.".length())}); - } else if (val.startsWith("system.property.")) { - String v = System.getProperty(val.substring("system.property.".length())); - if (v != null) defaultAddHeaders.add(new String[]{addHeader.getValue("name"), v}); - } else { - defaultAddHeaders.add(new String[]{addHeader.getValue("name"), val}); - } - } - } - AnyValue[] setHeaders = resps.getAnyValues("setheader"); - if (setHeaders.length > 0) { - for (AnyValue setHeader : setHeaders) { - String val = setHeader.getValue("value"); - if (val == null) continue; - if (val.startsWith("request.parameters.")) { - defaultSetHeaders.add(new String[]{setHeader.getValue("name"), val, val.substring("request.parameters.".length()), null}); - } else if (val.startsWith("request.headers.")) { - defaultSetHeaders.add(new String[]{setHeader.getValue("name"), val, val.substring("request.headers.".length())}); - } else if (val.startsWith("system.property.")) { - String v = System.getProperty(val.substring("system.property.".length())); - if (v != null) defaultSetHeaders.add(new String[]{setHeader.getValue("name"), v}); - } else { - defaultSetHeaders.add(new String[]{setHeader.getValue("name"), val}); - } - } - } - AnyValue defcookieValue = resps.getAnyValue("defcookie"); - if (defcookieValue != null) { - String domain = defcookieValue.getValue("domain"); - String path = defcookieValue.getValue("path"); - if (domain != null || path != null) { - defaultCookie = new HttpCookie("DEFAULTCOOKIE", ""); - defaultCookie.setDomain(domain); - defaultCookie.setPath(path); - } - } - AnyValue options = resps.getAnyValue("options"); - autoOptions = options != null && options.getBoolValue("auto", false); - - AnyValue dates = resps.getAnyValue("date"); - datePeriod = dates == null ? 0 : dates.getIntValue("period", 0); - } - - } - Supplier dateSupplier = null; - if (datePeriod == 0) { - final ZoneId gmtZone = ZoneId.of("GMT"); - dateSupplier = () -> ("Date: " + RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(gmtZone)) + "\r\n").getBytes(); - } else if (datePeriod > 0) { - if (this.dateScheduler == null) { - this.dateScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-HTTP:" + port + "-DateSchedule-Thread"); - t.setDaemon(true); - return t; - }); - final DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM y HH:mm:ss z", Locale.ENGLISH); - gmtDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); - currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes(); - final int dp = datePeriod; - this.dateScheduler.scheduleAtFixedRate(() -> { - try { - currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes(); - } catch (Throwable t) { - logger.log(Level.SEVERE, "HttpServer schedule(interval=" + dp + "ms) date-format error", t); - } - }, 1000 - System.currentTimeMillis() % 1000, datePeriod, TimeUnit.MILLISECONDS); - dateSupplier = () -> currDateBytes; - } - } - HttpRender httpRender = null; - AnyValue renderConfig = null; - { //设置TemplateEngine - renderConfig = config.getAnyValue("render"); - if (renderConfig != null) { - String renderType = renderConfig.getValue("value"); - try { - HttpRender render = (HttpRender) Thread.currentThread().getContextClassLoader().loadClass(renderType).getDeclaredConstructor().newInstance(); - getResourceFactory().inject(render); - httpRender = render; - } catch (Throwable e) { - logger.log(Level.WARNING, "init HttpRender(" + renderType + ") error", e); - } - } - } - - final String addrHeader = remoteAddrHeader; - - this.respConfig = new HttpResponseConfig(); - respConfig.plainContentType = plainContentType; - respConfig.jsonContentType = jsonContentType; - respConfig.defaultAddHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]); - respConfig.defaultSetHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]); - respConfig.defaultCookie = defaultCookie; - respConfig.autoOptions = autoOptions; - respConfig.dateSupplier = dateSupplier; - respConfig.httpRender = httpRender; - respConfig.init(config); - - final HttpContextConfig contextConfig = new HttpContextConfig(); - if (application != null) contextConfig.workExecutor = application.getWorkExecutor(); - contextConfig.serverStartTime = this.serverStartTime; - contextConfig.logger = this.logger; - contextConfig.sslContext = this.sslContext; - contextConfig.bufferCapacity = this.bufferCapacity; - contextConfig.maxconns = this.maxconns; - contextConfig.maxbody = this.maxbody; - contextConfig.charset = this.charset; - contextConfig.address = this.address; - contextConfig.prepare = this.prepare; - contextConfig.resourceFactory = this.resourceFactory; - contextConfig.aliveTimeoutSeconds = this.aliveTimeoutSeconds; - contextConfig.readTimeoutSeconds = this.readTimeoutSeconds; - contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds; - contextConfig.remoteAddrHeader = addrHeader; - - HttpContext c = new HttpContext(contextConfig); - if (httpRender != null) httpRender.init(c, renderConfig); - return c; - } - - @Override - protected ObjectPool createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize) { - if (createCounter == null) createCounter = new AtomicLong(); - if (cycleCounter == null) cycleCounter = new AtomicLong(); - final int rcapacity = this.bufferCapacity; - ObjectPool bufferPool = ObjectPool.createSafePool(createCounter, cycleCounter, bufferPoolSize, - (Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> { - if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false; - e.clear(); - return true; - }); - return bufferPool; - } - - @Override - protected ObjectPool createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize) { - Creator creator = (Object... params) -> new HttpResponse(this.context, new HttpRequest(this.context), this.respConfig); - ObjectPool pool = ObjectPool.createSafePool(createCounter, cycleCounter, responsePoolSize, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle()); - return pool; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.reflect.Field; +import java.net.HttpCookie; +import java.nio.ByteBuffer; +import java.text.*; +import java.time.ZoneId; +import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.LongAdder; +import java.util.function.Supplier; +import java.util.logging.Level; +import org.redkale.boot.Application; +import org.redkale.mq.*; +import org.redkale.net.*; +import org.redkale.net.http.HttpContext.HttpContextConfig; +import org.redkale.net.http.HttpResponse.HttpResponseConfig; +import org.redkale.net.sncp.Sncp; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * Http服务器 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpServer extends Server { + + private ScheduledThreadPoolExecutor dateScheduler; + + private byte[] currDateBytes; + + private HttpResponseConfig respConfig; + + public HttpServer() { + this(null, System.currentTimeMillis(), ResourceFactory.create()); + } + + public HttpServer(ResourceFactory resourceFactory) { + this(null, System.currentTimeMillis(), resourceFactory); + } + + public HttpServer(Application application, long serverStartTime, ResourceFactory resourceFactory) { + super(application, serverStartTime, "TCP", resourceFactory, new HttpPrepareServlet()); + } + + @Override + public void init(AnyValue config) throws Exception { + super.init(config); + } + + @Override + protected void postStart() { + ((HttpPrepareServlet) this.prepare).postStart(this.context, config); + } + + @Override + public void destroy(final AnyValue config) throws Exception { + super.destroy(config); + if (this.dateScheduler != null) { + this.dateScheduler.shutdownNow(); + this.dateScheduler = null; + } + } + + public List getHttpServlets() { + return this.prepare.getServlets(); + } + + public List getHttpFilters() { + return this.prepare.getFilters(); + } + + public HttpResponseConfig getResponseConfig() { + return respConfig; + } + + /** + * 获取静态资源HttpServlet + * + * @return HttpServlet + */ + public HttpResourceServlet getResourceServlet() { + return (HttpResourceServlet) ((HttpPrepareServlet) this.prepare).resourceHttpServlet; + } + + /** + * 删除HttpServlet + * + * @param service Service + * + * @return HttpServlet + */ + public HttpServlet removeHttpServlet(Service service) { + return ((HttpPrepareServlet) this.prepare).removeHttpServlet(service); + } + + /** + * 删除HttpServlet + * + * @param 泛型 + * @param websocketOrServletType Class + * + * @return HttpServlet + */ + public HttpServlet removeHttpServlet(Class websocketOrServletType) { + return ((HttpPrepareServlet) this.prepare).removeHttpServlet(websocketOrServletType); + } + + /** + * 屏蔽请求URL的正则表达式 + * + * @param urlreg 正则表达式 + * + * @return 是否成功 + */ + public boolean addForbidURIReg(final String urlreg) { + return ((HttpPrepareServlet) this.prepare).addForbidURIReg(urlreg); + } + + /** + * 删除屏蔽请求URL的正则表达式 + * + * @param urlreg 正则表达式 + * + * @return 是否成功 + */ + public boolean removeForbidURIReg(final String urlreg) { + return ((HttpPrepareServlet) this.prepare).removeForbidURIReg(urlreg); + } + + /** + * 删除HttpFilter + * + * @param 泛型 + * @param filterClass HttpFilter类 + * + * @return HttpFilter + */ + public T removeHttpFilter(Class filterClass) { + return (T) this.prepare.removeFilter(filterClass); + } + + /** + * 添加HttpFilter + * + * @param filter HttpFilter + * @param conf AnyValue + * + * @return HttpServer + */ + public HttpServer addHttpFilter(HttpFilter filter, AnyValue conf) { + this.prepare.addFilter(filter, conf); + return this; + } + + /** + * 添加HttpServlet + * + * @param prefix url前缀 + * @param servlet HttpServlet + * @param mappings 匹配规则 + * + * @return HttpServer + */ + public HttpServer addHttpServlet(String prefix, HttpServlet servlet, String... mappings) { + this.prepare.addServlet(servlet, prefix, null, mappings); + return this; + } + + /** + * 添加HttpServlet + * + * @param servlet HttpServlet + * @param mappings 匹配规则 + * + * @return HttpServer + */ + public HttpServer addHttpServlet(HttpServlet servlet, String... mappings) { + this.prepare.addServlet(servlet, null, null, mappings); + return this; + } + + /** + * 添加HttpServlet + * + * @param prefix url前缀 + * @param servlet HttpServlet + * @param conf 配置信息 + * @param mappings 匹配规则 + * + * @return HttpServer + */ + public HttpServer addHttpServlet(HttpServlet servlet, final String prefix, AnyValue conf, String... mappings) { + this.prepare.addServlet(servlet, prefix, conf, mappings); + return this; + } + + /** + * 添加WebSocketServlet + * + * @param WebSocket + * @param HttpServlet + * @param classLoader ClassLoader + * @param webSocketType WebSocket的类型 + * @param messageAgent MessageAgent + * @param prefix url前缀 + * @param conf 配置信息 + * + * @return RestServlet + */ + public T addRestWebSocketServlet(final ClassLoader classLoader, final Class webSocketType, MessageAgent messageAgent, final String prefix, final AnyValue conf) { + T servlet = Rest.createRestWebSocketServlet(classLoader, webSocketType, messageAgent); + if (servlet != null) this.prepare.addServlet(servlet, prefix, conf); + return servlet; + } + + /** + * 添加RestServlet + * + * @param Service + * @param HttpServlet + * @param classLoader ClassLoader + * @param service Service对象 + * @param userType 用户数据类型 + * @param baseServletType RestServlet基类 + * @param prefix url前缀 + * + * @return RestServlet + */ + public T addRestServlet(final ClassLoader classLoader, final S service, final Class userType, final Class baseServletType, final String prefix) { + return addRestServlet(classLoader, null, service, userType, baseServletType, prefix); + } + + /** + * 添加RestServlet + * + * @param Service + * @param HttpServlet + * @param classLoader ClassLoader + * @param name 资源名 + * @param service Service对象 + * @param userType 用户数据类型 + * @param baseServletType RestServlet基类 + * @param prefix url前缀 + * + * @return RestServlet + */ + @SuppressWarnings("unchecked") + public T addRestServlet(final ClassLoader classLoader, final String name, final S service, final Class userType, final Class baseServletType, final String prefix) { + T servlet = null; + final boolean sncp = Sncp.isSncpDyn(service); + final String resname = name == null ? (sncp ? Sncp.getResourceName(service) : "") : name; + final Class serviceType = Sncp.getServiceType(service); + if (name != null) { + for (final HttpServlet item : ((HttpPrepareServlet) this.prepare).getServlets()) { + if (!(item instanceof HttpServlet)) continue; + if (item.getClass().getAnnotation(Rest.RestDyn.class) == null) continue; + try { + Field field = item.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); + if (serviceType.equals(field.getType())) { + servlet = (T) item; + break; + } + } catch (NoSuchFieldException | SecurityException e) { + logger.log(Level.SEVERE, "serviceType = " + serviceType + ", servletClass = " + item.getClass(), e); + } + } + } + final boolean first = servlet == null; + if (servlet == null) { + servlet = Rest.createRestServlet(classLoader, userType, baseServletType, serviceType); + if (servlet != null) { + servlet._reqtopic = MessageAgent.generateHttpReqTopic(Rest.getRestModule(service)); + if (serviceType.getAnnotation(MessageMultiConsumer.class) != null) { + MessageMultiConsumer mmc = serviceType.getAnnotation(MessageMultiConsumer.class); + servlet._mmctopic = MessageAgent.generateHttpReqTopic(mmc.module(), resname); + } + } + } + if (servlet == null) return null; //没有HttpMapping方法的HttpServlet调用Rest.createRestServlet就会返回null + try { //若提供动态变更Service服务功能,则改Rest服务无法做出相应更新 + Field field = servlet.getClass().getDeclaredField(Rest.REST_SERVICE_FIELD_NAME); + field.setAccessible(true); + + Field mapfield = servlet.getClass().getDeclaredField(Rest.REST_SERVICEMAP_FIELD_NAME); + mapfield.setAccessible(true); + + Service firstService = (Service) field.get(servlet); + if (resname.isEmpty()) { + field.set(servlet, service); + firstService = service; + } + Map map = (Map) mapfield.get(servlet); + if (map == null && !resname.isEmpty()) map = new HashMap(); + if (map != null) { + map.put(resname, service); + if (firstService != null) map.put("", firstService); + } + mapfield.set(servlet, map); + } catch (Exception e) { + throw new RuntimeException(serviceType + " generate rest servlet error", e); + } + if (first) this.prepare.addServlet(servlet, prefix, sncp ? Sncp.getConf(service) : null); + return servlet; + } + + @Override + @SuppressWarnings("unchecked") + protected HttpContext createContext() { + final int port = this.address.getPort(); + //this.bufferCapacity = Math.max(this.bufferCapacity, 16 * 1024 + 16); //兼容 HTTP 2.0; + this.bufferCapacity = Math.max(this.bufferCapacity, 1024); + final List defaultAddHeaders = new ArrayList<>(); + final List defaultSetHeaders = new ArrayList<>(); + boolean autoOptions = false; + int datePeriod = 0; + String plainContentType = null; + String jsonContentType = null; + HttpCookie defaultCookie = null; + String remoteAddrHeader = null; + + if (config != null) { + AnyValue reqs = config.getAnyValue("request"); + if (reqs != null) { + AnyValue raddr = reqs.getAnyValue("remoteaddr"); + remoteAddrHeader = raddr == null ? null : raddr.getValue("value"); + if (remoteAddrHeader != null) { + if (remoteAddrHeader.startsWith("request.headers.")) { + remoteAddrHeader = remoteAddrHeader.substring("request.headers.".length()); + } else { + remoteAddrHeader = null; + } + } + } + + AnyValue resps = config.getAnyValue("response"); + if (resps != null) { + AnyValue contenttypes = resps.getAnyValue("content-type"); + if (contenttypes == null) contenttypes = resps.getAnyValue("contenttype"); //兼容旧的 + if (contenttypes != null) { + plainContentType = contenttypes.getValue("plain"); + jsonContentType = contenttypes.getValue("json"); + } + AnyValue[] addHeaders = resps.getAnyValues("addheader"); + if (addHeaders.length > 0) { + for (AnyValue addHeader : addHeaders) { + String val = addHeader.getValue("value"); + if (val == null) continue; + if (val.startsWith("request.parameters.")) { + defaultAddHeaders.add(new String[]{addHeader.getValue("name"), val, val.substring("request.parameters.".length()), null}); + } else if (val.startsWith("request.headers.")) { + defaultAddHeaders.add(new String[]{addHeader.getValue("name"), val, val.substring("request.headers.".length())}); + } else if (val.startsWith("system.property.")) { + String v = System.getProperty(val.substring("system.property.".length())); + if (v != null) defaultAddHeaders.add(new String[]{addHeader.getValue("name"), v}); + } else { + defaultAddHeaders.add(new String[]{addHeader.getValue("name"), val}); + } + } + } + AnyValue[] setHeaders = resps.getAnyValues("setheader"); + if (setHeaders.length > 0) { + for (AnyValue setHeader : setHeaders) { + String val = setHeader.getValue("value"); + if (val == null) continue; + if (val.startsWith("request.parameters.")) { + defaultSetHeaders.add(new String[]{setHeader.getValue("name"), val, val.substring("request.parameters.".length()), null}); + } else if (val.startsWith("request.headers.")) { + defaultSetHeaders.add(new String[]{setHeader.getValue("name"), val, val.substring("request.headers.".length())}); + } else if (val.startsWith("system.property.")) { + String v = System.getProperty(val.substring("system.property.".length())); + if (v != null) defaultSetHeaders.add(new String[]{setHeader.getValue("name"), v}); + } else { + defaultSetHeaders.add(new String[]{setHeader.getValue("name"), val}); + } + } + } + AnyValue defcookieValue = resps.getAnyValue("defcookie"); + if (defcookieValue != null) { + String domain = defcookieValue.getValue("domain"); + String path = defcookieValue.getValue("path"); + if (domain != null || path != null) { + defaultCookie = new HttpCookie("DEFAULTCOOKIE", ""); + defaultCookie.setDomain(domain); + defaultCookie.setPath(path); + } + } + AnyValue options = resps.getAnyValue("options"); + autoOptions = options != null && options.getBoolValue("auto", false); + + AnyValue dates = resps.getAnyValue("date"); + datePeriod = dates == null ? 0 : dates.getIntValue("period", 0); + } + + } + Supplier dateSupplier = null; + if (datePeriod == 0) { + final ZoneId gmtZone = ZoneId.of("GMT"); + dateSupplier = () -> ("Date: " + RFC_1123_DATE_TIME.format(java.time.ZonedDateTime.now(gmtZone)) + "\r\n").getBytes(); + } else if (datePeriod > 0) { + if (this.dateScheduler == null) { + this.dateScheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { + final Thread t = new Thread(r, "Redkale-HTTP:" + port + "-DateSchedule-Thread"); + t.setDaemon(true); + return t; + }); + final DateFormat gmtDateFormat = new SimpleDateFormat("EEE, d MMM y HH:mm:ss z", Locale.ENGLISH); + gmtDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); + currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes(); + final int dp = datePeriod; + this.dateScheduler.scheduleAtFixedRate(() -> { + try { + currDateBytes = ("Date: " + gmtDateFormat.format(new Date()) + "\r\n").getBytes(); + } catch (Throwable t) { + logger.log(Level.SEVERE, "HttpServer schedule(interval=" + dp + "ms) date-format error", t); + } + }, 1000 - System.currentTimeMillis() % 1000, datePeriod, TimeUnit.MILLISECONDS); + dateSupplier = () -> currDateBytes; + } + } + HttpRender httpRender = null; + AnyValue renderConfig = null; + { //设置TemplateEngine + renderConfig = config.getAnyValue("render"); + if (renderConfig != null) { + String renderType = renderConfig.getValue("value"); + try { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass(renderType); + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, clazz.getName()); + HttpRender render = (HttpRender) clazz.getDeclaredConstructor().newInstance(); + getResourceFactory().inject(render); + httpRender = render; + } catch (Throwable e) { + logger.log(Level.WARNING, "init HttpRender(" + renderType + ") error", e); + } + } + } + + final String addrHeader = remoteAddrHeader; + + this.respConfig = new HttpResponseConfig(); + respConfig.plainContentType = plainContentType; + respConfig.jsonContentType = jsonContentType; + respConfig.defaultAddHeaders = defaultAddHeaders.isEmpty() ? null : defaultAddHeaders.toArray(new String[defaultAddHeaders.size()][]); + respConfig.defaultSetHeaders = defaultSetHeaders.isEmpty() ? null : defaultSetHeaders.toArray(new String[defaultSetHeaders.size()][]); + respConfig.defaultCookie = defaultCookie; + respConfig.autoOptions = autoOptions; + respConfig.dateSupplier = dateSupplier; + respConfig.httpRender = httpRender; + respConfig.init(config); + + final HttpContextConfig contextConfig = new HttpContextConfig(); + initContextConfig(contextConfig); + contextConfig.remoteAddrHeader = addrHeader; + + HttpContext c = new HttpContext(contextConfig); + if (httpRender != null) httpRender.init(c, renderConfig); + return c; + } + + @Override + protected ObjectPool createBufferPool(LongAdder createCounter, LongAdder cycleCounter, int bufferPoolSize) { + final int rcapacity = this.bufferCapacity; + ObjectPool bufferPool = ObjectPool.createSafePool(createCounter, cycleCounter, bufferPoolSize, + (Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> { + if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false; + e.clear(); + return true; + }); + return bufferPool; + } + + @Override + protected ObjectPool createResponsePool(LongAdder createCounter, LongAdder cycleCounter, int responsePoolSize) { + Creator creator = (Object... params) -> new HttpResponse(this.context, new HttpRequest(this.context), this.respConfig); + ObjectPool pool = ObjectPool.createSafePool(createCounter, cycleCounter, responsePoolSize, creator, (x) -> ((HttpResponse) x).prepare(), (x) -> ((HttpResponse) x).recycle()); + return pool; + } +} diff --git a/src/org/redkale/net/http/HttpServlet.java b/src/main/java/org/redkale/net/http/HttpServlet.java similarity index 91% rename from src/org/redkale/net/http/HttpServlet.java rename to src/main/java/org/redkale/net/http/HttpServlet.java index 4797ae3f7..6159843b3 100644 --- a/src/org/redkale/net/http/HttpServlet.java +++ b/src/main/java/org/redkale/net/http/HttpServlet.java @@ -1,449 +1,462 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.IOException; -import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.*; -import org.redkale.asm.*; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; -import static org.redkale.asm.Opcodes.*; -import org.redkale.net.*; -import org.redkale.service.RetResult; -import org.redkale.util.*; - -/** - * HTTP版的Servlet, 执行顺序 execute --> preExecute --> authenticate --> HttpMapping对应的方法 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class HttpServlet extends Servlet { - - public static final int RET_SERVER_ERROR = 1200_0001; - - public static final int RET_METHOD_ERROR = 1200_0002; - - String _prefix = ""; //当前HttpServlet的path前缀 - - HashMap _actionmap; //Rest生成时赋值, 字段名Rest有用到 - - private Map.Entry[] mappings; //字段名Rest有用到 - - //这里不能直接使用HttpServlet,会造成死循环初始化HttpServlet - private final Servlet authSuccessServlet = new Servlet() { - @Override - public void execute(HttpRequest request, HttpResponse response) throws IOException { - ActionEntry entry = request.actionEntry; - if (entry.rpconly && !request.rpc) { - response.finish(503, null); - return; - } - if (entry.cacheseconds > 0) {//有缓存设置 - CacheEntry ce = entry.modeOneCache ? entry.oneCache : entry.cache.get(request.getRequestURI()); - if (ce != null && ce.time + entry.cacheseconds * 1000 > System.currentTimeMillis()) { //缓存有效 - response.setStatus(ce.status); - response.setContentType(ce.contentType); - response.skipHeader(); - response.finish(ce.getBytes()); - return; - } - response.setCacheHandler(entry.cacheHandler); - } - entry.servlet.execute(request, response); - } - }; - - //preExecute运行完后执行的Servlet - private final Servlet preSuccessServlet = new Servlet() { - @Override - public void execute(HttpRequest request, HttpResponse response) throws IOException { - if (request.actionEntry != null) { - ActionEntry entry = request.actionEntry; - if (!entry.checkMethod(request.getMethod())) { - response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error")); - return; - } - request.moduleid = entry.moduleid; - request.actionid = entry.actionid; - request.annotations = entry.annotations; - if (entry.auth) { - response.thenEvent(authSuccessServlet); - authenticate(request, response); - } else { - authSuccessServlet.execute(request, response); - } - return; - } - for (Map.Entry en : mappings) { - if (request.getRequestURI().startsWith(en.getKey())) { - ActionEntry entry = en.getValue(); - if (!entry.checkMethod(request.getMethod())) { - response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error")); - return; - } - request.actionEntry = entry; - request.moduleid = entry.moduleid; - request.actionid = entry.actionid; - request.annotations = entry.annotations; - if (entry.auth) { - response.thenEvent(authSuccessServlet); - authenticate(request, response); - } else { - authSuccessServlet.execute(request, response); - } - return; - } - } - response.finish404(); - //throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")"); - } - }; - - @SuppressWarnings("unchecked") - void preInit(HttpContext context, AnyValue config) { - if (this.mappings != null) return; //无需重复preInit - String path = _prefix == null ? "" : _prefix; - WebServlet ws = this.getClass().getAnnotation(WebServlet.class); - if (ws != null && !ws.repair()) path = ""; - HashMap map = this._actionmap != null ? this._actionmap : loadActionEntry(); - this.mappings = new Map.Entry[map.size()]; - int i = -1; - for (Map.Entry en : map.entrySet()) { - mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue()); - } - //必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query - Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey())); - } - - void postDestroy(HttpContext context, AnyValue config) { - } - - //Server执行start后运行此方法 - public void postStart(HttpContext context, AnyValue config) { - } - - /** - *

    - * 预执行方法,在execute方法之前运行,设置当前用户信息,或者加入常规统计和基础检测,例如 :
    - *

    -     *      @Override
    -     *      public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
    -     *          //设置当前用户信息
    -     *          final String sessionid = request.getSessionid(false);
    -     *          if (sessionid != null) request.setCurrentUserid(userService.currentUserid(sessionid));
    -     *
    -     *          if (finer) response.recycleListener((req, resp) -> {  //记录处理时间比较长的请求
    -     *              long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
    -     *              if (e > 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
    -     *          });
    -     *          response.nextEvent();
    -     *      }
    -     * 
    - *

    - * - * @param request HttpRequest - * @param response HttpResponse - * - * @throws IOException IOException - */ - protected void preExecute(HttpRequest request, HttpResponse response) throws IOException { - response.nextEvent(); - } - - /** - *

    - * 用户登录或权限验证, 注解为@HttpMapping.auth == true 的方法会执行authenticate方法, 若验证成功则必须调用response.nextEvent();进行下一步操作, 例如:
    - *

    -     *      @Override
    -     *      public void authenticate(HttpRequest request, HttpResponse response) throws IOException {
    -     *          Serializable userid = request.currentUserid();
    -     *          if (userid == null) {
    -     *              response.finishJson(RET_UNLOGIN);
    -     *              return;
    -     *          }
    -     *          response.nextEvent();
    -     *      }
    -     * 
    - *

    - * - * - * @param request HttpRequest - * @param response HttpResponse - * - * @throws IOException IOException - */ - protected void authenticate(HttpRequest request, HttpResponse response) throws IOException { - response.nextEvent(); - } - - @Override - public void execute(HttpRequest request, HttpResponse response) throws IOException { - response.thenEvent(preSuccessServlet); - preExecute(request, response); - } - - private HashMap loadActionEntry() { - WebServlet module = this.getClass().getAnnotation(WebServlet.class); - final int serviceid = module == null ? 0 : module.moduleid(); - final HashMap map = new HashMap<>(); - HashMap nameset = new HashMap<>(); - final Class selfClz = this.getClass(); - Class clz = this.getClass(); - do { - if (java.lang.reflect.Modifier.isAbstract(clz.getModifiers())) break; - for (final Method method : clz.getMethods()) { - //----------------------------------------------- - String methodname = method.getName(); - if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue; - //----------------------------------------------- - Class[] paramTypes = method.getParameterTypes(); - if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class || paramTypes[1] != HttpResponse.class) continue; - //----------------------------------------------- - Class[] exps = method.getExceptionTypes(); - if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue; - //----------------------------------------------- - - final HttpMapping mapping = method.getAnnotation(HttpMapping.class); - if (mapping == null) continue; - final boolean inherited = mapping.inherited(); - if (!inherited && selfClz != clz) continue; //忽略不被继承的方法 - final int actionid = mapping.actionid(); - final String name = mapping.url().trim(); - final String[] methods = mapping.methods(); - if (nameset.containsKey(name)) { - if (nameset.get(name) != clz) continue; - throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + HttpMapping.class.getSimpleName() + "(" + name + ")"); - } - nameset.put(name, clz); - map.put(name, new ActionEntry(serviceid, actionid, name, methods, method, createActionServlet(method))); - } - } while ((clz = clz.getSuperclass()) != HttpServlet.class); - return map; - } - - protected static final class ActionEntry { - - ActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) { - this(moduleid, actionid, name, methods, method, rpconly(method), auth(method), cacheseconds(method), servlet); - this.annotations = annotations(method); - } - - //供Rest类使用,参数不能随便更改 - public ActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean rpconly, boolean auth, int cacheseconds, HttpServlet servlet) { - this.moduleid = moduleid; - this.actionid = actionid; - this.name = name; - this.methods = methods; - this.method = method; //rest构建会为null - this.servlet = servlet; - this.rpconly = rpconly; - this.auth = auth; - this.cacheseconds = cacheseconds; - if (Utility.contains(name, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\') || name.endsWith("/")) { //是否是正则表达式 - this.modeOneCache = false; - this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null; - this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, byte[] content) -> { - int status = response.getStatus(); - if (status != 200) return; - CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), content); - cache.put(response.getRequest().getRequestURI(), ce); - } : null; - } else { //单一url - this.modeOneCache = true; - this.cache = null; - this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, byte[] content) -> { - int status = response.getStatus(); - if (status != 200) return; - oneCache = new CacheEntry(response.getStatus(), response.getContentType(), content); - } : null; - } - } - - protected static boolean auth(Method method) { - HttpMapping mapping = method.getAnnotation(HttpMapping.class); - return mapping == null || mapping.auth(); - } - - protected static boolean rpconly(Method method) { - HttpMapping mapping = method.getAnnotation(HttpMapping.class); - return mapping == null || mapping.rpconly(); - } - - protected static int cacheseconds(Method method) { - HttpMapping mapping = method.getAnnotation(HttpMapping.class); - return mapping == null ? 0 : mapping.cacheseconds(); - } - - //Rest.class会用到此方法 - protected static Annotation[] annotations(Method method) { - return method.getAnnotations(); - } - - boolean isNeedCheck() { - return this.moduleid != 0 || this.actionid != 0; - } - - boolean checkMethod(final String reqMethod) { - if (methods.length == 0) return true; - for (String m : methods) { - if (reqMethod.equalsIgnoreCase(m)) return true; - } - return false; - } - - final BiConsumer cacheHandler; - - final ConcurrentHashMap cache; - - final boolean modeOneCache; - - final int cacheseconds; - - final boolean rpconly; - - final boolean auth; - - final int moduleid; - - final int actionid; - - final String name; - - final String[] methods; - - final HttpServlet servlet; - - Method method; - - CacheEntry oneCache; - - Annotation[] annotations; - } - - private HttpServlet createActionServlet(final Method method) { - //------------------------------------------------------------------------------ - final String supDynName = HttpServlet.class.getName().replace('.', '/'); - final String interName = this.getClass().getName().replace('.', '/'); - final String interDesc = org.redkale.asm.Type.getDescriptor(this.getClass()); - final String requestSupDesc = org.redkale.asm.Type.getDescriptor(Request.class); - final String responseSupDesc = org.redkale.asm.Type.getDescriptor(Response.class); - final String requestDesc = org.redkale.asm.Type.getDescriptor(HttpRequest.class); - final String responseDesc = org.redkale.asm.Type.getDescriptor(HttpResponse.class); - String newDynName = interName + "_Dyn_" + method.getName(); - int i = 0; - for (;;) { - try { - Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.')); - newDynName += "_" + (++i); - } catch (Throwable ex) { - break; - } - } - //------------------------------------------------------------------------------ - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodVisitor mv; - AnnotationVisitor av0; - final String factfield = "_factServlet"; - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); - { - fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null); - fv.visitEnd(); - } - { //构造函数 - mv = (cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { - mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"})); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - { - mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"}); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/')); - mv.visitVarInsn(ALOAD, 2); - mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/')); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - cw.visitEnd(); - //------------------------------------------------------------------------------ - byte[] bytes = cw.toByteArray(); - Class newClazz = new ClassLoader(this.getClass().getClassLoader()) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - HttpServlet instance = (HttpServlet) newClazz.getDeclaredConstructor().newInstance(); - instance.getClass().getField(factfield).set(instance, this); - return instance; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - private static final class CacheEntry { - - public final long time = System.currentTimeMillis(); - - private final byte[] cacheBytes; - - private final int status; - - private final String contentType; - - public CacheEntry(int status, String contentType, byte[] cacheBytes) { - this.status = status; - this.contentType = contentType; - this.cacheBytes = cacheBytes; - } - - public byte[] getBytes() { - return cacheBytes; - } - } - - static class HttpActionServlet extends HttpServlet { - - final ActionEntry action; - - final HttpServlet servlet; - - public HttpActionServlet(ActionEntry actionEntry, HttpServlet servlet) { - this.action = actionEntry; - this.servlet = servlet; - } - - @Override - public void execute(HttpRequest request, HttpResponse response) throws IOException { - request.actionEntry = action; - servlet.execute(request, response); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.*; +import org.redkale.asm.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import static org.redkale.asm.Opcodes.*; +import org.redkale.boot.Application; +import org.redkale.net.*; +import org.redkale.service.RetResult; +import org.redkale.util.*; + +/** + * HTTP版的Servlet, 执行顺序 execute --> preExecute --> authenticate --> HttpMapping对应的方法 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class HttpServlet extends Servlet { + + public static final int RET_SERVER_ERROR = 1200_0001; + + public static final int RET_METHOD_ERROR = 1200_0002; + + String _prefix = ""; //当前HttpServlet的path前缀 + + String _reqtopic; //根据RestService+MQ生成的值 @since 2.5.0 + + String _mmctopic; //根据RestService+@MessageMultiConsumer生成的值 @since 2.5.0 + + HashMap _actionmap; //Rest生成时赋值, 字段名Rest有用到 + + private Map.Entry[] mappings; //字段名Rest有用到 + + //这里不能直接使用HttpServlet,会造成死循环初始化HttpServlet + private final Servlet authSuccessServlet = new Servlet() { + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + ActionEntry entry = request.actionEntry; + if (entry.rpconly && !request.rpc) { + response.finish(503, null); + return; + } + if (entry.cacheseconds > 0) {//有缓存设置 + CacheEntry ce = entry.modeOneCache ? entry.oneCache : entry.cache.get(request.getRequestURI()); + if (ce != null && ce.time + entry.cacheseconds * 1000 > System.currentTimeMillis()) { //缓存有效 + response.setStatus(ce.status); + response.setContentType(ce.contentType); + response.skipHeader(); + response.finish(ce.getBytes()); + return; + } + response.setCacheHandler(entry.cacheHandler); + } + entry.servlet.execute(request, response); + } + }; + + //preExecute运行完后执行的Servlet + private final Servlet preSuccessServlet = new Servlet() { + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + if (request.actionEntry != null) { + ActionEntry entry = request.actionEntry; + if (!entry.checkMethod(request.getMethod())) { + response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error")); + return; + } + request.moduleid = entry.moduleid; + request.actionid = entry.actionid; + request.annotations = entry.annotations; + if (entry.auth) { + response.thenEvent(authSuccessServlet); + authenticate(request, response); + } else { + authSuccessServlet.execute(request, response); + } + return; + } + for (Map.Entry en : mappings) { + if (request.getRequestURI().startsWith(en.getKey())) { + ActionEntry entry = en.getValue(); + if (!entry.checkMethod(request.getMethod())) { + response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error")); + return; + } + request.actionEntry = entry; + request.moduleid = entry.moduleid; + request.actionid = entry.actionid; + request.annotations = entry.annotations; + if (entry.auth) { + response.thenEvent(authSuccessServlet); + authenticate(request, response); + } else { + authSuccessServlet.execute(request, response); + } + return; + } + } + response.finish404(); + //throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")"); + } + }; + + @SuppressWarnings("unchecked") + void preInit(Application application, HttpContext context, AnyValue config) { + if (this.mappings != null) return; //无需重复preInit + String path = _prefix == null ? "" : _prefix; + WebServlet ws = this.getClass().getAnnotation(WebServlet.class); + if (ws != null && !ws.repair()) path = ""; + HashMap map = this._actionmap != null ? this._actionmap : loadActionEntry(); + this.mappings = new Map.Entry[map.size()]; + int i = -1; + for (Map.Entry en : map.entrySet()) { + mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue()); + } + //必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query + Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey())); + } + + void postDestroy(Application application, HttpContext context, AnyValue config) { + } + + //Server执行start后运行此方法 + public void postStart(HttpContext context, AnyValue config) { + } + + /** + *

    + * 预执行方法,在execute方法之前运行,设置当前用户信息,或者加入常规统计和基础检测,例如 :
    + *

    +     *      @Override
    +     *      public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
    +     *          //设置当前用户信息
    +     *          final String sessionid = request.getSessionid(false);
    +     *          if (sessionid != null) request.setCurrentUserid(userService.currentUserid(sessionid));
    +     *
    +     *          if (finer) response.recycleListener((req, resp) -> {  //记录处理时间比较长的请求
    +     *              long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
    +     *              if (e > 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
    +     *          });
    +     *          response.nextEvent();
    +     *      }
    +     * 
    + *

    + * + * @param request HttpRequest + * @param response HttpResponse + * + * @throws IOException IOException + */ + protected void preExecute(HttpRequest request, HttpResponse response) throws IOException { + response.nextEvent(); + } + + /** + *

    + * 用户登录或权限验证, 注解为@HttpMapping.auth == true 的方法会执行authenticate方法, 若验证成功则必须调用response.nextEvent();进行下一步操作, 例如:
    + *

    +     *      @Override
    +     *      public void authenticate(HttpRequest request, HttpResponse response) throws IOException {
    +     *          Serializable userid = request.currentUserid();
    +     *          if (userid == null) {
    +     *              response.finishJson(RET_UNLOGIN);
    +     *              return;
    +     *          }
    +     *          response.nextEvent();
    +     *      }
    +     * 
    + *

    + * + * + * @param request HttpRequest + * @param response HttpResponse + * + * @throws IOException IOException + */ + protected void authenticate(HttpRequest request, HttpResponse response) throws IOException { + response.nextEvent(); + } + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + response.thenEvent(preSuccessServlet); + preExecute(request, response); + } + + private HashMap loadActionEntry() { + WebServlet module = this.getClass().getAnnotation(WebServlet.class); + final int serviceid = module == null ? 0 : module.moduleid(); + final HashMap map = new HashMap<>(); + HashMap nameset = new HashMap<>(); + final Class selfClz = this.getClass(); + Class clz = this.getClass(); + do { + if (java.lang.reflect.Modifier.isAbstract(clz.getModifiers())) break; + RedkaleClassLoader.putReflectionPublicMethods(clz.getName()); + for (final Method method : clz.getMethods()) { + //----------------------------------------------- + String methodname = method.getName(); + if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue; + //----------------------------------------------- + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class || paramTypes[1] != HttpResponse.class) continue; + //----------------------------------------------- + Class[] exps = method.getExceptionTypes(); + if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue; + //----------------------------------------------- + + final HttpMapping mapping = method.getAnnotation(HttpMapping.class); + if (mapping == null) continue; + final boolean inherited = mapping.inherited(); + if (!inherited && selfClz != clz) continue; //忽略不被继承的方法 + final int actionid = mapping.actionid(); + final String name = mapping.url().trim(); + final String[] methods = mapping.methods(); + if (nameset.containsKey(name)) { + if (nameset.get(name) != clz) continue; + throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + HttpMapping.class.getSimpleName() + "(" + name + ")"); + } + nameset.put(name, clz); + map.put(name, new ActionEntry(serviceid, actionid, name, methods, method, createActionServlet(method))); + } + } while ((clz = clz.getSuperclass()) != HttpServlet.class); + return map; + } + + protected static final class ActionEntry { + + ActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) { + this(moduleid, actionid, name, methods, method, rpconly(method), auth(method), cacheseconds(method), servlet); + this.annotations = annotations(method); + } + + //供Rest类使用,参数不能随便更改 + public ActionEntry(int moduleid, int actionid, String name, String[] methods, Method method, boolean rpconly, boolean auth, int cacheseconds, HttpServlet servlet) { + this.moduleid = moduleid; + this.actionid = actionid; + this.name = name; + this.methods = methods; + this.method = method; //rest构建会为null + this.servlet = servlet; + this.rpconly = rpconly; + this.auth = auth; + this.cacheseconds = cacheseconds; + if (Utility.contains(name, '*', '{', '[', '(', '|', '^', '$', '+', '?', '\\') || name.endsWith("/")) { //是否是正则表达式 + this.modeOneCache = false; + this.cache = cacheseconds > 0 ? new ConcurrentHashMap<>() : null; + this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, byte[] content) -> { + int status = response.getStatus(); + if (status != 200) return; + CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), content); + cache.put(response.getRequest().getRequestURI(), ce); + } : null; + } else { //单一url + this.modeOneCache = true; + this.cache = null; + this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, byte[] content) -> { + int status = response.getStatus(); + if (status != 200) return; + oneCache = new CacheEntry(response.getStatus(), response.getContentType(), content); + } : null; + } + } + + protected static boolean auth(Method method) { + HttpMapping mapping = method.getAnnotation(HttpMapping.class); + return mapping == null || mapping.auth(); + } + + protected static boolean rpconly(Method method) { + HttpMapping mapping = method.getAnnotation(HttpMapping.class); + return mapping == null || mapping.rpconly(); + } + + protected static int cacheseconds(Method method) { + HttpMapping mapping = method.getAnnotation(HttpMapping.class); + return mapping == null ? 0 : mapping.cacheseconds(); + } + + //Rest.class会用到此方法 + protected static Annotation[] annotations(Method method) { + return method.getAnnotations(); + } + + boolean isNeedCheck() { + return this.moduleid != 0 || this.actionid != 0; + } + + boolean checkMethod(final String reqMethod) { + if (methods.length == 0) return true; + for (String m : methods) { + if (reqMethod.equalsIgnoreCase(m)) return true; + } + return false; + } + + final BiConsumer cacheHandler; + + final ConcurrentHashMap cache; + + final boolean modeOneCache; + + final int cacheseconds; + + final boolean rpconly; + + final boolean auth; + + final int moduleid; + + final int actionid; + + final String name; + + final String[] methods; + + final HttpServlet servlet; + + Method method; + + CacheEntry oneCache; + + Annotation[] annotations; + } + + private HttpServlet createActionServlet(final Method method) { + //------------------------------------------------------------------------------ + final String supDynName = HttpServlet.class.getName().replace('.', '/'); + final String interName = this.getClass().getName().replace('.', '/'); + final String interDesc = org.redkale.asm.Type.getDescriptor(this.getClass()); + final String requestSupDesc = org.redkale.asm.Type.getDescriptor(Request.class); + final String responseSupDesc = org.redkale.asm.Type.getDescriptor(Response.class); + final String requestDesc = org.redkale.asm.Type.getDescriptor(HttpRequest.class); + final String responseDesc = org.redkale.asm.Type.getDescriptor(HttpResponse.class); + final String factfield = "_factServlet"; + StringBuilder tmpps = new StringBuilder(); + for (Class cz : method.getParameterTypes()) { + tmpps.append("__").append(cz.getName().replace('.', '_')); + } + final String newDynName = "org/redkaledyn/http/servlet/action/_DynHttpActionServlet__" + this.getClass().getName().replace('.', '_').replace('$', '_') + "__" + method.getName() + tmpps; + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + Class newClazz = clz == null ? Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.')) : clz; + HttpServlet instance = (HttpServlet) newClazz.getDeclaredConstructor().newInstance(); + instance.getClass().getField("_factServlet").set(instance, this); + return instance; + } catch (Throwable ex) { + } + //------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); + { + fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null); + fv.visitEnd(); + } + { //构造函数 + mv = (cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"})); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"}); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/')); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/')); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw.visitEnd(); + //------------------------------------------------------------------------------ + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(this.getClass().getClassLoader()) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + try { + HttpServlet instance = (HttpServlet) newClazz.getDeclaredConstructor().newInstance(); + java.lang.reflect.Field field = instance.getClass().getField(factfield); + field.set(instance, this); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), field); + return instance; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + private static final class CacheEntry { + + public final long time = System.currentTimeMillis(); + + private final byte[] cacheBytes; + + private final int status; + + private final String contentType; + + public CacheEntry(int status, String contentType, byte[] cacheBytes) { + this.status = status; + this.contentType = contentType; + this.cacheBytes = cacheBytes; + } + + public byte[] getBytes() { + return cacheBytes; + } + } + + static class HttpActionServlet extends HttpServlet { + + final ActionEntry action; + + final HttpServlet servlet; + + public HttpActionServlet(ActionEntry actionEntry, HttpServlet servlet) { + this.action = actionEntry; + this.servlet = servlet; + } + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + request.actionEntry = action; + servlet.execute(request, response); + } + } +} diff --git a/src/org/redkale/net/http/HttpSimpleRequest.java b/src/main/java/org/redkale/net/http/HttpSimpleRequest.java similarity index 91% rename from src/org/redkale/net/http/HttpSimpleRequest.java rename to src/main/java/org/redkale/net/http/HttpSimpleRequest.java index d4ab4dbbf..737fe1dbc 100644 --- a/src/org/redkale/net/http/HttpSimpleRequest.java +++ b/src/main/java/org/redkale/net/http/HttpSimpleRequest.java @@ -1,398 +1,396 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.*; - -/** - * HttpRequest的缩减版, 只提供部分字段 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class HttpSimpleRequest implements java.io.Serializable { - - @ConvertColumn(index = 1) - @Comment("是否RPC请求, 该类通常是为RPC创建的,故默认是true") - protected boolean rpc = true; - - @ConvertColumn(index = 2) - @Comment("是否从body中获取参数,比如protobuf数据格式") - protected boolean frombody; - - @ConvertColumn(index = 3) - @Comment("请求参数的ConvertType") - protected ConvertType reqConvertType; - - @ConvertColumn(index = 4) - @Comment("输出结果的ConvertType") - protected ConvertType respConvertType; - - @ConvertColumn(index = 5) - @Comment("请求的URI") - protected String requestURI; - - @ConvertColumn(index = 6) - @Comment("请求的前缀") - protected String path; - - @ConvertColumn(index = 7) - @Comment("客户端IP") - protected String remoteAddr; - - @ConvertColumn(index = 8) - @Comment("会话ID") - protected String sessionid; - - @ConvertColumn(index = 9) - @Comment("Content-Type") - protected String contentType; - - @ConvertColumn(index = 10) - protected int hashid; - - @ConvertColumn(index = 11) - protected int currentUserid; - - @ConvertColumn(index = 12) - @Comment("http header信息") - protected Map headers; - - @ConvertColumn(index = 13) - @Comment("参数信息") - protected Map params; - - @ConvertColumn(index = 14) - @Comment("http body信息") - protected byte[] body; //对应HttpRequest.array - - public static HttpSimpleRequest create(String requestURI) { - return new HttpSimpleRequest().requestURI(requestURI); - } - - public static HttpSimpleRequest create(String requestURI, Object... params) { - HttpSimpleRequest req = new HttpSimpleRequest().requestURI(requestURI); - int len = params.length / 2; - for (int i = 0; i < len; i++) { - req.param(params[i * 2].toString(), params[i * 2 + 1]); - } - return req; - } - - @ConvertDisabled - public String getParametersToString() { - if (this.params == null || this.params.isEmpty()) return null; - final StringBuilder sb = new StringBuilder(); - AtomicBoolean no2 = new AtomicBoolean(false); - this.params.forEach((n, v) -> { - if (no2.get()) sb.append('&'); //JDK9+ 可直接用 URLEncoder.encode(v, StandardCharsets.UTF_8) - try { - sb.append(n).append('=').append(URLEncoder.encode(v, "UTF-8")); - } catch (UnsupportedEncodingException e) { - } - no2.set(true); - }); - return sb.toString(); - } - - public HttpSimpleRequest rpc(boolean rpc) { - this.rpc = rpc; - return this; - } - - public HttpSimpleRequest requestURI(String requestURI) { - this.requestURI = requestURI; - return this; - } - - public HttpSimpleRequest path(String path) { - this.path = path; - return this; - } - - public HttpSimpleRequest requestURI(boolean frombody) { - this.frombody = frombody; - return this; - } - - public HttpSimpleRequest frombody(boolean frombody) { - this.frombody = frombody; - return this; - } - - public HttpSimpleRequest bothConvertType(ConvertType convertType) { - this.reqConvertType = convertType; - this.respConvertType = convertType; - return this; - } - - public HttpSimpleRequest reqConvertType(ConvertType reqConvertType) { - this.reqConvertType = reqConvertType; - return this; - } - - public HttpSimpleRequest respConvertType(ConvertType respConvertType) { - this.respConvertType = respConvertType; - return this; - } - - public HttpSimpleRequest remoteAddr(String remoteAddr) { - this.remoteAddr = remoteAddr; - return this; - } - - public HttpSimpleRequest sessionid(String sessionid) { - this.sessionid = sessionid; - return this; - } - - public HttpSimpleRequest contentType(String contentType) { - this.contentType = contentType; - return this; - } - - public HttpSimpleRequest hashid(int hashid) { - this.hashid = hashid; - return this; - } - - public HttpSimpleRequest currentUserid(int userid) { - this.currentUserid = userid; - return this; - } - - public HttpSimpleRequest removeHeader(String name) { - if (this.headers != null) this.headers.remove(name); - return this; - } - - public HttpSimpleRequest removeParam(String name) { - if (this.params != null) this.params.remove(name); - return this; - } - - public HttpSimpleRequest headers(Map headers) { - this.headers = headers; - return this; - } - - public HttpSimpleRequest params(Map params) { - this.params = params; - return this; - } - - public HttpSimpleRequest header(String key, String value) { - if (this.headers == null) this.headers = new HashMap<>(); - this.headers.put(key, value); - return this; - } - - public HttpSimpleRequest header(String key, JsonConvert convert, Object value) { - if (value == null) return this; - if (this.headers == null) this.headers = new HashMap<>(); - if (convert == null) convert = JsonConvert.root(); - this.headers.put(key, convert.convertTo(value)); - return this; - } - - public HttpSimpleRequest header(String key, Object value) { - if (value == null) return this; - if (this.headers == null) this.headers = new HashMap<>(); - this.headers.put(key, JsonConvert.root().convertTo(value)); - return this; - } - - public HttpSimpleRequest header(String key, int value) { - if (this.headers == null) this.headers = new HashMap<>(); - this.headers.put(key, String.valueOf(value)); - return this; - } - - public HttpSimpleRequest header(String key, long value) { - if (this.headers == null) this.headers = new HashMap<>(); - this.headers.put(key, String.valueOf(value)); - return this; - } - - public HttpSimpleRequest param(String key, String value) { - if (this.params == null) this.params = new HashMap<>(); - this.params.put(key, value); - return this; - } - - public HttpSimpleRequest param(String key, JsonConvert convert, Object value) { - if (value == null) return this; - if (this.params == null) this.params = new HashMap<>(); - if (convert == null) convert = JsonConvert.root(); - this.params.put(key, convert.convertTo(value)); - return this; - } - - public HttpSimpleRequest param(String key, Object value) { - if (value == null) return this; - if (this.params == null) this.params = new HashMap<>(); - this.params.put(key, value instanceof CharSequence ? value.toString() : JsonConvert.root().convertTo(value)); - return this; - } - - public HttpSimpleRequest body(byte[] body) { - this.body = body; - return this; - } - - public HttpSimpleRequest clearParams() { - this.params = null; - return this; - } - - public HttpSimpleRequest clearHeaders() { - this.headers = null; - return this; - } - - public HttpSimpleRequest clearRemoteAddr() { - this.remoteAddr = null; - return this; - } - - public HttpSimpleRequest clearSessionid() { - this.sessionid = null; - return this; - } - - public HttpSimpleRequest clearContentType() { - this.contentType = null; - return this; - } - - public boolean isRpc() { - return rpc; - } - - public void setRpc(boolean rpc) { - this.rpc = rpc; - } - - public String getRequestURI() { - return requestURI; - } - - public void setRequestURI(String requestURI) { - this.requestURI = requestURI; - } - - public String getPath() { - return path; - } - - public void setPath(String path) { - this.path = path; - } - - public String getSessionid() { - return sessionid; - } - - public void setSessionid(String sessionid) { - this.sessionid = sessionid; - } - - public String getRemoteAddr() { - return remoteAddr; - } - - public void setRemoteAddr(String remoteAddr) { - this.remoteAddr = remoteAddr; - } - - public int getHashid() { - return hashid; - } - - public void setHashid(int hashid) { - this.hashid = hashid; - } - - public int getCurrentUserid() { - return currentUserid; - } - - public void setCurrentUserid(int currentUserid) { - this.currentUserid = currentUserid; - } - - public String getContentType() { - return contentType; - } - - public void setContentType(String contentType) { - this.contentType = contentType; - } - - public Map getHeaders() { - return headers; - } - - public void setHeaders(Map headers) { - this.headers = headers; - } - - public Map getParams() { - return params; - } - - public void setParams(Map params) { - this.params = params; - } - - public byte[] getBody() { - return body; - } - - public void setBody(byte[] body) { - this.body = body; - } - - public boolean isFrombody() { - return frombody; - } - - public void setFrombody(boolean frombody) { - this.frombody = frombody; - } - - public ConvertType getReqConvertType() { - return reqConvertType; - } - - public void setReqConvertType(ConvertType reqConvertType) { - this.reqConvertType = reqConvertType; - } - - public ConvertType getRespConvertType() { - return respConvertType; - } - - public void setRespConvertType(ConvertType respConvertType) { - this.respConvertType = respConvertType; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.*; + +/** + * HttpRequest的缩减版, 只提供部分字段 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class HttpSimpleRequest implements java.io.Serializable { + + @ConvertColumn(index = 1) + @Comment("是否RPC请求, 该类通常是为RPC创建的,故默认是true") + protected boolean rpc = true; + + @ConvertColumn(index = 2) + @Comment("是否从body中获取参数,比如protobuf数据格式") + protected boolean frombody; + + @ConvertColumn(index = 3) + @Comment("请求参数的ConvertType") + protected ConvertType reqConvertType; + + @ConvertColumn(index = 4) + @Comment("输出结果的ConvertType") + protected ConvertType respConvertType; + + @ConvertColumn(index = 5) + @Comment("请求的URI") + protected String requestURI; + + @ConvertColumn(index = 6) + @Comment("请求的前缀") + protected String path; + + @ConvertColumn(index = 7) + @Comment("客户端IP") + protected String remoteAddr; + + @ConvertColumn(index = 8) + @Comment("会话ID") + protected String sessionid; + + @ConvertColumn(index = 9) + @Comment("Content-Type") + protected String contentType; + + @ConvertColumn(index = 10) + protected int hashid; + + @ConvertColumn(index = 11) //@since 2.5.0 由int改成Serializable, 具体数据类型只能是int、long、String + protected Serializable currentUserid; + + @ConvertColumn(index = 12) + @Comment("http header信息") + protected Map headers; + + @ConvertColumn(index = 13) + @Comment("参数信息") + protected Map params; + + @ConvertColumn(index = 14) + @Comment("http body信息") + protected byte[] body; //对应HttpRequest.array + + public static HttpSimpleRequest create(String requestURI) { + return new HttpSimpleRequest().requestURI(requestURI); + } + + public static HttpSimpleRequest create(String requestURI, Object... params) { + HttpSimpleRequest req = new HttpSimpleRequest().requestURI(requestURI); + int len = params.length / 2; + for (int i = 0; i < len; i++) { + req.param(params[i * 2].toString(), params[i * 2 + 1]); + } + return req; + } + + @ConvertDisabled + public String getParametersToString() { + if (this.params == null || this.params.isEmpty()) return null; + final StringBuilder sb = new StringBuilder(); + AtomicBoolean no2 = new AtomicBoolean(false); + this.params.forEach((n, v) -> { + if (no2.get()) sb.append('&'); + sb.append(n).append('=').append(URLEncoder.encode(v, StandardCharsets.UTF_8)); + no2.set(true); + }); + return sb.toString(); + } + + public HttpSimpleRequest rpc(boolean rpc) { + this.rpc = rpc; + return this; + } + + public HttpSimpleRequest requestURI(String requestURI) { + this.requestURI = requestURI; + return this; + } + + public HttpSimpleRequest path(String path) { + this.path = path; + return this; + } + + public HttpSimpleRequest requestURI(boolean frombody) { + this.frombody = frombody; + return this; + } + + public HttpSimpleRequest frombody(boolean frombody) { + this.frombody = frombody; + return this; + } + + public HttpSimpleRequest bothConvertType(ConvertType convertType) { + this.reqConvertType = convertType; + this.respConvertType = convertType; + return this; + } + + public HttpSimpleRequest reqConvertType(ConvertType reqConvertType) { + this.reqConvertType = reqConvertType; + return this; + } + + public HttpSimpleRequest respConvertType(ConvertType respConvertType) { + this.respConvertType = respConvertType; + return this; + } + + public HttpSimpleRequest remoteAddr(String remoteAddr) { + this.remoteAddr = remoteAddr; + return this; + } + + public HttpSimpleRequest sessionid(String sessionid) { + this.sessionid = sessionid; + return this; + } + + public HttpSimpleRequest contentType(String contentType) { + this.contentType = contentType; + return this; + } + + public HttpSimpleRequest hashid(int hashid) { + this.hashid = hashid; + return this; + } + + public HttpSimpleRequest currentUserid(Serializable userid) { + this.currentUserid = userid; + return this; + } + + public HttpSimpleRequest removeHeader(String name) { + if (this.headers != null) this.headers.remove(name); + return this; + } + + public HttpSimpleRequest removeParam(String name) { + if (this.params != null) this.params.remove(name); + return this; + } + + public HttpSimpleRequest headers(Map headers) { + this.headers = headers; + return this; + } + + public HttpSimpleRequest params(Map params) { + this.params = params; + return this; + } + + public HttpSimpleRequest header(String key, String value) { + if (this.headers == null) this.headers = new HashMap<>(); + this.headers.put(key, value); + return this; + } + + public HttpSimpleRequest header(String key, JsonConvert convert, Object value) { + if (value == null) return this; + if (this.headers == null) this.headers = new HashMap<>(); + if (convert == null) convert = JsonConvert.root(); + this.headers.put(key, convert.convertTo(value)); + return this; + } + + public HttpSimpleRequest header(String key, Object value) { + if (value == null) return this; + if (this.headers == null) this.headers = new HashMap<>(); + this.headers.put(key, JsonConvert.root().convertTo(value)); + return this; + } + + public HttpSimpleRequest header(String key, int value) { + if (this.headers == null) this.headers = new HashMap<>(); + this.headers.put(key, String.valueOf(value)); + return this; + } + + public HttpSimpleRequest header(String key, long value) { + if (this.headers == null) this.headers = new HashMap<>(); + this.headers.put(key, String.valueOf(value)); + return this; + } + + public HttpSimpleRequest param(String key, String value) { + if (this.params == null) this.params = new HashMap<>(); + this.params.put(key, value); + return this; + } + + public HttpSimpleRequest param(String key, JsonConvert convert, Object value) { + if (value == null) return this; + if (this.params == null) this.params = new HashMap<>(); + if (convert == null) convert = JsonConvert.root(); + this.params.put(key, convert.convertTo(value)); + return this; + } + + public HttpSimpleRequest param(String key, Object value) { + if (value == null) return this; + if (this.params == null) this.params = new HashMap<>(); + this.params.put(key, value instanceof CharSequence ? value.toString() : JsonConvert.root().convertTo(value)); + return this; + } + + public HttpSimpleRequest body(byte[] body) { + this.body = body; + return this; + } + + public HttpSimpleRequest clearParams() { + this.params = null; + return this; + } + + public HttpSimpleRequest clearHeaders() { + this.headers = null; + return this; + } + + public HttpSimpleRequest clearRemoteAddr() { + this.remoteAddr = null; + return this; + } + + public HttpSimpleRequest clearSessionid() { + this.sessionid = null; + return this; + } + + public HttpSimpleRequest clearContentType() { + this.contentType = null; + return this; + } + + public boolean isRpc() { + return rpc; + } + + public void setRpc(boolean rpc) { + this.rpc = rpc; + } + + public String getRequestURI() { + return requestURI; + } + + public void setRequestURI(String requestURI) { + this.requestURI = requestURI; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getSessionid() { + return sessionid; + } + + public void setSessionid(String sessionid) { + this.sessionid = sessionid; + } + + public String getRemoteAddr() { + return remoteAddr; + } + + public void setRemoteAddr(String remoteAddr) { + this.remoteAddr = remoteAddr; + } + + public int getHashid() { + return hashid; + } + + public void setHashid(int hashid) { + this.hashid = hashid; + } + + public Serializable getCurrentUserid() { + return currentUserid; + } + + public void setCurrentUserid(Serializable currentUserid) { + this.currentUserid = currentUserid; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public Map getParams() { + return params; + } + + public void setParams(Map params) { + this.params = params; + } + + public byte[] getBody() { + return body; + } + + public void setBody(byte[] body) { + this.body = body; + } + + public boolean isFrombody() { + return frombody; + } + + public void setFrombody(boolean frombody) { + this.frombody = frombody; + } + + public ConvertType getReqConvertType() { + return reqConvertType; + } + + public void setReqConvertType(ConvertType reqConvertType) { + this.reqConvertType = reqConvertType; + } + + public ConvertType getRespConvertType() { + return respConvertType; + } + + public void setRespConvertType(ConvertType respConvertType) { + this.respConvertType = respConvertType; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + +} diff --git a/src/org/redkale/net/http/HttpUserType.java b/src/main/java/org/redkale/net/http/HttpUserType.java similarity index 84% rename from src/org/redkale/net/http/HttpUserType.java rename to src/main/java/org/redkale/net/http/HttpUserType.java index 5bbc7683f..b39d27c30 100644 --- a/src/org/redkale/net/http/HttpUserType.java +++ b/src/main/java/org/redkale/net/http/HttpUserType.java @@ -1,30 +1,30 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import static java.lang.annotation.ElementType.*; -import java.lang.annotation.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 配合 HttpServlet 使用。 - * 用于指定HttpRequest.currentUser的数据类型。
    - * 注意: 数据类型是JavaBean - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface HttpUserType { - - Class value(); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import static java.lang.annotation.ElementType.*; +import java.lang.annotation.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 配合 HttpServlet 使用。 + * 用于指定HttpRequest.currentUser的数据类型。
    + * 注意: 数据类型是JavaBean, 不能是基本数据类型、String、byte[]、File等Java内置的数据类型 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface HttpUserType { + + Class value(); + +} diff --git a/src/org/redkale/net/http/MimeType.java b/src/main/java/org/redkale/net/http/MimeType.java similarity index 97% rename from src/org/redkale/net/http/MimeType.java rename to src/main/java/org/redkale/net/http/MimeType.java index 04140dd42..073034c34 100644 --- a/src/org/redkale/net/http/MimeType.java +++ b/src/main/java/org/redkale/net/http/MimeType.java @@ -1,206 +1,206 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.util.*; - -/** - * MimeType - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class MimeType { - - private final static Map contentTypes = new HashMap<>(); - - static { - contentTypes.put("abs", "audio/x-mpeg"); - contentTypes.put("ai", "application/postscript"); - contentTypes.put("aif", "audio/x-aiff"); - contentTypes.put("aifc", "audio/x-aiff"); - contentTypes.put("aiff", "audio/x-aiff"); - contentTypes.put("aim", "application/x-aim"); - contentTypes.put("art", "image/x-jg"); - contentTypes.put("asf", "video/x-ms-asf"); - contentTypes.put("asx", "video/x-ms-asf"); - contentTypes.put("au", "audio/basic"); - contentTypes.put("avi", "video/x-msvideo"); - contentTypes.put("avx", "video/x-rad-screenplay"); - contentTypes.put("bcpio", "application/x-bcpio"); - contentTypes.put("bin", "application/octet-stream"); - contentTypes.put("bmp", "image/bmp"); - contentTypes.put("body", "text/html"); - contentTypes.put("cdf", "application/x-cdf"); - contentTypes.put("cer", "application/x-x509-ca-cert"); - contentTypes.put("class", "application/java"); - contentTypes.put("cpio", "application/x-cpio"); - contentTypes.put("csh", "application/x-csh"); - contentTypes.put("css", "text/css"); - contentTypes.put("dib", "image/bmp"); - contentTypes.put("doc", "application/msword"); - contentTypes.put("dtd", "application/xml-dtd"); - contentTypes.put("dv", "video/x-dv"); - contentTypes.put("dvi", "application/x-dvi"); - contentTypes.put("eps", "application/postscript"); - contentTypes.put("etx", "text/x-setext"); - contentTypes.put("exe", "application/octet-stream"); - contentTypes.put("gif", "image/gif"); - contentTypes.put("gk", "application/octet-stream"); - contentTypes.put("gtar", "application/x-gtar"); - contentTypes.put("gz", "application/x-gzip"); - contentTypes.put("hdf", "application/x-hdf"); - contentTypes.put("hqx", "application/mac-binhex40"); - contentTypes.put("htc", "text/x-component"); - contentTypes.put("htm", "text/html"); - contentTypes.put("html", "text/html"); - contentTypes.put("hqx", "application/mac-binhex40"); - contentTypes.put("ico", "image/x-icon"); - contentTypes.put("ief", "image/ief"); - contentTypes.put("jad", "text/vnd.sun.j2me.app-descriptor"); - contentTypes.put("jar", "application/java-archive"); - contentTypes.put("java", "text/plain"); - contentTypes.put("jnlp", "application/x-java-jnlp-file"); - contentTypes.put("jpe", "image/jpeg"); - contentTypes.put("jpeg", "image/jpeg"); - contentTypes.put("jpg", "image/jpeg"); - contentTypes.put("js", "text/javascript"); - contentTypes.put("json", "application/json"); - contentTypes.put("kar", "audio/x-midi"); - contentTypes.put("latex", "application/x-latex"); - contentTypes.put("log", "text/plain"); - contentTypes.put("m3u", "audio/x-mpegurl"); - contentTypes.put("mac", "image/x-macpaint"); - contentTypes.put("man", "application/x-troff-man"); - contentTypes.put("manifest", "text/cache-manifest"); - contentTypes.put("mathml", "application/mathml+xml"); - contentTypes.put("me", "application/x-troff-me"); - contentTypes.put("mid", "audio/x-midi"); - contentTypes.put("midi", "audio/x-midi"); - contentTypes.put("mif", "application/x-mif"); - contentTypes.put("mov", "video/quicktime"); - contentTypes.put("movie", "video/x-sgi-movie"); - contentTypes.put("mp1", "audio/x-mpeg"); - contentTypes.put("mp2", "audio/x-mpeg"); - contentTypes.put("mp3", "audio/x-mpeg"); - contentTypes.put("mpa", "audio/x-mpeg"); - contentTypes.put("mp4", "video/mp4"); - contentTypes.put("ogv", "video/ogv"); - contentTypes.put("webm", "video/webm"); - contentTypes.put("flv", "video/x-flv"); - contentTypes.put("mpe", "video/mpeg"); - contentTypes.put("mpeg", "video/mpeg"); - contentTypes.put("mpega", "audio/x-mpeg"); - contentTypes.put("mpg", "video/mpeg"); - contentTypes.put("mpv2", "video/mpeg2"); - contentTypes.put("ms", "application/x-wais-source"); - contentTypes.put("nc", "application/x-netcdf"); - contentTypes.put("oda", "application/oda"); - contentTypes.put("ogg", "application/ogg"); - contentTypes.put("out", "text/plain"); - contentTypes.put("pac", "application/x-javascript-config"); - contentTypes.put("pbm", "image/x-portable-bitmap"); - contentTypes.put("pct", "image/pict"); - contentTypes.put("pdf", "application/pdf"); - contentTypes.put("pgm", "image/x-portable-graymap"); - contentTypes.put("pic", "image/pict"); - contentTypes.put("pict", "image/pict"); - contentTypes.put("pls", "audio/x-scpls"); - contentTypes.put("png", "image/png"); - contentTypes.put("pnm", "image/x-portable-anymap"); - contentTypes.put("pnt", "image/x-macpaint"); - contentTypes.put("ppm", "image/x-portable-pixmap"); - contentTypes.put("ppt", "application/powerpoint"); - contentTypes.put("ps", "application/postscript"); - contentTypes.put("psd", "image/x-photoshop"); - contentTypes.put("qt", "video/quicktime"); - contentTypes.put("qti", "image/x-quicktime"); - contentTypes.put("qtif", "image/x-quicktime"); - contentTypes.put("ras", "image/x-cmu-raster"); - contentTypes.put("rdf", "application/rdf+xml"); - contentTypes.put("rgb", "image/x-rgb"); - contentTypes.put("rm", "application/vnd.rn-realmedia"); - contentTypes.put("roff", "application/x-troff"); - contentTypes.put("rtf", "application/rtf"); - contentTypes.put("rtx", "text/richtext"); - contentTypes.put("sh", "application/x-sh"); - contentTypes.put("shar", "application/x-shar"); - contentTypes.put("shtml", "text/x-server-parsed-html"); - contentTypes.put("sit", "application/x-stuffit"); - contentTypes.put("smf", "audio/x-midi"); - contentTypes.put("snd", "audio/basic"); - contentTypes.put("src", "application/x-wais-source"); - contentTypes.put("sv4cpio", "application/x-sv4cpio"); - contentTypes.put("sv4crc", "application/x-sv4crc"); - contentTypes.put("svg", "image/svg+xml"); - contentTypes.put("svgz", "image/svg+xml"); - contentTypes.put("swf", "application/x-shockwave-flash"); - contentTypes.put("t", "application/x-troff"); - contentTypes.put("tar", "application/x-tar"); - contentTypes.put("tcl", "application/x-tcl"); - contentTypes.put("tex", "application/x-tex"); - contentTypes.put("texi", "application/x-texinfo"); - contentTypes.put("texinfo", "application/x-texinfo"); - contentTypes.put("tif", "image/tiff"); - contentTypes.put("tiff", "image/tiff"); - contentTypes.put("tr", "application/x-troff"); - contentTypes.put("tsv", "text/tab-separated-values"); - contentTypes.put("txt", "text/plain"); - contentTypes.put("ulw", "audio/basic"); - contentTypes.put("ustar", "application/x-ustar"); - contentTypes.put("xbm", "image/x-xbitmap"); - contentTypes.put("xml", "application/xml"); - contentTypes.put("xpm", "image/x-xpixmap"); - contentTypes.put("xsl", "application/xml"); - contentTypes.put("xslt", "application/xslt+xml"); - contentTypes.put("xwd", "image/x-xwindowdump"); - contentTypes.put("vsd", "application/x-visio"); - contentTypes.put("vxml", "application/voicexml+xml"); - contentTypes.put("wav", "audio/x-wav"); - contentTypes.put("wbmp", "image/vnd.wap.wbmp"); - contentTypes.put("wml", "text/vnd.wap.wml"); - contentTypes.put("wmlc", "application/vnd.wap.wmlc"); - contentTypes.put("wmls", "text/vnd.wap.wmls"); - contentTypes.put("wmlscriptc", "application/vnd.wap.wmlscriptc"); - contentTypes.put("wrl", "x-world/x-vrml"); - contentTypes.put("xht", "application/xhtml+xml"); - contentTypes.put("xhtml", "application/xhtml+xml"); - contentTypes.put("xls", "application/vnd.ms-excel"); - contentTypes.put("xul", "application/vnd.mozilla.xul+xml"); - contentTypes.put("Z", "application/x-compress"); - contentTypes.put("z", "application/x-compress"); - contentTypes.put("zip", "application/zip"); - } - - public static String get(String extension) { - return contentTypes.getOrDefault(extension.toLowerCase(), "text/plain"); - } - - public static String get(String extension, String defaultCt) { - return contentTypes.getOrDefault(extension.toLowerCase(), defaultCt); - } - - public static boolean contains(String extension) { - return contentTypes.containsKey(extension.toLowerCase()); - } - - public static void add(String extension, String contentType) { - if (extension != null && extension.length() != 0 && contentType != null && contentType.length() != 0) { - contentTypes.put(extension.toLowerCase(), contentType); - } - } - - public static String getByFilename(String fileName) { - int length = fileName.length(); - int newEnd = fileName.lastIndexOf('#'); - if (newEnd == -1) newEnd = length; - int i = fileName.lastIndexOf('.', newEnd); - return (i < 0) ? null : get(fileName.substring(i + 1, newEnd).toLowerCase()); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.util.*; + +/** + * MimeType + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class MimeType { + + private final static Map contentTypes = new HashMap<>(); + + static { + contentTypes.put("abs", "audio/x-mpeg"); + contentTypes.put("ai", "application/postscript"); + contentTypes.put("aif", "audio/x-aiff"); + contentTypes.put("aifc", "audio/x-aiff"); + contentTypes.put("aiff", "audio/x-aiff"); + contentTypes.put("aim", "application/x-aim"); + contentTypes.put("art", "image/x-jg"); + contentTypes.put("asf", "video/x-ms-asf"); + contentTypes.put("asx", "video/x-ms-asf"); + contentTypes.put("au", "audio/basic"); + contentTypes.put("avi", "video/x-msvideo"); + contentTypes.put("avx", "video/x-rad-screenplay"); + contentTypes.put("bcpio", "application/x-bcpio"); + contentTypes.put("bin", "application/octet-stream"); + contentTypes.put("bmp", "image/bmp"); + contentTypes.put("body", "text/html"); + contentTypes.put("cdf", "application/x-cdf"); + contentTypes.put("cer", "application/x-x509-ca-cert"); + contentTypes.put("class", "application/java"); + contentTypes.put("cpio", "application/x-cpio"); + contentTypes.put("csh", "application/x-csh"); + contentTypes.put("css", "text/css"); + contentTypes.put("dib", "image/bmp"); + contentTypes.put("doc", "application/msword"); + contentTypes.put("dtd", "application/xml-dtd"); + contentTypes.put("dv", "video/x-dv"); + contentTypes.put("dvi", "application/x-dvi"); + contentTypes.put("eps", "application/postscript"); + contentTypes.put("etx", "text/x-setext"); + contentTypes.put("exe", "application/octet-stream"); + contentTypes.put("gif", "image/gif"); + contentTypes.put("gk", "application/octet-stream"); + contentTypes.put("gtar", "application/x-gtar"); + contentTypes.put("gz", "application/x-gzip"); + contentTypes.put("hdf", "application/x-hdf"); + contentTypes.put("hqx", "application/mac-binhex40"); + contentTypes.put("htc", "text/x-component"); + contentTypes.put("htm", "text/html"); + contentTypes.put("html", "text/html"); + contentTypes.put("hqx", "application/mac-binhex40"); + contentTypes.put("ico", "image/x-icon"); + contentTypes.put("ief", "image/ief"); + contentTypes.put("jad", "text/vnd.sun.j2me.app-descriptor"); + contentTypes.put("jar", "application/java-archive"); + contentTypes.put("java", "text/plain"); + contentTypes.put("jnlp", "application/x-java-jnlp-file"); + contentTypes.put("jpe", "image/jpeg"); + contentTypes.put("jpeg", "image/jpeg"); + contentTypes.put("jpg", "image/jpeg"); + contentTypes.put("js", "text/javascript"); + contentTypes.put("json", "application/json"); + contentTypes.put("kar", "audio/x-midi"); + contentTypes.put("latex", "application/x-latex"); + contentTypes.put("log", "text/plain"); + contentTypes.put("m3u", "audio/x-mpegurl"); + contentTypes.put("mac", "image/x-macpaint"); + contentTypes.put("man", "application/x-troff-man"); + contentTypes.put("manifest", "text/cache-manifest"); + contentTypes.put("mathml", "application/mathml+xml"); + contentTypes.put("me", "application/x-troff-me"); + contentTypes.put("mid", "audio/x-midi"); + contentTypes.put("midi", "audio/x-midi"); + contentTypes.put("mif", "application/x-mif"); + contentTypes.put("mov", "video/quicktime"); + contentTypes.put("movie", "video/x-sgi-movie"); + contentTypes.put("mp1", "audio/x-mpeg"); + contentTypes.put("mp2", "audio/x-mpeg"); + contentTypes.put("mp3", "audio/x-mpeg"); + contentTypes.put("mpa", "audio/x-mpeg"); + contentTypes.put("mp4", "video/mp4"); + contentTypes.put("ogv", "video/ogv"); + contentTypes.put("webm", "video/webm"); + contentTypes.put("flv", "video/x-flv"); + contentTypes.put("mpe", "video/mpeg"); + contentTypes.put("mpeg", "video/mpeg"); + contentTypes.put("mpega", "audio/x-mpeg"); + contentTypes.put("mpg", "video/mpeg"); + contentTypes.put("mpv2", "video/mpeg2"); + contentTypes.put("ms", "application/x-wais-source"); + contentTypes.put("nc", "application/x-netcdf"); + contentTypes.put("oda", "application/oda"); + contentTypes.put("ogg", "application/ogg"); + contentTypes.put("out", "text/plain"); + contentTypes.put("pac", "application/x-javascript-config"); + contentTypes.put("pbm", "image/x-portable-bitmap"); + contentTypes.put("pct", "image/pict"); + contentTypes.put("pdf", "application/pdf"); + contentTypes.put("pgm", "image/x-portable-graymap"); + contentTypes.put("pic", "image/pict"); + contentTypes.put("pict", "image/pict"); + contentTypes.put("pls", "audio/x-scpls"); + contentTypes.put("png", "image/png"); + contentTypes.put("pnm", "image/x-portable-anymap"); + contentTypes.put("pnt", "image/x-macpaint"); + contentTypes.put("ppm", "image/x-portable-pixmap"); + contentTypes.put("ppt", "application/powerpoint"); + contentTypes.put("ps", "application/postscript"); + contentTypes.put("psd", "image/x-photoshop"); + contentTypes.put("qt", "video/quicktime"); + contentTypes.put("qti", "image/x-quicktime"); + contentTypes.put("qtif", "image/x-quicktime"); + contentTypes.put("ras", "image/x-cmu-raster"); + contentTypes.put("rdf", "application/rdf+xml"); + contentTypes.put("rgb", "image/x-rgb"); + contentTypes.put("rm", "application/vnd.rn-realmedia"); + contentTypes.put("roff", "application/x-troff"); + contentTypes.put("rtf", "application/rtf"); + contentTypes.put("rtx", "text/richtext"); + contentTypes.put("sh", "application/x-sh"); + contentTypes.put("shar", "application/x-shar"); + contentTypes.put("shtml", "text/x-server-parsed-html"); + contentTypes.put("sit", "application/x-stuffit"); + contentTypes.put("smf", "audio/x-midi"); + contentTypes.put("snd", "audio/basic"); + contentTypes.put("src", "application/x-wais-source"); + contentTypes.put("sv4cpio", "application/x-sv4cpio"); + contentTypes.put("sv4crc", "application/x-sv4crc"); + contentTypes.put("svg", "image/svg+xml"); + contentTypes.put("svgz", "image/svg+xml"); + contentTypes.put("swf", "application/x-shockwave-flash"); + contentTypes.put("t", "application/x-troff"); + contentTypes.put("tar", "application/x-tar"); + contentTypes.put("tcl", "application/x-tcl"); + contentTypes.put("tex", "application/x-tex"); + contentTypes.put("texi", "application/x-texinfo"); + contentTypes.put("texinfo", "application/x-texinfo"); + contentTypes.put("tif", "image/tiff"); + contentTypes.put("tiff", "image/tiff"); + contentTypes.put("tr", "application/x-troff"); + contentTypes.put("tsv", "text/tab-separated-values"); + contentTypes.put("txt", "text/plain"); + contentTypes.put("ulw", "audio/basic"); + contentTypes.put("ustar", "application/x-ustar"); + contentTypes.put("xbm", "image/x-xbitmap"); + contentTypes.put("xml", "application/xml"); + contentTypes.put("xpm", "image/x-xpixmap"); + contentTypes.put("xsl", "application/xml"); + contentTypes.put("xslt", "application/xslt+xml"); + contentTypes.put("xwd", "image/x-xwindowdump"); + contentTypes.put("vsd", "application/x-visio"); + contentTypes.put("vxml", "application/voicexml+xml"); + contentTypes.put("wav", "audio/x-wav"); + contentTypes.put("wbmp", "image/vnd.wap.wbmp"); + contentTypes.put("wml", "text/vnd.wap.wml"); + contentTypes.put("wmlc", "application/vnd.wap.wmlc"); + contentTypes.put("wmls", "text/vnd.wap.wmls"); + contentTypes.put("wmlscriptc", "application/vnd.wap.wmlscriptc"); + contentTypes.put("wrl", "x-world/x-vrml"); + contentTypes.put("xht", "application/xhtml+xml"); + contentTypes.put("xhtml", "application/xhtml+xml"); + contentTypes.put("xls", "application/vnd.ms-excel"); + contentTypes.put("xul", "application/vnd.mozilla.xul+xml"); + contentTypes.put("Z", "application/x-compress"); + contentTypes.put("z", "application/x-compress"); + contentTypes.put("zip", "application/zip"); + } + + public static String get(String extension) { + return contentTypes.getOrDefault(extension.toLowerCase(), "text/plain"); + } + + public static String get(String extension, String defaultCt) { + return contentTypes.getOrDefault(extension.toLowerCase(), defaultCt); + } + + public static boolean contains(String extension) { + return contentTypes.containsKey(extension.toLowerCase()); + } + + public static void add(String extension, String contentType) { + if (extension != null && extension.length() != 0 && contentType != null && contentType.length() != 0) { + contentTypes.put(extension.toLowerCase(), contentType); + } + } + + public static String getByFilename(String fileName) { + int length = fileName.length(); + int newEnd = fileName.lastIndexOf('#'); + if (newEnd == -1) newEnd = length; + int i = fileName.lastIndexOf('.', newEnd); + return (i < 0) ? null : get(fileName.substring(i + 1, newEnd).toLowerCase()); + } + +} diff --git a/src/org/redkale/net/http/MultiContext.java b/src/main/java/org/redkale/net/http/MultiContext.java similarity index 95% rename from src/org/redkale/net/http/MultiContext.java rename to src/main/java/org/redkale/net/http/MultiContext.java index 1b313e990..9e625f06c 100644 --- a/src/org/redkale/net/http/MultiContext.java +++ b/src/main/java/org/redkale/net/http/MultiContext.java @@ -1,368 +1,368 @@ -/* - * To change this license header, choose License Headers input Project Properties. - * To change this template file, choose Tools | Templates - * and open the template input the editor. - */ -package org.redkale.net.http; - -import org.redkale.util.ByteArray; -import java.io.*; -import java.nio.charset.*; -import java.util.*; -import java.util.concurrent.atomic.*; -import java.util.logging.*; -import java.util.regex.*; - -/** - * HTTP的文件上传请求的上下文对象 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class MultiContext { - - private static final Logger logger = Logger.getLogger(MultiContext.class.getSimpleName()); - - private final String contentType; - - private final InputStream in; - - private final Charset charset; - - private final String boundary; - - private final byte[] endboundarray; - - private final ByteArray buf = new ByteArray(64); - - private final Map parameters; - - private final Pattern fielnamePattern; - - private static final Iterable emptyIterable = () -> new Iterator() { - - @Override - public boolean hasNext() { - return false; - } - - @Override - public MultiPart next() { - return null; - } - }; - - public MultiContext(final Charset charsetName, final String contentType, final Map params, final InputStream in, String fielnameRegex) { - this.charset = charsetName == null ? StandardCharsets.UTF_8 : charsetName; - this.contentType = contentType == null ? "" : contentType.trim(); - this.parameters = params; - this.boundary = parseBoundary(this.contentType); - this.endboundarray = ("--" + this.boundary + "--").getBytes(); - this.in = in instanceof BufferedInputStream ? in : new BufferedInputStream(in); - this.fielnamePattern = fielnameRegex == null || fielnameRegex.isEmpty() ? null : Pattern.compile(fielnameRegex); - } - - private String parseBoundary(String contentType) { - if (!contentType.startsWith("multipart/")) { - return null; - } - for (String str : contentType.split(";")) { - int pos = str.indexOf("boundary="); - if (pos >= 0) return str.substring(pos + "boundary=".length()).trim(); - } - return null; - } - - /** - * 判断请求是否包含上传文件 - * - * @return boolean - */ - public boolean isMultipart() { - return this.boundary != null; - } - - //或被 REST 用到 - /** - * 获取第一个文件的二进制 - * - * @param max 可接收的文件大小最大值 - * @param filenameReg 可接收的文件名正则表达式 - * @param contentTypeReg 可接收的ContentType正则表达式 - * - * @return 二进制文件 - * @throws IOException IOException - */ - public byte[] partsFirstBytes(final long max, final String filenameReg, final String contentTypeReg) throws IOException { - if (!isMultipart()) return null; - byte[] tmpfile = null; - boolean has = false; - for (MultiPart part : parts()) { - if (has) continue;//不遍历完后面getParameter可能获取不到值 - has = true; - if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue; - if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue; - tmpfile = part.getContentBytes(max < 1 ? Long.MAX_VALUE : max); - } - return tmpfile; - } - - /** - * 根据临时文件获取上传时的文件名 - * - * @param file 临时文件 - * - * @return 上传的文件名 - */ - public static String getFileName(File file) { - if (file == null) return null; - String name = file.getName(); - return name.startsWith("redkale-") ? name.substring(name.indexOf('_') + 1) : name; - } - - //或被 REST 用到 - /** - * 获取第一个文件 - * - * @param home 进程目录 - * @param max 可接收的文件大小最大值 - * @param filenameReg 可接收的文件名正则表达式 - * @param contentTypeReg 可接收的ContentType正则表达式 - * - * @return 文件 - * @throws IOException IOException - */ - public File partsFirstFile(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException { - if (!isMultipart()) return null; - File tmpfile = null; - boolean has = false; - for (MultiPart part : parts()) { - if (has) continue; //不遍历完后面getParameter可能获取不到值 - has = true; - if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue; - if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue; - File file = new File(home, "tmp/redkale-" + System.nanoTime() + "_" + part.getFilename()); - File parent = file.getParentFile(); - if (!parent.isDirectory()) parent.mkdirs(); - boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file); - if (!rs) { - file.delete(); - parent.delete(); - } else { - tmpfile = file; - } - } - return tmpfile; - } - - //或被 REST 用到 - /** - * 获取所有文件 - * - * @param home 进程目录 - * @param max 可接收的文件大小最大值 - * @param filenameReg 可接收的文件名正则表达式 - * @param contentTypeReg 可接收的ContentType正则表达式 - * - * @return 文件列表 - * @throws IOException IOException - */ - public File[] partsFiles(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException { - if (!isMultipart()) return null; - List files = null; - for (MultiPart part : parts()) { - if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue; - if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue; - File file = new File(home, "tmp/redkale-" + System.nanoTime() + "_" + part.getFilename()); - File parent = file.getParentFile(); - if (!parent.isDirectory()) parent.mkdirs(); - boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file); - if (!rs) { - file.delete(); - parent.delete(); - continue; - } - if (files == null) files = new ArrayList<>(); - files.add(file); - } - return files == null ? null : files.toArray(new File[files.size()]); - } - - /** - * 获取上传文件信息列表 - * - * @return Iterable - * @throws IOException IOException - */ - public Iterable parts() throws IOException { - if (!isMultipart()) return emptyIterable; - final String boundarystr = "--" + this.boundary; - final Pattern fielnameReg = this.fielnamePattern; - final String endboundary = boundarystr + "--"; - final byte[] boundarray = ("\n" + boundarystr).getBytes(); - final byte[] buffer = new byte[boundarray.length]; - final InputStream input = this.in; - final Map params = this.parameters; - final AtomicBoolean finaled = new AtomicBoolean(false); - return () -> new Iterator() { - - private String boundaryline; - - private MultiPart lastentry; - - @Override - public boolean hasNext() { - try { - if (lastentry != null) { - lastentry.skip(); - if (finaled.get()) return false; - } - if (boundaryline == null) boundaryline = readBoundary(); - //if (debug) System.out.print("boundaryline=" + boundaryline + " "); - if (endboundary.equals(boundaryline) || !boundarystr.equals(boundaryline)) { //结尾或异常 - lastentry = null; - return false; - } - final String disposition = readLine(); - //if (debug) System.out.println("disposition=" + disposition); - if (disposition.contains("; filename=\"")) { //是上传文件 - String contentType = ""; - //读掉HTTP Header和空白行 通常情况下Content-Type后面就是内容,但是有些特殊情况下后面会跟其他如Content-Length: xxx等HTTP header,所以需要循环读取 - String rl; - while (!(rl = readLine()).isEmpty()) { - if (rl.startsWith("Content-Type:")) contentType = rl.substring(rl.indexOf(':') + 1).trim(); - } - //if (debug) System.out.println("file.contentType=" + contentType); - - String name = parseValue(disposition, "name"); - String filename = parseValue(disposition, "filename"); - if (filename == null || filename.isEmpty()) { //没有上传 - readLine(); //读掉空白行 - this.boundaryline = null; - this.lastentry = null; - return this.hasNext(); - } else { - int p1 = filename.lastIndexOf('/'); - if (p1 < 0) p1 = filename.lastIndexOf('\\'); - if (p1 >= 0) filename = filename.substring(p1 + 1); - } - final AtomicLong counter = new AtomicLong(0); - InputStream source = new InputStream() { - - private int bufposition = buffer.length; - - private boolean end; - - @Override - public int read() throws IOException { - if (end) return -1; - final byte[] buf = buffer; - int ch = (this.bufposition < buf.length) ? (buf[this.bufposition++] & 0xff) : input.read(); - if ((ch == '\r' && readBuffer())) return -1; - counter.incrementAndGet(); - return ch; - } - - private boolean readBuffer() throws IOException { - final byte[] buf = buffer; - final int pos = this.bufposition; - int s = 0; - for (int i = pos; i < buf.length; i++) { - buf[s++] = buf[i]; - } - int readed = 0; - int t = 0; - while ((t = input.read(buf, s + readed, pos - readed)) > 0) { - readed += t; - if (readed == pos) break; - } - this.bufposition = 0; - if (Arrays.equals(boundarray, buf)) { - this.end = true; - int c1 = input.read(); - int c2 = input.read(); - finaled.set(c1 == '-' && c2 == '-'); - return true; - } - return false; - } - - @Override - public long skip(long count) throws IOException { - if (end) return -1; - if (count <= 0) return 0; - long s = 0; - while (read() != -1) { - s++; - if (--count <= 0) break; - } - return s; - } - }; - this.lastentry = new MultiPart(filename, name, contentType, counter, source); - if (fielnameReg != null && !fielnameReg.matcher(filename).matches()) { - return this.hasNext(); - } - return true; - } else { //不是文件 - readLine(); //读掉空白 - params.put(parseValue(disposition, "name"), readLine()); - this.boundaryline = null; - this.lastentry = null; - return this.hasNext(); - } - } catch (IOException ex) { - logger.log(Level.FINER, "list multiparts abort", ex); - return false; - } - } - - @Override - public MultiPart next() { - return lastentry; - } - - }; - } - - private String readLine() throws IOException { - return readLine(false); - } - - private String readBoundary() throws IOException { - return readLine(true); - } - - private String readLine(boolean bd) throws IOException { // bd : 是否是读取boundary - byte lasted = '\r'; - buf.clear(); - final int bc = this.endboundarray.length; - int c = 0; - for (;;) { - int b = in.read(); - c++; - if (b == -1 || (lasted == '\r' && b == '\n')) break; - if (lasted != '\r') buf.put(lasted); - lasted = (byte) b; - if (bd && bc == c) { - buf.put(lasted); - if (buf.equal(this.endboundarray)) break; - buf.removeLastByte(); - } - } - if (buf.length() == 0) return ""; - return buf.toString(this.charset).trim(); - } - - private static String parseValue(final String str, String name) { - if (str == null) return null; - final String key = "; " + name + "=\""; - int pos = str.indexOf(key); - if (pos < 0) return null; - String sub = str.substring(pos + key.length()); - return sub.substring(0, sub.indexOf('"')); - } - -} +/* + * To change this license header, choose License Headers input Project Properties. + * To change this template file, choose Tools | Templates + * and open the template input the editor. + */ +package org.redkale.net.http; + +import org.redkale.util.ByteArray; +import java.io.*; +import java.nio.charset.*; +import java.util.*; +import java.util.concurrent.atomic.*; +import java.util.logging.*; +import java.util.regex.*; + +/** + * HTTP的文件上传请求的上下文对象 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class MultiContext { + + private static final Logger logger = Logger.getLogger(MultiContext.class.getSimpleName()); + + private final String contentType; + + private final InputStream in; + + private final Charset charset; + + private final String boundary; + + private final byte[] endboundarray; + + private final ByteArray buf = new ByteArray(64); + + private final Map parameters; + + private final Pattern fielnamePattern; + + private static final Iterable emptyIterable = () -> new Iterator() { + + @Override + public boolean hasNext() { + return false; + } + + @Override + public MultiPart next() { + return null; + } + }; + + public MultiContext(final Charset charsetName, final String contentType, final Map params, final InputStream in, String fielnameRegex) { + this.charset = charsetName == null ? StandardCharsets.UTF_8 : charsetName; + this.contentType = contentType == null ? "" : contentType.trim(); + this.parameters = params; + this.boundary = parseBoundary(this.contentType); + this.endboundarray = ("--" + this.boundary + "--").getBytes(); + this.in = in instanceof BufferedInputStream ? in : new BufferedInputStream(in); + this.fielnamePattern = fielnameRegex == null || fielnameRegex.isEmpty() ? null : Pattern.compile(fielnameRegex); + } + + private String parseBoundary(String contentType) { + if (!contentType.startsWith("multipart/")) { + return null; + } + for (String str : contentType.split(";")) { + int pos = str.indexOf("boundary="); + if (pos >= 0) return str.substring(pos + "boundary=".length()).trim(); + } + return null; + } + + /** + * 判断请求是否包含上传文件 + * + * @return boolean + */ + public boolean isMultipart() { + return this.boundary != null; + } + + //或被 REST 用到 + /** + * 获取第一个文件的二进制 + * + * @param max 可接收的文件大小最大值 + * @param filenameReg 可接收的文件名正则表达式 + * @param contentTypeReg 可接收的ContentType正则表达式 + * + * @return 二进制文件 + * @throws IOException IOException + */ + public byte[] partsFirstBytes(final long max, final String filenameReg, final String contentTypeReg) throws IOException { + if (!isMultipart()) return null; + byte[] tmpfile = null; + boolean has = false; + for (MultiPart part : parts()) { + if (has) continue;//不遍历完后面getParameter可能获取不到值 + has = true; + if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue; + if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue; + tmpfile = part.getContentBytes(max < 1 ? Long.MAX_VALUE : max); + } + return tmpfile; + } + + /** + * 根据临时文件获取上传时的文件名 + * + * @param file 临时文件 + * + * @return 上传的文件名 + */ + public static String getFileName(File file) { + if (file == null) return null; + String name = file.getName(); + return name.startsWith("redkale-") ? name.substring(name.indexOf('_') + 1) : name; + } + + //或被 REST 用到 + /** + * 获取第一个文件 + * + * @param home 进程目录 + * @param max 可接收的文件大小最大值 + * @param filenameReg 可接收的文件名正则表达式 + * @param contentTypeReg 可接收的ContentType正则表达式 + * + * @return 文件 + * @throws IOException IOException + */ + public File partsFirstFile(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException { + if (!isMultipart()) return null; + File tmpfile = null; + boolean has = false; + for (MultiPart part : parts()) { + if (has) continue; //不遍历完后面getParameter可能获取不到值 + has = true; + if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue; + if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue; + File file = new File(home, "tmp/redkale-" + System.nanoTime() + "_" + part.getFilename()); + File parent = file.getParentFile(); + if (!parent.isDirectory()) parent.mkdirs(); + boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file); + if (!rs) { + file.delete(); + parent.delete(); + } else { + tmpfile = file; + } + } + return tmpfile; + } + + //或被 REST 用到 + /** + * 获取所有文件 + * + * @param home 进程目录 + * @param max 可接收的文件大小最大值 + * @param filenameReg 可接收的文件名正则表达式 + * @param contentTypeReg 可接收的ContentType正则表达式 + * + * @return 文件列表 + * @throws IOException IOException + */ + public File[] partsFiles(final File home, final long max, final String filenameReg, final String contentTypeReg) throws IOException { + if (!isMultipart()) return null; + List files = null; + for (MultiPart part : parts()) { + if (filenameReg != null && !filenameReg.isEmpty() && !part.getFilename().matches(filenameReg)) continue; + if (contentTypeReg != null && !contentTypeReg.isEmpty() && !part.getContentType().matches(contentTypeReg)) continue; + File file = new File(home, "tmp/redkale-" + System.nanoTime() + "_" + part.getFilename()); + File parent = file.getParentFile(); + if (!parent.isDirectory()) parent.mkdirs(); + boolean rs = part.save(max < 1 ? Long.MAX_VALUE : max, file); + if (!rs) { + file.delete(); + parent.delete(); + continue; + } + if (files == null) files = new ArrayList<>(); + files.add(file); + } + return files == null ? null : files.toArray(new File[files.size()]); + } + + /** + * 获取上传文件信息列表 + * + * @return Iterable + * @throws IOException IOException + */ + public Iterable parts() throws IOException { + if (!isMultipart()) return emptyIterable; + final String boundarystr = "--" + this.boundary; + final Pattern fielnameReg = this.fielnamePattern; + final String endboundary = boundarystr + "--"; + final byte[] boundarray = ("\n" + boundarystr).getBytes(); + final byte[] buffer = new byte[boundarray.length]; + final InputStream input = this.in; + final Map params = this.parameters; + final AtomicBoolean finaled = new AtomicBoolean(false); + return () -> new Iterator() { + + private String boundaryline; + + private MultiPart lastentry; + + @Override + public boolean hasNext() { + try { + if (lastentry != null) { + lastentry.skip(); + if (finaled.get()) return false; + } + if (boundaryline == null) boundaryline = readBoundary(); + //if (debug) System.out.print("boundaryline=" + boundaryline + " "); + if (endboundary.equals(boundaryline) || !boundarystr.equals(boundaryline)) { //结尾或异常 + lastentry = null; + return false; + } + final String disposition = readLine(); + //if (debug) System.out.println("disposition=" + disposition); + if (disposition.contains("; filename=\"")) { //是上传文件 + String contentType = ""; + //读掉HTTP Header和空白行 通常情况下Content-Type后面就是内容,但是有些特殊情况下后面会跟其他如Content-Length: xxx等HTTP header,所以需要循环读取 + String rl; + while (!(rl = readLine()).isEmpty()) { + if (rl.startsWith("Content-Type:") || rl.startsWith("content-type:")) contentType = rl.substring(rl.indexOf(':') + 1).trim(); + } + //if (debug) System.out.println("file.contentType=" + contentType); + + String name = parseValue(disposition, "name"); + String filename = parseValue(disposition, "filename"); + if (filename == null || filename.isEmpty()) { //没有上传 + readLine(); //读掉空白行 + this.boundaryline = null; + this.lastentry = null; + return this.hasNext(); + } else { + int p1 = filename.lastIndexOf('/'); + if (p1 < 0) p1 = filename.lastIndexOf('\\'); + if (p1 >= 0) filename = filename.substring(p1 + 1); + } + final LongAdder counter = new LongAdder(); + InputStream source = new InputStream() { + + private int bufposition = buffer.length; + + private boolean end; + + @Override + public int read() throws IOException { + if (end) return -1; + final byte[] buf = buffer; + int ch = (this.bufposition < buf.length) ? (buf[this.bufposition++] & 0xff) : input.read(); + if ((ch == '\r' && readBuffer())) return -1; + counter.increment(); + return ch; + } + + private boolean readBuffer() throws IOException { + final byte[] buf = buffer; + final int pos = this.bufposition; + int s = 0; + for (int i = pos; i < buf.length; i++) { + buf[s++] = buf[i]; + } + int readed = 0; + int t = 0; + while ((t = input.read(buf, s + readed, pos - readed)) > 0) { + readed += t; + if (readed == pos) break; + } + this.bufposition = 0; + if (Arrays.equals(boundarray, buf)) { + this.end = true; + int c1 = input.read(); + int c2 = input.read(); + finaled.set(c1 == '-' && c2 == '-'); + return true; + } + return false; + } + + @Override + public long skip(long count) throws IOException { + if (end) return -1; + if (count <= 0) return 0; + long s = 0; + while (read() != -1) { + s++; + if (--count <= 0) break; + } + return s; + } + }; + this.lastentry = new MultiPart(filename, name, contentType, counter, source); + if (fielnameReg != null && !fielnameReg.matcher(filename).matches()) { + return this.hasNext(); + } + return true; + } else { //不是文件 + readLine(); //读掉空白 + params.put(parseValue(disposition, "name"), readLine()); + this.boundaryline = null; + this.lastentry = null; + return this.hasNext(); + } + } catch (IOException ex) { + logger.log(Level.FINER, "list multiparts abort", ex); + return false; + } + } + + @Override + public MultiPart next() { + return lastentry; + } + + }; + } + + private String readLine() throws IOException { + return readLine(false); + } + + private String readBoundary() throws IOException { + return readLine(true); + } + + private String readLine(boolean bd) throws IOException { // bd : 是否是读取boundary + byte lasted = '\r'; + buf.clear(); + final int bc = this.endboundarray.length; + int c = 0; + for (;;) { + int b = in.read(); + c++; + if (b == -1 || (lasted == '\r' && b == '\n')) break; + if (lasted != '\r') buf.put(lasted); + lasted = (byte) b; + if (bd && bc == c) { + buf.put(lasted); + if (buf.equal(this.endboundarray)) break; + buf.removeLastByte(); + } + } + if (buf.length() == 0) return ""; + return buf.toString(this.charset).trim(); + } + + private static String parseValue(final String str, String name) { + if (str == null) return null; + final String key = "; " + name + "=\""; + int pos = str.indexOf(key); + if (pos < 0) return null; + String sub = str.substring(pos + key.length()); + return sub.substring(0, sub.indexOf('"')); + } + +} diff --git a/src/org/redkale/net/http/MultiPart.java b/src/main/java/org/redkale/net/http/MultiPart.java similarity index 91% rename from src/org/redkale/net/http/MultiPart.java rename to src/main/java/org/redkale/net/http/MultiPart.java index 7da8935a1..818db3299 100644 --- a/src/org/redkale/net/http/MultiPart.java +++ b/src/main/java/org/redkale/net/http/MultiPart.java @@ -1,116 +1,116 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.*; -import java.util.concurrent.atomic.AtomicLong; - -/** - * - *

    详情见: https://redkale.org - * @author zhangjx - */ -public final class MultiPart { - - private final String filename; - - private final String name; - - private final String contentType; - - private final InputStream in; - - private final AtomicLong received; - - MultiPart(String filename, String name, String contentType, AtomicLong received, InputStream in) { - this.filename = filename; - this.name = name; - this.in = in; - this.contentType = contentType; - this.received = received; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "{" + "name=" + name + ", filename=" + filename + ", contentType=" + contentType + ", received=" + received + '}'; - } - - public boolean save(File file) throws IOException { - return save(Long.MAX_VALUE, file); - } - - public boolean save(long max, File file) throws IOException { - OutputStream out = new FileOutputStream(file); - boolean rs = save(max, out); - out.close(); - return rs; - } - - public byte[] getContentBytes() throws IOException { - return getContentBytes(Long.MAX_VALUE); - } - - /** - * 将文件流读进bytes, 如果超出max指定的值则返回null - * - * @param max 最大长度限制 - * @return 内容 - * @throws IOException 异常 - */ - public byte[] getContentBytes(long max) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - return save(max, out) ? out.toByteArray() : null; - } - - public boolean save(OutputStream out) throws IOException { - return save(Long.MAX_VALUE, out); - } - - /** - * 将文件流写进out, 如果超出max指定的值则中断并返回false - * - * @param max 最大长度限制 - * @param out 输出流 - * @return 是否成功 - * @throws IOException 异常 - */ - public boolean save(long max, OutputStream out) throws IOException { - byte[] bytes = new byte[4096]; - int pos; - InputStream in0 = this.getInputStream(); - while ((pos = in0.read(bytes)) != -1) { - if (max < 0) return false; - out.write(bytes, 0, pos); - max -= pos; - } - return true; - } - - public String getContentType() { - return contentType; - } - - public String getFilename() { - return filename; - } - - public String getName() { - return name; - } - - public InputStream getInputStream() { - return in; - } - - public long getReceived() { - return received.get(); - } - - public void skip() throws IOException { - in.skip(Long.MAX_VALUE); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.*; +import java.util.concurrent.atomic.LongAdder; + +/** + * + *

    详情见: https://redkale.org + * @author zhangjx + */ +public final class MultiPart { + + private final String filename; + + private final String name; + + private final String contentType; + + private final InputStream in; + + private final LongAdder received; + + MultiPart(String filename, String name, String contentType, LongAdder received, InputStream in) { + this.filename = filename; + this.name = name; + this.in = in; + this.contentType = contentType; + this.received = received; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{" + "name=" + name + ", filename=" + filename + ", contentType=" + contentType + ", received=" + received + '}'; + } + + public boolean save(File file) throws IOException { + return save(Long.MAX_VALUE, file); + } + + public boolean save(long max, File file) throws IOException { + OutputStream out = new FileOutputStream(file); + boolean rs = save(max, out); + out.close(); + return rs; + } + + public byte[] getContentBytes() throws IOException { + return getContentBytes(Long.MAX_VALUE); + } + + /** + * 将文件流读进bytes, 如果超出max指定的值则返回null + * + * @param max 最大长度限制 + * @return 内容 + * @throws IOException 异常 + */ + public byte[] getContentBytes(long max) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + return save(max, out) ? out.toByteArray() : null; + } + + public boolean save(OutputStream out) throws IOException { + return save(Long.MAX_VALUE, out); + } + + /** + * 将文件流写进out, 如果超出max指定的值则中断并返回false + * + * @param max 最大长度限制 + * @param out 输出流 + * @return 是否成功 + * @throws IOException 异常 + */ + public boolean save(long max, OutputStream out) throws IOException { + byte[] bytes = new byte[4096]; + int pos; + InputStream in0 = this.getInputStream(); + while ((pos = in0.read(bytes)) != -1) { + if (max < 0) return false; + out.write(bytes, 0, pos); + max -= pos; + } + return true; + } + + public String getContentType() { + return contentType; + } + + public String getFilename() { + return filename; + } + + public String getName() { + return name; + } + + public InputStream getInputStream() { + return in; + } + + public long getReceived() { + return received.longValue(); + } + + public void skip() throws IOException { + in.skip(Long.MAX_VALUE); + } + +} diff --git a/src/org/redkale/net/http/Rest.java b/src/main/java/org/redkale/net/http/Rest.java similarity index 70% rename from src/org/redkale/net/http/Rest.java rename to src/main/java/org/redkale/net/http/Rest.java index 2c19242ee..146f8de72 100644 --- a/src/org/redkale/net/http/Rest.java +++ b/src/main/java/org/redkale/net/http/Rest.java @@ -1,2330 +1,3047 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import org.redkale.asm.MethodDebugVisitor; -import java.io.*; -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.reflect.*; -import java.net.InetSocketAddress; -import java.nio.channels.CompletionHandler; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Resource; -import org.redkale.asm.*; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; -import static org.redkale.asm.Opcodes.*; -import org.redkale.asm.Type; -import org.redkale.convert.*; -import org.redkale.convert.json.*; -import org.redkale.mq.*; -import org.redkale.net.*; -import org.redkale.net.sncp.Sncp; -import org.redkale.service.*; -import org.redkale.util.*; -import org.redkale.source.Flipper; - -/** - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public final class Rest { - - public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name"; - - public static final String REST_HEADER_RPC_NAME = "rest-rpc-name"; - - public static final String REST_HEADER_CURRUSERID_NAME = "rest-curruserid-name"; - - public static final String REST_HEADER_PARAM_FROM_BODY = "rest-paramfrombody"; - - public static final String REST_HEADER_REQ_CONVERT_TYPE = "rest-req-convert-type"; - - public static final String REST_HEADER_RESP_CONVERT_TYPE = "rest-resp-convert-type"; - - static final String REST_SERVICE_FIELD_NAME = "_redkale_service"; - - static final String REST_TOSTRINGOBJ_FIELD_NAME = "_redkale_tostringsupplier"; - - static final String REST_JSONCONVERT_FIELD_PREFIX = "_redkale_jsonconvert_"; - - static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源,则实例中_servicemap必须为null - - private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标, 第二维度是参数的下标 - - private static final java.lang.reflect.Type TYPE_MAP_STRING_STRING = new TypeToken>() { - }.getType(); - - private static final Set EXCLUDERMETHODS = new HashSet<>(); - - static { - for (Method m : Object.class.getMethods()) { - EXCLUDERMETHODS.add(m.getName()); - } - } - - /** - * 用于标记由Rest.createRestServlet 方法创建的RestServlet - */ - @Inherited - @Documented - @Target({TYPE}) - @Retention(RUNTIME) - public static @interface RestDyn { - - //是否不需要解析HttpHeader,对应HttpContext.lazyHeaders - boolean simple() default false; - } - - /** - * 用于标记由Rest.createRestServlet 方法创建的RestServlet - */ - @Inherited - @Documented - @Target({TYPE}) - @Retention(RUNTIME) - public static @interface RestDynSourceType { - - Class value(); - } - - private Rest() { - } - - static class MethodParamClassVisitor extends ClassVisitor { - - private final Map> fieldmap; - - public MethodParamClassVisitor(int api, final Map> fieldmap) { - super(api); - this.fieldmap = fieldmap; - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - if (java.lang.reflect.Modifier.isStatic(access)) return null; - List fieldnames = new ArrayList<>(); - String key = name + ":" + desc; - if (fieldmap.containsKey(key)) return null; - fieldmap.put(key, fieldnames); - return new MethodVisitor(Opcodes.ASM6) { - @Override - public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { - if (index < 1) return; - int size = fieldnames.size(); - //index并不会按顺序执行的 - if (index > size) { - for (int i = size; i < index; i++) { - fieldnames.add(" "); - } - fieldnames.set(index - 1, name); - } - fieldnames.set(index - 1, name); - } - }; - } - - //返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多 - public static Map> getMethodParamNames(Map> map, Class clazz) { - String n = clazz.getName(); - InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class"); - if (in == null) return map; - try { - new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM6, map), 0); - } catch (Exception e) { //无需理会 - } - Class superClass = clazz.getSuperclass(); - if (superClass == Object.class) return map; - return getMethodParamNames(map, superClass); - } - } - - static JsonConvert createJsonConvert(RestConvert[] converts, RestConvertCoder[] coders) { - if ((converts == null || converts.length < 1) && (coders == null || coders.length < 1)) return JsonConvert.root(); - final JsonFactory childFactory = JsonFactory.create(); - List types = new ArrayList<>(); - Set reloadTypes = new HashSet<>(); - if (coders != null) { - for (RestConvertCoder rcc : coders) { - reloadTypes.add(rcc.type()); - childFactory.register(rcc.type(), rcc.field(), Creator.create(rcc.coder()).create()); - } - } - if (converts != null) { - for (RestConvert rc : converts) { - if (rc.type() == void.class || rc.type() == Void.class) { - return JsonFactory.create().skipAllIgnore(true).getConvert(); - } - if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat"); - if (rc.skipIgnore()) { - childFactory.registerSkipIgnore(rc.type()); - childFactory.reloadCoder(rc.type()); - } else { - childFactory.register(rc.type(), false, rc.convertColumns()); - childFactory.register(rc.type(), true, rc.ignoreColumns()); - childFactory.reloadCoder(rc.type()); - } - types.add(rc.type()); - childFactory.tiny(rc.tiny()); - } - } - for (Class type : reloadTypes) { - childFactory.reloadCoder(type); - } - return childFactory.getConvert(); - } - - static String getWebModuleNameLowerCase(Class serviceType) { - final RestService controller = serviceType.getAnnotation(RestService.class); - if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); - if (controller.ignore()) return null; - return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); - } - - static String getWebModuleName(Class serviceType) { - final RestService controller = serviceType.getAnnotation(RestService.class); - if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", ""); - if (controller.ignore()) return null; - return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", ""); - } - - /** - * 判断HttpServlet是否为Rest动态生成的 - * - * @param servlet 检测的HttpServlet - * - * @return 是否是动态生成的RestHttpServlet - */ - public static boolean isRestDyn(HttpServlet servlet) { - return servlet.getClass().getAnnotation(RestDyn.class) != null; - } - - /** - * 判断HttpServlet是否为Rest动态生成的,且simple - * - * @param servlet 检测的HttpServlet - * - * @return 是否是动态生成的RestHttpServlet - */ - static boolean isSimpleRestDyn(HttpServlet servlet) { - RestDyn dyn = servlet.getClass().getAnnotation(RestDyn.class); - return dyn != null && dyn.simple(); - } - - /** - * 获取Rest动态生成HttpServlet里的Service对象,若不是Rest动态生成的HttpServlet,返回null - * - * @param servlet HttpServlet - * - * @return Service - */ - public static Service getService(HttpServlet servlet) { - if (servlet == null) return null; - if (!isRestDyn(servlet)) return null; - try { - Field ts = servlet.getClass().getDeclaredField(REST_SERVICE_FIELD_NAME); - ts.setAccessible(true); - return (Service) ts.get(servlet); - } catch (Exception e) { - return null; - } - } - - public static Map getServiceMap(HttpServlet servlet) { - if (servlet == null) return null; - try { - Field ts = servlet.getClass().getDeclaredField(REST_SERVICEMAP_FIELD_NAME); - ts.setAccessible(true); - return (Map) ts.get(servlet); - } catch (Exception e) { - return null; - } - } - - public static String getRestModule(Service service) { - final RestService controller = service.getClass().getAnnotation(RestService.class); - if (controller != null && !controller.name().isEmpty()) return controller.name(); - final Class serviceType = Sncp.getServiceType(service); - return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); - } - - //仅供Rest动态构建里 currentUserid() 使用 - public static T orElse(T t, T defValue) { - return t == null ? defValue : t; - } - - public static T createRestWebSocketServlet(final ClassLoader classLoader, final Class webSocketType, MessageAgent messageAgent) { - if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet"); - if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet"); - if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet"); - final RestWebSocket rws = webSocketType.getAnnotation(RestWebSocket.class); - if (rws == null || rws.ignore()) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") have not @RestWebSocket or @RestWebSocket.ignore=true on createRestWebSocketServlet"); - boolean valid = false; - for (Constructor c : webSocketType.getDeclaredConstructors()) { - if (c.getParameterCount() == 0 && (Modifier.isPublic(c.getModifiers()) || Modifier.isProtected(c.getModifiers()))) { - valid = true; - break; - } - } - if (!valid) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") must have public or protected Constructor on createRestWebSocketServlet"); - final String rwsname = ResourceFactory.formatResourceName(rws.name()); - if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9"); - if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9"); - - //---------------------------------------------------------------------------------------- - final Set resourcesFieldSet = new LinkedHashSet<>(); - final ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; - final Set resourcesFieldNameSet = new HashSet<>(); - Class clzz = webSocketType; - do { - for (Field field : clzz.getDeclaredFields()) { - if (field.getAnnotation(Resource.class) == null) continue; - if (resourcesFieldNameSet.contains(field.getName())) continue; - if (Modifier.isStatic(field.getModifiers())) throw new RuntimeException(field + " cannot static on createRestWebSocketServlet"); - if (Modifier.isFinal(field.getModifiers())) throw new RuntimeException(field + " cannot final on createRestWebSocketServlet"); - if (!Modifier.isPublic(field.getModifiers()) && !Modifier.isProtected(field.getModifiers())) { - throw new RuntimeException(field + " must be public or protected on createRestWebSocketServlet"); - } - resourcesFieldNameSet.add(field.getName()); - resourcesFieldSet.add(field); - } - } while ((clzz = clzz.getSuperclass()) != Object.class); - - final List resourcesFields = new ArrayList<>(resourcesFieldSet); - StringBuilder sb1 = new StringBuilder(); - StringBuilder sb2 = new StringBuilder(); - for (int i = 0; i < resourcesFields.size(); i++) { - Field field = resourcesFields.get(i); - sb1.append(Type.getDescriptor(field.getType())); - sb2.append(Utility.getTypeDescriptor(field.getGenericType())); - } - final String resourceDescriptor = sb1.toString(); - final String resourceGenericDescriptor = sb1.length() == sb2.length() ? null : sb2.toString(); - - //---------------------------------------------------------------------------------------- - boolean namePresent = false; - try { - Method m0 = null; - for (Method method : webSocketType.getMethods()) { - if (method.getParameterCount() > 0) { - m0 = method; - break; - } - } - namePresent = m0 == null ? true : m0.getParameters()[0].isNamePresent(); - } catch (Exception e) { - } - final Map> asmParamMap = namePresent ? null : MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), webSocketType); - final Set messageNames = new HashSet<>(); - final List messageMethods = new ArrayList<>(); - for (Method method : webSocketType.getMethods()) { - RestOnMessage rom = method.getAnnotation(RestOnMessage.class); - if (rom == null) continue; - String name = rom.name(); - if (Modifier.isFinal(method.getModifiers())) throw new RuntimeException("@RestOnMessage method can not final but (" + method + ")"); - if (Modifier.isStatic(method.getModifiers())) throw new RuntimeException("@RestOnMessage method can not static but (" + method + ")"); - if (method.getReturnType() != void.class) throw new RuntimeException("@RestOnMessage method must return void but (" + method + ")"); - if (method.getExceptionTypes().length > 0) throw new RuntimeException("@RestOnMessage method can not throw exception but (" + method + ")"); - if (name.isEmpty()) throw new RuntimeException(method + " RestOnMessage.name is empty createRestWebSocketServlet"); - if (messageNames.contains(name)) throw new RuntimeException(method + " repeat RestOnMessage.name(" + name + ") createRestWebSocketServlet"); - messageNames.add(name); - messageMethods.add(method); - } - //---------------------------------------------------------------------------------------- - final String resDesc = Type.getDescriptor(Resource.class); - final String wsDesc = Type.getDescriptor(WebSocket.class); - final String wsParamDesc = Type.getDescriptor(WebSocketParam.class); - final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class); - final String convertDisabledDesc = Type.getDescriptor(ConvertDisabled.class); - final String webSocketParamName = Type.getInternalName(WebSocketParam.class); - final String supDynName = WebSocketServlet.class.getName().replace('.', '/'); - final String webServletDesc = Type.getDescriptor(WebServlet.class); - final String webSocketInternalName = Type.getInternalName(webSocketType); - - final String newDynName = webSocketInternalName.substring(0, webSocketInternalName.lastIndexOf('/') + 1) + "_Dyn" + webSocketType.getSimpleName() + "Servlet"; - - final String newDynWebSokcetSimpleName = "_Dyn" + webSocketType.getSimpleName(); - final String newDynWebSokcetFullName = newDynName + "$" + newDynWebSokcetSimpleName; - - final String newDynMessageSimpleName = "_Dyn" + webSocketType.getSimpleName() + "Message"; - final String newDynMessageFullName = newDynName + "$" + newDynMessageSimpleName; - - final String newDynConsumerSimpleName = "_DynRestOnMessageConsumer"; - final String newDynConsumerFullName = newDynName + "$" + newDynConsumerSimpleName; - //---------------------------------------------------------------------------------------- - - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodDebugVisitor mv; - AnnotationVisitor av0; - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); - - { //RestDynSourceType - av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true); - av0.visit("value", Type.getType(Type.getDescriptor(webSocketType))); - av0.visitEnd(); - } - { //注入 @WebServlet 注解 - String urlpath = (rws.catalog().isEmpty() ? "/" : ("/" + rws.catalog() + "/")) + rwsname; - av0 = cw.visitAnnotation(webServletDesc, true); - { - AnnotationVisitor av1 = av0.visitArray("value"); - av1.visit(null, urlpath); - av1.visitEnd(); - } - av0.visit("moduleid", 0); - av0.visit("repair", rws.repair()); - av0.visit("comment", rws.comment()); - av0.visitEnd(); - } - { //内部类 - cw.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC); - - cw.visitInnerClass(newDynWebSokcetFullName, newDynName, newDynWebSokcetSimpleName, ACC_PUBLIC + ACC_STATIC); - - cw.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC); - - for (int i = 0; i < messageMethods.size(); i++) { - Method method = messageMethods.get(i); - String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); - cw.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); - } - } - { //@Resource - for (int i = 0; i < resourcesFields.size(); i++) { - Field field = resourcesFields.get(i); - Resource res = field.getAnnotation(Resource.class); - java.lang.reflect.Type fieldType = field.getGenericType(); - fv = cw.visitField(ACC_PRIVATE, "_redkale_resource_" + i, Type.getDescriptor(field.getType()), fieldType == field.getType() ? null : Utility.getTypeDescriptor(fieldType), null); - { - av0 = fv.visitAnnotation(resDesc, true); - av0.visit("name", res.name()); - av0.visitEnd(); - } - fv.visitEnd(); - } - } - { //_redkale_annotations - fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, "_redkale_annotations", "Ljava/util/Map;", "Ljava/util/Map;", null); - fv.visitEnd(); - } - { //_DynWebSocketServlet构造函数 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); - mv.visitVarInsn(ALOAD, 0); - mv.visitLdcInsn(Type.getObjectType(newDynName + "$" + newDynWebSokcetSimpleName + "Message")); - mv.visitFieldInsn(PUTFIELD, newDynName, "messageTextType", "Ljava/lang/reflect/Type;"); - - mv.visitVarInsn(ALOAD, 0); - pushInt(mv, rws.liveinterval()); - mv.visitFieldInsn(PUTFIELD, newDynName, "liveinterval", "I"); - - mv.visitVarInsn(ALOAD, 0); - pushInt(mv, rws.wsmaxconns()); - mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxconns", "I"); - - mv.visitVarInsn(ALOAD, 0); - pushInt(mv, rws.wsmaxbody()); - mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxbody", "I"); - - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(rws.mergemsg() ? ICONST_1 : ICONST_0); - mv.visitFieldInsn(PUTFIELD, newDynName, "mergemsg", "Z"); - - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(rws.single() ? ICONST_1 : ICONST_0); - mv.visitFieldInsn(PUTFIELD, newDynName, "single", "Z"); - - mv.visitVarInsn(ALOAD, 0); - mv.visitInsn(rws.anyuser() ? ICONST_1 : ICONST_0); - mv.visitFieldInsn(PUTFIELD, newDynName, "anyuser", "Z"); - - mv.visitInsn(RETURN); - mv.visitMaxs(3, 1); - mv.visitEnd(); - } - { //createWebSocket 方法 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createWebSocket", "()" + wsDesc, "()L" + WebSocket.class.getName().replace('.', '/') + ";", null)); - mv.visitTypeInsn(NEW, newDynName + "$" + newDynWebSokcetSimpleName); - mv.visitInsn(DUP); - for (int i = 0; i < resourcesFields.size(); i++) { - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_resource_" + i, Type.getDescriptor(resourcesFields.get(i).getType())); - } - mv.visitMethodInsn(INVOKESPECIAL, newDynWebSokcetFullName, "", "(" + resourceDescriptor + ")V", false); - mv.visitInsn(ARETURN); - mv.visitMaxs(2 + resourcesFields.size(), 1); - mv.visitEnd(); - } - { //createRestOnMessageConsumer - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createRestOnMessageConsumer", "()Ljava/util/function/BiConsumer;", "()Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", null)); - mv.visitTypeInsn(NEW, newDynConsumerFullName); - mv.visitInsn(DUP); - mv.visitMethodInsn(INVOKESPECIAL, newDynConsumerFullName, "", "()V", false); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 1); - mv.visitEnd(); - } - { //resourceName - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null)); - mv.visitLdcInsn(rwsname); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - - RestClassLoader newLoader = new RestClassLoader(loader); - Map msgclassToAnnotations = new HashMap<>(); - for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage 子消息List - Method method = messageMethods.get(i); - String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); - - ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); - cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName + endfix, null, "java/lang/Object", new String[]{webSocketParamName, "java/lang/Runnable"}); - cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); - Set paramnames = new HashSet<>(); - String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method); - List names = asmParamMap == null ? null : asmParamMap.get(methodesc); - if (names != null) while (names.remove(" ")); //删掉空元素 - Parameter[] params = method.getParameters(); - final LinkedHashMap paramap = new LinkedHashMap(); //必须使用LinkedHashMap确保顺序 - for (int j = 0; j < params.length; j++) { //字段列表 - Parameter param = params[j]; - String paramname = param.getName(); - RestParam rp = param.getAnnotation(RestParam.class); - if (rp != null && !rp.name().isEmpty()) { - paramname = rp.name(); - } else if (names != null && names.size() > j) { - paramname = names.get(j); - } - if (paramnames.contains(paramname)) throw new RuntimeException(method + " has same @RestParam.name"); - paramnames.add(paramname); - paramap.put(paramname, param); - fv = cw2.visitField(ACC_PUBLIC, paramname, Type.getDescriptor(param.getType()), - param.getType() == param.getParameterizedType() ? null : Utility.getTypeDescriptor(param.getParameterizedType()), null); - fv.visitEnd(); - } - { //_redkale_websocket - fv = cw2.visitField(ACC_PUBLIC, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";", null, null); - av0 = fv.visitAnnotation(convertDisabledDesc, true); - av0.visitEnd(); - fv.visitEnd(); - } - { //空构造函数 - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //getNames - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getNames", "()[Ljava/lang/String;", null, null)); - pushInt(mv, paramap.size()); - mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); - int index = -1; - for (Map.Entry en : paramap.entrySet()) { - mv.visitInsn(DUP); - pushInt(mv, ++index); - mv.visitLdcInsn(en.getKey()); - mv.visitInsn(AASTORE); - } - mv.visitInsn(ARETURN); - mv.visitMaxs(paramap.size() + 2, 1); - mv.visitEnd(); - } - { //getValue - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/String;)Ljava/lang/Object;", "(Ljava/lang/String;)TT;", null)); - for (Map.Entry en : paramap.entrySet()) { - Class paramType = en.getValue().getType(); - mv.visitLdcInsn(en.getKey()); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); - Label l1 = new Label(); - mv.visitJumpInsn(IFEQ, l1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, en.getKey(), Type.getDescriptor(paramType)); - if (paramType.isPrimitive()) { - Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(paramType, 1), 0).getClass(); - mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(paramType) + ")" + Type.getDescriptor(bigclaz), false); - } - mv.visitInsn(ARETURN); - mv.visitLabel(l1); - } - mv.visitInsn(ACONST_NULL); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - { //getAnnotations - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getAnnotations", "()[Ljava/lang/annotation/Annotation;", null, null)); - mv.visitFieldInsn(GETSTATIC, newDynName, "_redkale_annotations", "Ljava/util/Map;"); - mv.visitLdcInsn(newDynMessageFullName + endfix); - mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); - mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/annotation/Annotation;"); - mv.visitVarInsn(ASTORE, 1); - mv.visitVarInsn(ALOAD, 1); - Label l2 = new Label(); - mv.visitJumpInsn(IFNONNULL, l2); - mv.visitInsn(ICONST_0); - mv.visitTypeInsn(ANEWARRAY, "java/lang/annotation/Annotation"); - mv.visitInsn(ARETURN); - mv.visitLabel(l2); - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"[Ljava/lang/annotation/Annotation;"}, 0, null); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 1); - mv.visitInsn(ARRAYLENGTH); - mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "copyOf", "([Ljava/lang/Object;I)[Ljava/lang/Object;", false); - mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/annotation/Annotation;"); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - { //execute - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(L" + newDynWebSokcetFullName + ";)V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(PUTFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";"); - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(method.getAnnotation(RestOnMessage.class).name()); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, "preOnMessage", "(Ljava/lang/String;" + wsParamDesc + "Ljava/lang/Runnable;)V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(4, 2); - mv.visitEnd(); - } - { //run - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "run", "()V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";"); - - for (Map.Entry en : paramap.entrySet()) { - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, (newDynMessageFullName + endfix), en.getKey(), Type.getDescriptor(en.getValue().getType())); - } - mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, method.getName(), Type.getMethodDescriptor(method), false); - - mv.visitInsn(RETURN); - mv.visitMaxs(3, 1); - mv.visitEnd(); - } - { //toString - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); - mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 1); - mv.visitEnd(); - } - cw2.visitEnd(); - newLoader.loadClass((newDynMessageFullName + endfix).replace('/', '.'), cw2.toByteArray()); - msgclassToAnnotations.put(newDynMessageFullName + endfix, method.getAnnotations()); - } - - { //_DynXXXWebSocketMessage class - ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); - cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName, null, "java/lang/Object", null); - - cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC); - - for (int i = 0; i < messageMethods.size(); i++) { - Method method = messageMethods.get(i); - String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); - cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); - - fv = cw2.visitField(ACC_PUBLIC, method.getAnnotation(RestOnMessage.class).name(), "L" + newDynMessageFullName + endfix + ";", null, null); - fv.visitEnd(); - } - { //构造函数 - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //toString - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); - mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 1); - mv.visitEnd(); - } - cw2.visitEnd(); - newLoader.loadClass(newDynMessageFullName.replace('/', '.'), cw2.toByteArray()); - } - - { //_DynXXXWebSocket class - ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); - cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynWebSokcetFullName, null, webSocketInternalName, null); - - cw2.visitInnerClass(newDynWebSokcetFullName, newDynName, newDynWebSokcetSimpleName, ACC_PUBLIC + ACC_STATIC); - - { - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "(" + resourceDescriptor + ")V", resourceGenericDescriptor == null ? null : ("(" + resourceGenericDescriptor + ")V"), null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, webSocketInternalName, "", "()V", false); - for (int i = 0; i < resourcesFields.size(); i++) { - Field field = resourcesFields.get(i); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, i + 1); - mv.visitFieldInsn(PUTFIELD, newDynWebSokcetFullName, field.getName(), Type.getDescriptor(field.getType())); - } - mv.visitInsn(RETURN); - mv.visitMaxs(2, 1 + resourcesFields.size()); - mv.visitEnd(); - } - cw2.visitEnd(); - newLoader.loadClass(newDynWebSokcetFullName.replace('/', '.'), cw2.toByteArray()); - } - - { //_DynRestOnMessageConsumer class - ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); - cw2.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynConsumerFullName, "Ljava/lang/Object;Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", "java/lang/Object", new String[]{"java/util/function/BiConsumer"}); - - cw2.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC); - cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC); - for (int i = 0; i < messageMethods.size(); i++) { - Method method = messageMethods.get(i); - String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); - cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); - } - - { //构造函数 - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - - { //accept函数 - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", null, null)); - mv.visitVarInsn(ALOAD, 1); - mv.visitTypeInsn(CHECKCAST, newDynWebSokcetFullName); - mv.visitVarInsn(ASTORE, 3); - - mv.visitVarInsn(ALOAD, 2); - mv.visitTypeInsn(CHECKCAST, newDynMessageFullName); - mv.visitVarInsn(ASTORE, 4); - - for (int i = 0; i < messageMethods.size(); i++) { - final Method method = messageMethods.get(i); - String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); - final String messagename = method.getAnnotation(RestOnMessage.class).name(); - - mv.visitVarInsn(ALOAD, 4); - mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";"); - Label ifLabel = new Label(); - mv.visitJumpInsn(IFNULL, ifLabel); - - mv.visitVarInsn(ALOAD, 4); - mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";"); - mv.visitVarInsn(ALOAD, 3); - mv.visitMethodInsn(INVOKEVIRTUAL, (newDynMessageFullName + endfix), "execute", "(L" + newDynWebSokcetFullName + ";)V", false); - mv.visitInsn(RETURN); - mv.visitLabel(ifLabel); - } - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3 + messageMethods.size()); - mv.visitEnd(); - } - {//虚拟accept函数 - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "accept", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitTypeInsn(CHECKCAST, WebSocket.class.getName().replace('.', '/')); - mv.visitVarInsn(ALOAD, 2); - mv.visitTypeInsn(CHECKCAST, "java/lang/Object"); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynConsumerFullName, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - cw2.visitEnd(); - newLoader.loadClass(newDynConsumerFullName.replace('/', '.'), cw2.toByteArray()); - } - cw.visitEnd(); - Class newClazz = newLoader.loadClass(newDynName.replace('/', '.'), cw.toByteArray()); - try { - T servlet = (T) newClazz.getDeclaredConstructor().newInstance(); - newClazz.getField("_redkale_annotations").set(null, msgclassToAnnotations); - if (rws.cryptor() != Cryptor.class) { - Cryptor cryptor = rws.cryptor().getDeclaredConstructor().newInstance(); - Field cryptorField = newClazz.getSuperclass().getDeclaredField("cryptor"); //WebSocketServlet - cryptorField.setAccessible(true); - cryptorField.set(servlet, cryptor); - } - if (messageAgent != null) ((WebSocketServlet) servlet).messageAgent = messageAgent; - return servlet; - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public static T createRestServlet(final ClassLoader classLoader, final Class userType0, final Class baseServletType, final Class serviceType) { - if (baseServletType == null || serviceType == null) throw new RuntimeException(" Servlet or Service is null Class on createRestServlet"); - if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet"); - int mod = baseServletType.getModifiers(); - if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet"); - if (java.lang.reflect.Modifier.isAbstract(mod)) { - for (Method m : baseServletType.getDeclaredMethods()) { - if (java.lang.reflect.Modifier.isAbstract(m.getModifiers())) { //@since 2.4.0 - throw new RuntimeException(baseServletType + " cannot contains a abstract Method on " + baseServletType); - } - } - } - - final String restInternalName = Type.getInternalName(Rest.class); - final String serviceDesc = Type.getDescriptor(serviceType); - final String webServletDesc = Type.getDescriptor(WebServlet.class); - final String resDesc = Type.getDescriptor(Resource.class); - final String reqDesc = Type.getDescriptor(HttpRequest.class); - final String respDesc = Type.getDescriptor(HttpResponse.class); - final String convertDesc = Type.getDescriptor(Convert.class); - final String typeDesc = Type.getDescriptor(java.lang.reflect.Type.class); - final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class); - final String retDesc = Type.getDescriptor(RetResult.class); - final String httpretDesc = Type.getDescriptor(HttpResult.class); - final String scopeDesc = Type.getDescriptor(HttpScope.class); - final String futureDesc = Type.getDescriptor(CompletableFuture.class); - final String flipperDesc = Type.getDescriptor(Flipper.class); - final String channelDesc = Type.getDescriptor(ChannelContext.class); - final String httpServletName = HttpServlet.class.getName().replace('.', '/'); - final String actionEntryName = HttpServlet.ActionEntry.class.getName().replace('.', '/'); - final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class); - final String multiContextDesc = Type.getDescriptor(MultiContext.class); - final String multiContextName = MultiContext.class.getName().replace('.', '/'); - final String mappingDesc = Type.getDescriptor(HttpMapping.class); - final String webparamDesc = Type.getDescriptor(HttpParam.class); - final String webparamsDesc = Type.getDescriptor(HttpParam.HttpParams.class); - final String sourcetypeDesc = Type.getDescriptor(HttpParam.HttpParamSourceType.class); - - final String reqInternalName = Type.getInternalName(HttpRequest.class); - final String respInternalName = Type.getInternalName(HttpResponse.class); - final String attrInternalName = Type.getInternalName(org.redkale.util.Attribute.class); - final String retInternalName = Type.getInternalName(RetResult.class); - final String serviceTypeInternalName = Type.getInternalName(serviceType); - - HttpUserType hut = baseServletType.getAnnotation(HttpUserType.class); - final Class userType = (userType0 == null || userType0 == Object.class) ? (hut == null ? null : hut.value()) : userType0; - if (userType != null && (userType.isPrimitive() || userType.getName().startsWith("java.") || userType.getName().startsWith("javax."))) { - throw new RuntimeException(HttpUserType.class.getSimpleName() + " must be a JavaBean but found " + userType); - } - - final String supDynName = baseServletType.getName().replace('.', '/'); - final RestService controller = serviceType.getAnnotation(RestService.class); - if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet - final boolean serrpconly = controller != null && controller.rpconly(); - ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; - String stname = serviceType.getSimpleName(); - if (stname.startsWith("Service")) { //类似ServiceWatchService这样的类保留第一个Service字样 - stname = "Service" + stname.substring("Service".length()).replaceAll("Service.*$", ""); - } else { - stname = stname.replaceAll("Service.*$", ""); - } - String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + stname + "RestServlet"; - - //------------------------------------------------------------------------------ - final String defmodulename = getWebModuleNameLowerCase(serviceType); - final String bigmodulename = getWebModuleName(serviceType); - final String catalog = controller == null ? "" : controller.catalog(); - if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9"); - if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9"); - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodDebugVisitor mv; - AnnotationVisitor av0; - Map classMap = new LinkedHashMap<>(); - List> mappingMaps = new ArrayList<>(); - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); - - { //RestDynSourceType - av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true); - av0.visit("value", Type.getType(Type.getDescriptor(serviceType))); - av0.visitEnd(); - } - boolean dynsimple = true; - final List entrys = new ArrayList<>(); - final Map restAttributes = new LinkedHashMap<>(); - //获取所有可以转换成HttpMapping的方法 - int methodidex = 0; - final List paramtypes = new ArrayList<>(); - final MessageMultiConsumer mmc = serviceType.getAnnotation(MessageMultiConsumer.class); - if (mmc != null && (mmc.module() == null || mmc.module().isEmpty())) { - throw new RuntimeException("@" + MessageMultiConsumer.class.getSimpleName() + ".module can not empty in " + serviceType.getName()); - } - for (final Method method : serviceType.getMethods()) { - if (Modifier.isStatic(method.getModifiers())) continue; - if (method.isSynthetic()) continue; - if (EXCLUDERMETHODS.contains(method.getName())) continue; - if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) { - if ("init".equals(method.getName())) continue; - if ("stop".equals(method.getName())) continue; - if ("destroy".equals(method.getName())) continue; - } - if (controller == null) continue; - - RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class); - if (!controller.automapping() && mappings.length < 1) continue; - boolean ignore = false; - for (RestMapping mapping : mappings) { - if (mapping.ignore()) { - ignore = true; - break; - } - } - if (ignore) continue; - - Class[] extypes = method.getExceptionTypes(); - if (extypes.length > 0) { - for (Class exp : extypes) { - if (!RuntimeException.class.isAssignableFrom(exp) && !IOException.class.isAssignableFrom(exp)) { - throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method(" + method + ") with throws IOException"); - } - } - } - if (mmc != null && method.getReturnType() != void.class) { - throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method(" + method + ") with return void by @" + MessageMultiConsumer.class.getSimpleName() + " Service"); - } - paramtypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType)); - if (mappings.length == 0) { //没有Mapping,设置一个默认值 - MappingEntry entry = new MappingEntry(serrpconly, methodidex, null, bigmodulename, method); - if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat"); - entrys.add(entry); - } else { - for (RestMapping mapping : mappings) { - MappingEntry entry = new MappingEntry(serrpconly, methodidex, mapping, defmodulename, method); - if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat"); - entrys.add(entry); - } - } - methodidex++; - } - if (entrys.isEmpty()) return null; //没有可HttpMapping的方法 - - Collections.sort(entrys); - RestClassLoader newLoader = new RestClassLoader(loader); - final int moduleid = controller == null ? 0 : controller.moduleid(); - { //注入 @WebServlet 注解 - String urlpath = ""; - boolean repair = controller == null ? true : controller.repair(); - String comment = controller == null ? "" : controller.comment(); - av0 = cw.visitAnnotation(webServletDesc, true); - { - AnnotationVisitor av1 = av0.visitArray("value"); - boolean pound = false; - for (MappingEntry entry : entrys) { - if (entry.existsPound) { - pound = true; - break; - } - } - if (defmodulename.isEmpty() || (!pound && entrys.size() <= 2)) { - for (MappingEntry entry : entrys) { - String suburl = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name; - if ("//".equals(suburl)) { - suburl = "/"; - } else if (suburl.length() > 2 && suburl.endsWith("/")) { - suburl += "*"; - } - urlpath += "," + suburl; - av1.visit(null, suburl); - } - if (urlpath.length() > 0) urlpath = urlpath.substring(1); - } else { - urlpath = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/*"; - av1.visit(null, urlpath); - } - av1.visitEnd(); - } - av0.visit("moduleid", moduleid); - av0.visit("repair", repair); - av0.visit("comment", comment); - av0.visitEnd(); - classMap.put("type", serviceType.getName()); - classMap.put("url", urlpath); - classMap.put("moduleid", moduleid); - classMap.put("repair", repair); - //classMap.put("comment", comment); //不显示太多信息 - } - { //内部类 - cw.visitInnerClass(actionEntryName, httpServletName, HttpServlet.ActionEntry.class.getSimpleName(), ACC_PROTECTED + ACC_FINAL + ACC_STATIC); - - for (final MappingEntry entry : entrys) { - cw.visitInnerClass(newDynName + "$" + entry.newActionClassName, newDynName, entry.newActionClassName, ACC_PRIVATE + ACC_STATIC); - } - } - { //注入 @Resource private XXXService _service; - fv = cw.visitField(ACC_PRIVATE, REST_SERVICE_FIELD_NAME, serviceDesc, null, null); - av0 = fv.visitAnnotation(resDesc, true); - av0.visit("name", ""); - av0.visitEnd(); - fv.visitEnd(); - } - { //注入 @Resource(name = "APP_HOME") private File _redkale_home; - fv = cw.visitField(ACC_PRIVATE, "_redkale_home", Type.getDescriptor(File.class), null, null); - av0 = fv.visitAnnotation(resDesc, true); - av0.visit("name", "APP_HOME"); - av0.visitEnd(); - fv.visitEnd(); - } - { //_servicemap字段 Map - fv = cw.visitField(ACC_PRIVATE, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;", "Ljava/util/Map;", null); - fv.visitEnd(); - } - { //_paramtypes字段 java.lang.reflect.Type[][] - fv = cw.visitField(ACC_PRIVATE, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;", null, null); - fv.visitEnd(); - } - { //_redkale_tostringsupplier字段 Supplier - fv = cw.visitField(ACC_PRIVATE, REST_TOSTRINGOBJ_FIELD_NAME, "Ljava/util/function/Supplier;", "Ljava/util/function/Supplier;", null); - fv.visitEnd(); - } - { //构造函数 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - - //将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法 - boolean namePresent = false; - try { - Method m0 = null; - for (final MappingEntry entry : entrys) { - if (entry.mappingMethod.getParameterCount() > 0) { - m0 = entry.mappingMethod; - break; - } - } - namePresent = m0 == null ? true : m0.getParameters()[0].isNamePresent(); - } catch (Exception e) { - } - final Map> asmParamMap = namePresent ? null : MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType); - final Map bodyTypes = new HashMap<>(); - - final List restConverts = new ArrayList<>(); - for (final MappingEntry entry : entrys) { - RestUploadFile mupload = null; - Class muploadType = null; - final Method method = entry.mappingMethod; - final Class returnType = method.getReturnType(); - final String methodDesc = Type.getMethodDescriptor(method); - final Parameter[] params = method.getParameters(); - - final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class); - final RestConvertCoder[] rcc = method.getAnnotationsByType(RestConvertCoder.class); - if ((rcs != null && rcs.length > 0) || (rcc != null && rcc.length > 0)) { - restConverts.add(new Object[]{rcs, rcc}); - } - - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, entry.newMethodName, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"})); - //mv.setDebug(true); - mv.debugLine(); - - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;"); - Label lmapif = new Label(); - mv.visitJumpInsn(IFNONNULL, lmapif); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc); - Label lserif = new Label(); - mv.visitJumpInsn(GOTO, lserif); - mv.visitLabel(lmapif); - - mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); - - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;"); - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(REST_HEADER_RESOURCE_NAME); - mv.visitLdcInsn(""); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); - mv.visitTypeInsn(CHECKCAST, serviceTypeInternalName); - mv.visitLabel(lserif); - mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{serviceTypeInternalName}); - mv.visitVarInsn(ASTORE, 3); - - final int maxStack = 3 + params.length; - List varInsns = new ArrayList<>(); - int maxLocals = 4; - - List asmParamNames = asmParamMap == null ? null : asmParamMap.get(method.getName() + ":" + Type.getMethodDescriptor(method)); - if (asmParamNames != null) while (asmParamNames.remove(" ")); //删掉空元素 - List paramlist = new ArrayList<>(); - //解析方法中的每个参数 - for (int i = 0; i < params.length; i++) { - final Parameter param = params[i]; - final Class ptype = param.getType(); - String n = null; - String comment = ""; - boolean required = true; - int radix = 10; - - RestHeader annhead = param.getAnnotation(RestHeader.class); - if (annhead != null) { - if (ptype != String.class && ptype != InetSocketAddress.class) throw new RuntimeException("@RestHeader must on String or InetSocketAddress Parameter in " + method); - n = annhead.name(); - radix = annhead.radix(); - comment = annhead.comment(); - if (n.isEmpty()) throw new RuntimeException("@RestHeader.value is illegal in " + method); - } - RestCookie anncookie = param.getAnnotation(RestCookie.class); - if (anncookie != null) { - if (annhead != null) throw new RuntimeException("@RestCookie and @RestHeader cannot on the same Parameter in " + method); - if (ptype != String.class) throw new RuntimeException("@RestCookie must on String Parameter in " + method); - n = anncookie.name(); - radix = anncookie.radix(); - comment = anncookie.comment(); - if (n.isEmpty()) throw new RuntimeException("@RestCookie.value is illegal in " + method); - } - RestSessionid annsid = param.getAnnotation(RestSessionid.class); - if (annsid != null) { - if (annhead != null) throw new RuntimeException("@RestSessionid and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestSessionid and @RestCookie cannot on the same Parameter in " + method); - if (ptype != String.class) throw new RuntimeException("@RestSessionid must on String Parameter in " + method); - } - RestAddress annaddr = param.getAnnotation(RestAddress.class); - if (annaddr != null) { - if (annhead != null) throw new RuntimeException("@RestAddress and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestAddress and @RestCookie cannot on the same Parameter in " + method); - if (annsid != null) throw new RuntimeException("@RestAddress and @RestSessionid cannot on the same Parameter in " + method); - if (ptype != String.class) throw new RuntimeException("@RestAddress must on String Parameter in " + method); - comment = annaddr.comment(); - } - RestBody annbody = param.getAnnotation(RestBody.class); - if (annbody != null) { - if (annhead != null) throw new RuntimeException("@RestBody and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestBody and @RestCookie cannot on the same Parameter in " + method); - if (annsid != null) throw new RuntimeException("@RestBody and @RestSessionid cannot on the same Parameter in " + method); - if (annaddr != null) throw new RuntimeException("@RestBody and @RestAddress cannot on the same Parameter in " + method); - if (ptype.isPrimitive()) throw new RuntimeException("@RestBody cannot on primitive type Parameter in " + method); - comment = annbody.comment(); - } - RestUploadFile annfile = param.getAnnotation(RestUploadFile.class); - if (annfile != null) { - if (mupload != null) throw new RuntimeException("@RestUploadFile repeat in " + method); - mupload = annfile; - muploadType = ptype; - if (annhead != null) throw new RuntimeException("@RestUploadFile and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestUploadFile and @RestCookie cannot on the same Parameter in " + method); - if (annsid != null) throw new RuntimeException("@RestUploadFile and @RestSessionid cannot on the same Parameter in " + method); - if (annaddr != null) throw new RuntimeException("@RestUploadFile and @RestAddress cannot on the same Parameter in " + method); - if (annbody != null) throw new RuntimeException("@RestUploadFile and @RestBody cannot on the same Parameter in " + method); - if (ptype != byte[].class && ptype != File.class && ptype != File[].class) throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Parameter in " + method); - comment = annfile.comment(); - } - - RestURI annuri = param.getAnnotation(RestURI.class); - if (annuri != null) { - if (annhead != null) throw new RuntimeException("@RestURI and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestURI and @RestCookie cannot on the same Parameter in " + method); - if (annsid != null) throw new RuntimeException("@RestURI and @RestSessionid cannot on the same Parameter in " + method); - if (annaddr != null) throw new RuntimeException("@RestURI and @RestAddress cannot on the same Parameter in " + method); - if (annbody != null) throw new RuntimeException("@RestURI and @RestBody cannot on the same Parameter in " + method); - if (annfile != null) throw new RuntimeException("@RestURI and @RestUploadFile cannot on the same Parameter in " + method); - if (ptype != String.class) throw new RuntimeException("@RestURI must on String Parameter in " + method); - comment = annuri.comment(); - } - - RestUserid userid = param.getAnnotation(RestUserid.class); - if (userid != null) { - if (annhead != null) throw new RuntimeException("@RestUserid and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestUserid and @RestCookie cannot on the same Parameter in " + method); - if (annsid != null) throw new RuntimeException("@RestUserid and @RestSessionid cannot on the same Parameter in " + method); - if (annaddr != null) throw new RuntimeException("@RestUserid and @RestAddress cannot on the same Parameter in " + method); - if (annbody != null) throw new RuntimeException("@RestUserid and @RestBody cannot on the same Parameter in " + method); - if (annfile != null) throw new RuntimeException("@RestUserid and @RestUploadFile cannot on the same Parameter in " + method); - if (!ptype.isPrimitive() && !java.io.Serializable.class.isAssignableFrom(ptype)) throw new RuntimeException("@RestUserid must on java.io.Serializable Parameter in " + method); - comment = ""; - } - - RestHeaders annheaders = param.getAnnotation(RestHeaders.class); - if (annheaders != null) { - if (annhead != null) throw new RuntimeException("@RestHeaders and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestHeaders and @RestCookie cannot on the same Parameter in " + method); - if (annsid != null) throw new RuntimeException("@RestHeaders and @RestSessionid cannot on the same Parameter in " + method); - if (annaddr != null) throw new RuntimeException("@RestHeaders and @RestAddress cannot on the same Parameter in " + method); - if (annbody != null) throw new RuntimeException("@RestHeaders and @RestBody cannot on the same Parameter in " + method); - if (annfile != null) throw new RuntimeException("@RestHeaders and @RestUploadFile cannot on the same Parameter in " + method); - if (userid != null) throw new RuntimeException("@RestHeaders and @RestUserid cannot on the same Parameter in " + method); - if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestHeaders must on Map Parameter in " + method); - comment = ""; - } - RestParams annparams = param.getAnnotation(RestParams.class); - if (annparams != null) { - if (annhead != null) throw new RuntimeException("@RestParams and @RestHeader cannot on the same Parameter in " + method); - if (anncookie != null) throw new RuntimeException("@RestParams and @RestCookie cannot on the same Parameter in " + method); - if (annsid != null) throw new RuntimeException("@RestParams and @RestSessionid cannot on the same Parameter in " + method); - if (annaddr != null) throw new RuntimeException("@RestParams and @RestAddress cannot on the same Parameter in " + method); - if (annbody != null) throw new RuntimeException("@RestParams and @RestBody cannot on the same Parameter in " + method); - if (annfile != null) throw new RuntimeException("@RestParams and @RestUploadFile cannot on the same Parameter in " + method); - if (userid != null) throw new RuntimeException("@RestParams and @RestUserid cannot on the same Parameter in " + method); - if (annheaders != null) throw new RuntimeException("@RestParams and @RestHeaders cannot on the same Parameter in " + method); - if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestParams must on Map Parameter in " + method); - comment = ""; - } - - RestParam annpara = param.getAnnotation(RestParam.class); - if (annpara != null) radix = annpara.radix(); - if (annpara != null) comment = annpara.comment(); - if (annpara != null) required = annpara.required(); - if (n == null) n = (annpara == null || annpara.name().isEmpty()) ? null : annpara.name(); - if (n == null && ptype == userType) n = "&"; //用户类型特殊处理 - if (n == null && asmParamNames != null && asmParamNames.size() > i) n = asmParamNames.get(i); - if (n == null) { - if (param.isNamePresent()) { - n = param.getName(); - } else if (ptype == Flipper.class) { - n = "flipper"; - } else { - throw new RuntimeException("Parameter " + param.getName() + " not found name by @RestParam in " + method); - } - } - if (annhead == null && anncookie == null && annsid == null && annaddr == null && annbody == null && annfile == null - && !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !CompletionHandler.class.isAssignableFrom(ptype) - && !ptype.getName().startsWith("java") && n.charAt(0) != '#' && !"&".equals(n)) { //判断Json对象是否包含@RestUploadFile - Class loop = ptype; - do { - if (loop == null || loop.isInterface()) break; //接口时getSuperclass可能会得到null - for (Field field : loop.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (Modifier.isFinal(field.getModifiers())) continue; - RestUploadFile ruf = field.getAnnotation(RestUploadFile.class); - if (ruf == null) continue; - if (mupload != null) throw new RuntimeException("@RestUploadFile repeat in " + method + " or field " + field); - mupload = ruf; - muploadType = field.getType(); - } - } while ((loop = loop.getSuperclass()) != Object.class); - } - java.lang.reflect.Type paramtype = TypeToken.getGenericType(param.getParameterizedType(), serviceType); - paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, userid, annheaders, annparams, paramtype}); - } - - Map mappingMap = new LinkedHashMap<>(); - { // 设置 Annotation - //设置 HttpMapping - boolean reqpath = false; - for (Object[] ps : paramlist) { - if ("#".equals((String) ps[1])) { - reqpath = true; - break; - } - } - av0 = mv.visitAnnotation(mappingDesc, true); - String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : ""); - if ("//".equals(url)) url = "/"; - av0.visit("url", url); - av0.visit("rpconly", entry.rpconly); - av0.visit("auth", entry.auth); - av0.visit("cacheseconds", entry.cacheseconds); - av0.visit("actionid", entry.actionid); - av0.visit("comment", entry.comment); - - AnnotationVisitor av1 = av0.visitArray("methods"); - for (String m : entry.methods) { - av1.visit(null, m); - } - av1.visitEnd(); - - java.lang.reflect.Type grt = TypeToken.getGenericType(method.getGenericReturnType(), serviceType); - av0.visit("result", grt == returnType ? returnType.getName() : String.valueOf(grt)); - - av0.visitEnd(); - mappingMap.put("url", url); - mappingMap.put("rpconly", entry.rpconly); - mappingMap.put("auth", entry.auth); - mappingMap.put("cacheseconds", entry.cacheseconds); - mappingMap.put("actionid", entry.actionid); - mappingMap.put("comment", entry.comment); - mappingMap.put("methods", entry.methods); - mappingMap.put("result", grt == returnType ? returnType.getName() : String.valueOf(grt)); - entry.mappingurl = url; - } - - { // 设置 Annotation - av0 = mv.visitAnnotation(webparamsDesc, true); - AnnotationVisitor av1 = av0.visitArray("value"); - //设置 WebParam - for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, pgentype} - final boolean ishead = ((RestHeader) ps[9]) != null; //是否取getHeader 而不是 getParameter - final boolean iscookie = ((RestCookie) ps[10]) != null; //是否取getCookie - - AnnotationVisitor av2 = av1.visitAnnotation(null, webparamDesc); - av2.visit("name", (String) ps[1]); - av2.visit("type", Type.getType(Type.getDescriptor((Class) ps[2]))); - av2.visit("radix", (Integer) ps[3]); - av2.visitEnum("src", sourcetypeDesc, ishead ? HttpParam.HttpParamSourceType.HEADER.name() - : (iscookie ? HttpParam.HttpParamSourceType.COOKIE.name() : HttpParam.HttpParamSourceType.PARAMETER.name())); - av2.visit("comment", (String) ps[4]); - av2.visit("required", (Boolean) ps[5]); - av2.visitEnd(); - } - av1.visitEnd(); - av0.visitEnd(); - } - int uploadLocal = 0; - if (mupload != null) { //存在文件上传 - if (muploadType == byte[].class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false); - mv.visitLdcInsn(mupload.maxLength()); - mv.visitLdcInsn(mupload.fileNameReg()); - mv.visitLdcInsn(mupload.contentTypeReg()); - mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false); - mv.visitVarInsn(ASTORE, maxLocals); - uploadLocal = maxLocals; - } else if (muploadType == File.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;"); - mv.visitLdcInsn(mupload.maxLength()); - mv.visitLdcInsn(mupload.fileNameReg()); - mv.visitLdcInsn(mupload.contentTypeReg()); - mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false); - mv.visitVarInsn(ASTORE, maxLocals); - uploadLocal = maxLocals; - } else if (muploadType == File[].class) { //File[] - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;"); - mv.visitLdcInsn(mupload.maxLength()); - mv.visitLdcInsn(mupload.fileNameReg()); - mv.visitLdcInsn(mupload.contentTypeReg()); - mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false); - mv.visitVarInsn(ASTORE, maxLocals); - uploadLocal = maxLocals; - } - maxLocals++; - } - - List> paramMaps = new ArrayList<>(); - //获取每个参数的值 - boolean hasAsyncHandler = false; - for (Object[] ps : paramlist) { - Map paramMap = new LinkedHashMap<>(); - final Parameter param = (Parameter) ps[0]; //参数类型 - String pname = (String) ps[1]; //参数名 - Class ptype = (Class) ps[2]; //参数类型 - int radix = (Integer) ps[3]; - String comment = (String) ps[4]; - boolean required = (Boolean) ps[5]; - RestParam annpara = (RestParam) ps[6]; - RestSessionid annsid = (RestSessionid) ps[7]; - RestAddress annaddr = (RestAddress) ps[8]; - RestHeader annhead = (RestHeader) ps[9]; - RestCookie anncookie = (RestCookie) ps[10]; - RestBody annbody = (RestBody) ps[11]; - RestUploadFile annfile = (RestUploadFile) ps[12]; - RestURI annuri = (RestURI) ps[13]; - RestUserid userid = (RestUserid) ps[14]; - RestHeaders annheaders = (RestHeaders) ps[15]; - RestParams annparams = (RestParams) ps[16]; - java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[17]; - if (dynsimple && (annsid != null || annaddr != null || annhead != null || anncookie != null || annfile != null || annheaders != null)) dynsimple = false; - - final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter - final boolean iscookie = anncookie != null; //是否取getCookie - - paramMap.put("name", pname); - paramMap.put("type", ptype.getName()); - if (CompletionHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class) - if (ptype == CompletionHandler.class) { - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Ljava/nio/channels/CompletionHandler;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else { - mv.visitVarInsn(ALOAD, 3); - mv.visitVarInsn(ALOAD, 2); - mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype))); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "(Ljava/lang/Class;)Ljava/nio/channels/CompletionHandler;", false); - mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/')); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } - hasAsyncHandler = true; - } else if (annsid != null) { //HttpRequest.getSessionid(true|false) - mv.visitVarInsn(ALOAD, 1); - mv.visitInsn(annsid.create() ? ICONST_1 : ICONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (annaddr != null) { //HttpRequest.getRemoteAddr - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (annheaders != null) { //HttpRequest.getHeaders - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeaders", "()Ljava/util/Map;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (annparams != null) { //HttpRequest.getParameters - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getParameters", "()Ljava/util/Map;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody - if (ptype == String.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (ptype == byte[].class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else { //JavaBean 转 Json - String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); - bodyTypes.put(typefieldname, pgentype); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype)); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } - } else if (annfile != null) { //MultiContext.partsFirstBytes / HttpRequest.partsFirstFile / HttpRequest.partsFiles - mv.visitVarInsn(ALOAD, 4); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (annuri != null) { //HttpRequest.getRequestURI - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (userid != null) { //HttpRequest.currentUserid - mv.visitVarInsn(ALOAD, 1); - if (ptype == int.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); - } else if (ptype == long.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); - } else { - mv.visitLdcInsn(Type.getType(Type.getInternalName(ptype))); - } - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "currentUserid", "(Ljava/lang/Class;)Ljava/io/Serializable;", false); - if (ptype == int.class) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); - mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false); - - mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == long.class) { - mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); - mv.visitInsn(LCONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); - mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false); - - mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); - mv.visitVarInsn(LSTORE, maxLocals); - varInsns.add(new int[]{LLOAD, maxLocals}); - maxLocals++; - } else { - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype)); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } - } else if ("#".equals(pname)) { //从request.getRequstURI 中取参数 - if (ptype == boolean.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == byte.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == short.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "parseShort", "(Ljava/lang/String;I)S", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == char.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == int.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;I)I", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == float.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false); - mv.visitVarInsn(FSTORE, maxLocals); - varInsns.add(new int[]{FLOAD, maxLocals}); - } else if (ptype == long.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;I)J", false); - mv.visitVarInsn(LSTORE, maxLocals); - varInsns.add(new int[]{LLOAD, maxLocals}); - maxLocals++; - } else if (ptype == double.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false); - mv.visitVarInsn(DSTORE, maxLocals); - varInsns.add(new int[]{DLOAD, maxLocals}); - maxLocals++; - } else if (ptype == String.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else { - throw new RuntimeException(method + " only " + RestParam.class.getSimpleName() + "(#) to Type(primitive class or String)"); - } - } else if (pname.charAt(0) == '#') { //从request.getRequstURIPath 中去参数 - if (ptype == boolean.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("false"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == byte.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == short.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "parseShort", "(Ljava/lang/String;I)S", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == char.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == int.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;I)I", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == float.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false); - mv.visitVarInsn(FSTORE, maxLocals); - varInsns.add(new int[]{FLOAD, maxLocals}); - } else if (ptype == long.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;I)J", false); - mv.visitVarInsn(LSTORE, maxLocals); - varInsns.add(new int[]{LLOAD, maxLocals}); - maxLocals++; - } else if (ptype == double.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false); - mv.visitVarInsn(DSTORE, maxLocals); - varInsns.add(new int[]{DLOAD, maxLocals}); - maxLocals++; - } else if (ptype == String.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname.substring(1)); - mv.visitLdcInsn(""); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else { - throw new RuntimeException(method + " only " + RestParam.class.getSimpleName() + "(#) to Type(primitive class or String)"); - } - } else if ("&".equals(pname) && ptype == userType) { //当前用户对象的类名 - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "currentUser", "()Ljava/lang/Object;", false); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype)); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (ptype == boolean.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getBooleanHeader" : "getBooleanParameter", "(Ljava/lang/String;Z)Z", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == byte.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getHeader" : "getParameter", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitIntInsn(BIPUSH, radix); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == short.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitIntInsn(BIPUSH, radix); - mv.visitLdcInsn(pname); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getShortHeader" : "getShortParameter", "(ILjava/lang/String;S)S", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == char.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname); - mv.visitLdcInsn("0"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getHeader" : "getParameter", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == int.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitIntInsn(BIPUSH, radix); - mv.visitLdcInsn(pname); - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getIntHeader" : "getIntParameter", "(ILjava/lang/String;I)I", false); - mv.visitVarInsn(ISTORE, maxLocals); - varInsns.add(new int[]{ILOAD, maxLocals}); - } else if (ptype == float.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname); - mv.visitInsn(FCONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getFloatHeader" : "getFloatParameter", "(Ljava/lang/String;F)F", false); - mv.visitVarInsn(FSTORE, maxLocals); - varInsns.add(new int[]{FLOAD, maxLocals}); - } else if (ptype == long.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitIntInsn(BIPUSH, radix); - mv.visitLdcInsn(pname); - mv.visitInsn(LCONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getLongHeader" : "getLongParameter", "(ILjava/lang/String;J)J", false); - mv.visitVarInsn(LSTORE, maxLocals); - varInsns.add(new int[]{LLOAD, maxLocals}); - maxLocals++; - } else if (ptype == double.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname); - mv.visitInsn(DCONST_0); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getDoubleHeader" : "getDoubleParameter", "(Ljava/lang/String;D)D", false); - mv.visitVarInsn(DSTORE, maxLocals); - varInsns.add(new int[]{DLOAD, maxLocals}); - maxLocals++; - } else if (ptype == String.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(pname); - mv.visitLdcInsn(""); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, iscookie ? "getCookie" : (ishead ? "getHeader" : "getParameter"), "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (ptype == ChannelContext.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getChannelContext", "()" + channelDesc, false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else if (ptype == Flipper.class) { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getFlipper", "()" + flipperDesc, false); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - } else { //其他Json对象 - mv.visitVarInsn(ALOAD, 1); - if (param.getType() == param.getParameterizedType()) { - mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype))); - } else { - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;"); - pushInt(mv, entry.methodidx);//方法下标 - mv.visitInsn(AALOAD); - int paramidx = 0; - for (int i = 0; i < params.length; i++) { - if (params[i] == param) { - paramidx = i; - break; - } - } - pushInt(mv, paramidx); //参数下标 - mv.visitInsn(AALOAD); - } - mv.visitLdcInsn(pname); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getJsonHeader" : "getJsonParameter", "(Ljava/lang/reflect/Type;Ljava/lang/String;)Ljava/lang/Object;", false); - mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/')); - mv.visitVarInsn(ASTORE, maxLocals); - varInsns.add(new int[]{ALOAD, maxLocals}); - - //构建 RestHeader、RestCookie、RestAddress 等赋值操作 - Class loop = ptype; - Set fields = new HashSet<>(); - Map attrParaNames = new LinkedHashMap<>(); - do { - if (loop == null || loop.isInterface()) break; //接口时getSuperclass可能会得到null - for (Field field : loop.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (Modifier.isFinal(field.getModifiers())) continue; - if (fields.contains(field.getName())) continue; - RestHeader rh = field.getAnnotation(RestHeader.class); - RestCookie rc = field.getAnnotation(RestCookie.class); - RestSessionid rs = field.getAnnotation(RestSessionid.class); - RestAddress ra = field.getAnnotation(RestAddress.class); - RestBody rb = field.getAnnotation(RestBody.class); - RestUploadFile ru = field.getAnnotation(RestUploadFile.class); - RestURI ri = field.getAnnotation(RestURI.class); - if (rh == null && rc == null && ra == null && rb == null && rs == null && ru == null && ri == null) continue; - if (rh != null && field.getType() != String.class && field.getType() != InetSocketAddress.class) throw new RuntimeException("@RestHeader must on String Field in " + field); - if (rc != null && field.getType() != String.class) throw new RuntimeException("@RestCookie must on String Field in " + field); - if (rs != null && field.getType() != String.class) throw new RuntimeException("@RestSessionid must on String Field in " + field); - if (ra != null && field.getType() != String.class) throw new RuntimeException("@RestAddress must on String Field in " + field); - if (rb != null && field.getType().isPrimitive()) throw new RuntimeException("@RestBody must on cannot on primitive type Field in " + field); - if (ru != null && field.getType() != byte[].class && field.getType() != File.class && field.getType() != File[].class) { - throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Field in " + field); - } - - if (ri != null && field.getType() != String.class) throw new RuntimeException("@RestURI must on String Field in " + field); - org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field); - String attrFieldName; - String restname = ""; - if (rh != null) { - attrFieldName = "_redkale_attr_header_" + (field.getType() != String.class ? "json_" : "") + restAttributes.size(); - restname = rh.name(); - } else if (rc != null) { - attrFieldName = "_redkale_attr_cookie_" + restAttributes.size(); - restname = rc.name(); - } else if (rs != null) { - attrFieldName = "_redkale_attr_sessionid_" + restAttributes.size(); - restname = rs.create() ? "1" : ""; //用于下面区分create值 - } else if (ra != null) { - attrFieldName = "_redkale_attr_address_" + restAttributes.size(); - //restname = ""; - } else if (rb != null && field.getType() == String.class) { - attrFieldName = "_redkale_attr_bodystring_" + restAttributes.size(); - //restname = ""; - } else if (rb != null && field.getType() == byte[].class) { - attrFieldName = "_redkale_attr_bodybytes_" + restAttributes.size(); - //restname = ""; - } else if (rb != null && field.getType() != String.class && field.getType() != byte[].class) { - attrFieldName = "_redkale_attr_bodyjson_" + restAttributes.size(); - //restname = ""; - } else if (ru != null && field.getType() == byte[].class) { - attrFieldName = "_redkale_attr_uploadbytes_" + restAttributes.size(); - //restname = ""; - } else if (ru != null && field.getType() == File.class) { - attrFieldName = "_redkale_attr_uploadfile_" + restAttributes.size(); - //restname = ""; - } else if (ru != null && field.getType() == File[].class) { - attrFieldName = "_redkale_attr_uploadfiles_" + restAttributes.size(); - //restname = ""; - } else if (ri != null && field.getType() == String.class) { - attrFieldName = "_redkale_attr_uri_" + restAttributes.size(); - //restname = ""; - } else { - continue; - } - restAttributes.put(attrFieldName, attr); - attrParaNames.put(attrFieldName, new Object[]{restname, field.getType(), field.getGenericType(), ru}); - fields.add(field.getName()); - } - } while ((loop = loop.getSuperclass()) != Object.class); - - if (!attrParaNames.isEmpty()) { //参数存在 RestHeader、RestCookie、RestSessionid、RestAddress、RestBody字段 - mv.visitVarInsn(ALOAD, maxLocals); //加载JsonBean - Label lif = new Label(); - mv.visitJumpInsn(IFNULL, lif); //if(bean != null) { - for (Map.Entry en : attrParaNames.entrySet()) { - RestUploadFile ru = (RestUploadFile) en.getValue()[3]; - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, en.getKey(), attrDesc); - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitVarInsn(ALOAD, en.getKey().contains("_upload") ? uploadLocal : 1); - if (en.getKey().contains("_header_")) { - String headerkey = en.getValue()[0].toString(); - if ("Host".equalsIgnoreCase(headerkey)) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHost", "()Ljava/lang/String;", false); - } else if ("Content-Type".equalsIgnoreCase(headerkey)) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getContentType", "()Ljava/lang/String;", false); - } else if ("Connection".equalsIgnoreCase(headerkey)) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getConnection", "()Ljava/lang/String;", false); - } else if ("Method".equalsIgnoreCase(headerkey)) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMethod", "()Ljava/lang/String;", false); - } else if (en.getKey().contains("_header_json_")) { - String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); - bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); - mv.visitLdcInsn(headerkey); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getJsonHeader", "(Ljava/lang/reflect/Type;Ljava/lang/String;)Ljava/lang/Object;", false); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName((Class) en.getValue()[1])); - } else { - mv.visitLdcInsn(headerkey); - mv.visitLdcInsn(""); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - } - } else if (en.getKey().contains("_cookie_")) { - mv.visitLdcInsn(en.getValue()[0].toString()); - mv.visitLdcInsn(""); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getCookie", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); - } else if (en.getKey().contains("_sessionid_")) { - mv.visitInsn(en.getValue()[0].toString().isEmpty() ? ICONST_0 : ICONST_1); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false); - } else if (en.getKey().contains("_address_")) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false); - } else if (en.getKey().contains("_uri_")) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false); - } else if (en.getKey().contains("_bodystring_")) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false); - } else if (en.getKey().contains("_bodybytes_")) { - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false); - } else if (en.getKey().contains("_bodyjson_")) {//JavaBean 转 Json - String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); - bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); - mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false); - mv.visitTypeInsn(CHECKCAST, Type.getInternalName((Class) en.getValue()[1])); - } else if (en.getKey().contains("_uploadbytes_")) { - //只需mv.visitVarInsn(ALOAD, 4), 无需处理 - } else if (en.getKey().contains("_uploadfile_")) { - //只需mv.visitVarInsn(ALOAD, 4), 无需处理 - } else if (en.getKey().contains("_uploadfiles_")) { - //只需mv.visitVarInsn(ALOAD, 4), 无需处理 - } - mv.visitMethodInsn(INVOKEINTERFACE, attrInternalName, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); - } - mv.visitLabel(lif); // end if } - mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{ptype.getName().replace('.', '/')}, 0, null); - } - } - maxLocals++; - paramMaps.add(paramMap); - } // end params for each - - //mv.visitVarInsn(ALOAD, 0); //调用this - //mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc); - mv.visitVarInsn(ALOAD, 3); - for (int[] ins : varInsns) { - mv.visitVarInsn(ins[0], ins[1]); - } - mv.visitMethodInsn(INVOKEVIRTUAL, serviceTypeInternalName, method.getName(), methodDesc, false); - if (hasAsyncHandler) { - mv.visitInsn(RETURN); - } else if (returnType == void.class) { - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKESTATIC, retInternalName, "success", "()" + retDesc, false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + retDesc + ")V", false); - mv.visitInsn(RETURN); - } else if (returnType == boolean.class) { - mv.visitVarInsn(ISTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ILOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == byte.class) { - mv.visitVarInsn(ISTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ILOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == short.class) { - mv.visitVarInsn(ISTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ILOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == char.class) { - mv.visitVarInsn(ISTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ILOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == int.class) { - mv.visitVarInsn(ISTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ILOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == float.class) { - mv.visitVarInsn(FSTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(FLOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == long.class) { - mv.visitVarInsn(LSTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(LLOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals += 2; - } else if (returnType == double.class) { - mv.visitVarInsn(DSTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(DLOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals += 2; - } else if (returnType == byte[].class) { - mv.visitVarInsn(ASTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "([B)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == String.class) { - mv.visitVarInsn(ASTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (returnType == File.class) { - mv.visitVarInsn(ASTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面 - mv.visitVarInsn(ASTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else { - if (!CompletableFuture.class.isAssignableFrom(returnType) && !org.redkale.service.RetResult.class.isAssignableFrom(returnType) - && !HttpResult.class.isAssignableFrom(returnType) && !HttpScope.class.isAssignableFrom(returnType)) { - mv.visitVarInsn(ASTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(Ljava/lang/Object;)V", false); - mv.visitInsn(RETURN); - maxLocals++; - } else { - mv.visitVarInsn(ASTORE, maxLocals); - mv.visitVarInsn(ALOAD, 2); //response - String objdesc = HttpScope.class.isAssignableFrom(returnType) ? scopeDesc : (HttpResult.class.isAssignableFrom(returnType) ? httpretDesc : "Ljava/lang/Object;"); - if (rcs != null && rcs.length > 0) { - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, REST_JSONCONVERT_FIELD_PREFIX + restConverts.size(), jsonConvertDesc); - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + objdesc + ")V", false); - } else { - mv.visitVarInsn(ALOAD, maxLocals); - mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + objdesc + ")V", false); - } - mv.visitInsn(RETURN); - maxLocals++; - } - } - mv.visitMaxs(maxStack, maxLocals); - mappingMap.put("params", paramMaps); - mappingMaps.add(mappingMap); - - { //_Dync_XXX__HttpServlet.class - ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); - cw2.visit(V1_8, ACC_SUPER, newDynName + "$" + entry.newActionClassName, null, httpServletName, null); - - cw2.visitInnerClass(newDynName + "$" + entry.newActionClassName, newDynName, entry.newActionClassName, ACC_PRIVATE + ACC_STATIC); - { - fv = cw2.visitField(0, "servlet", "L" + newDynName + ";", null, null); - fv.visitEnd(); - } - { - mv = new MethodDebugVisitor(cw2.visitMethod(0, "", "(L" + newDynName + ";)V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, httpServletName, "", "()V", false); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(PUTFIELD, newDynName + "$" + entry.newActionClassName, "servlet", "L" + newDynName + ";"); - mv.visitInsn(RETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - if (false) { - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_SYNTHETIC, "", "(L" + newDynName + ";L" + newDynName + "$" + entry.newActionClassName + ";)V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "", "L" + newDynName + ";", false); - mv.visitInsn(RETURN); - mv.visitMaxs(2, 3); - mv.visitEnd(); - } - { - mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"})); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName + "$" + entry.newActionClassName, "servlet", "L" + newDynName + ";"); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, entry.newMethodName, "(" + reqDesc + respDesc + ")V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - cw2.visitEnd(); - newLoader.addClass((newDynName + "$" + entry.newActionClassName).replace('/', '.'), cw2.toByteArray()); - } - } // end for each - -// HashMap _createRestActionEntry() { -// HashMap map = new HashMap<>(); -// map.put("asyncfind3", new ActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,false,0, new _Dync_asyncfind3_HttpServlet())); -// map.put("asyncfind2", new ActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,false,true,0, new _Dync_asyncfind2_HttpServlet())); -// return map; -// } - Map mappingurlToMethod = new HashMap<>(); - { //_createRestActionEntry 方法 - mv = new MethodDebugVisitor(cw.visitMethod(0, "_createRestActionEntry", "()Ljava/util/HashMap;", "()Ljava/util/HashMap;", null)); - //mv.setDebug(true); - mv.visitTypeInsn(NEW, "java/util/HashMap"); - mv.visitInsn(DUP); - mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "", "()V", false); - mv.visitVarInsn(ASTORE, 1); - - for (final MappingEntry entry : entrys) { - mappingurlToMethod.put(entry.mappingurl, entry.mappingMethod); - mv.visitVarInsn(ALOAD, 1); - mv.visitLdcInsn(entry.mappingurl); //name - mv.visitTypeInsn(NEW, actionEntryName); //new ActionEntry - mv.visitInsn(DUP); - pushInt(mv, moduleid); //moduleid - pushInt(mv, entry.actionid); //actionid - mv.visitLdcInsn(entry.mappingurl); //name - pushInt(mv, entry.methods.length); //methods - mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); - for (int i = 0; i < entry.methods.length; i++) { - mv.visitInsn(DUP); - pushInt(mv, i); - mv.visitLdcInsn(entry.methods[i]); - mv.visitInsn(AASTORE); - } - mv.visitInsn(ACONST_NULL); //method - mv.visitInsn(entry.rpconly ? ICONST_1 : ICONST_0); //rpconly - mv.visitInsn(entry.auth ? ICONST_1 : ICONST_0); //auth - pushInt(mv, entry.cacheseconds); //cacheseconds - mv.visitTypeInsn(NEW, newDynName + "$" + entry.newActionClassName); - mv.visitInsn(DUP); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "", "(L" + newDynName + ";)V", false); - mv.visitMethodInsn(INVOKESPECIAL, actionEntryName, "", "(IILjava/lang/String;[Ljava/lang/String;Ljava/lang/reflect/Method;ZZILorg/redkale/net/http/HttpServlet;)V", false); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false); - mv.visitInsn(POP); - } - mv.visitVarInsn(ALOAD, 1); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - - for (Map.Entry en : bodyTypes.entrySet()) { - fv = cw.visitField(ACC_PRIVATE, en.getKey(), "Ljava/lang/reflect/Type;", null, null); - fv.visitEnd(); - } - - for (String attrname : restAttributes.keySet()) { - fv = cw.visitField(ACC_PRIVATE, attrname, attrDesc, null, null); - fv.visitEnd(); - } - - for (int i = 1; i <= restConverts.size(); i++) { - fv = cw.visitField(ACC_PRIVATE, REST_JSONCONVERT_FIELD_PREFIX + i, jsonConvertDesc, null, null); - fv.visitEnd(); - } - - //classMap.put("mappings", mappingMaps); //不显示太多信息 - { //toString函数 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, REST_TOSTRINGOBJ_FIELD_NAME, "Ljava/util/function/Supplier;"); - mv.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Supplier", "get", "()Ljava/lang/Object;", true); - mv.visitTypeInsn(CHECKCAST, "java/lang/String"); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - - { //RestDyn - av0 = cw.visitAnnotation(Type.getDescriptor(RestDyn.class), true); - av0.visit("simple", (Boolean) dynsimple); - av0.visitEnd(); - } - - cw.visitEnd(); - newLoader.addClass(newDynName.replace('/', '.'), cw.toByteArray()); - try { - Class newClazz = newLoader.findClass(newDynName.replace('/', '.')); - - T obj = ((Class) newClazz).getDeclaredConstructor().newInstance(); - for (Map.Entry en : restAttributes.entrySet()) { - Field attrField = newClazz.getDeclaredField(en.getKey()); - attrField.setAccessible(true); - attrField.set(obj, en.getValue()); - } - for (Map.Entry en : bodyTypes.entrySet()) { - Field genField = newClazz.getDeclaredField(en.getKey()); - genField.setAccessible(true); - genField.set(obj, en.getValue()); - } - for (int i = 0; i < restConverts.size(); i++) { - Field genField = newClazz.getDeclaredField(REST_JSONCONVERT_FIELD_PREFIX + (i + 1)); - genField.setAccessible(true); - Object[] rc = restConverts.get(i); - - genField.set(obj, createJsonConvert((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1])); - } - Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); - typesfield.setAccessible(true); - java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramtypes.size()][]; - paramtypeArray = paramtypes.toArray(paramtypeArray); - typesfield.set(obj, paramtypeArray); - - Field tostringfield = newClazz.getDeclaredField(REST_TOSTRINGOBJ_FIELD_NAME); - tostringfield.setAccessible(true); - java.util.function.Supplier sSupplier = () -> JsonConvert.root().convertTo(classMap); - tostringfield.set(obj, sSupplier); - - Method restactMethod = newClazz.getDeclaredMethod("_createRestActionEntry"); - restactMethod.setAccessible(true); - Field tmpentrysfield = HttpServlet.class.getDeclaredField("_actionmap"); - tmpentrysfield.setAccessible(true); - HashMap innerEntryMap = (HashMap) restactMethod.invoke(obj); - for (Map.Entry en : innerEntryMap.entrySet()) { - Method m = mappingurlToMethod.get(en.getKey()); - if (m != null) en.getValue().annotations = HttpServlet.ActionEntry.annotations(m); - } - tmpentrysfield.set(obj, innerEntryMap); - return obj; - } catch (Throwable e) { - throw new RuntimeException(e); - } - } - - private static boolean checkName(String name) { //不能含特殊字符 - if (name.isEmpty()) return true; - if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false; - for (char ch : name.toCharArray()) { - if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 - return false; - } - } - return true; - } - - private static void pushInt(MethodDebugVisitor mv, int num) { - if (num < 6) { - mv.visitInsn(ICONST_0 + num); - } else if (num <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, num); - } else if (num <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, num); - } else { - mv.visitLdcInsn(num); - } - } - - private static void pushInt(MethodVisitor mv, int num) { - if (num < 6) { - mv.visitInsn(ICONST_0 + num); - } else if (num <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, num); - } else if (num <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, num); - } else { - mv.visitLdcInsn(num); - } - } - - private static class RestClassLoader extends ClassLoader { - - private Map classes = new HashMap<>(); - - public RestClassLoader(ClassLoader parent) { - super(parent); - } - - @Override - protected Class findClass(String name) throws ClassNotFoundException { - byte[] classData = classes.get(name); - if (classData == null) return super.findClass(name); - return super.defineClass(name, classData, 0, classData.length); - } - - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - - public final void addClass(String name, byte[] b) { - classes.put(name, b); - } - } - - private static class MappingEntry implements Comparable { - - private static final RestMapping DEFAULT__MAPPING; - - static { - try { - DEFAULT__MAPPING = MappingEntry.class.getDeclaredMethod("mapping").getAnnotation(RestMapping.class); - } catch (Exception e) { - throw new Error(e); - } - } - - public MappingEntry(final boolean serrpconly, int methodidx, RestMapping mapping, final String defmodulename, Method method) { - if (mapping == null) mapping = DEFAULT__MAPPING; - this.methodidx = methodidx; - this.ignore = mapping.ignore(); - String n = mapping.name(); - if (n.isEmpty()) { - String t = method.getName(); - int pos = t.indexOf(defmodulename); - n = pos > 0 ? t.substring(0, pos) : t; - } - this.name = n.trim(); - this.mappingMethod = method; - this.methods = mapping.methods(); - this.auth = mapping.auth(); - this.rpconly = serrpconly || mapping.rpconly(); - this.actionid = mapping.actionid(); - this.cacheseconds = mapping.cacheseconds(); - this.comment = mapping.comment(); - boolean pound = false; - Parameter[] params = method.getParameters(); - for (Parameter param : params) { - RestParam rp = param.getAnnotation(RestParam.class); - if (rp != null && !rp.name().isEmpty() && rp.name().charAt(0) == '#') { - pound = true; - break; - } - } - this.existsPound = pound; - this.newMethodName = this.name.replace('/', '$').replace('.', '_'); - this.newActionClassName = "_Dyn_" + this.newMethodName + "_ActionHttpServlet"; - } - - public final int methodidx; // _paramtypes 的下标,从0开始 - - public final Method mappingMethod; - - public final boolean ignore; - - public final String newMethodName; - - public final String newActionClassName; - - public final String name; - - public final String comment; - - public final String[] methods; - - public final boolean rpconly; - - public final boolean auth; - - public final int actionid; - - public final int cacheseconds; - - public final boolean existsPound; //是否包含#的参数 - - String mappingurl; //在生成方法时赋值, 供 _createRestActionEntry 使用 - - @RestMapping() - void mapping() { //用于获取Mapping 默认值 - } - - @Override - public int hashCode() { - return this.name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - return this.name.equals(((MappingEntry) obj).name); - } - - @Override - public int compareTo(MappingEntry o) { - return this.name.compareTo(o.name); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import org.redkale.asm.MethodDebugVisitor; +import java.io.*; +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.reflect.*; +import java.net.InetSocketAddress; +import java.nio.channels.CompletionHandler; +import java.util.*; +import java.util.concurrent.*; +import javax.annotation.Resource; +import org.redkale.asm.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import static org.redkale.asm.Opcodes.*; +import org.redkale.asm.Type; +import org.redkale.convert.*; +import org.redkale.convert.json.*; +import org.redkale.mq.*; +import org.redkale.net.*; +import org.redkale.net.sncp.Sncp; +import org.redkale.service.*; +import org.redkale.util.*; +import org.redkale.source.Flipper; + +/** + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public final class Rest { + + public static final String REST_HEADER_RESOURCE_NAME = "rest-resource-name"; + + public static final String REST_HEADER_RPC_NAME = "rest-rpc-name"; + + public static final String REST_HEADER_CURRUSERID_NAME = "rest-curruserid-name"; + + public static final String REST_HEADER_PARAM_FROM_BODY = "rest-paramfrombody"; + + public static final String REST_HEADER_REQ_CONVERT_TYPE = "rest-req-convert-type"; + + public static final String REST_HEADER_RESP_CONVERT_TYPE = "rest-resp-convert-type"; + + static final String REST_TOSTRINGOBJ_FIELD_NAME = "_redkale_tostringsupplier"; + + static final String REST_CONVERT_FIELD_PREFIX = "_redkale_restconvert_"; + + static final String REST_SERVICE_FIELD_NAME = "_redkale_service"; + + static final String REST_SERVICEMAP_FIELD_NAME = "_redkale_servicemap"; //如果只有name=""的Service资源,则实例中_servicemap必须为null + + private static final String REST_PARAMTYPES_FIELD_NAME = "_redkale_paramtypes"; //存在泛型的参数数组 Type[][] 第1维度是方法的下标, 第二维度是参数的下标 + + private static final String REST_RETURNTYPES_FIELD_NAME = "_redkale_returntypes"; //存在泛型的结果数组 + + private static final java.lang.reflect.Type TYPE_MAP_STRING_STRING = new TypeToken>() { + }.getType(); + + private static final java.lang.reflect.Type TYPE_RETRESULT_STRING = new TypeToken>() { + }.getType(); + + private static final Set EXCLUDERMETHODS = new HashSet<>(); + + static { + for (Method m : Object.class.getMethods()) { + EXCLUDERMETHODS.add(m.getName()); + } + } + + /** + * 用于标记由Rest.createRestServlet 方法创建的RestServlet + */ + @Inherited + @Documented + @Target({TYPE}) + @Retention(RUNTIME) + public static @interface RestDyn { + + //是否不需要解析HttpHeader,对应HttpContext.lazyHeaders + boolean simple() default false; + + //动态生成的类的子类需要关联一下,否则在运行过程中可能出现NoClassDefFoundError + Class[] types() default {}; + } + + /** + * 用于标记由Rest.createRestServlet 方法创建的RestServlet + */ + @Inherited + @Documented + @Target({TYPE}) + @Retention(RUNTIME) + public static @interface RestDynSourceType { + + Class value(); + } + + private Rest() { + } + + static class MethodParamClassVisitor extends ClassVisitor { + + private final Map> fieldmap; + + public MethodParamClassVisitor(int api, final Map> fieldmap) { + super(api); + this.fieldmap = fieldmap; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (java.lang.reflect.Modifier.isStatic(access)) return null; + List fieldnames = new ArrayList<>(); + String key = name + ":" + desc; + if (fieldmap.containsKey(key)) return null; + fieldmap.put(key, fieldnames); + return new MethodVisitor(Opcodes.ASM6) { + @Override + public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { + if (index < 1) return; + int size = fieldnames.size(); + //index并不会按顺序执行的 + if (index > size) { + for (int i = size; i < index; i++) { + fieldnames.add(" "); + } + fieldnames.set(index - 1, name); + } + fieldnames.set(index - 1, name); + } + }; + } + + //返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多 + public static Map> getMethodParamNames(Map> map, Class clazz) { + String n = clazz.getName(); + InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class"); + if (in == null) return map; + try { + new ClassReader(Utility.readBytesThenClose(in)).accept(new MethodParamClassVisitor(Opcodes.ASM6, map), 0); + } catch (Exception e) { //无需理会 + } + Class superClass = clazz.getSuperclass(); + if (superClass == Object.class) return map; + return getMethodParamNames(map, superClass); + } + } + + static JsonConvert createJsonConvert(RestConvert[] converts, RestConvertCoder[] coders) { + if ((converts == null || converts.length < 1) && (coders == null || coders.length < 1)) return JsonConvert.root(); + final JsonFactory childFactory = JsonFactory.create(); + List types = new ArrayList<>(); + Set reloadTypes = new HashSet<>(); + if (coders != null) { + for (RestConvertCoder rcc : coders) { + reloadTypes.add(rcc.type()); + childFactory.register(rcc.type(), rcc.field(), (SimpledCoder) Creator.create(rcc.coder()).create()); + } + } + if (converts != null) { + for (RestConvert rc : converts) { + if (rc.type() == void.class || rc.type() == Void.class) { + return JsonFactory.create().skipAllIgnore(true).getConvert(); + } + if (types.contains(rc.type())) throw new RuntimeException("@RestConvert type(" + rc.type() + ") repeat"); + if (rc.skipIgnore()) { + childFactory.registerSkipIgnore(rc.type()); + childFactory.reloadCoder(rc.type()); + } else { + childFactory.register(rc.type(), false, rc.convertColumns()); + childFactory.register(rc.type(), true, rc.ignoreColumns()); + childFactory.reloadCoder(rc.type()); + } + types.add(rc.type()); + childFactory.tiny(rc.tiny()); + } + } + for (Class type : reloadTypes) { + childFactory.reloadCoder(type); + } + return childFactory.getConvert(); + } + + static String getWebModuleNameLowerCase(Class serviceType) { + final RestService controller = serviceType.getAnnotation(RestService.class); + if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); + if (controller.ignore()) return null; + return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); + } + + static String getWebModuleName(Class serviceType) { + final RestService controller = serviceType.getAnnotation(RestService.class); + if (controller == null) return serviceType.getSimpleName().replaceAll("Service.*$", ""); + if (controller.ignore()) return null; + return (!controller.name().isEmpty()) ? controller.name().trim() : serviceType.getSimpleName().replaceAll("Service.*$", ""); + } + + /** + * 判断HttpServlet是否为Rest动态生成的 + * + * @param servlet 检测的HttpServlet + * + * @return 是否是动态生成的RestHttpServlet + */ + public static boolean isRestDyn(HttpServlet servlet) { + return servlet.getClass().getAnnotation(RestDyn.class) != null; + } + + /** + * 判断HttpServlet是否为Rest动态生成的,且simple + * + * @param servlet 检测的HttpServlet + * + * @return 是否是动态生成的RestHttpServlet + */ + static boolean isSimpleRestDyn(HttpServlet servlet) { + RestDyn dyn = servlet.getClass().getAnnotation(RestDyn.class); + return dyn != null && dyn.simple(); + } + + /** + * 获取Rest动态生成HttpServlet里的Service对象,若不是Rest动态生成的HttpServlet,返回null + * + * @param servlet HttpServlet + * + * @return Service + */ + public static Service getService(HttpServlet servlet) { + if (servlet == null) return null; + if (!isRestDyn(servlet)) return null; + try { + Field ts = servlet.getClass().getDeclaredField(REST_SERVICE_FIELD_NAME); + ts.setAccessible(true); + return (Service) ts.get(servlet); + } catch (Exception e) { + return null; + } + } + + public static Map getServiceMap(HttpServlet servlet) { + if (servlet == null) return null; + try { + Field ts = servlet.getClass().getDeclaredField(REST_SERVICEMAP_FIELD_NAME); + ts.setAccessible(true); + return (Map) ts.get(servlet); + } catch (Exception e) { + return null; + } + } + + public static String getRestModule(Service service) { + final RestService controller = service.getClass().getAnnotation(RestService.class); + if (controller != null && !controller.name().isEmpty()) return controller.name(); + final Class serviceType = Sncp.getServiceType(service); + return serviceType.getSimpleName().replaceAll("Service.*$", "").toLowerCase(); + } + + //仅供Rest动态构建里 currentUserid() 使用 + public static T orElse(T t, T defValue) { + return t == null ? defValue : t; + } + + public static T createRestWebSocketServlet(final ClassLoader classLoader, final Class webSocketType, MessageAgent messageAgent) { + if (webSocketType == null) throw new RuntimeException("Rest WebSocket Class is null on createRestWebSocketServlet"); + if (Modifier.isAbstract(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot abstract on createRestWebSocketServlet"); + if (Modifier.isFinal(webSocketType.getModifiers())) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") cannot final on createRestWebSocketServlet"); + final RestWebSocket rws = webSocketType.getAnnotation(RestWebSocket.class); + if (rws == null || rws.ignore()) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") have not @RestWebSocket or @RestWebSocket.ignore=true on createRestWebSocketServlet"); + boolean valid = false; + for (Constructor c : webSocketType.getDeclaredConstructors()) { + if (c.getParameterCount() == 0 && (Modifier.isPublic(c.getModifiers()) || Modifier.isProtected(c.getModifiers()))) { + valid = true; + break; + } + } + if (!valid) throw new RuntimeException("Rest WebSocket Class(" + webSocketType + ") must have public or protected Constructor on createRestWebSocketServlet"); + final String rwsname = ResourceFactory.formatResourceName(rws.name()); + if (!checkName(rws.catalog())) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9"); + if (!checkName(rwsname)) throw new RuntimeException(webSocketType.getName() + " have illegal " + RestWebSocket.class.getSimpleName() + ".name, only 0-9 a-z A-Z _ cannot begin 0-9"); + + //---------------------------------------------------------------------------------------- + final Set resourcesFieldSet = new LinkedHashSet<>(); + final ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; + final Set resourcesFieldNameSet = new HashSet<>(); + Class clzz = webSocketType; + do { + for (Field field : clzz.getDeclaredFields()) { + if (field.getAnnotation(Resource.class) == null) continue; + if (resourcesFieldNameSet.contains(field.getName())) continue; + if (Modifier.isStatic(field.getModifiers())) throw new RuntimeException(field + " cannot static on createRestWebSocketServlet"); + if (Modifier.isFinal(field.getModifiers())) throw new RuntimeException(field + " cannot final on createRestWebSocketServlet"); + if (!Modifier.isPublic(field.getModifiers()) && !Modifier.isProtected(field.getModifiers())) { + throw new RuntimeException(field + " must be public or protected on createRestWebSocketServlet"); + } + resourcesFieldNameSet.add(field.getName()); + resourcesFieldSet.add(field); + } + } while ((clzz = clzz.getSuperclass()) != Object.class); + + //---------------------------------------------------------------------------------------- + boolean namePresent = false; + try { + Method m0 = null; + for (Method method : webSocketType.getMethods()) { + if (method.getParameterCount() > 0) { + m0 = method; + break; + } + } + namePresent = m0 == null ? true : m0.getParameters()[0].isNamePresent(); + } catch (Exception e) { + } + final Map> asmParamMap = namePresent ? null : MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), webSocketType); + final Set messageNames = new HashSet<>(); + final List messageMethods = new ArrayList<>(); + for (Method method : webSocketType.getMethods()) { + RestOnMessage rom = method.getAnnotation(RestOnMessage.class); + if (rom == null) continue; + String name = rom.name(); + if (Modifier.isFinal(method.getModifiers())) throw new RuntimeException("@RestOnMessage method can not final but (" + method + ")"); + if (Modifier.isStatic(method.getModifiers())) throw new RuntimeException("@RestOnMessage method can not static but (" + method + ")"); + if (method.getReturnType() != void.class) throw new RuntimeException("@RestOnMessage method must return void but (" + method + ")"); + if (method.getExceptionTypes().length > 0) throw new RuntimeException("@RestOnMessage method can not throw exception but (" + method + ")"); + if (name.isEmpty()) throw new RuntimeException(method + " RestOnMessage.name is empty createRestWebSocketServlet"); + if (messageNames.contains(name)) throw new RuntimeException(method + " repeat RestOnMessage.name(" + name + ") createRestWebSocketServlet"); + messageNames.add(name); + messageMethods.add(method); + } + //---------------------------------------------------------------------------------------- + final String resDesc = Type.getDescriptor(Resource.class); + final String wsDesc = Type.getDescriptor(WebSocket.class); + final String wsParamDesc = Type.getDescriptor(WebSocketParam.class); + final String jsonConvertDesc = Type.getDescriptor(JsonConvert.class); + final String convertDisabledDesc = Type.getDescriptor(ConvertDisabled.class); + final String webSocketParamName = Type.getInternalName(WebSocketParam.class); + final String supDynName = WebSocketServlet.class.getName().replace('.', '/'); + final String webServletDesc = Type.getDescriptor(WebServlet.class); + final String webSocketInternalName = Type.getInternalName(webSocketType); + + final String newDynName = "org/redkaledyn/http/restws/" + "_DynWebScoketServlet__" + webSocketType.getName().replace('.', '_').replace('$', '_'); + + final String newDynWebSokcetSimpleName = "_Dyn" + webSocketType.getSimpleName(); + final String newDynWebSokcetFullName = newDynName + "$" + newDynWebSokcetSimpleName; + + final String newDynMessageSimpleName = "_Dyn" + webSocketType.getSimpleName() + "Message"; + final String newDynMessageFullName = newDynName + "$" + newDynMessageSimpleName; + + final String newDynConsumerSimpleName = "_DynRestOnMessageConsumer"; + final String newDynConsumerFullName = newDynName + "$" + newDynConsumerSimpleName; + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + if (clz == null) clz = loader.loadClass(newDynName.replace('/', '.')); + T servlet = (T) clz.getDeclaredConstructor().newInstance(); + Map msgclassToAnnotations = new HashMap<>(); + for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage 子消息List + Method method = messageMethods.get(i); + String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); + msgclassToAnnotations.put(newDynMessageFullName + endfix, method.getAnnotations()); + } + clz.getField("_redkale_annotations").set(null, msgclassToAnnotations); + if (rws.cryptor() != Cryptor.class) { + Cryptor cryptor = rws.cryptor().getDeclaredConstructor().newInstance(); + Field cryptorField = clz.getSuperclass().getDeclaredField("cryptor"); //WebSocketServlet + cryptorField.setAccessible(true); + cryptorField.set(servlet, cryptor); + } + if (messageAgent != null) ((WebSocketServlet) servlet).messageAgent = messageAgent; + return servlet; + } catch (Throwable e) { + } + + final List resourcesFields = new ArrayList<>(resourcesFieldSet); + StringBuilder sb1 = new StringBuilder(); + StringBuilder sb2 = new StringBuilder(); + for (int i = 0; i < resourcesFields.size(); i++) { + Field field = resourcesFields.get(i); + sb1.append(Type.getDescriptor(field.getType())); + sb2.append(Utility.getTypeDescriptor(field.getGenericType())); + } + final String resourceDescriptor = sb1.toString(); + final String resourceGenericDescriptor = sb1.length() == sb2.length() ? null : sb2.toString(); + //---------------------------------------------------------------------------------------- + + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + AnnotationVisitor av0; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); + { //RestDyn + av0 = cw.visitAnnotation(Type.getDescriptor(RestDyn.class), true); + av0.visit("simple", false); + { + AnnotationVisitor av1 = av0.visitArray("types"); + av1.visit(null, Type.getType("L" + newDynConsumerFullName.replace('.', '/') + ";")); + av1.visit(null, Type.getType("L" + newDynWebSokcetFullName.replace('.', '/') + ";")); + av1.visit(null, Type.getType("L" + newDynMessageFullName.replace('.', '/') + ";")); //位置固定第三个,下面用Message类进行loadDecoder会用到 + av1.visitEnd(); + } + av0.visitEnd(); + } + { //RestDynSourceType + av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true); + av0.visit("value", Type.getType(Type.getDescriptor(webSocketType))); + av0.visitEnd(); + } + { //注入 @WebServlet 注解 + String urlpath = (rws.catalog().isEmpty() ? "/" : ("/" + rws.catalog() + "/")) + rwsname; + av0 = cw.visitAnnotation(webServletDesc, true); + { + AnnotationVisitor av1 = av0.visitArray("value"); + av1.visit(null, urlpath); + av1.visitEnd(); + } + av0.visit("name", rwsname); + av0.visit("moduleid", 0); + av0.visit("repair", rws.repair()); + av0.visit("comment", rws.comment()); + av0.visitEnd(); + } + { //内部类 + cw.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC); + + cw.visitInnerClass(newDynWebSokcetFullName, newDynName, newDynWebSokcetSimpleName, ACC_PUBLIC + ACC_STATIC); + + cw.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC); + + for (int i = 0; i < messageMethods.size(); i++) { + Method method = messageMethods.get(i); + String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); + cw.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); + } + } + { //@Resource + for (int i = 0; i < resourcesFields.size(); i++) { + Field field = resourcesFields.get(i); + Resource res = field.getAnnotation(Resource.class); + java.lang.reflect.Type fieldType = field.getGenericType(); + fv = cw.visitField(ACC_PRIVATE, "_redkale_resource_" + i, Type.getDescriptor(field.getType()), fieldType == field.getType() ? null : Utility.getTypeDescriptor(fieldType), null); + { + av0 = fv.visitAnnotation(resDesc, true); + av0.visit("name", res.name()); + av0.visitEnd(); + } + fv.visitEnd(); + } + } + { //_redkale_annotations + fv = cw.visitField(ACC_PUBLIC + ACC_STATIC, "_redkale_annotations", "Ljava/util/Map;", "Ljava/util/Map;", null); + fv.visitEnd(); + } + { //_DynWebSocketServlet构造函数 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitLdcInsn(Type.getObjectType(newDynName + "$" + newDynWebSokcetSimpleName + "Message")); + mv.visitFieldInsn(PUTFIELD, newDynName, "messageTextType", "Ljava/lang/reflect/Type;"); + + mv.visitVarInsn(ALOAD, 0); + MethodDebugVisitor.pushInt(mv, rws.liveinterval()); + mv.visitFieldInsn(PUTFIELD, newDynName, "liveinterval", "I"); + + mv.visitVarInsn(ALOAD, 0); + MethodDebugVisitor.pushInt(mv, rws.wsmaxconns()); + mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxconns", "I"); + + mv.visitVarInsn(ALOAD, 0); + MethodDebugVisitor.pushInt(mv, rws.wsmaxbody()); + mv.visitFieldInsn(PUTFIELD, newDynName, "wsmaxbody", "I"); + + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(rws.mergemsg() ? ICONST_1 : ICONST_0); + mv.visitFieldInsn(PUTFIELD, newDynName, "mergemsg", "Z"); + + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(rws.single() ? ICONST_1 : ICONST_0); + mv.visitFieldInsn(PUTFIELD, newDynName, "single", "Z"); + + mv.visitVarInsn(ALOAD, 0); + mv.visitInsn(rws.anyuser() ? ICONST_1 : ICONST_0); + mv.visitFieldInsn(PUTFIELD, newDynName, "anyuser", "Z"); + + mv.visitInsn(RETURN); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { //createWebSocket 方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createWebSocket", "()" + wsDesc, "()L" + WebSocket.class.getName().replace('.', '/') + ";", null)); + mv.visitTypeInsn(NEW, newDynName + "$" + newDynWebSokcetSimpleName); + mv.visitInsn(DUP); + for (int i = 0; i < resourcesFields.size(); i++) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_resource_" + i, Type.getDescriptor(resourcesFields.get(i).getType())); + } + mv.visitMethodInsn(INVOKESPECIAL, newDynWebSokcetFullName, "", "(" + resourceDescriptor + ")V", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2 + resourcesFields.size(), 1); + mv.visitEnd(); + } + { //createRestOnMessageConsumer + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PROTECTED, "createRestOnMessageConsumer", "()Ljava/util/function/BiConsumer;", "()Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", null)); + mv.visitTypeInsn(NEW, newDynConsumerFullName); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, newDynConsumerFullName, "", "()V", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + { //resourceName + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "resourceName", "()Ljava/lang/String;", null, null)); + mv.visitLdcInsn(rwsname); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + RestClassLoader newLoader = new RestClassLoader(loader); + Map msgclassToAnnotations = new HashMap<>(); + for (int i = 0; i < messageMethods.size(); i++) { // _DyncXXXWebSocketMessage 子消息List + Method method = messageMethods.get(i); + String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); + msgclassToAnnotations.put(newDynMessageFullName + endfix, method.getAnnotations()); + + ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); + cw2.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName + endfix, null, "java/lang/Object", new String[]{webSocketParamName, "java/lang/Runnable"}); + cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); + Set paramnames = new HashSet<>(); + String methodesc = method.getName() + ":" + Type.getMethodDescriptor(method); + List names = asmParamMap == null ? null : asmParamMap.get(methodesc); + if (names != null) while (names.remove(" ")); //删掉空元素 + Parameter[] params = method.getParameters(); + final LinkedHashMap paramap = new LinkedHashMap(); //必须使用LinkedHashMap确保顺序 + for (int j = 0; j < params.length; j++) { //字段列表 + Parameter param = params[j]; + String paramname = param.getName(); + RestParam rp = param.getAnnotation(RestParam.class); + if (rp != null && !rp.name().isEmpty()) { + paramname = rp.name(); + } else if (names != null && names.size() > j) { + paramname = names.get(j); + } + if (paramnames.contains(paramname)) throw new RuntimeException(method + " has same @RestParam.name"); + paramnames.add(paramname); + paramap.put(paramname, param); + fv = cw2.visitField(ACC_PUBLIC, paramname, Type.getDescriptor(param.getType()), + param.getType() == param.getParameterizedType() ? null : Utility.getTypeDescriptor(param.getParameterizedType()), null); + fv.visitEnd(); + } + { //_redkale_websocket + fv = cw2.visitField(ACC_PUBLIC, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";", null, null); + av0 = fv.visitAnnotation(convertDisabledDesc, true); + av0.visitEnd(); + fv.visitEnd(); + } + { //空构造函数 + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //getNames + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getNames", "()[Ljava/lang/String;", null, null)); + MethodDebugVisitor.pushInt(mv, paramap.size()); + mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); + int index = -1; + for (Map.Entry en : paramap.entrySet()) { + mv.visitInsn(DUP); + MethodDebugVisitor.pushInt(mv, ++index); + mv.visitLdcInsn(en.getKey()); + mv.visitInsn(AASTORE); + } + mv.visitInsn(ARETURN); + mv.visitMaxs(paramap.size() + 2, 1); + mv.visitEnd(); + } + { //getValue + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getValue", "(Ljava/lang/String;)Ljava/lang/Object;", "(Ljava/lang/String;)TT;", null)); + for (Map.Entry en : paramap.entrySet()) { + Class paramType = en.getValue().getType(); + mv.visitLdcInsn(en.getKey()); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "equals", "(Ljava/lang/Object;)Z", false); + Label l1 = new Label(); + mv.visitJumpInsn(IFEQ, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, en.getKey(), Type.getDescriptor(paramType)); + if (paramType.isPrimitive()) { + Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(paramType, 1), 0).getClass(); + mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(paramType) + ")" + Type.getDescriptor(bigclaz), false); + } + mv.visitInsn(ARETURN); + mv.visitLabel(l1); + } + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { //getAnnotations + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "getAnnotations", "()[Ljava/lang/annotation/Annotation;", null, null)); + mv.visitFieldInsn(GETSTATIC, newDynName, "_redkale_annotations", "Ljava/util/Map;"); + mv.visitLdcInsn(newDynMessageFullName + endfix); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); + mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/annotation/Annotation;"); + mv.visitVarInsn(ASTORE, 1); + mv.visitVarInsn(ALOAD, 1); + Label l2 = new Label(); + mv.visitJumpInsn(IFNONNULL, l2); + mv.visitInsn(ICONST_0); + mv.visitTypeInsn(ANEWARRAY, "java/lang/annotation/Annotation"); + mv.visitInsn(ARETURN); + mv.visitLabel(l2); + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{"[Ljava/lang/annotation/Annotation;"}, 0, null); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ARRAYLENGTH); + mv.visitMethodInsn(INVOKESTATIC, "java/util/Arrays", "copyOf", "([Ljava/lang/Object;I)[Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/annotation/Annotation;"); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { //execute + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(L" + newDynWebSokcetFullName + ";)V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";"); + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(method.getAnnotation(RestOnMessage.class).name()); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, "preOnMessage", "(Ljava/lang/String;" + wsParamDesc + "Ljava/lang/Runnable;)V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(4, 2); + mv.visitEnd(); + } + { //run + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "run", "()V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynMessageFullName + endfix, "_redkale_websocket", "L" + newDynWebSokcetFullName + ";"); + + for (Map.Entry en : paramap.entrySet()) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, (newDynMessageFullName + endfix), en.getKey(), Type.getDescriptor(en.getValue().getType())); + } + mv.visitMethodInsn(INVOKEVIRTUAL, newDynWebSokcetFullName, method.getName(), Type.getMethodDescriptor(method), false); + + mv.visitInsn(RETURN); + mv.visitMaxs(3, 1); + mv.visitEnd(); + } + { //toString + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); + mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + cw2.visitEnd(); + byte[] bytes = cw2.toByteArray(); + Class cz = newLoader.loadClass((newDynMessageFullName + endfix).replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass((newDynMessageFullName + endfix).replace('/', '.'), bytes, cz); + } + + { //_DynXXXWebSocketMessage class + ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); + cw2.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynMessageFullName, null, "java/lang/Object", null); + + cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC); + + for (int i = 0; i < messageMethods.size(); i++) { + Method method = messageMethods.get(i); + String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); + cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); + + fv = cw2.visitField(ACC_PUBLIC, method.getAnnotation(RestOnMessage.class).name(), "L" + newDynMessageFullName + endfix + ";", null, null); + fv.visitEnd(); + } + { //构造函数 + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //toString + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); + mv.visitMethodInsn(INVOKESTATIC, JsonConvert.class.getName().replace('.', '/'), "root", "()" + jsonConvertDesc, false); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, JsonConvert.class.getName().replace('.', '/'), "convertTo", "(Ljava/lang/Object;)Ljava/lang/String;", false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 1); + mv.visitEnd(); + } + cw2.visitEnd(); + byte[] bytes = cw2.toByteArray(); + Class cz = newLoader.loadClass(newDynMessageFullName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynMessageFullName.replace('/', '.'), bytes, cz); + } + + { //_DynXXXWebSocket class + ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); + cw2.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynWebSokcetFullName, null, webSocketInternalName, null); + + cw2.visitInnerClass(newDynWebSokcetFullName, newDynName, newDynWebSokcetSimpleName, ACC_PUBLIC + ACC_STATIC); + { + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "(" + resourceDescriptor + ")V", resourceGenericDescriptor == null ? null : ("(" + resourceGenericDescriptor + ")V"), null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, webSocketInternalName, "", "()V", false); + for (int i = 0; i < resourcesFields.size(); i++) { + Field field = resourcesFields.get(i); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, i + 1); + mv.visitFieldInsn(PUTFIELD, newDynWebSokcetFullName, field.getName(), Type.getDescriptor(field.getType())); + } + mv.visitInsn(RETURN); + mv.visitMaxs(2, 1 + resourcesFields.size()); + mv.visitEnd(); + } + { //RestDyn + av0 = cw2.visitAnnotation(Type.getDescriptor(RestDyn.class), true); + av0.visit("simple", false); + av0.visitEnd(); + } + cw2.visitEnd(); + byte[] bytes = cw2.toByteArray(); + Class cz = newLoader.loadClass(newDynWebSokcetFullName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynWebSokcetFullName.replace('/', '.'), bytes, cz); + } + + { //_DynRestOnMessageConsumer class + ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); + cw2.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynConsumerFullName, "Ljava/lang/Object;Ljava/util/function/BiConsumer<" + wsDesc + "Ljava/lang/Object;>;", "java/lang/Object", new String[]{"java/util/function/BiConsumer"}); + + cw2.visitInnerClass(newDynConsumerFullName, newDynName, newDynConsumerSimpleName, ACC_PUBLIC + ACC_STATIC); + cw2.visitInnerClass(newDynMessageFullName, newDynName, newDynMessageSimpleName, ACC_PUBLIC + ACC_STATIC); + for (int i = 0; i < messageMethods.size(); i++) { + Method method = messageMethods.get(i); + String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); + cw2.visitInnerClass(newDynMessageFullName + endfix, newDynName, newDynMessageSimpleName + endfix, ACC_PUBLIC + ACC_STATIC); + } + + { //构造函数 + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + { //accept函数 + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", null, null)); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, newDynWebSokcetFullName); + mv.visitVarInsn(ASTORE, 3); + + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, newDynMessageFullName); + mv.visitVarInsn(ASTORE, 4); + + for (int i = 0; i < messageMethods.size(); i++) { + final Method method = messageMethods.get(i); + String endfix = "_" + method.getName() + "_" + (i > 9 ? i : ("0" + i)); + final String messagename = method.getAnnotation(RestOnMessage.class).name(); + + mv.visitVarInsn(ALOAD, 4); + mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";"); + Label ifLabel = new Label(); + mv.visitJumpInsn(IFNULL, ifLabel); + + mv.visitVarInsn(ALOAD, 4); + mv.visitFieldInsn(GETFIELD, newDynMessageFullName, messagename, "L" + (newDynMessageFullName + endfix) + ";"); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKEVIRTUAL, (newDynMessageFullName + endfix), "execute", "(L" + newDynWebSokcetFullName + ";)V", false); + mv.visitInsn(RETURN); + mv.visitLabel(ifLabel); + } + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3 + messageMethods.size()); + mv.visitEnd(); + } + {//虚拟accept函数 + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "accept", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, WebSocket.class.getName().replace('.', '/')); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, "java/lang/Object"); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynConsumerFullName, "accept", "(" + wsDesc + "Ljava/lang/Object;)V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw2.visitEnd(); + byte[] bytes = cw2.toByteArray(); + Class cz = newLoader.loadClass(newDynConsumerFullName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynConsumerFullName.replace('/', '.'), bytes, cz); + } + cw.visitEnd(); + + byte[] bytes = cw.toByteArray(); + Class newClazz = newLoader.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + JsonFactory.root().loadDecoder(newClazz.getAnnotation(RestDyn.class).types()[2]); //固定Message类 + + RedkaleClassLoader.putReflectionPublicMethods(webSocketType.getName()); + Class cwt = webSocketType; + do { + RedkaleClassLoader.putReflectionDeclaredFields(cwt.getName()); + } while ((cwt = cwt.getSuperclass()) != Object.class); + RedkaleClassLoader.putReflectionDeclaredConstructors(webSocketType, webSocketType.getName()); + + try { + T servlet = (T) newClazz.getDeclaredConstructor().newInstance(); + Field field = newClazz.getField("_redkale_annotations"); + field.set(null, msgclassToAnnotations); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), field); + if (rws.cryptor() != Cryptor.class) { + RedkaleClassLoader.putReflectionDeclaredConstructors(rws.cryptor(), rws.cryptor().getName()); + Cryptor cryptor = rws.cryptor().getDeclaredConstructor().newInstance(); + Field cryptorField = newClazz.getSuperclass().getDeclaredField("cryptor"); //WebSocketServlet + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), cryptorField); + cryptorField.setAccessible(true); + cryptorField.set(servlet, cryptor); + } + if (messageAgent != null) ((WebSocketServlet) servlet).messageAgent = messageAgent; + return servlet; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static T createRestServlet(final ClassLoader classLoader, final Class userType0, + final Class baseServletType, final Class serviceType) { + + if (baseServletType == null || serviceType == null) throw new RuntimeException(" Servlet or Service is null Class on createRestServlet"); + if (!HttpServlet.class.isAssignableFrom(baseServletType)) throw new RuntimeException(baseServletType + " is not HttpServlet Class on createRestServlet"); + int mod = baseServletType.getModifiers(); + if (!java.lang.reflect.Modifier.isPublic(mod)) throw new RuntimeException(baseServletType + " is not Public Class on createRestServlet"); + if (java.lang.reflect.Modifier.isAbstract(mod)) { + for (Method m : baseServletType.getDeclaredMethods()) { + if (java.lang.reflect.Modifier.isAbstract(m.getModifiers())) { //@since 2.4.0 + throw new RuntimeException(baseServletType + " cannot contains a abstract Method on " + baseServletType); + } + } + } + final String restInternalName = Type.getInternalName(Rest.class); + final String serviceDesc = Type.getDescriptor(serviceType); + final String webServletDesc = Type.getDescriptor(WebServlet.class); + final String resDesc = Type.getDescriptor(Resource.class); + final String reqDesc = Type.getDescriptor(HttpRequest.class); + final String respDesc = Type.getDescriptor(HttpResponse.class); + final String convertDesc = Type.getDescriptor(Convert.class); + final String typeDesc = Type.getDescriptor(java.lang.reflect.Type.class); + final String retDesc = Type.getDescriptor(RetResult.class); + final String httpResultDesc = Type.getDescriptor(HttpResult.class); + final String httpScopeDesc = Type.getDescriptor(HttpScope.class); + final String stageDesc = Type.getDescriptor(CompletionStage.class); + final String flipperDesc = Type.getDescriptor(Flipper.class); + final String channelDesc = Type.getDescriptor(ChannelContext.class); + final String httpServletName = HttpServlet.class.getName().replace('.', '/'); + final String actionEntryName = HttpServlet.ActionEntry.class.getName().replace('.', '/'); + final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class); + final String multiContextDesc = Type.getDescriptor(MultiContext.class); + final String multiContextName = MultiContext.class.getName().replace('.', '/'); + final String mappingDesc = Type.getDescriptor(HttpMapping.class); + final String httpParamDesc = Type.getDescriptor(HttpParam.class); + final String httpParamsDesc = Type.getDescriptor(HttpParam.HttpParams.class); + final String sourcetypeDesc = Type.getDescriptor(HttpParam.HttpParameterStyle.class); + + final String reqInternalName = Type.getInternalName(HttpRequest.class); + final String respInternalName = Type.getInternalName(HttpResponse.class); + final String attrInternalName = Type.getInternalName(org.redkale.util.Attribute.class); + final String retInternalName = Type.getInternalName(RetResult.class); + final String serviceTypeInternalName = Type.getInternalName(serviceType); + + HttpUserType hut = baseServletType.getAnnotation(HttpUserType.class); + final Class userType = (userType0 == null || userType0 == Object.class) ? (hut == null ? null : hut.value()) : userType0; + if (userType != null && (userType.isPrimitive() || userType.getName().startsWith("java.") || userType.getName().startsWith("javax."))) { + throw new RuntimeException(HttpUserType.class.getSimpleName() + " must be a JavaBean but found " + userType); + } + + final String supDynName = baseServletType.getName().replace('.', '/'); + final RestService controller = serviceType.getAnnotation(RestService.class); + if (controller != null && controller.ignore()) throw new RuntimeException(serviceType + " is ignore Rest Service Class"); //标记为ignore=true不创建Servlet + final boolean serrpconly = controller != null && controller.rpconly(); + ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; + String stname = serviceType.getSimpleName(); + if (stname.startsWith("Service")) { //类似ServiceWatchService这样的类保留第一个Service字样 + stname = "Service" + stname.substring("Service".length()).replaceAll("Service.*$", ""); + } else { + stname = stname.replaceAll("Service.*$", ""); + } + //String newDynName = serviceTypeInternalName.substring(0, serviceTypeInternalName.lastIndexOf('/') + 1) + "_Dyn" + stname + "RestServlet"; + final String newDynName = "org/redkaledyn/http/rest/" + "_Dyn" + stname + "RestServlet__" + serviceType.getName().replace('.', '_').replace('$', '_') + "DynServlet"; + + try { + Class newClazz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + if (newClazz == null) newClazz = loader.loadClass(newDynName.replace('/', '.')); + T obj = (T) newClazz.getDeclaredConstructor().newInstance(); + + final String defmodulename = getWebModuleNameLowerCase(serviceType); + final String bigmodulename = getWebModuleName(serviceType); + final Map classMap = new LinkedHashMap<>(); + + final List entrys = new ArrayList<>(); + final List paramTypes = new ArrayList<>(); + final List retvalTypes = new ArrayList<>(); + + final List restConverts = new ArrayList<>(); + final Map typeRefs = new LinkedHashMap<>(); + final Map mappingurlToMethod = new HashMap<>(); + final Map restAttributes = new LinkedHashMap<>(); + final Map bodyTypes = new HashMap<>(); + + { //entrys、paramTypes赋值 + final Method[] allMethods = serviceType.getMethods(); + Arrays.sort(allMethods, (m1, m2) -> { //必须排序,否则paramTypes顺序容易乱 + int s = m1.getName().compareTo(m2.getName()); + if (s != 0) return s; + s = Arrays.toString(m1.getParameterTypes()).compareTo(Arrays.toString(m2.getParameterTypes())); + return s; + }); + int methodidex = 0; + for (final Method method : allMethods) { + if (Modifier.isStatic(method.getModifiers())) continue; + if (method.isSynthetic()) continue; + if (EXCLUDERMETHODS.contains(method.getName())) continue; + if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) { + if ("init".equals(method.getName())) continue; + if ("stop".equals(method.getName())) continue; + if ("destroy".equals(method.getName())) continue; + } + if (controller == null) continue; + + RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class); + if (!controller.automapping() && mappings.length < 1) continue; + boolean ignore = false; + for (RestMapping mapping : mappings) { + if (mapping.ignore()) { + ignore = true; + break; + } + } + if (ignore) continue; + paramTypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType)); + retvalTypes.add(formatRestReturnType(method, serviceType)); + if (mappings.length == 0) { //没有Mapping,设置一个默认值 + MappingEntry entry = new MappingEntry(serrpconly, methodidex, null, bigmodulename, method); + entrys.add(entry); + } else { + for (RestMapping mapping : mappings) { + MappingEntry entry = new MappingEntry(serrpconly, methodidex, mapping, defmodulename, method); + entrys.add(entry); + } + } + methodidex++; + } + Collections.sort(entrys); + } + { //restConverts、typeRefs、mappingurlToMethod、restAttributes、bodyTypes赋值 + for (final MappingEntry entry : entrys) { + mappingurlToMethod.put(entry.mappingurl, entry.mappingMethod); + final Method method = entry.mappingMethod; + final Class returnType = method.getReturnType(); + final Parameter[] params = method.getParameters(); + final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class); + final RestConvertCoder[] rcc = method.getAnnotationsByType(RestConvertCoder.class); + if ((rcs != null && rcs.length > 0) || (rcc != null && rcc.length > 0)) { + restConverts.add(new Object[]{rcs, rcc}); + } + //解析方法中的每个参数 + List paramlist = new ArrayList<>(); + for (int i = 0; i < params.length; i++) { + final Parameter param = params[i]; + final Class ptype = param.getType(); + String n = null; + String comment = ""; + boolean required = true; + int radix = 10; + RestHeader annhead = param.getAnnotation(RestHeader.class); + if (annhead != null) { + n = annhead.name(); + radix = annhead.radix(); + comment = annhead.comment(); + required = annhead.required(); + } + RestCookie anncookie = param.getAnnotation(RestCookie.class); + if (anncookie != null) { + n = anncookie.name(); + radix = anncookie.radix(); + comment = anncookie.comment(); + } + RestSessionid annsid = param.getAnnotation(RestSessionid.class); + RestAddress annaddr = param.getAnnotation(RestAddress.class); + if (annaddr != null) { + comment = annaddr.comment(); + } + RestBody annbody = param.getAnnotation(RestBody.class); + if (annbody != null) { + comment = annbody.comment(); + } + RestUploadFile annfile = param.getAnnotation(RestUploadFile.class); + if (annfile != null) { + comment = annfile.comment(); + } + RestURI annuri = param.getAnnotation(RestURI.class); + if (annuri != null) { + comment = annuri.comment(); + } + RestUserid userid = param.getAnnotation(RestUserid.class); + + if (userid != null) { + comment = ""; + } + RestHeaders annheaders = param.getAnnotation(RestHeaders.class); + if (annheaders != null) { + comment = ""; + } + RestParams annparams = param.getAnnotation(RestParams.class); + if (annparams != null) { + comment = ""; + } + RestParam annpara = param.getAnnotation(RestParam.class); + if (annpara != null) radix = annpara.radix(); + if (annpara != null) comment = annpara.comment(); + if (annpara != null) required = annpara.required(); + if (n == null) n = (annpara == null || annpara.name().isEmpty()) ? null : annpara.name(); + if (n == null && ptype == userType) n = "&"; //用户类型特殊处理 + if (n == null) { + if (param.isNamePresent()) { + n = param.getName(); + } else if (ptype == Flipper.class) { + n = "flipper"; + } + } //n maybe is null + + java.lang.reflect.Type paramtype = TypeToken.getGenericType(param.getParameterizedType(), serviceType); + paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, userid, annheaders, annparams, paramtype}); + } + for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, annuserid, annheaders, annparams, paramtype} + final boolean isuserid = ((RestUserid) ps[14]) != null; //是否取userid + if ((ps[1] != null && ps[1].toString().indexOf('&') >= 0) || isuserid) continue; //@RestUserid 不需要生成 @HttpParam + if (((RestAddress) ps[8]) != null) continue; //@RestAddress 不需要生成 @HttpParam + java.lang.reflect.Type pgtype = TypeToken.getGenericType(((Parameter) ps[0]).getParameterizedType(), serviceType); + if (pgtype != (Class) ps[2]) { + String refid = typeRefs.get(pgtype); + if (refid == null) { + refid = "_typeref_" + typeRefs.size(); + typeRefs.put(pgtype, refid); + } + } + + final Parameter param = (Parameter) ps[0]; //参数类型 + String pname = (String) ps[1]; //参数名 + Class ptype = (Class) ps[2]; //参数类型 + int radix = (Integer) ps[3]; + String comment = (String) ps[4]; + boolean required = (Boolean) ps[5]; + RestParam annpara = (RestParam) ps[6]; + RestSessionid annsid = (RestSessionid) ps[7]; + RestAddress annaddr = (RestAddress) ps[8]; + RestHeader annhead = (RestHeader) ps[9]; + RestCookie anncookie = (RestCookie) ps[10]; + RestBody annbody = (RestBody) ps[11]; + RestUploadFile annfile = (RestUploadFile) ps[12]; + RestURI annuri = (RestURI) ps[13]; + RestUserid annuserid = (RestUserid) ps[14]; + RestHeaders annheaders = (RestHeaders) ps[15]; + RestParams annparams = (RestParams) ps[16]; + + if (CompletionHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class) + } else if (annsid != null) { //HttpRequest.getSessionid(true|false) + } else if (annaddr != null) { //HttpRequest.getRemoteAddr + } else if (annheaders != null) { //HttpRequest.getHeaders + } else if (annparams != null) { //HttpRequest.getParameters + } else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody + } else if (annfile != null) { //MultiContext.partsFirstBytes / HttpRequest.partsFirstFile / HttpRequest.partsFiles + } else if (annuri != null) { //HttpRequest.getRequestURI + } else if (annuserid != null) { //HttpRequest.currentUserid + } else if ("#".equals(pname)) { //从request.getRequstURI 中取参数 + } else if (pname != null && pname.charAt(0) == '#') { //从request.getRequstURIPath 中去参数 + } else if ("&".equals(pname) && ptype == userType) { //当前用户对象的类名 + } else if (ptype.isPrimitive()) { + } else if (ptype == String.class) { + } else if (ptype == ChannelContext.class) { + } else if (ptype == Flipper.class) { + } else { //其他Json对象 + //构建 RestHeader、RestCookie、RestAddress 等赋值操作 + Class loop = ptype; + Set fields = new HashSet<>(); + Map attrParaNames = new LinkedHashMap<>(); + do { + if (loop == null || loop.isInterface()) break; //接口时getSuperclass可能会得到null + for (Field field : loop.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (Modifier.isFinal(field.getModifiers())) continue; + if (fields.contains(field.getName())) continue; + RestHeader rh = field.getAnnotation(RestHeader.class); + RestCookie rc = field.getAnnotation(RestCookie.class); + RestSessionid rs = field.getAnnotation(RestSessionid.class); + RestAddress ra = field.getAnnotation(RestAddress.class); + RestBody rb = field.getAnnotation(RestBody.class); + RestUploadFile ru = field.getAnnotation(RestUploadFile.class); + RestURI ri = field.getAnnotation(RestURI.class); + if (rh == null && rc == null && ra == null && rb == null && rs == null && ru == null && ri == null) continue; + + org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field); + String attrFieldName; + String restname = ""; + if (rh != null) { + attrFieldName = "_redkale_attr_header_" + (field.getType() != String.class ? "json_" : "") + restAttributes.size(); + restname = rh.name(); + } else if (rc != null) { + attrFieldName = "_redkale_attr_cookie_" + restAttributes.size(); + restname = rc.name(); + } else if (rs != null) { + attrFieldName = "_redkale_attr_sessionid_" + restAttributes.size(); + restname = rs.create() ? "1" : ""; //用于下面区分create值 + } else if (ra != null) { + attrFieldName = "_redkale_attr_address_" + restAttributes.size(); + //restname = ""; + } else if (rb != null && field.getType() == String.class) { + attrFieldName = "_redkale_attr_bodystring_" + restAttributes.size(); + //restname = ""; + } else if (rb != null && field.getType() == byte[].class) { + attrFieldName = "_redkale_attr_bodybytes_" + restAttributes.size(); + //restname = ""; + } else if (rb != null && field.getType() != String.class && field.getType() != byte[].class) { + attrFieldName = "_redkale_attr_bodyjson_" + restAttributes.size(); + //restname = ""; + } else if (ru != null && field.getType() == byte[].class) { + attrFieldName = "_redkale_attr_uploadbytes_" + restAttributes.size(); + //restname = ""; + } else if (ru != null && field.getType() == File.class) { + attrFieldName = "_redkale_attr_uploadfile_" + restAttributes.size(); + //restname = ""; + } else if (ru != null && field.getType() == File[].class) { + attrFieldName = "_redkale_attr_uploadfiles_" + restAttributes.size(); + //restname = ""; + } else if (ri != null && field.getType() == String.class) { + attrFieldName = "_redkale_attr_uri_" + restAttributes.size(); + //restname = ""; + } else { + continue; + } + restAttributes.put(attrFieldName, attr); + attrParaNames.put(attrFieldName, new Object[]{restname, field.getType(), field.getGenericType(), ru}); + fields.add(field.getName()); + } + } while ((loop = loop.getSuperclass()) != Object.class); + + if (!attrParaNames.isEmpty()) { //参数存在 RestHeader、RestCookie、RestSessionid、RestAddress、RestBody字段 + for (Map.Entry en : attrParaNames.entrySet()) { + if (en.getKey().contains("_header_")) { + String headerkey = en.getValue()[0].toString(); + if ("Host".equalsIgnoreCase(headerkey)) { + } else if ("Content-Type".equalsIgnoreCase(headerkey)) { + } else if ("Connection".equalsIgnoreCase(headerkey)) { + } else if ("Method".equalsIgnoreCase(headerkey)) { + } else if (en.getKey().contains("_header_json_")) { + String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); + bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]); + } + } else if (en.getKey().contains("_cookie_")) { + } else if (en.getKey().contains("_sessionid_")) { + } else if (en.getKey().contains("_address_")) { + } else if (en.getKey().contains("_uri_")) { + } else if (en.getKey().contains("_bodystring_")) { + } else if (en.getKey().contains("_bodybytes_")) { + } else if (en.getKey().contains("_bodyjson_")) {//JavaBean 转 Json + String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); + bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]); + } else if (en.getKey().contains("_uploadbytes_")) { + //只需mv.visitVarInsn(ALOAD, 4), 无需处理 + } else if (en.getKey().contains("_uploadfile_")) { + //只需mv.visitVarInsn(ALOAD, 4), 无需处理 + } else if (en.getKey().contains("_uploadfiles_")) { + //只需mv.visitVarInsn(ALOAD, 4), 无需处理 + } + } + } + } + } + java.lang.reflect.Type grt = TypeToken.getGenericType(method.getGenericReturnType(), serviceType); + Class rtc = returnType; + if (rtc == void.class) { + rtc = RetResult.class; + grt = TYPE_RETRESULT_STRING; + } else if (CompletionStage.class.isAssignableFrom(returnType)) { + ParameterizedType ptgrt = (ParameterizedType) grt; + grt = ptgrt.getActualTypeArguments()[0]; + rtc = TypeToken.typeToClass(grt); + if (rtc == null) rtc = Object.class; //应该不会发生吧? + } else if (Flows.maybePublisherClass(returnType)) { + java.lang.reflect.Type grt0 = Flows.maybePublisherSubType(grt); + if (grt0 != null) grt = grt0; + } + if (grt != rtc) { + String refid = typeRefs.get(grt); + if (refid == null) { + refid = "_typeref_" + typeRefs.size(); + typeRefs.put(grt, refid); + } + } + } + } + for (Map.Entry en : typeRefs.entrySet()) { + Field refField = newClazz.getDeclaredField(en.getValue()); + refField.setAccessible(true); + refField.set(obj, en.getKey()); + } + for (Map.Entry en : restAttributes.entrySet()) { + Field attrField = newClazz.getDeclaredField(en.getKey()); + attrField.setAccessible(true); + attrField.set(obj, en.getValue()); + } + for (Map.Entry en : bodyTypes.entrySet()) { + Field genField = newClazz.getDeclaredField(en.getKey()); + genField.setAccessible(true); + genField.set(obj, en.getValue()); + } + for (int i = 0; i < restConverts.size(); i++) { + Field genField = newClazz.getDeclaredField(REST_CONVERT_FIELD_PREFIX + (i + 1)); + genField.setAccessible(true); + Object[] rc = restConverts.get(i); + + genField.set(obj, createJsonConvert((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1])); + } + Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); + typesfield.setAccessible(true); + java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramTypes.size()][]; + paramtypeArray = paramTypes.toArray(paramtypeArray); + typesfield.set(obj, paramtypeArray); + + Field retfield = newClazz.getDeclaredField(REST_RETURNTYPES_FIELD_NAME); + retfield.setAccessible(true); + java.lang.reflect.Type[] rettypeArray = new java.lang.reflect.Type[retvalTypes.size()]; + rettypeArray = retvalTypes.toArray(rettypeArray); + retfield.set(obj, rettypeArray); + + Field tostringfield = newClazz.getDeclaredField(REST_TOSTRINGOBJ_FIELD_NAME); + tostringfield.setAccessible(true); + java.util.function.Supplier sSupplier = () -> JsonConvert.root().convertTo(classMap); + tostringfield.set(obj, sSupplier); + + Method restactMethod = newClazz.getDeclaredMethod("_createRestActionEntry"); + restactMethod.setAccessible(true); + Field tmpentrysfield = HttpServlet.class.getDeclaredField("_actionmap"); + tmpentrysfield.setAccessible(true); + HashMap innerEntryMap = (HashMap) restactMethod.invoke(obj); + for (Map.Entry en : innerEntryMap.entrySet()) { + Method m = mappingurlToMethod.get(en.getKey()); + if (m != null) en.getValue().annotations = HttpServlet.ActionEntry.annotations(m); + } + tmpentrysfield.set(obj, innerEntryMap); + return obj; + } catch (ClassNotFoundException e) { + } catch (Throwable e) { + e.printStackTrace(); + } + //------------------------------------------------------------------------------ + final String defmodulename = getWebModuleNameLowerCase(serviceType); + final String bigmodulename = getWebModuleName(serviceType); + final String catalog = controller == null ? "" : controller.catalog(); + if (!checkName(catalog)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".catalog, only 0-9 a-z A-Z _ cannot begin 0-9"); + if (!checkName(defmodulename)) throw new RuntimeException(serviceType.getName() + " have illegal " + RestService.class.getSimpleName() + ".value, only 0-9 a-z A-Z _ cannot begin 0-9"); + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + AnnotationVisitor av0; + final List entrys = new ArrayList<>(); + final Map restAttributes = new LinkedHashMap<>(); + final Map classMap = new LinkedHashMap<>(); + final Map typeRefs = new LinkedHashMap<>(); + final List paramTypes = new ArrayList<>(); + final List retvalTypes = new ArrayList<>(); + final Map bodyTypes = new HashMap<>(); + final List restConverts = new ArrayList<>(); + final Map mappingurlToMethod = new HashMap<>(); + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); + + { //RestDynSourceType + av0 = cw.visitAnnotation(Type.getDescriptor(RestDynSourceType.class), true); + av0.visit("value", Type.getType(Type.getDescriptor(serviceType))); + av0.visitEnd(); + } + boolean dynsimple = true; + //获取所有可以转换成HttpMapping的方法 + int methodidex = 0; + final MessageMultiConsumer mmc = serviceType.getAnnotation(MessageMultiConsumer.class); + if (mmc != null && (mmc.module() == null || mmc.module().isEmpty())) { + throw new RuntimeException("@" + MessageMultiConsumer.class.getSimpleName() + ".module can not empty in " + serviceType.getName()); + } + if (mmc != null && !checkName2(mmc.module())) { + throw new RuntimeException(serviceType.getName() + " have illegal " + MessageMultiConsumer.class.getSimpleName() + ".module, only 0-9 a-z A-Z _ - . cannot begin 0-9"); + } + if (mmc != null) { + MethodDebugVisitor.visitAnnotation(cw.visitAnnotation(Type.getDescriptor(mmc.annotationType()), true), mmc); + } + final Method[] allMethods = serviceType.getMethods(); + Arrays.sort(allMethods, (m1, m2) -> { //必须排序,否则paramTypes顺序容易乱 + int s = m1.getName().compareTo(m2.getName()); + if (s != 0) return s; + s = Arrays.toString(m1.getParameterTypes()).compareTo(Arrays.toString(m2.getParameterTypes())); + return s; + }); + for (final Method method : allMethods) { + if (Modifier.isStatic(method.getModifiers())) continue; + if (method.isSynthetic()) continue; + if (EXCLUDERMETHODS.contains(method.getName())) continue; + if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) { + if ("init".equals(method.getName())) continue; + if ("stop".equals(method.getName())) continue; + if ("destroy".equals(method.getName())) continue; + } + if (controller == null) continue; + + RestMapping[] mappings = method.getAnnotationsByType(RestMapping.class); + if (!controller.automapping() && mappings.length < 1) continue; + boolean ignore = false; + for (RestMapping mapping : mappings) { + if (mapping.ignore()) { + ignore = true; + break; + } + } + if (ignore) continue; + + Class[] extypes = method.getExceptionTypes(); + if (extypes.length > 0) { + for (Class exp : extypes) { + if (!RuntimeException.class.isAssignableFrom(exp) && !IOException.class.isAssignableFrom(exp)) { + throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method(" + method + ") with throws IOException"); + } + } + } + if (mmc != null && method.getReturnType() != void.class) { + throw new RuntimeException("@" + RestMapping.class.getSimpleName() + " only for method(" + method + ") with return void by @" + MessageMultiConsumer.class.getSimpleName() + " Service"); + } + paramTypes.add(TypeToken.getGenericType(method.getGenericParameterTypes(), serviceType)); + retvalTypes.add(formatRestReturnType(method, serviceType)); + if (mappings.length == 0) { //没有Mapping,设置一个默认值 + MappingEntry entry = new MappingEntry(serrpconly, methodidex, null, bigmodulename, method); + if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat"); + entrys.add(entry); + } else { + for (RestMapping mapping : mappings) { + MappingEntry entry = new MappingEntry(serrpconly, methodidex, mapping, defmodulename, method); + if (entrys.contains(entry)) throw new RuntimeException(serviceType.getName() + " on " + method.getName() + " 's mapping(" + entry.name + ") is repeat"); + entrys.add(entry); + } + } + methodidex++; + } + if (entrys.isEmpty()) return null; //没有可HttpMapping的方法 + + Collections.sort(entrys); + RestClassLoader newLoader = new RestClassLoader(loader); + final int moduleid = controller == null ? 0 : controller.moduleid(); + { //注入 @WebServlet 注解 + String urlpath = ""; + boolean repair = controller == null ? true : controller.repair(); + String comment = controller == null ? "" : controller.comment(); + av0 = cw.visitAnnotation(webServletDesc, true); + { + AnnotationVisitor av1 = av0.visitArray("value"); + boolean pound = false; + for (MappingEntry entry : entrys) { + if (entry.existsPound) { + pound = true; + break; + } + } + if (defmodulename.isEmpty() || (!pound && entrys.size() <= 2)) { + for (MappingEntry entry : entrys) { + String suburl = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name; + if ("//".equals(suburl)) { + suburl = "/"; + } else if (suburl.length() > 2 && suburl.endsWith("/")) { + suburl += "*"; + } + urlpath += "," + suburl; + av1.visit(null, suburl); + } + if (urlpath.length() > 0) urlpath = urlpath.substring(1); + } else { + urlpath = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + defmodulename + "/*"; + av1.visit(null, urlpath); + } + av1.visitEnd(); + } + av0.visit("name", defmodulename); + av0.visit("moduleid", moduleid); + av0.visit("repair", repair); + av0.visit("comment", comment); + av0.visitEnd(); + classMap.put("type", serviceType.getName()); + classMap.put("url", urlpath); + classMap.put("moduleid", moduleid); + classMap.put("repair", repair); + //classMap.put("comment", comment); //不显示太多信息 + } + { //内部类 + cw.visitInnerClass(actionEntryName, httpServletName, HttpServlet.ActionEntry.class.getSimpleName(), ACC_PROTECTED + ACC_FINAL + ACC_STATIC); + + for (final MappingEntry entry : entrys) { + cw.visitInnerClass(newDynName + "$" + entry.newActionClassName, newDynName, entry.newActionClassName, ACC_PRIVATE + ACC_STATIC); + } + } + { //注入 @Resource private XXXService _service; + fv = cw.visitField(ACC_PRIVATE, REST_SERVICE_FIELD_NAME, serviceDesc, null, null); + av0 = fv.visitAnnotation(resDesc, true); + av0.visit("name", ""); + av0.visitEnd(); + fv.visitEnd(); + } + { //注入 @Resource(name = "APP_HOME") private File _redkale_home; + fv = cw.visitField(ACC_PRIVATE, "_redkale_home", Type.getDescriptor(File.class), null, null); + av0 = fv.visitAnnotation(resDesc, true); + av0.visit("name", "APP_HOME"); + av0.visitEnd(); + fv.visitEnd(); + } + { //_servicemap字段 Map + fv = cw.visitField(ACC_PRIVATE, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;", "Ljava/util/Map;", null); + fv.visitEnd(); + } + { //_redkale_tostringsupplier字段 Supplier + fv = cw.visitField(ACC_PRIVATE, REST_TOSTRINGOBJ_FIELD_NAME, "Ljava/util/function/Supplier;", "Ljava/util/function/Supplier;", null); + fv.visitEnd(); + } + { //构造函数 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + //将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法 + boolean namePresent = false; + try { + Method m0 = null; + for (final MappingEntry entry : entrys) { + if (entry.mappingMethod.getParameterCount() > 0) { + m0 = entry.mappingMethod; + break; + } + } + namePresent = m0 == null ? true : m0.getParameters()[0].isNamePresent(); + } catch (Exception e) { + } + final Map> asmParamMap = namePresent ? null : MethodParamClassVisitor.getMethodParamNames(new HashMap<>(), serviceType); + + Map innerClassBytesMap = new LinkedHashMap<>(); + for (final MappingEntry entry : entrys) { + RestUploadFile mupload = null; + Class muploadType = null; + final Method method = entry.mappingMethod; + final Class returnType = method.getReturnType(); + final java.lang.reflect.Type retvalType = formatRestReturnType(method, serviceType); + final String methodDesc = Type.getMethodDescriptor(method); + final Parameter[] params = method.getParameters(); + + final RestConvert[] rcs = method.getAnnotationsByType(RestConvert.class); + final RestConvertCoder[] rcc = method.getAnnotationsByType(RestConvertCoder.class); + if ((rcs != null && rcs.length > 0) || (rcc != null && rcc.length > 0)) { + restConverts.add(new Object[]{rcs, rcc}); + } + + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, entry.newMethodName, "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"})); + //mv.setDebug(true); + mv.debugLine(); + + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;"); + Label lmapif = new Label(); + mv.visitJumpInsn(IFNONNULL, lmapif); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc); + Label lserif = new Label(); + mv.visitJumpInsn(GOTO, lserif); + mv.visitLabel(lmapif); + + mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null); + + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICEMAP_FIELD_NAME, "Ljava/util/Map;"); + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(REST_HEADER_RESOURCE_NAME); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); + mv.visitTypeInsn(CHECKCAST, serviceTypeInternalName); + mv.visitLabel(lserif); + mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{serviceTypeInternalName}); + mv.visitVarInsn(ASTORE, 3); + + final int maxStack = 3 + params.length; + List varInsns = new ArrayList<>(); + int maxLocals = 4; + + List asmParamNames = asmParamMap == null ? null : asmParamMap.get(method.getName() + ":" + Type.getMethodDescriptor(method)); + if (asmParamNames != null) while (asmParamNames.remove(" ")); //删掉空元素 + List paramlist = new ArrayList<>(); + //解析方法中的每个参数 + for (int i = 0; i < params.length; i++) { + final Parameter param = params[i]; + final Class ptype = param.getType(); + String n = null; + String comment = ""; + boolean required = true; + int radix = 10; + + RestHeader annhead = param.getAnnotation(RestHeader.class); + if (annhead != null) { + if (ptype != String.class && ptype != InetSocketAddress.class) throw new RuntimeException("@RestHeader must on String or InetSocketAddress Parameter in " + method); + n = annhead.name(); + radix = annhead.radix(); + comment = annhead.comment(); + if (n.isEmpty()) throw new RuntimeException("@RestHeader.value is illegal in " + method); + } + RestCookie anncookie = param.getAnnotation(RestCookie.class); + if (anncookie != null) { + if (annhead != null) throw new RuntimeException("@RestCookie and @RestHeader cannot on the same Parameter in " + method); + if (ptype != String.class) throw new RuntimeException("@RestCookie must on String Parameter in " + method); + n = anncookie.name(); + radix = anncookie.radix(); + comment = anncookie.comment(); + if (n.isEmpty()) throw new RuntimeException("@RestCookie.value is illegal in " + method); + } + RestSessionid annsid = param.getAnnotation(RestSessionid.class); + if (annsid != null) { + if (annhead != null) throw new RuntimeException("@RestSessionid and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestSessionid and @RestCookie cannot on the same Parameter in " + method); + if (ptype != String.class) throw new RuntimeException("@RestSessionid must on String Parameter in " + method); + } + RestAddress annaddr = param.getAnnotation(RestAddress.class); + if (annaddr != null) { + if (annhead != null) throw new RuntimeException("@RestAddress and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestAddress and @RestCookie cannot on the same Parameter in " + method); + if (annsid != null) throw new RuntimeException("@RestAddress and @RestSessionid cannot on the same Parameter in " + method); + if (ptype != String.class) throw new RuntimeException("@RestAddress must on String Parameter in " + method); + comment = annaddr.comment(); + } + RestBody annbody = param.getAnnotation(RestBody.class); + if (annbody != null) { + if (annhead != null) throw new RuntimeException("@RestBody and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestBody and @RestCookie cannot on the same Parameter in " + method); + if (annsid != null) throw new RuntimeException("@RestBody and @RestSessionid cannot on the same Parameter in " + method); + if (annaddr != null) throw new RuntimeException("@RestBody and @RestAddress cannot on the same Parameter in " + method); + if (ptype.isPrimitive()) throw new RuntimeException("@RestBody cannot on primitive type Parameter in " + method); + comment = annbody.comment(); + } + RestUploadFile annfile = param.getAnnotation(RestUploadFile.class); + if (annfile != null) { + if (mupload != null) throw new RuntimeException("@RestUploadFile repeat in " + method); + mupload = annfile; + muploadType = ptype; + if (annhead != null) throw new RuntimeException("@RestUploadFile and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestUploadFile and @RestCookie cannot on the same Parameter in " + method); + if (annsid != null) throw new RuntimeException("@RestUploadFile and @RestSessionid cannot on the same Parameter in " + method); + if (annaddr != null) throw new RuntimeException("@RestUploadFile and @RestAddress cannot on the same Parameter in " + method); + if (annbody != null) throw new RuntimeException("@RestUploadFile and @RestBody cannot on the same Parameter in " + method); + if (ptype != byte[].class && ptype != File.class && ptype != File[].class) throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Parameter in " + method); + comment = annfile.comment(); + } + + RestURI annuri = param.getAnnotation(RestURI.class); + if (annuri != null) { + if (annhead != null) throw new RuntimeException("@RestURI and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestURI and @RestCookie cannot on the same Parameter in " + method); + if (annsid != null) throw new RuntimeException("@RestURI and @RestSessionid cannot on the same Parameter in " + method); + if (annaddr != null) throw new RuntimeException("@RestURI and @RestAddress cannot on the same Parameter in " + method); + if (annbody != null) throw new RuntimeException("@RestURI and @RestBody cannot on the same Parameter in " + method); + if (annfile != null) throw new RuntimeException("@RestURI and @RestUploadFile cannot on the same Parameter in " + method); + if (ptype != String.class) throw new RuntimeException("@RestURI must on String Parameter in " + method); + comment = annuri.comment(); + } + + RestUserid userid = param.getAnnotation(RestUserid.class); + if (userid != null) { + if (annhead != null) throw new RuntimeException("@RestUserid and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestUserid and @RestCookie cannot on the same Parameter in " + method); + if (annsid != null) throw new RuntimeException("@RestUserid and @RestSessionid cannot on the same Parameter in " + method); + if (annaddr != null) throw new RuntimeException("@RestUserid and @RestAddress cannot on the same Parameter in " + method); + if (annbody != null) throw new RuntimeException("@RestUserid and @RestBody cannot on the same Parameter in " + method); + if (annfile != null) throw new RuntimeException("@RestUserid and @RestUploadFile cannot on the same Parameter in " + method); + if (!ptype.isPrimitive() && !java.io.Serializable.class.isAssignableFrom(ptype)) throw new RuntimeException("@RestUserid must on java.io.Serializable Parameter in " + method); + comment = ""; + } + + RestHeaders annheaders = param.getAnnotation(RestHeaders.class); + if (annheaders != null) { + if (annhead != null) throw new RuntimeException("@RestHeaders and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestHeaders and @RestCookie cannot on the same Parameter in " + method); + if (annsid != null) throw new RuntimeException("@RestHeaders and @RestSessionid cannot on the same Parameter in " + method); + if (annaddr != null) throw new RuntimeException("@RestHeaders and @RestAddress cannot on the same Parameter in " + method); + if (annbody != null) throw new RuntimeException("@RestHeaders and @RestBody cannot on the same Parameter in " + method); + if (annfile != null) throw new RuntimeException("@RestHeaders and @RestUploadFile cannot on the same Parameter in " + method); + if (userid != null) throw new RuntimeException("@RestHeaders and @RestUserid cannot on the same Parameter in " + method); + if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestHeaders must on Map Parameter in " + method); + comment = ""; + } + RestParams annparams = param.getAnnotation(RestParams.class); + if (annparams != null) { + if (annhead != null) throw new RuntimeException("@RestParams and @RestHeader cannot on the same Parameter in " + method); + if (anncookie != null) throw new RuntimeException("@RestParams and @RestCookie cannot on the same Parameter in " + method); + if (annsid != null) throw new RuntimeException("@RestParams and @RestSessionid cannot on the same Parameter in " + method); + if (annaddr != null) throw new RuntimeException("@RestParams and @RestAddress cannot on the same Parameter in " + method); + if (annbody != null) throw new RuntimeException("@RestParams and @RestBody cannot on the same Parameter in " + method); + if (annfile != null) throw new RuntimeException("@RestParams and @RestUploadFile cannot on the same Parameter in " + method); + if (userid != null) throw new RuntimeException("@RestParams and @RestUserid cannot on the same Parameter in " + method); + if (annheaders != null) throw new RuntimeException("@RestParams and @RestHeaders cannot on the same Parameter in " + method); + if (!TYPE_MAP_STRING_STRING.equals(param.getParameterizedType())) throw new RuntimeException("@RestParams must on Map Parameter in " + method); + comment = ""; + } + + RestParam annpara = param.getAnnotation(RestParam.class); + if (annpara != null) radix = annpara.radix(); + if (annpara != null) comment = annpara.comment(); + if (annpara != null) required = annpara.required(); + if (n == null) n = (annpara == null || annpara.name().isEmpty()) ? null : annpara.name(); + if (n == null && ptype == userType) n = "&"; //用户类型特殊处理 + if (n == null && asmParamNames != null && asmParamNames.size() > i) n = asmParamNames.get(i); + if (n == null) { + if (param.isNamePresent()) { + n = param.getName(); + } else if (ptype == Flipper.class) { + n = "flipper"; + } else { + throw new RuntimeException("Parameter " + param.getName() + " not found name by @RestParam in " + method); + } + } + if (annhead == null && anncookie == null && annsid == null && annaddr == null && annbody == null && annfile == null + && !ptype.isPrimitive() && ptype != String.class && ptype != Flipper.class && !CompletionHandler.class.isAssignableFrom(ptype) + && !ptype.getName().startsWith("java") && n.charAt(0) != '#' && !"&".equals(n)) { //判断Json对象是否包含@RestUploadFile + Class loop = ptype; + do { + if (loop == null || loop.isInterface()) break; //接口时getSuperclass可能会得到null + for (Field field : loop.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (Modifier.isFinal(field.getModifiers())) continue; + RestUploadFile ruf = field.getAnnotation(RestUploadFile.class); + if (ruf == null) continue; + if (mupload != null) throw new RuntimeException("@RestUploadFile repeat in " + method + " or field " + field); + mupload = ruf; + muploadType = field.getType(); + } + } while ((loop = loop.getSuperclass()) != Object.class); + } + java.lang.reflect.Type paramtype = TypeToken.getGenericType(param.getParameterizedType(), serviceType); + paramlist.add(new Object[]{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, userid, annheaders, annparams, paramtype}); + } + + Map mappingMap = new LinkedHashMap<>(); + { // 设置 Annotation + //设置 HttpMapping + boolean reqpath = false; + for (Object[] ps : paramlist) { + if ("#".equals((String) ps[1])) { + reqpath = true; + break; + } + } + if (method.getAnnotation(Deprecated.class) != null) { + av0 = mv.visitAnnotation(Type.getDescriptor(Deprecated.class), true); + av0.visitEnd(); + } + av0 = mv.visitAnnotation(mappingDesc, true); + String url = (catalog.isEmpty() ? "/" : ("/" + catalog + "/")) + (defmodulename.isEmpty() ? "" : (defmodulename + "/")) + entry.name + (reqpath ? "/" : ""); + if ("//".equals(url)) url = "/"; + av0.visit("url", url); + av0.visit("name", (defmodulename.isEmpty() ? "" : (defmodulename + "_")) + entry.name); + av0.visit("example", entry.example); + av0.visit("rpconly", entry.rpconly); + av0.visit("auth", entry.auth); + av0.visit("cacheseconds", entry.cacheseconds); + av0.visit("actionid", entry.actionid); + av0.visit("comment", entry.comment); + + AnnotationVisitor av1 = av0.visitArray("methods"); + for (String m : entry.methods) { + av1.visit(null, m); + } + av1.visitEnd(); + + java.lang.reflect.Type grt = TypeToken.getGenericType(method.getGenericReturnType(), serviceType); + Class rtc = returnType; + if (rtc == void.class) { + rtc = RetResult.class; + grt = TYPE_RETRESULT_STRING; + } else if (CompletionStage.class.isAssignableFrom(returnType)) { + ParameterizedType ptgrt = (ParameterizedType) grt; + grt = ptgrt.getActualTypeArguments()[0]; + rtc = TypeToken.typeToClass(grt); + if (rtc == null) rtc = Object.class; //应该不会发生吧? + } + av0.visit("result", Type.getType(Type.getDescriptor(rtc))); + if (grt != rtc) { + String refid = typeRefs.get(grt); + if (refid == null) { + refid = "_typeref_" + typeRefs.size(); + typeRefs.put(grt, refid); + } + av0.visit("resultref", refid); + } + + av0.visitEnd(); + mappingMap.put("url", url); + mappingMap.put("rpconly", entry.rpconly); + mappingMap.put("auth", entry.auth); + mappingMap.put("cacheseconds", entry.cacheseconds); + mappingMap.put("actionid", entry.actionid); + mappingMap.put("comment", entry.comment); + mappingMap.put("methods", entry.methods); + mappingMap.put("result", grt == returnType ? returnType.getName() : String.valueOf(grt)); + entry.mappingurl = url; + } + + { // 设置 Annotation + av0 = mv.visitAnnotation(httpParamsDesc, true); + AnnotationVisitor av1 = av0.visitArray("value"); + //设置 HttpParam + for (Object[] ps : paramlist) { //{param, n, ptype, radix, comment, required, annpara, annsid, annaddr, annhead, anncookie, annbody, annfile, annuri, annuserid, annheaders, annparams, paramtype} + String n = ps[1].toString(); + final boolean isuserid = ((RestUserid) ps[14]) != null; //是否取userid + if (n.indexOf('&') >= 0 || isuserid) continue; //@RestUserid 不需要生成 @HttpParam + if (((RestAddress) ps[8]) != null) continue; //@RestAddress 不需要生成 @HttpParam + final boolean ishead = ((RestHeader) ps[9]) != null; //是否取getHeader 而不是 getParameter + final boolean iscookie = ((RestCookie) ps[10]) != null; //是否取getCookie + final boolean isbody = ((RestBody) ps[11]) != null; //是否取getBody + AnnotationVisitor av2 = av1.visitAnnotation(null, httpParamDesc); + av2.visit("name", (String) ps[1]); + if (((Parameter) ps[0]).getAnnotation(Deprecated.class) != null) { + av2.visit("deprecated", true); + } + av2.visit("type", Type.getType(Type.getDescriptor((Class) ps[2]))); + java.lang.reflect.Type pgtype = TypeToken.getGenericType(((Parameter) ps[0]).getParameterizedType(), serviceType); + if (pgtype != (Class) ps[2]) { + String refid = typeRefs.get(pgtype); + if (refid == null) { + refid = "_typeref_" + typeRefs.size(); + typeRefs.put(pgtype, refid); + } + av2.visit("typeref", refid); + } + av2.visit("radix", (Integer) ps[3]); + if (ishead) { + av2.visitEnum("style", sourcetypeDesc, HttpParam.HttpParameterStyle.HEADER.name()); + av2.visit("example", ((RestHeader) ps[9]).example()); + } else if (iscookie) { + av2.visitEnum("style", sourcetypeDesc, HttpParam.HttpParameterStyle.COOKIE.name()); + av2.visit("example", ((RestCookie) ps[10]).example()); + } else if (isbody) { + av2.visitEnum("style", sourcetypeDesc, HttpParam.HttpParameterStyle.BODY.name()); + av2.visit("example", ((RestBody) ps[11]).example()); + } else if (ps[6] != null) { + av2.visitEnum("style", sourcetypeDesc, HttpParam.HttpParameterStyle.QUERY.name()); + av2.visit("example", ((RestParam) ps[6]).example()); + } + av2.visit("comment", (String) ps[4]); + av2.visit("required", (Boolean) ps[5]); + av2.visitEnd(); + } + av1.visitEnd(); + av0.visitEnd(); + } + int uploadLocal = 0; + if (mupload != null) { //存在文件上传 + if (muploadType == byte[].class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false); + mv.visitLdcInsn(mupload.maxLength()); + mv.visitLdcInsn(mupload.fileNameReg()); + mv.visitLdcInsn(mupload.contentTypeReg()); + mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstBytes", "(JLjava/lang/String;Ljava/lang/String;)[B", false); + mv.visitVarInsn(ASTORE, maxLocals); + uploadLocal = maxLocals; + } else if (muploadType == File.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;"); + mv.visitLdcInsn(mupload.maxLength()); + mv.visitLdcInsn(mupload.fileNameReg()); + mv.visitLdcInsn(mupload.contentTypeReg()); + mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFirstFile", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)Ljava/io/File;", false); + mv.visitVarInsn(ASTORE, maxLocals); + uploadLocal = maxLocals; + } else if (muploadType == File[].class) { //File[] + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMultiContext", "()" + multiContextDesc, false); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "_redkale_home", "Ljava/io/File;"); + mv.visitLdcInsn(mupload.maxLength()); + mv.visitLdcInsn(mupload.fileNameReg()); + mv.visitLdcInsn(mupload.contentTypeReg()); + mv.visitMethodInsn(INVOKEVIRTUAL, multiContextName, "partsFiles", "(Ljava/io/File;JLjava/lang/String;Ljava/lang/String;)[Ljava/io/File;", false); + mv.visitVarInsn(ASTORE, maxLocals); + uploadLocal = maxLocals; + } + maxLocals++; + } + + List> paramMaps = new ArrayList<>(); + //获取每个参数的值 + boolean hasAsyncHandler = false; + for (Object[] ps : paramlist) { + Map paramMap = new LinkedHashMap<>(); + final Parameter param = (Parameter) ps[0]; //参数类型 + String pname = (String) ps[1]; //参数名 + Class ptype = (Class) ps[2]; //参数类型 + int radix = (Integer) ps[3]; + String comment = (String) ps[4]; + boolean required = (Boolean) ps[5]; + RestParam annpara = (RestParam) ps[6]; + RestSessionid annsid = (RestSessionid) ps[7]; + RestAddress annaddr = (RestAddress) ps[8]; + RestHeader annhead = (RestHeader) ps[9]; + RestCookie anncookie = (RestCookie) ps[10]; + RestBody annbody = (RestBody) ps[11]; + RestUploadFile annfile = (RestUploadFile) ps[12]; + RestURI annuri = (RestURI) ps[13]; + RestUserid userid = (RestUserid) ps[14]; + RestHeaders annheaders = (RestHeaders) ps[15]; + RestParams annparams = (RestParams) ps[16]; + java.lang.reflect.Type pgentype = (java.lang.reflect.Type) ps[17]; + if (dynsimple && (annsid != null || annaddr != null || annhead != null || anncookie != null || annfile != null || annheaders != null)) dynsimple = false; + + final boolean ishead = annhead != null; //是否取getHeader 而不是 getParameter + final boolean iscookie = anncookie != null; //是否取getCookie + + paramMap.put("name", pname); + paramMap.put("type", ptype.getName()); + if (CompletionHandler.class.isAssignableFrom(ptype)) { //HttpResponse.createAsyncHandler() or HttpResponse.createAsyncHandler(Class) + if (ptype == CompletionHandler.class) { + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "()Ljava/nio/channels/CompletionHandler;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else { + mv.visitVarInsn(ALOAD, 3); + mv.visitVarInsn(ALOAD, 2); + mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype))); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "createAsyncHandler", "(Ljava/lang/Class;)Ljava/nio/channels/CompletionHandler;", false); + mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/')); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } + hasAsyncHandler = true; + } else if (annsid != null) { //HttpRequest.getSessionid(true|false) + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(annsid.create() ? ICONST_1 : ICONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (annaddr != null) { //HttpRequest.getRemoteAddr + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (annheaders != null) { //HttpRequest.getHeaders + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeaders", "()Ljava/util/Map;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (annparams != null) { //HttpRequest.getParameters + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getParameters", "()Ljava/util/Map;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (annbody != null) { //HttpRequest.getBodyUTF8 / HttpRequest.getBody + if (ptype == String.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (ptype == byte[].class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else { //JavaBean 转 Json + String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); + bodyTypes.put(typefieldname, pgentype); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype)); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } + } else if (annfile != null) { //MultiContext.partsFirstBytes / HttpRequest.partsFirstFile / HttpRequest.partsFiles + mv.visitVarInsn(ALOAD, 4); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (annuri != null) { //HttpRequest.getRequestURI + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (userid != null) { //HttpRequest.currentUserid + mv.visitVarInsn(ALOAD, 1); + if (ptype == int.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); + } else if (ptype == long.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); + } else { + mv.visitLdcInsn(Type.getType(Type.getInternalName(ptype))); + } + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "currentUserid", "(Ljava/lang/Class;)Ljava/io/Serializable;", false); + if (ptype == int.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false); + + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == long.class) { + mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); + mv.visitInsn(LCONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + mv.visitMethodInsn(INVOKESTATIC, restInternalName, "orElse", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false); + + mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); + mv.visitVarInsn(LSTORE, maxLocals); + varInsns.add(new int[]{LLOAD, maxLocals}); + maxLocals++; + } else { + mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype)); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } + } else if ("#".equals(pname)) { //从request.getRequstURI 中取参数 + if (ptype == boolean.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == byte.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == short.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "parseShort", "(Ljava/lang/String;I)S", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == char.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == int.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;I)I", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == float.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false); + mv.visitVarInsn(FSTORE, maxLocals); + varInsns.add(new int[]{FLOAD, maxLocals}); + } else if (ptype == long.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;I)J", false); + mv.visitVarInsn(LSTORE, maxLocals); + varInsns.add(new int[]{LLOAD, maxLocals}); + maxLocals++; + } else if (ptype == double.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false); + mv.visitVarInsn(DSTORE, maxLocals); + varInsns.add(new int[]{DLOAD, maxLocals}); + maxLocals++; + } else if (ptype == String.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURILastPath", "()Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else { + throw new RuntimeException(method + " only " + RestParam.class.getSimpleName() + "(#) to Type(primitive class or String)"); + } + } else if (pname.charAt(0) == '#') { //从request.getRequstURIPath 中去参数 + if (ptype == boolean.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("false"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "parseBoolean", "(Ljava/lang/String;)Z", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == byte.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == short.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "parseShort", "(Ljava/lang/String;I)S", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == char.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == int.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "parseInt", "(Ljava/lang/String;I)I", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == float.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "parseFloat", "(Ljava/lang/String;)F", false); + mv.visitVarInsn(FSTORE, maxLocals); + varInsns.add(new int[]{FLOAD, maxLocals}); + } else if (ptype == long.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "parseLong", "(Ljava/lang/String;I)J", false); + mv.visitVarInsn(LSTORE, maxLocals); + varInsns.add(new int[]{LLOAD, maxLocals}); + maxLocals++; + } else if (ptype == double.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "parseDouble", "(Ljava/lang/String;)D", false); + mv.visitVarInsn(DSTORE, maxLocals); + varInsns.add(new int[]{DLOAD, maxLocals}); + maxLocals++; + } else if (ptype == String.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname.substring(1)); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequstURIPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else { + throw new RuntimeException(method + " only " + RestParam.class.getSimpleName() + "(#) to Type(primitive class or String)"); + } + } else if ("&".equals(pname) && ptype == userType) { //当前用户对象的类名 + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "currentUser", "()Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName(ptype)); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (ptype == boolean.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getBooleanHeader" : "getBooleanParameter", "(Ljava/lang/String;Z)Z", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == byte.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getHeader" : "getParameter", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitIntInsn(BIPUSH, radix); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "parseByte", "(Ljava/lang/String;I)B", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == short.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitIntInsn(BIPUSH, radix); + mv.visitLdcInsn(pname); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getShortHeader" : "getShortParameter", "(ILjava/lang/String;S)S", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == char.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname); + mv.visitLdcInsn("0"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getHeader" : "getParameter", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/String", "charAt", "(I)C", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == int.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitIntInsn(BIPUSH, radix); + mv.visitLdcInsn(pname); + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getIntHeader" : "getIntParameter", "(ILjava/lang/String;I)I", false); + mv.visitVarInsn(ISTORE, maxLocals); + varInsns.add(new int[]{ILOAD, maxLocals}); + } else if (ptype == float.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname); + mv.visitInsn(FCONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getFloatHeader" : "getFloatParameter", "(Ljava/lang/String;F)F", false); + mv.visitVarInsn(FSTORE, maxLocals); + varInsns.add(new int[]{FLOAD, maxLocals}); + } else if (ptype == long.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitIntInsn(BIPUSH, radix); + mv.visitLdcInsn(pname); + mv.visitInsn(LCONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getLongHeader" : "getLongParameter", "(ILjava/lang/String;J)J", false); + mv.visitVarInsn(LSTORE, maxLocals); + varInsns.add(new int[]{LLOAD, maxLocals}); + maxLocals++; + } else if (ptype == double.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname); + mv.visitInsn(DCONST_0); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getDoubleHeader" : "getDoubleParameter", "(Ljava/lang/String;D)D", false); + mv.visitVarInsn(DSTORE, maxLocals); + varInsns.add(new int[]{DLOAD, maxLocals}); + maxLocals++; + } else if (ptype == String.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(pname); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, iscookie ? "getCookie" : (ishead ? "getHeader" : "getParameter"), "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (ptype == ChannelContext.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getChannelContext", "()" + channelDesc, false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else if (ptype == Flipper.class) { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getFlipper", "()" + flipperDesc, false); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + } else { //其他Json对象 + mv.visitVarInsn(ALOAD, 1); + if (param.getType() == param.getParameterizedType()) { + mv.visitLdcInsn(Type.getType(Type.getDescriptor(ptype))); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + int paramidx = -1; + for (int i = 0; i < params.length; i++) { + if (params[i] == param) { + paramidx = i; + break; + } + } + MethodDebugVisitor.pushInt(mv, paramidx); //参数下标 + mv.visitInsn(AALOAD); + } + mv.visitLdcInsn(pname); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, ishead ? "getJsonHeader" : "getJsonParameter", "(Ljava/lang/reflect/Type;Ljava/lang/String;)Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, ptype.getName().replace('.', '/')); + mv.visitVarInsn(ASTORE, maxLocals); + varInsns.add(new int[]{ALOAD, maxLocals}); + JsonFactory.root().loadDecoder(param.getParameterizedType()); + + //构建 RestHeader、RestCookie、RestAddress 等赋值操作 + Class loop = ptype; + Set fields = new HashSet<>(); + Map attrParaNames = new LinkedHashMap<>(); + do { + if (loop == null || loop.isInterface()) break; //接口时getSuperclass可能会得到null + for (Field field : loop.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (Modifier.isFinal(field.getModifiers())) continue; + if (fields.contains(field.getName())) continue; + RestHeader rh = field.getAnnotation(RestHeader.class); + RestCookie rc = field.getAnnotation(RestCookie.class); + RestSessionid rs = field.getAnnotation(RestSessionid.class); + RestAddress ra = field.getAnnotation(RestAddress.class); + RestBody rb = field.getAnnotation(RestBody.class); + RestUploadFile ru = field.getAnnotation(RestUploadFile.class); + RestURI ri = field.getAnnotation(RestURI.class); + if (rh == null && rc == null && ra == null && rb == null && rs == null && ru == null && ri == null) continue; + if (rh != null && field.getType() != String.class && field.getType() != InetSocketAddress.class) throw new RuntimeException("@RestHeader must on String Field in " + field); + if (rc != null && field.getType() != String.class) throw new RuntimeException("@RestCookie must on String Field in " + field); + if (rs != null && field.getType() != String.class) throw new RuntimeException("@RestSessionid must on String Field in " + field); + if (ra != null && field.getType() != String.class) throw new RuntimeException("@RestAddress must on String Field in " + field); + if (rb != null && field.getType().isPrimitive()) throw new RuntimeException("@RestBody must on cannot on primitive type Field in " + field); + if (ru != null && field.getType() != byte[].class && field.getType() != File.class && field.getType() != File[].class) { + throw new RuntimeException("@RestUploadFile must on byte[] or File or File[] Field in " + field); + } + + if (ri != null && field.getType() != String.class) throw new RuntimeException("@RestURI must on String Field in " + field); + org.redkale.util.Attribute attr = org.redkale.util.Attribute.create(loop, field); + String attrFieldName; + String restname = ""; + if (rh != null) { + attrFieldName = "_redkale_attr_header_" + (field.getType() != String.class ? "json_" : "") + restAttributes.size(); + restname = rh.name(); + } else if (rc != null) { + attrFieldName = "_redkale_attr_cookie_" + restAttributes.size(); + restname = rc.name(); + } else if (rs != null) { + attrFieldName = "_redkale_attr_sessionid_" + restAttributes.size(); + restname = rs.create() ? "1" : ""; //用于下面区分create值 + } else if (ra != null) { + attrFieldName = "_redkale_attr_address_" + restAttributes.size(); + //restname = ""; + } else if (rb != null && field.getType() == String.class) { + attrFieldName = "_redkale_attr_bodystring_" + restAttributes.size(); + //restname = ""; + } else if (rb != null && field.getType() == byte[].class) { + attrFieldName = "_redkale_attr_bodybytes_" + restAttributes.size(); + //restname = ""; + } else if (rb != null && field.getType() != String.class && field.getType() != byte[].class) { + attrFieldName = "_redkale_attr_bodyjson_" + restAttributes.size(); + //restname = ""; + } else if (ru != null && field.getType() == byte[].class) { + attrFieldName = "_redkale_attr_uploadbytes_" + restAttributes.size(); + //restname = ""; + } else if (ru != null && field.getType() == File.class) { + attrFieldName = "_redkale_attr_uploadfile_" + restAttributes.size(); + //restname = ""; + } else if (ru != null && field.getType() == File[].class) { + attrFieldName = "_redkale_attr_uploadfiles_" + restAttributes.size(); + //restname = ""; + } else if (ri != null && field.getType() == String.class) { + attrFieldName = "_redkale_attr_uri_" + restAttributes.size(); + //restname = ""; + } else { + continue; + } + restAttributes.put(attrFieldName, attr); + attrParaNames.put(attrFieldName, new Object[]{restname, field.getType(), field.getGenericType(), ru}); + fields.add(field.getName()); + } + } while ((loop = loop.getSuperclass()) != Object.class); + + if (!attrParaNames.isEmpty()) { //参数存在 RestHeader、RestCookie、RestSessionid、RestAddress、RestBody字段 + mv.visitVarInsn(ALOAD, maxLocals); //加载JsonBean + Label lif = new Label(); + mv.visitJumpInsn(IFNULL, lif); //if(bean != null) { + for (Map.Entry en : attrParaNames.entrySet()) { + RestUploadFile ru = (RestUploadFile) en.getValue()[3]; + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, en.getKey(), attrDesc); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitVarInsn(ALOAD, en.getKey().contains("_upload") ? uploadLocal : 1); + if (en.getKey().contains("_header_")) { + String headerkey = en.getValue()[0].toString(); + if ("Host".equalsIgnoreCase(headerkey)) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHost", "()Ljava/lang/String;", false); + } else if ("Content-Type".equalsIgnoreCase(headerkey)) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getContentType", "()Ljava/lang/String;", false); + } else if ("Connection".equalsIgnoreCase(headerkey)) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getConnection", "()Ljava/lang/String;", false); + } else if ("Method".equalsIgnoreCase(headerkey)) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getMethod", "()Ljava/lang/String;", false); + } else if (en.getKey().contains("_header_json_")) { + String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); + bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); + mv.visitLdcInsn(headerkey); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getJsonHeader", "(Ljava/lang/reflect/Type;Ljava/lang/String;)Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName((Class) en.getValue()[1])); + JsonFactory.root().loadDecoder((java.lang.reflect.Type) en.getValue()[2]); + } else { + mv.visitLdcInsn(headerkey); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getHeader", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + } + } else if (en.getKey().contains("_cookie_")) { + mv.visitLdcInsn(en.getValue()[0].toString()); + mv.visitLdcInsn(""); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getCookie", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;", false); + } else if (en.getKey().contains("_sessionid_")) { + mv.visitInsn(en.getValue()[0].toString().isEmpty() ? ICONST_0 : ICONST_1); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getSessionid", "(Z)Ljava/lang/String;", false); + } else if (en.getKey().contains("_address_")) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRemoteAddr", "()Ljava/lang/String;", false); + } else if (en.getKey().contains("_uri_")) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getRequestURI", "()Ljava/lang/String;", false); + } else if (en.getKey().contains("_bodystring_")) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyUTF8", "()Ljava/lang/String;", false); + } else if (en.getKey().contains("_bodybytes_")) { + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBody", "()[B", false); + } else if (en.getKey().contains("_bodyjson_")) {//JavaBean 转 Json + String typefieldname = "_redkale_body_jsontype_" + bodyTypes.size(); + bodyTypes.put(typefieldname, (java.lang.reflect.Type) en.getValue()[2]); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, typefieldname, "Ljava/lang/reflect/Type;"); + mv.visitMethodInsn(INVOKEVIRTUAL, reqInternalName, "getBodyJson", "(Ljava/lang/reflect/Type;)Ljava/lang/Object;", false); + mv.visitTypeInsn(CHECKCAST, Type.getInternalName((Class) en.getValue()[1])); + JsonFactory.root().loadDecoder((java.lang.reflect.Type) en.getValue()[2]); + } else if (en.getKey().contains("_uploadbytes_")) { + //只需mv.visitVarInsn(ALOAD, 4), 无需处理 + } else if (en.getKey().contains("_uploadfile_")) { + //只需mv.visitVarInsn(ALOAD, 4), 无需处理 + } else if (en.getKey().contains("_uploadfiles_")) { + //只需mv.visitVarInsn(ALOAD, 4), 无需处理 + } + mv.visitMethodInsn(INVOKEINTERFACE, attrInternalName, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); + } + mv.visitLabel(lif); // end if } + mv.visitFrame(Opcodes.F_APPEND, 1, new Object[]{ptype.getName().replace('.', '/')}, 0, null); + } + } + maxLocals++; + paramMaps.add(paramMap); + } // end params for each + + //mv.visitVarInsn(ALOAD, 0); //调用this + //mv.visitFieldInsn(GETFIELD, newDynName, REST_SERVICE_FIELD_NAME, serviceDesc); + mv.visitVarInsn(ALOAD, 3); + for (int[] ins : varInsns) { + mv.visitVarInsn(ins[0], ins[1]); + } + mv.visitMethodInsn(INVOKEVIRTUAL, serviceTypeInternalName, method.getName(), methodDesc, false); + if (hasAsyncHandler) { + mv.visitInsn(RETURN); + } else if (returnType == void.class) { + mv.visitVarInsn(ALOAD, 2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitMethodInsn(INVOKESTATIC, retInternalName, "success", "()" + retDesc, false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + typeDesc + retDesc + ")V", false); + mv.visitInsn(RETURN); + } else if (returnType == boolean.class) { + mv.visitVarInsn(ISTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ILOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Z)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == byte.class) { + mv.visitVarInsn(ISTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ILOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == short.class) { + mv.visitVarInsn(ISTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ILOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == char.class) { + mv.visitVarInsn(ISTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ILOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(C)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == int.class) { + mv.visitVarInsn(ISTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ILOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(I)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == float.class) { + mv.visitVarInsn(FSTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(FLOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(F)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == long.class) { + mv.visitVarInsn(LSTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(LLOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(J)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals += 2; + } else if (returnType == double.class) { + mv.visitVarInsn(DSTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(DLOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(D)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals += 2; + } else if (returnType == byte[].class) { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "([B)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == String.class) { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == File.class) { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/io/File;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (Number.class.isAssignableFrom(returnType) || CharSequence.class.isAssignableFrom(returnType)) { //returnType == String.class 必须放在前面 + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(Ljava/lang/String;)V", false); + mv.visitInsn(RETURN); + maxLocals++; + } else if (RetResult.class.isAssignableFrom(returnType)) { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + typeDesc + retDesc + ")V", false); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + typeDesc + retDesc + ")V", false); + } + mv.visitInsn(RETURN); + maxLocals++; + } else if (HttpResult.class.isAssignableFrom(returnType)) { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + typeDesc + httpResultDesc + ")V", false); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + typeDesc + httpResultDesc + ")V", false); + } + mv.visitInsn(RETURN); + maxLocals++; + } else if (HttpScope.class.isAssignableFrom(returnType)) { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + httpScopeDesc + ")V", false); + } else { + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + httpScopeDesc + ")V", false); + } + mv.visitInsn(RETURN); + maxLocals++; + } else if (CompletionStage.class.isAssignableFrom(returnType)) { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + typeDesc + stageDesc + ")V", false); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + typeDesc + stageDesc + ")V", false); + } + mv.visitInsn(RETURN); + maxLocals++; + } else if (Flows.maybePublisherClass(returnType)) { //Flow.Publisher + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishPublisher", "(" + convertDesc + typeDesc + "Ljava/lang/Object;)V", false); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishPublisher", "(" + typeDesc + "Ljava/lang/Object;)V", false); + } + mv.visitInsn(RETURN); + maxLocals++; + } else if (returnType == retvalType) { //普通JavaBean或JavaBean[] + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + Type.getDescriptor(JsonConvert.class) + typeDesc + "Ljava/lang/Object;)V", false); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finishJson", "(" + typeDesc + "Ljava/lang/Object;)V", false); + } + mv.visitInsn(RETURN); + maxLocals++; + } else { + mv.visitVarInsn(ASTORE, maxLocals); + mv.visitVarInsn(ALOAD, 2); //response + if (rcs != null && rcs.length > 0) { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_CONVERT_FIELD_PREFIX + restConverts.size(), convertDesc); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + convertDesc + typeDesc + "Ljava/lang/Object;)V", false); + } else { + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;"); + MethodDebugVisitor.pushInt(mv, entry.methodidx);//方法下标 + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, maxLocals); + mv.visitMethodInsn(INVOKEVIRTUAL, respInternalName, "finish", "(" + typeDesc + "Ljava/lang/Object;)V", false); + } + mv.visitInsn(RETURN); + maxLocals++; + } + mv.visitMaxs(maxStack, maxLocals); + mappingMap.put("params", paramMaps); + + { //_Dync_XXX__HttpServlet.class + ClassWriter cw2 = new ClassWriter(COMPUTE_FRAMES); + cw2.visit(V11, ACC_SUPER, newDynName + "$" + entry.newActionClassName, null, httpServletName, null); + + cw2.visitInnerClass(newDynName + "$" + entry.newActionClassName, newDynName, entry.newActionClassName, ACC_PRIVATE + ACC_STATIC); + { + fv = cw2.visitField(0, "servlet", "L" + newDynName + ";", null, null); + fv.visitEnd(); + } + { + mv = new MethodDebugVisitor(cw2.visitMethod(0, "", "(L" + newDynName + ";)V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, httpServletName, "", "()V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, newDynName + "$" + entry.newActionClassName, "servlet", "L" + newDynName + ";"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } +// if (false) { +// mv = new MethodDebugVisitor(cw2.visitMethod(ACC_SYNTHETIC, "", "(L" + newDynName + ";L" + newDynName + "$" + entry.newActionClassName + ";)V", null, null)); +// mv.visitVarInsn(ALOAD, 0); +// mv.visitVarInsn(ALOAD, 1); +// mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "", "L" + newDynName + ";", false); +// mv.visitInsn(RETURN); +// mv.visitMaxs(2, 3); +// mv.visitEnd(); +// } + { + mv = new MethodDebugVisitor(cw2.visitMethod(ACC_PUBLIC, "execute", "(" + reqDesc + respDesc + ")V", null, new String[]{"java/io/IOException"})); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName + "$" + entry.newActionClassName, "servlet", "L" + newDynName + ";"); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, entry.newMethodName, "(" + reqDesc + respDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw2.visitEnd(); + byte[] bytes = cw2.toByteArray(); + newLoader.addClass((newDynName + "$" + entry.newActionClassName).replace('/', '.'), bytes); + innerClassBytesMap.put((newDynName + "$" + entry.newActionClassName).replace('/', '.'), bytes); + } + } // end for each + +// HashMap _createRestActionEntry() { +// HashMap map = new HashMap<>(); +// map.put("asyncfind3", new ActionEntry(100000,200000,"asyncfind3", new String[]{},null,false,false,0, new _Dync_asyncfind3_HttpServlet())); +// map.put("asyncfind2", new ActionEntry(1,2,"asyncfind2", new String[]{"GET", "POST"},null,false,true,0, new _Dync_asyncfind2_HttpServlet())); +// return map; +// } + { //_createRestActionEntry 方法 + mv = new MethodDebugVisitor(cw.visitMethod(0, "_createRestActionEntry", "()Ljava/util/HashMap;", "()Ljava/util/HashMap;", null)); + //mv.setDebug(true); + mv.visitTypeInsn(NEW, "java/util/HashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "", "()V", false); + mv.visitVarInsn(ASTORE, 1); + + for (final MappingEntry entry : entrys) { + mappingurlToMethod.put(entry.mappingurl, entry.mappingMethod); + mv.visitVarInsn(ALOAD, 1); + mv.visitLdcInsn(entry.mappingurl); //name + mv.visitTypeInsn(NEW, actionEntryName); //new ActionEntry + mv.visitInsn(DUP); + MethodDebugVisitor.pushInt(mv, moduleid); //moduleid + MethodDebugVisitor.pushInt(mv, entry.actionid); //actionid + mv.visitLdcInsn(entry.mappingurl); //name + MethodDebugVisitor.pushInt(mv, entry.methods.length); //methods + mv.visitTypeInsn(ANEWARRAY, "java/lang/String"); + for (int i = 0; i < entry.methods.length; i++) { + mv.visitInsn(DUP); + MethodDebugVisitor.pushInt(mv, i); + mv.visitLdcInsn(entry.methods[i]); + mv.visitInsn(AASTORE); + } + mv.visitInsn(ACONST_NULL); //method + mv.visitInsn(entry.rpconly ? ICONST_1 : ICONST_0); //rpconly + mv.visitInsn(entry.auth ? ICONST_1 : ICONST_0); //auth + MethodDebugVisitor.pushInt(mv, entry.cacheseconds); //cacheseconds + mv.visitTypeInsn(NEW, newDynName + "$" + entry.newActionClassName); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, newDynName + "$" + entry.newActionClassName, "", "(L" + newDynName + ";)V", false); + mv.visitMethodInsn(INVOKESPECIAL, actionEntryName, "", "(IILjava/lang/String;[Ljava/lang/String;Ljava/lang/reflect/Method;ZZILorg/redkale/net/http/HttpServlet;)V", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/HashMap", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", false); + mv.visitInsn(POP); + } + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + + for (Map.Entry en : bodyTypes.entrySet()) { + fv = cw.visitField(ACC_PRIVATE, en.getKey(), "Ljava/lang/reflect/Type;", null, null); + av0 = fv.visitAnnotation(Type.getDescriptor(Comment.class), true); + av0.visit("value", en.getValue().toString()); + av0.visitEnd(); + fv.visitEnd(); + } + + for (Map.Entry en : typeRefs.entrySet()) { + fv = cw.visitField(ACC_PRIVATE, en.getValue(), "Ljava/lang/reflect/Type;", null, null); + av0 = fv.visitAnnotation(Type.getDescriptor(Comment.class), true); + av0.visit("value", en.getKey().toString()); + av0.visitEnd(); + fv.visitEnd(); + } + + for (Map.Entry en : restAttributes.entrySet()) { + fv = cw.visitField(ACC_PRIVATE, en.getKey(), attrDesc, null, null); + av0 = fv.visitAnnotation(Type.getDescriptor(Comment.class), true); + av0.visit("value", en.getValue().toString()); + av0.visitEnd(); + fv.visitEnd(); + } + + for (int i = 1; i <= restConverts.size(); i++) { + fv = cw.visitField(ACC_PRIVATE, REST_CONVERT_FIELD_PREFIX + i, convertDesc, null, null); + fv.visitEnd(); + } + + { //_paramtypes字段 java.lang.reflect.Type[][] + fv = cw.visitField(ACC_PRIVATE, REST_PARAMTYPES_FIELD_NAME, "[[Ljava/lang/reflect/Type;", null, null); + av0 = fv.visitAnnotation(Type.getDescriptor(Comment.class), true); + StringBuilder sb = new StringBuilder().append('['); + for (java.lang.reflect.Type[] rs : paramTypes) { + sb.append(Arrays.toString(rs)).append(','); + } + av0.visit("value", sb.append(']').toString()); + av0.visitEnd(); + fv.visitEnd(); + } + { //_returntypes字段 java.lang.reflect.Type[] + fv = cw.visitField(ACC_PRIVATE, REST_RETURNTYPES_FIELD_NAME, "[Ljava/lang/reflect/Type;", null, null); + av0 = fv.visitAnnotation(Type.getDescriptor(Comment.class), true); + av0.visit("value", retvalTypes.toString()); + av0.visitEnd(); + fv.visitEnd(); + } + + //classMap.put("mappings", mappingMaps); //不显示太多信息 + { //toString函数 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, REST_TOSTRINGOBJ_FIELD_NAME, "Ljava/util/function/Supplier;"); + mv.visitMethodInsn(INVOKEINTERFACE, "java/util/function/Supplier", "get", "()Ljava/lang/Object;", true); + mv.visitTypeInsn(CHECKCAST, "java/lang/String"); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + { //RestDyn + av0 = cw.visitAnnotation(Type.getDescriptor(RestDyn.class), true); + av0.visit("simple", (Boolean) dynsimple); + av0.visitEnd(); + } + + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + newLoader.addClass(newDynName.replace('/', '.'), bytes); + try { + Class newClazz = newLoader.findClass(newDynName.replace('/', '.')); + innerClassBytesMap.forEach((n, bs) -> { + try { + RedkaleClassLoader.putDynClass(n, bs, newLoader.findClass(n)); + RedkaleClassLoader.putReflectionClass(n); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + for (java.lang.reflect.Type t : retvalTypes) { + JsonFactory.root().loadEncoder(t); + } + + T obj = ((Class) newClazz).getDeclaredConstructor().newInstance(); + { + Field serviceField = newClazz.getDeclaredField(REST_SERVICE_FIELD_NAME); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), serviceField); + Field servicemapField = newClazz.getDeclaredField(REST_SERVICEMAP_FIELD_NAME); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), servicemapField); + } + for (Map.Entry en : typeRefs.entrySet()) { + Field refField = newClazz.getDeclaredField(en.getValue()); + refField.setAccessible(true); + refField.set(obj, en.getKey()); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), refField); + } + for (Map.Entry en : restAttributes.entrySet()) { + Field attrField = newClazz.getDeclaredField(en.getKey()); + attrField.setAccessible(true); + attrField.set(obj, en.getValue()); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), attrField); + } + for (Map.Entry en : bodyTypes.entrySet()) { + Field genField = newClazz.getDeclaredField(en.getKey()); + genField.setAccessible(true); + genField.set(obj, en.getValue()); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), genField); + } + for (int i = 0; i < restConverts.size(); i++) { + Field genField = newClazz.getDeclaredField(REST_CONVERT_FIELD_PREFIX + (i + 1)); + genField.setAccessible(true); + Object[] rc = restConverts.get(i); + genField.set(obj, createJsonConvert((RestConvert[]) rc[0], (RestConvertCoder[]) rc[1])); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), genField); + } + Field typesfield = newClazz.getDeclaredField(REST_PARAMTYPES_FIELD_NAME); + typesfield.setAccessible(true); + java.lang.reflect.Type[][] paramtypeArray = new java.lang.reflect.Type[paramTypes.size()][]; + paramtypeArray = paramTypes.toArray(paramtypeArray); + typesfield.set(obj, paramtypeArray); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), typesfield); + + Field retfield = newClazz.getDeclaredField(REST_RETURNTYPES_FIELD_NAME); + retfield.setAccessible(true); + java.lang.reflect.Type[] rettypeArray = new java.lang.reflect.Type[retvalTypes.size()]; + rettypeArray = retvalTypes.toArray(rettypeArray); + retfield.set(obj, rettypeArray); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), retfield); + + Field tostringfield = newClazz.getDeclaredField(REST_TOSTRINGOBJ_FIELD_NAME); + tostringfield.setAccessible(true); + java.util.function.Supplier sSupplier = () -> JsonConvert.root().convertTo(classMap); + tostringfield.set(obj, sSupplier); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), tostringfield); + + Method restactMethod = newClazz.getDeclaredMethod("_createRestActionEntry"); + restactMethod.setAccessible(true); + RedkaleClassLoader.putReflectionMethod(newDynName.replace('/', '.'), restactMethod); + Field tmpentrysfield = HttpServlet.class.getDeclaredField("_actionmap"); + tmpentrysfield.setAccessible(true); + HashMap innerEntryMap = (HashMap) restactMethod.invoke(obj); + for (Map.Entry en : innerEntryMap.entrySet()) { + Method m = mappingurlToMethod.get(en.getKey()); + if (m != null) en.getValue().annotations = HttpServlet.ActionEntry.annotations(m); + } + tmpentrysfield.set(obj, innerEntryMap); + RedkaleClassLoader.putReflectionField(HttpServlet.class.getName(), tmpentrysfield); + return obj; + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + private static java.lang.reflect.Type formatRestReturnType(Method method, Class serviceType) { + final Class returnType = method.getReturnType(); + java.lang.reflect.Type t = TypeToken.getGenericType(method.getGenericReturnType(), serviceType); + if (method.getReturnType() == void.class) { + return RetResult.TYPE_RET_STRING; + } else if (HttpResult.class.isAssignableFrom(returnType)) { + if (!(t instanceof ParameterizedType)) return Object.class; + ParameterizedType pt = (ParameterizedType) t; + return pt.getActualTypeArguments()[0]; + } else if (CompletionStage.class.isAssignableFrom(returnType)) { + ParameterizedType pt = (ParameterizedType) t; + java.lang.reflect.Type grt = pt.getActualTypeArguments()[0]; + Class gct = TypeToken.typeToClass(grt); + if (HttpResult.class.isAssignableFrom(gct)) { + if (!(grt instanceof ParameterizedType)) return Object.class; + ParameterizedType pt2 = (ParameterizedType) grt; + return pt2.getActualTypeArguments()[0]; + } else if (gct == void.class || gct == Void.class) { + return RetResult.TYPE_RET_STRING; + } else { + return grt; + } + } else if (Flows.maybePublisherClass(returnType)) { + return Flows.maybePublisherSubType(t); + } + return t; + } + + private static boolean checkName(String name) { //不能含特殊字符 + if (name.isEmpty()) return true; + if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false; + for (char ch : name.toCharArray()) { + if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 + return false; + } + } + return true; + } + + private static boolean checkName2(String name) { //不能含特殊字符 + if (name.isEmpty()) return true; + if (name.charAt(0) >= '0' && name.charAt(0) <= '9') return false; + for (char ch : name.toCharArray()) { + if (!((ch >= '0' && ch <= '9') || ch == '_' || ch == '-' || ch == '.' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) { //不能含特殊字符 + return false; + } + } + return true; + } + + private static class RestClassLoader extends ClassLoader { + + private Map classes = new HashMap<>(); + + public RestClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + byte[] classData = classes.get(name); + if (classData == null) return super.findClass(name); + return super.defineClass(name, classData, 0, classData.length); + } + + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + + public final void addClass(String name, byte[] b) { + classes.put(name, b); + } + } + + private static class MappingEntry implements Comparable { + + private static final RestMapping DEFAULT__MAPPING; + + static { + try { + DEFAULT__MAPPING = MappingEntry.class.getDeclaredMethod("mapping").getAnnotation(RestMapping.class); + } catch (Exception e) { + throw new Error(e); + } + } + + public MappingEntry(final boolean serrpconly, int methodidx, RestMapping mapping, final String defmodulename, Method method) { + if (mapping == null) mapping = DEFAULT__MAPPING; + this.methodidx = methodidx; + this.ignore = mapping.ignore(); + String n = mapping.name(); + if (n.isEmpty()) n = method.getName(); + this.name = n.trim(); + this.example = mapping.example(); + this.mappingMethod = method; + this.methods = mapping.methods(); + this.auth = mapping.auth(); + this.rpconly = serrpconly || mapping.rpconly(); + this.actionid = mapping.actionid(); + this.cacheseconds = mapping.cacheseconds(); + this.comment = mapping.comment(); + boolean pound = false; + Parameter[] params = method.getParameters(); + for (Parameter param : params) { + RestParam rp = param.getAnnotation(RestParam.class); + if (rp != null && !rp.name().isEmpty() && rp.name().charAt(0) == '#') { + pound = true; + break; + } + } + this.existsPound = pound; + this.newMethodName = this.name.replace('/', '$').replace('.', '_'); + this.newActionClassName = "_Dyn_" + this.newMethodName + "_ActionHttpServlet"; + } + + public final int methodidx; // _paramtypes 的下标,从0开始 + + public final Method mappingMethod; + + public final boolean ignore; + + public final String newMethodName; + + public final String newActionClassName; + + public final String name; + + public final String example; + + public final String comment; + + public final String[] methods; + + public final boolean rpconly; + + public final boolean auth; + + public final int actionid; + + public final int cacheseconds; + + public final boolean existsPound; //是否包含#的参数 + + String mappingurl; //在生成方法时赋值, 供 _createRestActionEntry 使用 + + @RestMapping() + void mapping() { //用于获取Mapping 默认值 + } + + @Override + public int hashCode() { + return this.name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + return this.name.equals(((MappingEntry) obj).name); + } + + @Override + public int compareTo(MappingEntry o) { + return this.name.compareTo(o.name); + } + + } +} diff --git a/src/org/redkale/net/http/RestAddress.java b/src/main/java/org/redkale/net/http/RestAddress.java similarity index 96% rename from src/org/redkale/net/http/RestAddress.java rename to src/main/java/org/redkale/net/http/RestAddress.java index 4530d4570..09127daf9 100644 --- a/src/org/redkale/net/http/RestAddress.java +++ b/src/main/java/org/redkale/net/http/RestAddress.java @@ -1,34 +1,34 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于Service类的方法的String参数或参数内的String字段 - *

    - * 用于获取HTTP请求端的IP地址 HttpRequest.getRemoteAddr - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestAddress { - - /** - * 备注描述, 对应@HttpParam.comment - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于Service类的方法的String参数或参数内的String字段 + *

    + * 用于获取HTTP请求端的IP地址 HttpRequest.getRemoteAddr + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestAddress { + + /** + * 备注描述, 对应@HttpParam.comment + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestBody.java b/src/main/java/org/redkale/net/http/RestBody.java similarity index 74% rename from src/org/redkale/net/http/RestBody.java rename to src/main/java/org/redkale/net/http/RestBody.java index fe7f96f44..8ab3e1bc4 100644 --- a/src/org/redkale/net/http/RestBody.java +++ b/src/main/java/org/redkale/net/http/RestBody.java @@ -1,34 +1,48 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于RestService类的方法的String/byte[]/JavaBean参数或参数内的String/byte[]/JavaBean字段 - *

    - * 用于获取HTTP请求端的请求内容UTF-8编码字符串、byte[]、JavaBean - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestBody { - - /** - * 备注描述, 对应@HttpParam.comment - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于RestService类的方法的String/byte[]/JavaBean参数或参数内的String/byte[]/JavaBean字段 + *

    + * 用于获取HTTP请求端的请求内容UTF-8编码字符串、byte[]、JavaBean + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestBody { + + /** + * 参数是否必传, 框架运行中不作验证, only for OpenAPI Specification 3 + * + * @return boolean + */ + boolean required() default true; + + /** + * for OpenAPI Specification 3.1.0 + * + * @return String + */ + String example() default ""; + + /** + * 备注描述, 对应@HttpParam.comment + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestConvert.java b/src/main/java/org/redkale/net/http/RestConvert.java similarity index 96% rename from src/org/redkale/net/http/RestConvert.java rename to src/main/java/org/redkale/net/http/RestConvert.java index 9ed108616..1f4f6d49e 100644 --- a/src/org/redkale/net/http/RestConvert.java +++ b/src/main/java/org/redkale/net/http/RestConvert.java @@ -1,46 +1,46 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。
    - * 注意: 如果 type() == void.class 则无视其他参数固定返回 JsonFactory.create().skipAllIgnore(true).getConvert(); - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -@Repeatable(RestConvert.RestConverts.class) -public @interface RestConvert { - - boolean tiny() default true; - - boolean skipIgnore() default false; - - Class type(); - - String[] ignoreColumns() default {}; - - String[] convertColumns() default {}; - - @Inherited - @Documented - @Target({METHOD}) - @Retention(RUNTIME) - @interface RestConverts { - - RestConvert[] value(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。
    + * 注意: 如果 type() == void.class 则无视其他参数固定返回 JsonFactory.create().skipAllIgnore(true).getConvert(); + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +@Repeatable(RestConvert.RestConverts.class) +public @interface RestConvert { + + boolean tiny() default true; + + boolean skipIgnore() default false; + + Class type(); + + String[] ignoreColumns() default {}; + + String[] convertColumns() default {}; + + @Inherited + @Documented + @Target({METHOD}) + @Retention(RUNTIME) + @interface RestConverts { + + RestConvert[] value(); + } +} diff --git a/src/org/redkale/net/http/RestConvertCoder.java b/src/main/java/org/redkale/net/http/RestConvertCoder.java similarity index 96% rename from src/org/redkale/net/http/RestConvertCoder.java rename to src/main/java/org/redkale/net/http/RestConvertCoder.java index 87005ae1e..a57e36b6a 100644 --- a/src/org/redkale/net/http/RestConvertCoder.java +++ b/src/main/java/org/redkale/net/http/RestConvertCoder.java @@ -1,43 +1,43 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import org.redkale.convert.*; - -/** - * 指定class某个字段的自定义序列化和反序列化策略。
    - * 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -@Repeatable(RestConvertCoder.RestConvertCoders.class) -public @interface RestConvertCoder { - - Class type(); - - String field(); - - Class coder(); - - @Inherited - @Documented - @Target({METHOD}) - @Retention(RUNTIME) - @interface RestConvertCoders { - - RestConvertCoder[] value(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import org.redkale.convert.*; + +/** + * 指定class某个字段的自定义序列化和反序列化策略。
    + * 只能依附在Service实现类的public方法上, 当方法的返回值以JSON输出时对指定类型的转换设定。
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +@Repeatable(RestConvertCoder.RestConvertCoders.class) +public @interface RestConvertCoder { + + Class type(); + + String field(); + + Class coder(); + + @Inherited + @Documented + @Target({METHOD}) + @Retention(RUNTIME) + @interface RestConvertCoders { + + RestConvertCoder[] value(); + } +} diff --git a/src/org/redkale/net/http/RestCookie.java b/src/main/java/org/redkale/net/http/RestCookie.java similarity index 88% rename from src/org/redkale/net/http/RestCookie.java rename to src/main/java/org/redkale/net/http/RestCookie.java index 2b10ffa8d..9c75b1ab1 100644 --- a/src/org/redkale/net/http/RestCookie.java +++ b/src/main/java/org/redkale/net/http/RestCookie.java @@ -1,45 +1,52 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于RestService类的方法的String参数或参数内的String字段 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestCookie { - - /** - * cookie名 - * - * @return String - */ - String name(); - - /** - * 转换数字byte/short/int/long时所用的进制数, 默认10进制 - * - * @return int - */ - int radix() default 10; - - /** - * 备注描述 - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于RestService类的方法的String参数或参数内的String字段 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestCookie { + + /** + * cookie名 + * + * @return String + */ + String name(); + + /** + * 转换数字byte/short/int/long时所用的进制数, 默认10进制 + * + * @return int + */ + int radix() default 10; + + /** + * for OpenAPI Specification 3.1.0 + * + * @return String + */ + String example() default ""; + + /** + * 备注描述 + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestHeader.java b/src/main/java/org/redkale/net/http/RestHeader.java similarity index 76% rename from src/org/redkale/net/http/RestHeader.java rename to src/main/java/org/redkale/net/http/RestHeader.java index 0528703f9..c8f98aeb4 100644 --- a/src/org/redkale/net/http/RestHeader.java +++ b/src/main/java/org/redkale/net/http/RestHeader.java @@ -1,45 +1,59 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于RestService类的方法的参数或参数内的String、java.net.InetSocketAddress字段 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestHeader { - - /** - * Header参数名 - * - * @return String - */ - String name(); - - /** - * 转换数字byte/short/int/long时所用的进制数, 默认10进制 - * - * @return int - */ - int radix() default 10; - - /** - * 备注描述 - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于RestService类的方法的参数或参数内的String、java.net.InetSocketAddress字段 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestHeader { + + /** + * Header参数名 + * + * @return String + */ + String name(); + + /** + * 转换数字byte/short/int/long时所用的进制数, 默认10进制 + * + * @return int + */ + int radix() default 10; + + /** + * 参数是否必传, 框架运行中不作验证, only for OpenAPI Specification 3 + * + * @return boolean + */ + boolean required() default true; + + /** + * for OpenAPI Specification 3.1.0 + * + * @return String + */ + String example() default ""; + + /** + * 备注描述 + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestHeaders.java b/src/main/java/org/redkale/net/http/RestHeaders.java similarity index 95% rename from src/org/redkale/net/http/RestHeaders.java rename to src/main/java/org/redkale/net/http/RestHeaders.java index 285e04d8d..fd5b074fc 100644 --- a/src/org/redkale/net/http/RestHeaders.java +++ b/src/main/java/org/redkale/net/http/RestHeaders.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于RestService类的方法的参数或参数内的Map<String, String>字段 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestHeaders { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于RestService类的方法的参数或参数内的Map<String, String>字段 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestHeaders { + +} diff --git a/src/org/redkale/net/http/RestMapping.java b/src/main/java/org/redkale/net/http/RestMapping.java similarity index 89% rename from src/org/redkale/net/http/RestMapping.java rename to src/main/java/org/redkale/net/http/RestMapping.java index 4fc48e744..1480f4efd 100644 --- a/src/org/redkale/net/http/RestMapping.java +++ b/src/main/java/org/redkale/net/http/RestMapping.java @@ -1,92 +1,99 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.*; - -/** - * 只能依附在Service实现类的public方法上,且方法如果throws只能是IOException
    - * value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService,的默认路径为/hello)。
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -@Repeatable(RestMapping.RestMappings.class) -public @interface RestMapping { - - /** - * 是否屏蔽该方法进行HttpMapping转换 - * - * @return boolean - */ - boolean ignore() default false; - - /** - * 请求的方法名, 不能含特殊字符 - * 默认为方法名的小写(若方法名以createXXX、updateXXX、deleteXXX、queryXXX、findXXX、existsXXX且XXXService为Service的类名将只截取XXX之前) - * - * @return String - */ - String name() default ""; - - /** - * 备注描述, 对应@HttpMapping.comment - * - * @return String - */ - String comment() default ""; - - /** - * 是否只接收RPC请求, 对应@HttpMapping.rpconly - * - * @return boolean - */ - boolean rpconly() default false; - - /** - * 是否鉴权,默认需要鉴权, 对应@HttpMapping.auth - * - * @return boolean - */ - boolean auth() default true; - - /** - * 操作ID值,鉴权时用到, 对应@HttpMapping.actionid - * - * @return int - */ - int actionid() default 0; - - /** - * 结果缓存的秒数, 为0表示不缓存, 对应@HttpMapping.cacheseconds - * - * @return int - */ - int cacheseconds() default 0; - - /** - * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@HttpMapping.methods - * - * @return String[] - */ - String[] methods() default {}; - - @Inherited - @Documented - @Target({METHOD}) - @Retention(RUNTIME) - @interface RestMappings { - - RestMapping[] value(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +/** + * 只能依附在Service实现类的public方法上,且方法如果throws只能是IOException
    + * value默认为"/" + Service的类名去掉Service字样的小写字符串 (如HelloService,的默认路径为/hello)。
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +@Repeatable(RestMapping.RestMappings.class) +public @interface RestMapping { + + /** + * 是否屏蔽该方法进行HttpMapping转换 + * + * @return boolean + */ + boolean ignore() default false; + + /** + * 请求的方法名, 不能含特殊字符 + * + * @return String + */ + String name() default ""; + + /** + * 备注描述, 对应@HttpMapping.comment + * + * @return String + */ + String comment() default ""; + + /** + * 是否只接收RPC请求, 对应@HttpMapping.rpconly + * + * @return boolean + */ + boolean rpconly() default false; + + /** + * 是否鉴权,默认需要鉴权, 对应@HttpMapping.auth + * + * @return boolean + */ + boolean auth() default true; + + /** + * 操作ID值,鉴权时用到, 对应@HttpMapping.actionid + * + * @return int + */ + int actionid() default 0; + + /** + * 结果缓存的秒数, 为0表示不缓存, 对应@HttpMapping.cacheseconds + * + * @return int + */ + int cacheseconds() default 0; + + /** + * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@HttpMapping.methods + * + * @return String[] + */ + String[] methods() default {}; + + /** + * 返回结果的样例 + * for OpenAPI Specification 3.1.0 + * + * @return String + */ + String example() default ""; + + @Inherited + @Documented + @Target({METHOD}) + @Retention(RUNTIME) + @interface RestMappings { + + RestMapping[] value(); + } +} diff --git a/src/org/redkale/net/http/RestOnMessage.java b/src/main/java/org/redkale/net/http/RestOnMessage.java similarity index 95% rename from src/org/redkale/net/http/RestOnMessage.java rename to src/main/java/org/redkale/net/http/RestOnMessage.java index b93cd1f01..2fa48dac9 100644 --- a/src/org/redkale/net/http/RestOnMessage.java +++ b/src/main/java/org/redkale/net/http/RestOnMessage.java @@ -1,44 +1,44 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 标记在RestWebSocket的接收消息方法上;
    - * 注意:被标记的方法必须同时符合以下条件:
    - * 1、必须修饰为public - * 2、不能修饰为final和static - * 3、返回值必须是void - * 4、不能throws检查型异常 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -public @interface RestOnMessage { - - /** - * 请求的方法名, 不能含特殊字符,不能以数字开头(能作为变量名) - * - * @return String - */ - String name(); - - /** - * 备注描述 - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 标记在RestWebSocket的接收消息方法上;
    + * 注意:被标记的方法必须同时符合以下条件:
    + * 1、必须修饰为public + * 2、不能修饰为final和static + * 3、返回值必须是void + * 4、不能throws检查型异常 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface RestOnMessage { + + /** + * 请求的方法名, 不能含特殊字符,不能以数字开头(能作为变量名) + * + * @return String + */ + String name(); + + /** + * 备注描述 + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestParam.java b/src/main/java/org/redkale/net/http/RestParam.java similarity index 85% rename from src/org/redkale/net/http/RestParam.java rename to src/main/java/org/redkale/net/http/RestParam.java index 6c737e884..61c93692c 100644 --- a/src/org/redkale/net/http/RestParam.java +++ b/src/main/java/org/redkale/net/http/RestParam.java @@ -1,60 +1,67 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * - * 依附在RestService类的方法的参数上
    - * name='&' 表示当前用户
    - * name='#'表示截取uri最后一段
    - * name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER}) -@Retention(RUNTIME) -public @interface RestParam { - - //name='&'表示当前用户; - /** - * 参数名
    - * name='&'表示当前用户;
    - * name='#'表示截取uri最后一段;
    - * name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值
    - * - * @return String - */ - String name(); - - /** - * 转换数字byte/short/int/long时所用的进制数, 默认10进制 - * - * @return int - */ - int radix() default 10; - - /** - * 参数是否必传, 仅供apidoc功能使用,框架运行中不作验证 - * - * @return boolean - */ - boolean required() default true; - - /** - * 备注描述 - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * + * 依附在RestService类的方法的参数上
    + * name='&' 表示当前用户
    + * name='#'表示截取uri最后一段
    + * name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER}) +@Retention(RUNTIME) +public @interface RestParam { + + //name='&'表示当前用户; + /** + * 参数名
    + * name='&'表示当前用户;
    + * name='#'表示截取uri最后一段;
    + * name='#xxx:'表示从uri中/pipes/xxx:v/截取xxx:的值
    + * + * @return String + */ + String name() default ""; + + /** + * 转换数字byte/short/int/long时所用的进制数, 默认10进制 + * + * @return int + */ + int radix() default 10; + + /** + * 参数是否必传, 框架运行中不作验证, only for OpenAPI Specification 3 + * + * @return boolean + */ + boolean required() default true; + + /** + * for OpenAPI Specification 3.1.0 + * + * @return String + */ + String example() default ""; + + /** + * 备注描述 + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestParams.java b/src/main/java/org/redkale/net/http/RestParams.java similarity index 95% rename from src/org/redkale/net/http/RestParams.java rename to src/main/java/org/redkale/net/http/RestParams.java index 8d1b15c6e..b89b01228 100644 --- a/src/org/redkale/net/http/RestParams.java +++ b/src/main/java/org/redkale/net/http/RestParams.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于RestService类的方法的参数或参数内的Map<String, String>字段 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.4.0 - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestParams { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于RestService类的方法的参数或参数内的Map<String, String>字段 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.4.0 + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestParams { + +} diff --git a/src/org/redkale/net/http/RestService.java b/src/main/java/org/redkale/net/http/RestService.java similarity index 93% rename from src/org/redkale/net/http/RestService.java rename to src/main/java/org/redkale/net/http/RestService.java index 174f735e9..24e8bcd5a 100644 --- a/src/org/redkale/net/http/RestService.java +++ b/src/main/java/org/redkale/net/http/RestService.java @@ -1,80 +1,80 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能依附在Service类上,name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloService/HelloServiceImpl,的默认路径为 hello)。 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface RestService { - - /** - * 模块名, 只能是模块名,不能含特殊字符, 只能小写字母+数字,且不能以数字开头 - * - * @return 模块名 - */ - String name() default ""; - - /** - * 目录名, 不能含特殊字符, 只能小写字母+数字,且不能以数字开头 - * - * @return 目录名 - */ - String catalog() default ""; - - /** - * 模块ID值,鉴权时用到, 对应@WebServlet.moduleid - * - * @return 模块ID值 - */ - int moduleid() default 0; - - /** - * 是否只接受RPC请求, 默认为false, 为true则覆盖所有@RestMapping的方法的rpconly值,都转为true - * - * @return 默认false - */ - boolean rpconly() default false; - - /** - * 没有标记@RestMapping的方法是否转换, 默认为false - * - * @return 默认false - */ - boolean automapping() default false; - - /** - * 是否屏蔽该类的转换 - * - * @return 默认false - */ - boolean ignore() default false; - - /** - * 同@WebServlet的repair属性 - * - * @return 默认true - */ - boolean repair() default true; - - /** - * 备注描述 - * - * @return 备注描述 - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能依附在Service类上,name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloService/HelloServiceImpl,的默认路径为 hello)。 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface RestService { + + /** + * 模块名, 只能是模块名,不能含特殊字符, 只能小写字母+数字,且不能以数字开头, 单独一个空格值为特殊值 + * + * @return 模块名 + */ + String name() default ""; + + /** + * 目录名, 不能含特殊字符, 只能小写字母+数字,且不能以数字开头 + * + * @return 目录名 + */ + String catalog() default ""; + + /** + * 模块ID值,鉴权时用到, 对应@WebServlet.moduleid + * + * @return 模块ID值 + */ + int moduleid() default 0; + + /** + * 是否只接受RPC请求, 默认为false, 为true则覆盖所有@RestMapping的方法的rpconly值,都转为true + * + * @return 默认false + */ + boolean rpconly() default false; + + /** + * 没有标记@RestMapping的方法是否转换, 默认为false + * + * @return 默认false + */ + boolean automapping() default false; + + /** + * 是否屏蔽该类的转换 + * + * @return 默认false + */ + boolean ignore() default false; + + /** + * 同@WebServlet的repair属性 + * + * @return 默认true + */ + boolean repair() default true; + + /** + * 备注描述 + * + * @return 备注描述 + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestSessionid.java b/src/main/java/org/redkale/net/http/RestSessionid.java similarity index 96% rename from src/org/redkale/net/http/RestSessionid.java rename to src/main/java/org/redkale/net/http/RestSessionid.java index a156a8d2f..c743131dc 100644 --- a/src/org/redkale/net/http/RestSessionid.java +++ b/src/main/java/org/redkale/net/http/RestSessionid.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于Service类的方法的参数或参数内的String字段 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestSessionid { - - boolean create() default false; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于Service类的方法的参数或参数内的String字段 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestSessionid { + + boolean create() default false; +} diff --git a/src/org/redkale/net/http/RestURI.java b/src/main/java/org/redkale/net/http/RestURI.java similarity index 96% rename from src/org/redkale/net/http/RestURI.java rename to src/main/java/org/redkale/net/http/RestURI.java index fb7fb6a01..aed6ec68c 100644 --- a/src/org/redkale/net/http/RestURI.java +++ b/src/main/java/org/redkale/net/http/RestURI.java @@ -1,33 +1,33 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于Service类的方法的String参数或参数内的String字段 - *

    - * 用于获取HTTP请求URL HttpRequest.getRequestURI - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestURI { - - /** - * 备注描述, 对应@HttpParam.comment - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于Service类的方法的String参数或参数内的String字段 + *

    + * 用于获取HTTP请求URL HttpRequest.getRequestURI + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestURI { + + /** + * 备注描述, 对应@HttpParam.comment + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestUploadFile.java b/src/main/java/org/redkale/net/http/RestUploadFile.java similarity index 95% rename from src/org/redkale/net/http/RestUploadFile.java rename to src/main/java/org/redkale/net/http/RestUploadFile.java index 6d1b11566..16bab2e98 100644 --- a/src/org/redkale/net/http/RestUploadFile.java +++ b/src/main/java/org/redkale/net/http/RestUploadFile.java @@ -1,55 +1,55 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * - * 依附在RestService类的方法的参数上, 用于接收上传文件
    - * 只能标记在byte[]/File/File[] 类型的参数上
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestUploadFile { - - /** - * 可接收的文件大小最大值, 小于1表示无大小限制 - * - * @return int - */ - long maxLength() default 0; - - /** - * 可接收的文件名正则表达式, 为空表示接收任何文件
    - * - * @return String - */ - String fileNameReg() default ""; - - /** - * 可接收的ContentType正则表达式, 为空表示接收任何文件类型
    - * - * @return String - */ - String contentTypeReg() default ""; - - /** - * 备注描述, 对应@HttpParam.comment - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * + * 依附在RestService类的方法的参数上, 用于接收上传文件
    + * 只能标记在byte[]/File/File[] 类型的参数上
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestUploadFile { + + /** + * 可接收的文件大小最大值, 小于1表示无大小限制 + * + * @return int + */ + long maxLength() default 0; + + /** + * 可接收的文件名正则表达式, 为空表示接收任何文件
    + * + * @return String + */ + String fileNameReg() default ""; + + /** + * 可接收的ContentType正则表达式, 为空表示接收任何文件类型
    + * + * @return String + */ + String contentTypeReg() default ""; + + /** + * 备注描述, 对应@HttpParam.comment + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/RestUserid.java b/src/main/java/org/redkale/net/http/RestUserid.java similarity index 96% rename from src/org/redkale/net/http/RestUserid.java rename to src/main/java/org/redkale/net/http/RestUserid.java index 61a58dd54..86ba3b398 100644 --- a/src/org/redkale/net/http/RestUserid.java +++ b/src/main/java/org/redkale/net/http/RestUserid.java @@ -1,29 +1,29 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 只能注解于Service类的方法的参数或参数内的Serializable字段 - *

    - * 用于获取HTTP请求端的用户ID HttpRequest.currentUserid - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -@Inherited -@Documented -@Target({PARAMETER, FIELD}) -@Retention(RUNTIME) -public @interface RestUserid { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 只能注解于Service类的方法的参数或参数内的Serializable字段 + *

    + * 用于获取HTTP请求端的用户ID HttpRequest.currentUserid + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +@Inherited +@Documented +@Target({PARAMETER, FIELD}) +@Retention(RUNTIME) +public @interface RestUserid { + +} diff --git a/src/org/redkale/net/http/RestWebSocket.java b/src/main/java/org/redkale/net/http/RestWebSocket.java similarity index 96% rename from src/org/redkale/net/http/RestWebSocket.java rename to src/main/java/org/redkale/net/http/RestWebSocket.java index 0cdc18b44..2ea2cdd1e 100644 --- a/src/org/redkale/net/http/RestWebSocket.java +++ b/src/main/java/org/redkale/net/http/RestWebSocket.java @@ -1,125 +1,125 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import org.redkale.net.Cryptor; - -/** - * 只能依附在WebSocket类上,name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloWebSocket/HelloWebSocketImpl,的默认路径为 hello)。
    - * 注意: 被标记@RestWebSocket的WebSocket不能被修饰为abstract或final,且其内部标记为@Resource的字段只能是protected或public,且必须要有一个protected或public的空参数构造函数。
    - * name值支持含{system.property.xxx}模式 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface RestWebSocket { - - /** - * 模块名, 只能是模块名,不能含特殊字符, 只能小写字母+数字,且不能以数字开头 - * - * @return 模块名 - */ - String name() default ""; - - /** - * 目录名, 不能含特殊字符, 只能小写字母+数字,且不能以数字开头 - * - * @return 目录名 - */ - String catalog() default ""; - - /** - * 是否为二进制消息, 默认为文本消息 - * - * @return boolean - */ - boolean binary() default false; - - /** - * 是否单用户单连接, 默认单用户单连接 - * - * @return 是否单用户单连接 - */ - boolean single() default true; - - /** - * WebSocket.createUserid返回的值是否不能表示用户登录态, 比如createUserid返回随机的UUID那么anyuser应该为true - * - * @return 默认false - */ - boolean anyuser() default false; - - /** - * 接收客户端的分包(last=false)消息时是否自动合并包 - * - * @return 默认true - */ - boolean mergemsg() default true; - - /** - * WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:15秒 - * - * @return int - */ - int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL; - - /** - * 加密解密器 - * - * @return Cryptor - */ - Class cryptor() default Cryptor.class; - - /** - * 最大连接数, 小于1表示无限制 - * - * @return 最大连接数 - */ - int wsmaxconns() default 0; - - /** - * 操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8) - * - * @return 最大连接数 - */ - int wsthreads() default 0; - - /** - * 最大消息体长度, 小于1表示无限制 - * - * @return 最大消息体长度 - */ - int wsmaxbody() default 32 * 1024; - - /** - * 是否屏蔽该类的转换 - * - * @return 默认false - */ - boolean ignore() default false; - - /** - * 同@WebServlet的repair属性 - * - * @return 默认true - */ - boolean repair() default true; - - /** - * 备注描述 - * - * @return 备注描述 - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import org.redkale.net.Cryptor; + +/** + * 只能依附在WebSocket类上,name默认为Service的类名小写并去掉Service字样及后面的字符串 (如HelloWebSocket/HelloWebSocketImpl,的默认路径为 hello)。
    + * 注意: 被标记@RestWebSocket的WebSocket不能被修饰为abstract或final,且其内部标记为@Resource的字段只能是protected或public,且必须要有一个protected或public的空参数构造函数。
    + * name值支持含{system.property.xxx}模式 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface RestWebSocket { + + /** + * 模块名, 只能是模块名,不能含特殊字符, 只能小写字母+数字,且不能以数字开头 + * + * @return 模块名 + */ + String name() default ""; + + /** + * 目录名, 不能含特殊字符, 只能小写字母+数字,且不能以数字开头 + * + * @return 目录名 + */ + String catalog() default ""; + + /** + * 是否为二进制消息, 默认为文本消息 + * + * @return boolean + */ + boolean binary() default false; + + /** + * 是否单用户单连接, 默认单用户单连接 + * + * @return 是否单用户单连接 + */ + boolean single() default true; + + /** + * WebSocket.createUserid返回的值是否不能表示用户登录态, 比如createUserid返回随机的UUID那么anyuser应该为true + * + * @return 默认false + */ + boolean anyuser() default false; + + /** + * 接收客户端的分包(last=false)消息时是否自动合并包 + * + * @return 默认true + */ + boolean mergemsg() default true; + + /** + * WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒, 默认值:15秒 + * + * @return int + */ + int liveinterval() default WebSocketServlet.DEFAILT_LIVEINTERVAL; + + /** + * 加密解密器 + * + * @return Cryptor + */ + Class cryptor() default Cryptor.class; + + /** + * 最大连接数, 小于1表示无限制 + * + * @return 最大连接数 + */ + int wsmaxconns() default 0; + + /** + * 操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8) + * + * @return 最大连接数 + */ + int wsthreads() default 0; + + /** + * 最大消息体长度, 小于1表示无限制 + * + * @return 最大消息体长度 + */ + int wsmaxbody() default 32 * 1024; + + /** + * 是否屏蔽该类的转换 + * + * @return 默认false + */ + boolean ignore() default false; + + /** + * 同@WebServlet的repair属性 + * + * @return 默认true + */ + boolean repair() default true; + + /** + * 备注描述 + * + * @return 备注描述 + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/WebServlet.java b/src/main/java/org/redkale/net/http/WebServlet.java similarity index 95% rename from src/org/redkale/net/http/WebServlet.java rename to src/main/java/org/redkale/net/http/WebServlet.java index 98f3d5070..578ce6527 100644 --- a/src/org/redkale/net/http/WebServlet.java +++ b/src/main/java/org/redkale/net/http/WebServlet.java @@ -1,58 +1,58 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; - -/** - * 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebServlet - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target({ElementType.TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface WebServlet { - - /** - * HttpServlet资源名 - * - * @return String - */ - String name() default ""; - - /** - * 是否自动添加url前缀, 对应application.xml中servlets节点的path属性 - * - * @return boolean - */ - boolean repair() default true; - - /** - * url匹配规则 - * - * @return String[] - */ - String[] value() default {}; - - /** - * 模块ID,一个HttpServlet尽量只有提供一个模块的服务 - * - * @return int - */ - int moduleid() default 0; - - /** - * 备注描述 - * - * @return String - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; + +/** + * 功能同JSR 315 (java-servlet 3.0) 规范中的 @WebServlet + * + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +public @interface WebServlet { + + /** + * HttpServlet资源名 + * + * @return String + */ + String name() default ""; + + /** + * 是否自动添加url前缀, 对应application.xml中servlets节点的path属性 + * + * @return boolean + */ + boolean repair() default true; + + /** + * url匹配规则 + * + * @return String[] + */ + String[] value() default {}; + + /** + * 模块ID,一个HttpServlet尽量只有提供一个模块的服务 + * + * @return int + */ + int moduleid() default 0; + + /** + * 备注描述 + * + * @return String + */ + String comment() default ""; +} diff --git a/src/org/redkale/net/http/WebSocket.java b/src/main/java/org/redkale/net/http/WebSocket.java similarity index 97% rename from src/org/redkale/net/http/WebSocket.java rename to src/main/java/org/redkale/net/http/WebSocket.java index f459aacbc..d8f98bfd7 100644 --- a/src/org/redkale/net/http/WebSocket.java +++ b/src/main/java/org/redkale/net/http/WebSocket.java @@ -1,921 +1,921 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import org.redkale.net.http.WebSocketPacket.FrameType; -import java.io.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.logging.*; -import java.util.stream.Stream; -import java.util.zip.*; -import org.redkale.convert.Convert; -import org.redkale.net.AsyncConnection; -import org.redkale.util.Comment; - -/** - *

    - * 一个WebSocket连接对应一个WebSocket实体,即一个WebSocket会绑定一个TCP连接。
    - * 协议上符合HTML5规范, 其流程顺序如下:
    - *      1.1 onOpen 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断登录态。
    - *      1.2 createUserid 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断用户权限是否符合。
    - *      1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
    - *      1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
    - *      1.5 onClose WebSocket被关闭后回调此方法。
    - *  普通模式下 以上方法都应该被重载。
    - * 
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Groupid的泛型 - * @param Message的泛型 - */ -public abstract class WebSocket { - - //--------------------------- CLOSECODE ------------------------------- - @Comment("服务器主动关闭") - public static final int CLOSECODE_SERVERCLOSE = 3001; - - @Comment("客户端主动关闭") - public static final int CLOSECODE_CLIENTCLOSE = 3002; - - @Comment("异常关闭") - public static final int CLOSECODE_WSEXCEPTION = 3003; - - @Comment("异常数据强制关闭") - public static final int CLOSECODE_ILLPACKET = 3004; - - //---------------------------- RETCODE -------------------------------- - @Comment("消息不合法") - public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2 - - @Comment("WebSocket已经关闭") - public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4 - - @Comment("Socket的buffer不合法") - public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8 - - @Comment("WebSocket发送消息异常") - public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16 - - @Comment("WebSocketEngine实例不存在") - public static final int RETCODE_ENGINE_NULL = 1 << 5; //32 - - @Comment("WebSocketNode实例不存在") - public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64 - - @Comment("WebSocket组为空, 表示无WebSocket连接") - public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128 - - @Comment("WebSocket已离线") - public static final int RETCODE_WSOFFLINE = 1 << 8; //256 - - @Comment("WebSocket将延迟发送") - public static final int RETCODE_DEAYSEND = 1 << 9; //512 - - WebSocketEngine _engine; //不可能为空 - - //WebSocketRunner _runner; //不可能为空 - WebSocketReadHandler _readHandler; - - WebSocketWriteHandler _writeHandler; - - InetSocketAddress _sncpAddress; //分布式下不可为空 - - AsyncConnection _channel;//不可能为空 - - String _sessionid; //不可能为空 - - G _userid; //不可能为空 - - SocketAddress _remoteAddress;//不可能为空 - - String _remoteAddr;//不可能为空 - - Convert _textConvert; //不可能为空 - - Convert _binaryConvert; //可能为空 - - Convert _sendConvert; //不可能为空 - - java.lang.reflect.Type _messageTextType; //不可能为空 - - Deflater deflater; //压缩 - - Inflater inflater; //解压 - - long createtime = System.currentTimeMillis(); - - List delayPackets; - - private Map attributes = new HashMap<>(); //非线程安全 - - private long lastPingTime; - - long lastReadTime; - - long lastSendTime; - - boolean initiateClosed; //收到客户端发送的CLOSE消息 - - private boolean closed = false; - - protected WebSocket() { - } - - //---------------------------------------------------------------- - public final CompletableFuture sendPing() { - this.lastPingTime = System.currentTimeMillis(); - //if (_engine.finest) _engine.logger.finest(this + " on "+_engine.getEngineid()+" ping..."); - return sendPacket(WebSocketPacket.DEFAULT_PING_PACKET); - } - - public final CompletableFuture sendPing(byte[] data) { - if (data == null) return sendPing(); - this.lastPingTime = System.currentTimeMillis(); - return sendPacket(new WebSocketPacket(FrameType.PING, data)); - } - - public final CompletableFuture sendPong(byte[] data) { - return sendPacket(new WebSocketPacket(FrameType.PONG, data)); - } - - public final long getCreatetime() { - return createtime; - } - - /** - * 给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 - * - * @param message 不可为空, 只能是String或byte[]或可JavaBean对象 - * - * @return 0表示成功, 非0表示错误码 - */ - public final CompletableFuture send(Object message) { - return send(message, true); - } - - /** - * 给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 - * - * @param message 不可为空, 只能是String或byte[]或可JavaBean对象 - * @param last 是否最后一条 - * - * @return 0表示成功, 非0表示错误码 - */ - public final CompletableFuture send(Object message, boolean last) { - if (message instanceof CompletableFuture) { - return ((CompletableFuture) message).thenCompose((json) -> { - if (json instanceof CharSequence) { - return sendPacket(new WebSocketPacket(json.toString(), last)); - } else if (json == null || json instanceof byte[]) { - return sendPacket(new WebSocketPacket((byte[]) json, last)); - } else if (message instanceof WebSocketPacket) { - return sendPacket((WebSocketPacket) message); - } else { - Convert convert = getSendConvert(); - return sendPacket(new WebSocketPacket(convert.isBinary() ? FrameType.BINARY : FrameType.TEXT, convert.convertToBytes(json), last)); - } - }); - } - Object json = message; - if (json instanceof CharSequence) { - return sendPacket(new WebSocketPacket(FrameType.TEXT, json.toString().getBytes(StandardCharsets.UTF_8), last)); - } else if (json == null || json instanceof byte[]) { - return sendPacket(new WebSocketPacket(FrameType.BINARY, (byte[]) json, last)); - } else if (message instanceof WebSocketPacket) { - return sendPacket((WebSocketPacket) message); - } else { - Convert convert = getSendConvert(); - return sendPacket(new WebSocketPacket(convert.isBinary() ? FrameType.BINARY : FrameType.TEXT, convert.convertToBytes(json), last)); - } - } - - /** - * 给自身发送消息, 消息类型是JavaBean对象 - * - * @param convert Convert - * @param message 不可为空, 只能是JSON对象 - * - * @return 0表示成功, 非0表示错误码 - */ - public final CompletableFuture send(Convert convert, Object message) { - return send(convert, message, true); - } - - /** - * 给自身发送消息, 消息类型是JavaBean对象 - * - * @param convert Convert - * @param message 不可为空, 只能是JavaBean对象 - * @param last 是否最后一条 - * - * @return 0表示成功, 非0表示错误码 - */ - public final CompletableFuture send(Convert convert, Object message, boolean last) { - final Convert c = convert == null ? getSendConvert() : convert; - if (message instanceof CompletableFuture) { - return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(c.isBinary() ? FrameType.BINARY : FrameType.TEXT, c.convertToBytes(json), last))); - } - return sendPacket(new WebSocketPacket(c.isBinary() ? FrameType.BINARY : FrameType.TEXT, c.convertToBytes(message), last)); - } - - /** - * 给自身发送消息体, 包含二进制/文本 - * - * @param packet WebSocketPacket - * - * @return 0表示成功, 非0表示错误码 - */ - CompletableFuture sendPacket(WebSocketPacket packet) { - if (this._writeHandler == null) { - if (delayPackets == null) delayPackets = new ArrayList<>(); - delayPackets.add(packet); - return CompletableFuture.completedFuture(RETCODE_DEAYSEND); - } - CompletableFuture rs = this._writeHandler.send(packet); - if (_engine.logger.isLoggable(Level.FINER) && packet != WebSocketPacket.DEFAULT_PING_PACKET) { - _engine.logger.finer("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this); - } - return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs; - } - - //---------------------------------------------------------------- - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param message 不可为空 - * @param userids Stream - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(Object message, Stream userids) { - return sendMessage(message, true, userids); - } - - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param message 不可为空 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(Object message, G... userids) { - return sendMessage(message, true, userids); - } - - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param convert Convert - * @param message 不可为空 - * @param userids Stream - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(final Convert convert, Object message, Stream userids) { - return sendMessage(convert, message, true, userids); - } - - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param convert Convert - * @param message 不可为空 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(final Convert convert, Object message, G... userids) { - return sendMessage(convert, message, true, userids); - } - - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param message 不可为空 - * @param last 是否最后一条 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(Object message, boolean last, Stream userids) { - return sendMessage((Convert) null, message, last, userids); - } - - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param message 不可为空 - * @param last 是否最后一条 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(Object message, boolean last, G... userids) { - return sendMessage((Convert) null, message, last, userids); - } - - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param convert Convert - * @param message 不可为空 - * @param last 是否最后一条 - * @param userids Stream - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(final Convert convert, Object message, boolean last, final Stream userids) { - Object[] array = userids.toArray(); - Serializable[] ss = new Serializable[array.length]; - for (int i = 0; i < array.length; i++) { - ss[i] = (Serializable) array[i]; - } - return sendMessage(convert, message, last, ss); - } - - /** - * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 - * - * @param convert Convert - * @param message 不可为空 - * @param last 是否最后一条 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendMessage(final Convert convert, Object message, boolean last, Serializable... userids) { - if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); - if (message instanceof CompletableFuture) { - return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(convert, json, last, userids)); - } - CompletableFuture rs = _engine.node.sendMessage(convert, message, last, userids); - if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")"); - return rs; - } - - /** - * 广播消息, 给所有人发消息 - * - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final Object message) { - return broadcastMessage((Convert) null, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message) { - return broadcastMessage((WebSocketRange) null, (Convert) null, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param convert Convert - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final Convert convert, final Object message) { - return broadcastMessage(convert, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param convert Convert - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) { - return broadcastMessage((WebSocketRange) null, convert, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param message 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final Object message, final boolean last) { - return broadcastMessage((Convert) null, message, last); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param message 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) { - return broadcastMessage(wsrange, (Convert) null, message, last); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param convert Convert - * @param message 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final Convert convert, final Object message, final boolean last) { - return broadcastMessage((WebSocketRange) null, convert, message, last); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param convert Convert - * @param message 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message, final boolean last) { - if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); - if (message instanceof CompletableFuture) { - return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(wsrange, convert, json, last)); - } - CompletableFuture rs = _engine.node.broadcastMessage(wsrange, convert, message, last); - if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("broadcast send websocket message(" + message + ")"); - return rs; - } - - /** - * 给指定userid的WebSocket节点发送操作 - * - * @param action 操作参数 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示异常 - */ - public final CompletableFuture sendAction(final WebSocketAction action, Serializable... userids) { - if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); - CompletableFuture rs = _engine.node.sendAction(action, userids); - if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("userids:" + Arrays.toString(userids) + " send websocket action(" + action + ")"); - return rs; - } - - /** - * 广播操作, 给所有人发操作指令 - * - * @param action 操作参数 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - public final CompletableFuture broadcastAction(final WebSocketAction action) { - if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); - CompletableFuture rs = _engine.node.broadcastAction(action); - if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("broadcast send websocket action(" + action + ")"); - return rs; - } - - /** - * 获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表
    - * InetSocketAddress 为 SNCP节点地址 - * - * @param userid Serializable - * - * @return 地址列表 - */ - public CompletableFuture> getRpcNodeAddresses(final Serializable userid) { - if (_engine.node == null) return CompletableFuture.completedFuture(null); - return _engine.node.getRpcNodeAddresses(userid); - } - - /** - * 获取在线用户的详细连接信息
    - * Map.key 为 SNCP节点地址, 含值为null的key表示没有分布式 - * Map.value 为 用户客户端的IP - * - * @param userid Serializable - * - * @return 地址集合 - */ - public CompletableFuture>> getRpcNodeWebSocketAddresses(final Serializable userid) { - if (_engine.node == null) return CompletableFuture.completedFuture(null); - return _engine.node.getRpcNodeWebSocketAddresses(userid); - } - - /** - * 更改本WebSocket的userid - * - * @param newuserid 新用户ID,不能为null - * - * @return CompletableFuture - */ - public CompletableFuture changeUserid(final G newuserid) { - if (newuserid == null) throw new NullPointerException("newuserid is null"); - return _engine.changeLocalUserid(this, newuserid); - } - - /** - * 强制关闭用户的所有WebSocket - * - * @param userid Serializable - * - * @return int - */ - @Comment("强制关闭用户的所有WebSocket") - public CompletableFuture forceCloseWebSocket(Serializable userid) { - return _engine.node.forceCloseWebSocket(userid); - } - - /** - * 获取WebSocketNode - * - * - * @return WebSocketNode - */ - @Comment("获取WebSocketNode") - public final WebSocketNode webSocketNode() { - return _engine.node; - } - - /** - * 获取当前WebSocket下的属性,非线程安全 - * - * @param 属性值的类型 - * @param name 属性名 - * - * @return 属性值 - */ - @SuppressWarnings("unchecked") - public final V getAttribute(String name) { - return attributes == null ? null : (V) attributes.get(name); - } - - /** - * 移出当前WebSocket下的属性,非线程安全 - * - * @param 属性值的类型 - * @param name 属性名 - * - * @return 属性值 - */ - public final V removeAttribute(String name) { - return attributes == null ? null : (V) attributes.remove(name); - } - - /** - * 给当前WebSocket下的增加属性,非线程安全 - * - * @param name 属性值 - * @param value 属性值 - */ - public final void setAttribute(String name, Object value) { - if (attributes == null) attributes = new HashMap<>(); - attributes.put(name, value); - } - - /** - * 获取当前WebSocket所属的userid - * - * @return userid - */ - public final G getUserid() { - return _userid; - } - - /** - * 获取当前WebSocket的会话ID, 不会为null - * - * @return sessionid - */ - public final String getSessionid() { - return _sessionid; - } - - /** - * 获取客户端直接地址, 当WebSocket连接是由代理服务器转发的,则该值固定为代理服务器的IP地址 - * - * @return SocketAddress - */ - public final SocketAddress getRemoteAddress() { - return _remoteAddress; - } - - /** - * 获取客户端真实地址 同 HttpRequest.getRemoteAddr() - * - * @return String - */ - public final String getRemoteAddr() { - return _remoteAddr; - } - - protected Convert getTextConvert() { - return _textConvert; - } - - protected Convert getBinaryConvert() { - return _binaryConvert; - } - - protected Convert getSendConvert() { - return _sendConvert; - } - - //------------------------------------------------------------------- - /** - * 获取指定userid的WebSocket数组, 没有返回null
    - * 此方法用于单用户多连接模式 - * - * @param userid Serializable - * - * @return WebSocket集合 - */ - protected final Stream getLocalWebSockets(G userid) { - return _engine.getLocalWebSockets(userid); - } - - /** - * 获取指定userid的WebSocket数组, 没有返回null
    - * 此方法用于单用户单连接模式 - * - * @param userid Serializable - * - * @return WebSocket - */ - protected final WebSocket findLocalWebSocket(G userid) { - return _engine.findLocalWebSocket(userid); - } - - /** - * 获取当前进程节点所有在线的WebSocket - * - * @return WebSocketGroup列表 - */ - protected final Collection getLocalWebSockets() { - return _engine.getLocalWebSockets(); - } - - /** - * 获取ByteBuffer生成器 - * - * @return Supplier - */ - protected Supplier getBufferSupplier() { - return this._channel.getBufferSupplier(); - } - - /** - * 获取ByteBuffer回收器 - * - * @return Consumer - */ - protected Consumer getBufferConsumer() { - return this._channel.getBufferConsumer(); - } - - //------------------------------------------------------------------- - /** - * 返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法 - * - * @param request HttpRequest - * - * @return sessionid - */ - protected CompletableFuture onOpen(final HttpRequest request) { - return CompletableFuture.completedFuture(request.getSessionid(true)); - } - - /** - * 创建userid, null表示异常, 必须实现该方法 - * - * @return userid - */ - protected abstract CompletableFuture createUserid(); - - /** - * WebSocket.broadcastMessage时的过滤条件 - * - * @param wsrange 过滤条件 - * - * @return boolean - */ - protected boolean predicate(WebSocketRange wsrange) { - return true; - } - - /** - * WebSocket.broadcastAction时的操作 - * - * @param action 操作参数 - * - * @return CompletableFuture - * - */ - protected CompletableFuture action(WebSocketAction action) { - return CompletableFuture.completedFuture(0); - } - - /** - * WebSokcet连接成功后的回调方法 - * - * @return Future 可以为null - */ - public CompletableFuture onConnected() { - return null; - } - - /** - * ping后的回调方法 - * - * @param bytes 数据 - */ - public void onPing(byte[] bytes) { - } - - /** - * pong后的回调方法 - * - * @param bytes 数据 - */ - public void onPong(byte[] bytes) { - } - - /** - * - * 接收到消息前的拦截方法, ping/pong不在其内
    - * 注意:处理完后需要调用 messageEvent.run() 才能响应onMessage - * - * @param restmapping Rest的方法名,没有则为空字符串 - * @param param onMessage方法的参数 - * @param messageEvent onMessage事件 - */ - public void preOnMessage(String restmapping, WebSocketParam param, Runnable messageEvent) { - messageEvent.run(); - } - - /** - * 接收到消息的回调方法 - * - * @param message 消息 - * @param last 是否最后一条 - */ - public void onMessage(T message, boolean last) { - } - - /** - * 接收到文本消息的回调方法 - * - * @param text 消息 - * @param last 是否最后一条 - */ - public void onMessage(String text, boolean last) { - } - - /** - * 接收到二进制消息的回调方法 - * - * @param bytes 消息 - * @param last 是否最后一条 - */ - public void onMessage(byte[] bytes, boolean last) { - } - - /** - * 关闭的回调方法,调用此方法时WebSocket已经被关闭 - * - * @param code 结果码,非0表示非正常关闭 - * @param reason 关闭原因 - * - * @return Future 可以为null - */ - public CompletableFuture onClose(int code, String reason) { - return null; - } - - /** - * 发生异常时调用 - * - * @param t 异常 - * @param buffers ByteBuffer[] - */ - public void onOccurException(Throwable t, ByteBuffer[] buffers) { - this.getLogger().log(Level.SEVERE, "WebSocket receive or send Message error", t); - } - - /** - * 当Single模式下用户重复登录时回调函数,默认处理方式: 关闭旧连接 - * - * @return Future 可以为null, 为null或者Future值为false表示关闭新连接, Future值为true表示关闭旧连接 - */ - public CompletableFuture onSingleRepeatConnect() { - return forceCloseWebSocket(getUserid()).thenApply((r) -> true); - } - - /** - * 获取分布式情况下的SNCP地址, 非分布式下为null - * - * @return InetSocketAddress sncpAddress - */ - public InetSocketAddress getSncpAddress() { - return _sncpAddress; - } - - /** - * 获取Logger - * - * @return Logger Logger - */ - public Logger getLogger() { - return this._engine.logger; - } - - /** - * 获取最后一次发送消息的时间 - * - * @return long - */ - public long getLastSendTime() { - return this.lastSendTime; - } - - /** - * 获取最后一次读取消息的时间 - * - * @return long - */ - public long getLastReadTime() { - return this.lastReadTime; - } - - /** - * 获取最后一次发送PING消息的时间 - * - * @return long - */ - public long getLastPingTime() { - return this.lastPingTime; - } - - /** - * 显式地关闭WebSocket - */ - public final void close() { - if (this.deflater != null) this.deflater.end(); - if (this.inflater != null) this.inflater.end(); - CompletableFuture future = kill(CLOSECODE_SERVERCLOSE, "user close"); - if (future != null) future.join(); - } - - //closeRunner - CompletableFuture kill(int code, String reason) { - if (closed) return null; - synchronized (this) { - if (closed) return null; - closed = true; - if (_channel == null) return null; - CompletableFuture future = _engine.removeLocalThenDisconnect(this); - _channel.dispose(); - CompletableFuture closeFuture = onClose(code, reason); - if (closeFuture == null) return future; - return CompletableFuture.allOf(future, closeFuture); - } - } - - /** - * 是否关闭 - * - * @return boolean - */ - public final boolean isClosed() { - return this.closed; - } - - @Override - public String toString() { - return this.getUserid() + "@" + _remoteAddr + "@" + Objects.hashCode(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import org.redkale.net.http.WebSocketPacket.FrameType; +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.logging.*; +import java.util.stream.Stream; +import java.util.zip.*; +import org.redkale.convert.Convert; +import org.redkale.net.AsyncConnection; +import org.redkale.util.Comment; + +/** + *

    + * 一个WebSocket连接对应一个WebSocket实体,即一个WebSocket会绑定一个TCP连接。
    + * 协议上符合HTML5规范, 其流程顺序如下:
    + *      1.1 onOpen 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断登录态。
    + *      1.2 createUserid 若返回null,视为WebSocket的连接不合法,强制关闭WebSocket连接;通常用于判断用户权限是否符合。
    + *      1.3 onConnected WebSocket成功连接后在准备接收数据前回调此方法。
    + *      1.4 onMessage/onFragment+ WebSocket接收到消息后回调此消息类方法。
    + *      1.5 onClose WebSocket被关闭后回调此方法。
    + *  普通模式下 以上方法都应该被重载。
    + * 
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Groupid的泛型 + * @param Message的泛型 + */ +public abstract class WebSocket { + + //--------------------------- CLOSECODE ------------------------------- + @Comment("服务器主动关闭") + public static final int CLOSECODE_SERVERCLOSE = 3001; + + @Comment("客户端主动关闭") + public static final int CLOSECODE_CLIENTCLOSE = 3002; + + @Comment("异常关闭") + public static final int CLOSECODE_WSEXCEPTION = 3003; + + @Comment("异常数据强制关闭") + public static final int CLOSECODE_ILLPACKET = 3004; + + //---------------------------- RETCODE -------------------------------- + @Comment("消息不合法") + public static final int RETCODE_SEND_ILLPACKET = 1 << 1; //2 + + @Comment("WebSocket已经关闭") + public static final int RETCODE_WSOCKET_CLOSED = 1 << 2; //4 + + @Comment("Socket的buffer不合法") + public static final int RETCODE_ILLEGALBUFFER = 1 << 3; //8 + + @Comment("WebSocket发送消息异常") + public static final int RETCODE_SENDEXCEPTION = 1 << 4; //16 + + @Comment("WebSocketEngine实例不存在") + public static final int RETCODE_ENGINE_NULL = 1 << 5; //32 + + @Comment("WebSocketNode实例不存在") + public static final int RETCODE_NODESERVICE_NULL = 1 << 6; //64 + + @Comment("WebSocket组为空, 表示无WebSocket连接") + public static final int RETCODE_GROUP_EMPTY = 1 << 7; //128 + + @Comment("WebSocket已离线") + public static final int RETCODE_WSOFFLINE = 1 << 8; //256 + + @Comment("WebSocket将延迟发送") + public static final int RETCODE_DEAYSEND = 1 << 9; //512 + + WebSocketEngine _engine; //不可能为空 + + //WebSocketRunner _runner; //不可能为空 + WebSocketReadHandler _readHandler; + + WebSocketWriteHandler _writeHandler; + + InetSocketAddress _sncpAddress; //分布式下不可为空 + + AsyncConnection _channel;//不可能为空 + + String _sessionid; //不可能为空 + + G _userid; //不可能为空 + + SocketAddress _remoteAddress;//不可能为空 + + String _remoteAddr;//不可能为空 + + Convert _textConvert; //不可能为空 + + Convert _binaryConvert; //可能为空 + + Convert _sendConvert; //不可能为空 + + java.lang.reflect.Type _messageTextType; //不可能为空 + + Deflater deflater; //压缩 + + Inflater inflater; //解压 + + long createtime = System.currentTimeMillis(); + + List delayPackets; + + private Map attributes = new HashMap<>(); //非线程安全 + + private long lastPingTime; + + long lastReadTime; + + long lastSendTime; + + boolean initiateClosed; //收到客户端发送的CLOSE消息 + + private boolean closed = false; + + protected WebSocket() { + } + + //---------------------------------------------------------------- + public final CompletableFuture sendPing() { + this.lastPingTime = System.currentTimeMillis(); + //if (_engine.finest) _engine.logger.finest(this + " on "+_engine.getEngineid()+" ping..."); + return sendPacket(WebSocketPacket.DEFAULT_PING_PACKET); + } + + public final CompletableFuture sendPing(byte[] data) { + if (data == null) return sendPing(); + this.lastPingTime = System.currentTimeMillis(); + return sendPacket(new WebSocketPacket(FrameType.PING, data)); + } + + public final CompletableFuture sendPong(byte[] data) { + return sendPacket(new WebSocketPacket(FrameType.PONG, data)); + } + + public final long getCreatetime() { + return createtime; + } + + /** + * 给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 + * + * @param message 不可为空, 只能是String或byte[]或可JavaBean对象 + * + * @return 0表示成功, 非0表示错误码 + */ + public final CompletableFuture send(Object message) { + return send(message, true); + } + + /** + * 给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 + * + * @param message 不可为空, 只能是String或byte[]或可JavaBean对象 + * @param last 是否最后一条 + * + * @return 0表示成功, 非0表示错误码 + */ + public final CompletableFuture send(Object message, boolean last) { + if (message instanceof CompletableFuture) { + return ((CompletableFuture) message).thenCompose((json) -> { + if (json instanceof CharSequence) { + return sendPacket(new WebSocketPacket(json.toString(), last)); + } else if (json == null || json instanceof byte[]) { + return sendPacket(new WebSocketPacket((byte[]) json, last)); + } else if (message instanceof WebSocketPacket) { + return sendPacket((WebSocketPacket) message); + } else { + Convert convert = getSendConvert(); + return sendPacket(new WebSocketPacket(convert.isBinary() ? FrameType.BINARY : FrameType.TEXT, convert.convertToBytes(json), last)); + } + }); + } + Object json = message; + if (json instanceof CharSequence) { + return sendPacket(new WebSocketPacket(FrameType.TEXT, json.toString().getBytes(StandardCharsets.UTF_8), last)); + } else if (json == null || json instanceof byte[]) { + return sendPacket(new WebSocketPacket(FrameType.BINARY, (byte[]) json, last)); + } else if (message instanceof WebSocketPacket) { + return sendPacket((WebSocketPacket) message); + } else { + Convert convert = getSendConvert(); + return sendPacket(new WebSocketPacket(convert.isBinary() ? FrameType.BINARY : FrameType.TEXT, convert.convertToBytes(json), last)); + } + } + + /** + * 给自身发送消息, 消息类型是JavaBean对象 + * + * @param convert Convert + * @param message 不可为空, 只能是JSON对象 + * + * @return 0表示成功, 非0表示错误码 + */ + public final CompletableFuture send(Convert convert, Object message) { + return send(convert, message, true); + } + + /** + * 给自身发送消息, 消息类型是JavaBean对象 + * + * @param convert Convert + * @param message 不可为空, 只能是JavaBean对象 + * @param last 是否最后一条 + * + * @return 0表示成功, 非0表示错误码 + */ + public final CompletableFuture send(Convert convert, Object message, boolean last) { + final Convert c = convert == null ? getSendConvert() : convert; + if (message instanceof CompletableFuture) { + return ((CompletableFuture) message).thenCompose((json) -> sendPacket(new WebSocketPacket(c.isBinary() ? FrameType.BINARY : FrameType.TEXT, c.convertToBytes(json), last))); + } + return sendPacket(new WebSocketPacket(c.isBinary() ? FrameType.BINARY : FrameType.TEXT, c.convertToBytes(message), last)); + } + + /** + * 给自身发送消息体, 包含二进制/文本 + * + * @param packet WebSocketPacket + * + * @return 0表示成功, 非0表示错误码 + */ + CompletableFuture sendPacket(WebSocketPacket packet) { + if (this._writeHandler == null) { + if (delayPackets == null) delayPackets = new ArrayList<>(); + delayPackets.add(packet); + return CompletableFuture.completedFuture(RETCODE_DEAYSEND); + } + CompletableFuture rs = this._writeHandler.send(packet); + if (_engine.logger.isLoggable(Level.FINER) && packet != WebSocketPacket.DEFAULT_PING_PACKET) { + _engine.logger.finer("userid:" + getUserid() + " send websocket message(" + packet + ")" + " on " + this); + } + return rs == null ? CompletableFuture.completedFuture(RETCODE_WSOCKET_CLOSED) : rs; + } + + //---------------------------------------------------------------- + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param message 不可为空 + * @param userids Stream + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(Object message, Stream userids) { + return sendMessage(message, true, userids); + } + + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param message 不可为空 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(Object message, G... userids) { + return sendMessage(message, true, userids); + } + + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param convert Convert + * @param message 不可为空 + * @param userids Stream + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(final Convert convert, Object message, Stream userids) { + return sendMessage(convert, message, true, userids); + } + + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param convert Convert + * @param message 不可为空 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(final Convert convert, Object message, G... userids) { + return sendMessage(convert, message, true, userids); + } + + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param message 不可为空 + * @param last 是否最后一条 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(Object message, boolean last, Stream userids) { + return sendMessage((Convert) null, message, last, userids); + } + + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param message 不可为空 + * @param last 是否最后一条 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(Object message, boolean last, G... userids) { + return sendMessage((Convert) null, message, last, userids); + } + + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param convert Convert + * @param message 不可为空 + * @param last 是否最后一条 + * @param userids Stream + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(final Convert convert, Object message, boolean last, final Stream userids) { + Object[] array = userids.toArray(); + Serializable[] ss = new Serializable[array.length]; + for (int i = 0; i < array.length; i++) { + ss[i] = (Serializable) array[i]; + } + return sendMessage(convert, message, last, ss); + } + + /** + * 给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 + * + * @param convert Convert + * @param message 不可为空 + * @param last 是否最后一条 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendMessage(final Convert convert, Object message, boolean last, Serializable... userids) { + if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); + if (message instanceof CompletableFuture) { + return ((CompletableFuture) message).thenCompose((json) -> _engine.node.sendMessage(convert, json, last, userids)); + } + CompletableFuture rs = _engine.node.sendMessage(convert, message, last, userids); + if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("userids:" + Arrays.toString(userids) + " send websocket message(" + message + ")"); + return rs; + } + + /** + * 广播消息, 给所有人发消息 + * + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final Object message) { + return broadcastMessage((Convert) null, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message) { + return broadcastMessage((WebSocketRange) null, (Convert) null, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param convert Convert + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final Convert convert, final Object message) { + return broadcastMessage(convert, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param convert Convert + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) { + return broadcastMessage((WebSocketRange) null, convert, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param message 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final Object message, final boolean last) { + return broadcastMessage((Convert) null, message, last); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param message 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) { + return broadcastMessage(wsrange, (Convert) null, message, last); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param convert Convert + * @param message 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final Convert convert, final Object message, final boolean last) { + return broadcastMessage((WebSocketRange) null, convert, message, last); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param convert Convert + * @param message 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message, final boolean last) { + if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); + if (message instanceof CompletableFuture) { + return ((CompletableFuture) message).thenCompose((json) -> _engine.node.broadcastMessage(wsrange, convert, json, last)); + } + CompletableFuture rs = _engine.node.broadcastMessage(wsrange, convert, message, last); + if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("broadcast send websocket message(" + message + ")"); + return rs; + } + + /** + * 给指定userid的WebSocket节点发送操作 + * + * @param action 操作参数 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示异常 + */ + public final CompletableFuture sendAction(final WebSocketAction action, Serializable... userids) { + if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); + CompletableFuture rs = _engine.node.sendAction(action, userids); + if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("userids:" + Arrays.toString(userids) + " send websocket action(" + action + ")"); + return rs; + } + + /** + * 广播操作, 给所有人发操作指令 + * + * @param action 操作参数 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + public final CompletableFuture broadcastAction(final WebSocketAction action) { + if (_engine.node == null) return CompletableFuture.completedFuture(RETCODE_NODESERVICE_NULL); + CompletableFuture rs = _engine.node.broadcastAction(action); + if (_engine.logger.isLoggable(Level.FINER)) _engine.logger.finer("broadcast send websocket action(" + action + ")"); + return rs; + } + + /** + * 获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表
    + * InetSocketAddress 为 SNCP节点地址 + * + * @param userid Serializable + * + * @return 地址列表 + */ + public CompletableFuture> getRpcNodeAddresses(final Serializable userid) { + if (_engine.node == null) return CompletableFuture.completedFuture(null); + return _engine.node.getRpcNodeAddresses(userid); + } + + /** + * 获取在线用户的详细连接信息
    + * Map.key 为 SNCP节点地址, 含值为null的key表示没有分布式 + * Map.value 为 用户客户端的IP + * + * @param userid Serializable + * + * @return 地址集合 + */ + public CompletableFuture>> getRpcNodeWebSocketAddresses(final Serializable userid) { + if (_engine.node == null) return CompletableFuture.completedFuture(null); + return _engine.node.getRpcNodeWebSocketAddresses(userid); + } + + /** + * 更改本WebSocket的userid + * + * @param newuserid 新用户ID,不能为null + * + * @return CompletableFuture + */ + public CompletableFuture changeUserid(final G newuserid) { + if (newuserid == null) throw new NullPointerException("newuserid is null"); + return _engine.changeLocalUserid(this, newuserid); + } + + /** + * 强制关闭用户的所有WebSocket + * + * @param userid Serializable + * + * @return int + */ + @Comment("强制关闭用户的所有WebSocket") + public CompletableFuture forceCloseWebSocket(Serializable userid) { + return _engine.node.forceCloseWebSocket(userid); + } + + /** + * 获取WebSocketNode + * + * + * @return WebSocketNode + */ + @Comment("获取WebSocketNode") + public final WebSocketNode webSocketNode() { + return _engine.node; + } + + /** + * 获取当前WebSocket下的属性,非线程安全 + * + * @param 属性值的类型 + * @param name 属性名 + * + * @return 属性值 + */ + @SuppressWarnings("unchecked") + public final V getAttribute(String name) { + return attributes == null ? null : (V) attributes.get(name); + } + + /** + * 移出当前WebSocket下的属性,非线程安全 + * + * @param 属性值的类型 + * @param name 属性名 + * + * @return 属性值 + */ + public final V removeAttribute(String name) { + return attributes == null ? null : (V) attributes.remove(name); + } + + /** + * 给当前WebSocket下的增加属性,非线程安全 + * + * @param name 属性值 + * @param value 属性值 + */ + public final void setAttribute(String name, Object value) { + if (attributes == null) attributes = new HashMap<>(); + attributes.put(name, value); + } + + /** + * 获取当前WebSocket所属的userid + * + * @return userid + */ + public final G getUserid() { + return _userid; + } + + /** + * 获取当前WebSocket的会话ID, 不会为null + * + * @return sessionid + */ + public final String getSessionid() { + return _sessionid; + } + + /** + * 获取客户端直接地址, 当WebSocket连接是由代理服务器转发的,则该值固定为代理服务器的IP地址 + * + * @return SocketAddress + */ + public final SocketAddress getRemoteAddress() { + return _remoteAddress; + } + + /** + * 获取客户端真实地址 同 HttpRequest.getRemoteAddr() + * + * @return String + */ + public final String getRemoteAddr() { + return _remoteAddr; + } + + protected Convert getTextConvert() { + return _textConvert; + } + + protected Convert getBinaryConvert() { + return _binaryConvert; + } + + protected Convert getSendConvert() { + return _sendConvert; + } + + //------------------------------------------------------------------- + /** + * 获取指定userid的WebSocket数组, 没有返回null
    + * 此方法用于单用户多连接模式 + * + * @param userid Serializable + * + * @return WebSocket集合 + */ + protected final Stream getLocalWebSockets(G userid) { + return _engine.getLocalWebSockets(userid); + } + + /** + * 获取指定userid的WebSocket数组, 没有返回null
    + * 此方法用于单用户单连接模式 + * + * @param userid Serializable + * + * @return WebSocket + */ + protected final WebSocket findLocalWebSocket(G userid) { + return _engine.findLocalWebSocket(userid); + } + + /** + * 获取当前进程节点所有在线的WebSocket + * + * @return WebSocketGroup列表 + */ + protected final Collection getLocalWebSockets() { + return _engine.getLocalWebSockets(); + } + + /** + * 获取ByteBuffer生成器 + * + * @return Supplier + */ + protected Supplier getBufferSupplier() { + return this._channel.getBufferSupplier(); + } + + /** + * 获取ByteBuffer回收器 + * + * @return Consumer + */ + protected Consumer getBufferConsumer() { + return this._channel.getBufferConsumer(); + } + + //------------------------------------------------------------------- + /** + * 返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法 + * + * @param request HttpRequest + * + * @return sessionid + */ + protected CompletableFuture onOpen(final HttpRequest request) { + return CompletableFuture.completedFuture(request.getSessionid(true)); + } + + /** + * 创建userid, null表示异常, 必须实现该方法 + * + * @return userid + */ + protected abstract CompletableFuture createUserid(); + + /** + * WebSocket.broadcastMessage时的过滤条件 + * + * @param wsrange 过滤条件 + * + * @return boolean + */ + protected boolean predicate(WebSocketRange wsrange) { + return true; + } + + /** + * WebSocket.broadcastAction时的操作 + * + * @param action 操作参数 + * + * @return CompletableFuture + * + */ + protected CompletableFuture action(WebSocketAction action) { + return CompletableFuture.completedFuture(0); + } + + /** + * WebSokcet连接成功后的回调方法 + * + * @return Future 可以为null + */ + public CompletableFuture onConnected() { + return null; + } + + /** + * ping后的回调方法 + * + * @param bytes 数据 + */ + public void onPing(byte[] bytes) { + } + + /** + * pong后的回调方法 + * + * @param bytes 数据 + */ + public void onPong(byte[] bytes) { + } + + /** + * + * 接收到消息前的拦截方法, ping/pong不在其内
    + * 注意:处理完后需要调用 messageEvent.run() 才能响应onMessage + * + * @param restmapping Rest的方法名,没有则为空字符串 + * @param param onMessage方法的参数 + * @param messageEvent onMessage事件 + */ + public void preOnMessage(String restmapping, WebSocketParam param, Runnable messageEvent) { + messageEvent.run(); + } + + /** + * 接收到消息的回调方法 + * + * @param message 消息 + * @param last 是否最后一条 + */ + public void onMessage(T message, boolean last) { + } + + /** + * 接收到文本消息的回调方法 + * + * @param text 消息 + * @param last 是否最后一条 + */ + public void onMessage(String text, boolean last) { + } + + /** + * 接收到二进制消息的回调方法 + * + * @param bytes 消息 + * @param last 是否最后一条 + */ + public void onMessage(byte[] bytes, boolean last) { + } + + /** + * 关闭的回调方法,调用此方法时WebSocket已经被关闭 + * + * @param code 结果码,非0表示非正常关闭 + * @param reason 关闭原因 + * + * @return Future 可以为null + */ + public CompletableFuture onClose(int code, String reason) { + return null; + } + + /** + * 发生异常时调用 + * + * @param t 异常 + * @param buffers ByteBuffer[] + */ + public void onOccurException(Throwable t, ByteBuffer[] buffers) { + this.getLogger().log(Level.SEVERE, "WebSocket receive or send Message error", t); + } + + /** + * 当Single模式下用户重复登录时回调函数,默认处理方式: 关闭旧连接 + * + * @return Future 可以为null, 为null或者Future值为false表示关闭新连接, Future值为true表示关闭旧连接 + */ + public CompletableFuture onSingleRepeatConnect() { + return forceCloseWebSocket(getUserid()).thenApply((r) -> true); + } + + /** + * 获取分布式情况下的SNCP地址, 非分布式下为null + * + * @return InetSocketAddress sncpAddress + */ + public InetSocketAddress getSncpAddress() { + return _sncpAddress; + } + + /** + * 获取Logger + * + * @return Logger Logger + */ + public Logger getLogger() { + return this._engine.logger; + } + + /** + * 获取最后一次发送消息的时间 + * + * @return long + */ + public long getLastSendTime() { + return this.lastSendTime; + } + + /** + * 获取最后一次读取消息的时间 + * + * @return long + */ + public long getLastReadTime() { + return this.lastReadTime; + } + + /** + * 获取最后一次发送PING消息的时间 + * + * @return long + */ + public long getLastPingTime() { + return this.lastPingTime; + } + + /** + * 显式地关闭WebSocket + */ + public final void close() { + if (this.deflater != null) this.deflater.end(); + if (this.inflater != null) this.inflater.end(); + CompletableFuture future = kill(CLOSECODE_SERVERCLOSE, "user close"); + if (future != null) future.join(); + } + + //closeRunner + CompletableFuture kill(int code, String reason) { + if (closed) return null; + synchronized (this) { + if (closed) return null; + closed = true; + if (_channel == null) return null; + CompletableFuture future = _engine.removeLocalThenDisconnect(this); + _channel.dispose(); + CompletableFuture closeFuture = onClose(code, reason); + if (closeFuture == null) return future; + return CompletableFuture.allOf(future, closeFuture); + } + } + + /** + * 是否关闭 + * + * @return boolean + */ + public final boolean isClosed() { + return this.closed; + } + + @Override + public String toString() { + return this.getUserid() + "@" + _remoteAddr + "@" + Objects.hashCode(this); + } +} diff --git a/src/org/redkale/net/http/WebSocketAction.java b/src/main/java/org/redkale/net/http/WebSocketAction.java similarity index 95% rename from src/org/redkale/net/http/WebSocketAction.java rename to src/main/java/org/redkale/net/http/WebSocketAction.java index 0324e5dc0..db53325db 100644 --- a/src/org/redkale/net/http/WebSocketAction.java +++ b/src/main/java/org/redkale/net/http/WebSocketAction.java @@ -1,66 +1,66 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.Serializable; -import java.util.Map; -import org.redkale.convert.json.JsonConvert; - -/** - * WebSocket.broadcastAction时的参数 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class WebSocketAction implements Serializable { - - protected String action; - - protected Map attach; - - public WebSocketAction() { - } - - public WebSocketAction(String action) { - this.action = action; - } - - public WebSocketAction(String action, Map attach) { - this.action = action; - this.attach = attach; - } - - public String findAttach(String name) { - return attach == null ? null : attach.get(name); - } - - public String findAttach(String name, String defvalue) { - return attach == null ? defvalue : attach.getOrDefault(name, defvalue); - } - - public String getAction() { - return action; - } - - public void setAction(String action) { - this.action = action; - } - - public Map getAttach() { - return attach; - } - - public void setAttach(Map attach) { - this.attach = attach; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.Serializable; +import java.util.Map; +import org.redkale.convert.json.JsonConvert; + +/** + * WebSocket.broadcastAction时的参数 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class WebSocketAction implements Serializable { + + protected String action; + + protected Map attach; + + public WebSocketAction() { + } + + public WebSocketAction(String action) { + this.action = action; + } + + public WebSocketAction(String action, Map attach) { + this.action = action; + this.attach = attach; + } + + public String findAttach(String name) { + return attach == null ? null : attach.get(name); + } + + public String findAttach(String name, String defvalue) { + return attach == null ? defvalue : attach.getOrDefault(name, defvalue); + } + + public String getAction() { + return action; + } + + public void setAction(String action) { + this.action = action; + } + + public Map getAttach() { + return attach; + } + + public void setAttach(Map attach) { + this.attach = attach; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/org/redkale/net/http/WebSocketAddress.java b/src/main/java/org/redkale/net/http/WebSocketAddress.java similarity index 96% rename from src/org/redkale/net/http/WebSocketAddress.java rename to src/main/java/org/redkale/net/http/WebSocketAddress.java index f04a1e130..5e2a29d35 100644 --- a/src/org/redkale/net/http/WebSocketAddress.java +++ b/src/main/java/org/redkale/net/http/WebSocketAddress.java @@ -1,74 +1,74 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.Serializable; -import java.net.InetSocketAddress; -import java.util.Objects; -import org.redkale.convert.json.JsonConvert; - -/** - * 存放用户WS连接的SNCP地址和MQ topic, 当消息使用MQ代理时,topic才会有值 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class WebSocketAddress implements Serializable { - - protected InetSocketAddress addr; - - protected String topic; - - public WebSocketAddress() { - } - - public WebSocketAddress(String topic, InetSocketAddress addr) { - this.topic = topic; - this.addr = addr; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 37 * hash + Objects.hashCode(this.addr); - hash = 37 * hash + Objects.hashCode(this.topic); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final WebSocketAddress other = (WebSocketAddress) obj; - return Objects.equals(this.topic, other.topic) && Objects.equals(this.addr, other.addr); - } - - public String getTopic() { - return topic; - } - - public void setTopic(String topic) { - this.topic = topic; - } - - public InetSocketAddress getAddr() { - return addr; - } - - public void setAddr(InetSocketAddress addr) { - this.addr = addr; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.Serializable; +import java.net.InetSocketAddress; +import java.util.Objects; +import org.redkale.convert.json.JsonConvert; + +/** + * 存放用户WS连接的SNCP地址和MQ topic, 当消息使用MQ代理时,topic才会有值 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class WebSocketAddress implements Serializable { + + protected InetSocketAddress addr; + + protected String topic; + + public WebSocketAddress() { + } + + public WebSocketAddress(String topic, InetSocketAddress addr) { + this.topic = topic; + this.addr = addr; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + Objects.hashCode(this.addr); + hash = 37 * hash + Objects.hashCode(this.topic); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final WebSocketAddress other = (WebSocketAddress) obj; + return Objects.equals(this.topic, other.topic) && Objects.equals(this.addr, other.addr); + } + + public String getTopic() { + return topic; + } + + public void setTopic(String topic) { + this.topic = topic; + } + + public InetSocketAddress getAddr() { + return addr; + } + + public void setAddr(InetSocketAddress addr) { + this.addr = addr; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/org/redkale/net/http/WebSocketEngine.java b/src/main/java/org/redkale/net/http/WebSocketEngine.java similarity index 97% rename from src/org/redkale/net/http/WebSocketEngine.java rename to src/main/java/org/redkale/net/http/WebSocketEngine.java index 4f6f45580..f7b6f61f2 100644 --- a/src/org/redkale/net/http/WebSocketEngine.java +++ b/src/main/java/org/redkale/net/http/WebSocketEngine.java @@ -1,490 +1,495 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import static org.redkale.net.http.WebSocketServlet.DEFAILT_LIVEINTERVAL; -import java.io.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.logging.*; -import java.util.stream.*; -import org.redkale.convert.Convert; -import org.redkale.net.Cryptor; -import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY; -import static org.redkale.net.http.WebSocketServlet.*; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class WebSocketEngine { - - @Comment("全局自增长ID, 为了确保在一个进程里多个WebSocketEngine定时发送ping时不会同时进行") - private static final AtomicInteger sequence = new AtomicInteger(); - - @Comment("Engine自增长序号ID") - private final int index; - - @Comment("当前WebSocket对应的Engine") - private final String engineid; - - @Comment("当前WebSocket对应的Node") - protected final WebSocketNode node; - - //HttpContext - protected final HttpContext context; - - //Convert - protected final Convert sendConvert; - - @Comment("是否单用户单连接") - protected final boolean single; - - @Comment("在线用户ID对应的WebSocket组,用于单用户单连接模式") - private final Map websockets = new ConcurrentHashMap<>(); - - @Comment("在线用户ID对应的WebSocket组,用于单用户多连接模式") - private final Map> websockets2 = new ConcurrentHashMap<>(); - - @Comment("当前连接数") - protected final AtomicInteger currconns = new AtomicInteger(); - - @Comment("用于PING的定时器") - private ScheduledThreadPoolExecutor scheduler; - - @Comment("日志") - protected final Logger logger; - - @Comment("PING的间隔秒数") - protected int liveinterval; - - @Comment("最大连接数, 为0表示无限制") - protected int wsmaxconns; - - @Comment("操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8)") - protected int wsthreads; - - @Comment("最大消息体长度, 小于1表示无限制") - protected int wsmaxbody; - - @Comment("接收客户端的分包(last=false)消息时是否自动合并包") - protected boolean mergemsg = true; - - @Comment("加密解密器") - protected Cryptor cryptor; - - protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, int wsmaxconns, - int wsthreads, int wsmaxbody, boolean mergemsg, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) { - this.engineid = engineid; - this.single = single; - this.context = context; - this.sendConvert = sendConvert; - this.node = node; - this.liveinterval = liveinterval; - this.wsmaxconns = wsmaxconns; - this.wsthreads = wsthreads; - this.wsmaxbody = wsmaxbody; - this.mergemsg = mergemsg; - this.cryptor = cryptor; - this.logger = logger; - this.index = sequence.getAndIncrement(); - } - - void init(AnyValue conf) { - AnyValue props = conf; - if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties"); - this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval)); - if (liveinterval <= 0) return; - if (props != null) this.wsmaxconns = props.getIntValue(WEBPARAM__WSMAXCONNS, this.wsmaxconns); - if (props != null) this.wsthreads = props.getIntValue(WEBPARAM__WSTHREADS, this.wsthreads); - if (props != null) this.wsmaxbody = props.getIntValue(WEBPARAM__WSMAXBODY, this.wsmaxbody); - if (scheduler != null) return; - this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-" + engineid + "-WebSocket-LiveInterval-Thread"); - t.setDaemon(true); - return t; - }); - long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5; - final int intervalms = liveinterval * 1000; - scheduler.scheduleWithFixedDelay(() -> { - try { - long now = System.currentTimeMillis(); - getLocalWebSockets().stream().filter(x -> ((now - x.getLastReadTime()) > intervalms && (now - x.getLastSendTime()) > intervalms)).forEach(x -> x.sendPing()); - } catch (Throwable t) { - logger.log(Level.SEVERE, "WebSocketEngine schedule(interval=" + liveinterval + "s) ping error", t); - } - }, delay, liveinterval, TimeUnit.SECONDS); - if (logger.isLoggable(Level.FINEST)) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(wsmaxconns:" + wsmaxconns + ", delay:" + delay + "s, interval:" + liveinterval + "s) scheduler executor"); - } - - void destroy(AnyValue conf) { - if (scheduler != null) scheduler.shutdownNow(); - } - - @Comment("添加WebSocket") - CompletableFuture addLocal(WebSocket socket) { - if (single) { - currconns.incrementAndGet(); - websockets.put(socket._userid, socket); - } else { //非线程安全, 在常规场景中无需锁 - List list = websockets2.get(socket._userid); - if (list == null) { - list = new CopyOnWriteArrayList<>(); - websockets2.put(socket._userid, list); - } - currconns.incrementAndGet(); - list.add(socket); - } - if (node != null) return node.connect(socket._userid); - return null; - } - - @Comment("从WebSocketEngine删除指定WebSocket") - CompletableFuture removeLocalThenDisconnect(WebSocket socket) { - Serializable userid = socket._userid; - if (userid == null) return null; //尚未登录成功 - if (single) { - currconns.decrementAndGet(); - websockets.remove(userid); - if (node != null) return node.disconnect(userid); - } else { //非线程安全, 在常规场景中无需锁 - List list = websockets2.get(userid); - if (list != null) { - currconns.decrementAndGet(); - list.remove(socket); - if (list.isEmpty()) { - websockets2.remove(userid); - return node.disconnect(userid); - } - } - } - return null; - } - - @Comment("更改WebSocket的userid") - CompletableFuture changeLocalUserid(WebSocket socket, final Serializable newuserid) { - if (newuserid == null) throw new NullPointerException("newuserid is null"); - final Serializable olduserid = socket._userid; - socket._userid = newuserid; - if (single) { - websockets.remove(olduserid); - websockets.put(newuserid, socket); - } else { //非线程安全, 在常规场景中无需锁 - List oldlist = websockets2.get(olduserid); - if (oldlist != null) { - oldlist.remove(socket); - if (oldlist.isEmpty()) websockets2.remove(olduserid); - } - List newlist = websockets2.get(newuserid); - if (newlist == null) { - newlist = new CopyOnWriteArrayList<>(); - websockets2.put(newuserid, newlist); - } - newlist.add(socket); - } - if (node != null) return node.changeUserid(olduserid, newuserid); - return CompletableFuture.completedFuture(null); - } - - @Comment("强制关闭本地用户的WebSocket") - public int forceCloseLocalWebSocket(Serializable userid) { - if (single) { - WebSocket ws = websockets.get(userid); - if (ws == null) return 0; - ws.close(); - return 1; - } - List list = websockets2.get(userid); - if (list == null || list.isEmpty()) return 0; - List list2 = new ArrayList<>(list); - for (WebSocket ws : list2) { - ws.close(); - } - return list2.size(); - } - - @Comment("给所有连接用户发送消息") - public CompletableFuture broadcastLocalMessage(final Object message, final boolean last) { - return WebSocketEngine.this.broadcastLocalMessage((Predicate) null, message, last); - } - - @Comment("给指定WebSocket连接用户发送消息") - public CompletableFuture broadcastLocalMessage(final WebSocketRange wsrange, final Object message, final boolean last) { - Predicate predicate = wsrange == null ? null : (ws) -> ws.predicate(wsrange); - return WebSocketEngine.this.broadcastLocalMessage(predicate, message, last); - } - - @Comment("给指定WebSocket连接用户发送消息") - public CompletableFuture broadcastLocalMessage(final Predicate predicate, final Object message, final boolean last) { - if (message instanceof CompletableFuture) { - return ((CompletableFuture) message).thenCompose((json) -> WebSocketEngine.this.broadcastLocalMessage(predicate, json, last)); - } -// final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null); -// if (more) { -// Supplier bufferSupplier = null; -// Consumer bufferConsumer = null; -// //此处的WebSocketPacket只能是包含payload或bytes内容的,不能包含sendConvert、sendJson、sendBuffers -// final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message -// : ((message == null || message instanceof CharSequence || message instanceof byte[]) -// ? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last)); -// //packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor)); -// CompletableFuture future = null; -// if (single) { -// for (WebSocket websocket : websockets.values()) { -// if (predicate != null && !predicate.test(websocket)) continue; -// if (bufferSupplier == null) { -// bufferSupplier = websocket.getBufferSupplier(); -// bufferConsumer = websocket.getBufferConsumer(); -// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); -// } -// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); -// } -// } else { -// for (List list : websockets2.values()) { -// for (WebSocket websocket : list) { -// if (predicate != null && !predicate.test(websocket)) continue; -// if (bufferSupplier == null) { -// bufferSupplier = websocket.getBufferSupplier(); -// bufferConsumer = websocket.getBufferConsumer(); -// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); -// } -// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); -// } -// } -// } -// final Consumer bufferConsumer0 = bufferConsumer; -// if (future != null) future.whenComplete((rs, ex) -> { -// if (packet.sendBuffers != null && bufferConsumer0 != null) { -// for (ByteBuffer buffer : packet.sendBuffers) { -// bufferConsumer0.accept(buffer); -// } -// } -// }); -// return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; -// } else { - CompletableFuture future = null; - if (single) { - for (WebSocket websocket : websockets.values()) { - if (predicate != null && !predicate.test(websocket)) continue; - future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); - } - } else { - for (List list : websockets2.values()) { - for (WebSocket websocket : list) { - if (predicate != null && !predicate.test(websocket)) continue; - future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); - } - } - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - //} - } - - @Comment("给指定用户组发送消息") - public CompletableFuture sendLocalMessage(final Object message, final boolean last, final Stream userids) { - Object[] array = userids.toArray(); - Serializable[] ss = new Serializable[array.length]; - for (int i = 0; i < array.length; i++) { - ss[i] = (Serializable) array[i]; - } - return WebSocketEngine.this.sendLocalMessage(message, last, ss); - } - - @Comment("给指定用户组发送消息") - public CompletableFuture sendLocalMessage(final Object message, final boolean last, final Serializable... userids) { - if (message instanceof CompletableFuture) { - return ((CompletableFuture) message).thenCompose((json) -> WebSocketEngine.this.sendLocalMessage(json, last, userids)); - } -// final boolean more = userids.length > 1; -// if (more) { -// Supplier bufferSupplier = null; -// Consumer bufferConsumer = null; -// //此处的WebSocketPacket只能是包含payload或bytes内容的,不能包含sendConvert、sendJson、sendBuffers -// final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message -// : ((message == null || message instanceof CharSequence || message instanceof byte[]) -// ? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last)); -// //packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor); -// CompletableFuture future = null; -// if (single) { -// for (Serializable userid : userids) { -// WebSocket websocket = websockets.get(userid); -// if (websocket == null) continue; -// if (bufferSupplier == null) { -// bufferSupplier = websocket.getBufferSupplier(); -// bufferConsumer = websocket.getBufferConsumer(); -// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); -// } -// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); -// } -// } else { -// for (Serializable userid : userids) { -// List list = websockets2.get(userid); -// if (list == null) continue; -// for (WebSocket websocket : list) { -// if (bufferSupplier == null) { -// bufferSupplier = websocket.getBufferSupplier(); -// bufferConsumer = websocket.getBufferConsumer(); -// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); -// } -// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); -// } -// } -// } -// final Consumer bufferConsumer0 = bufferConsumer; -// if (future != null) future.whenComplete((rs, ex) -> { -// if (packet.sendBuffers != null && bufferConsumer0 != null) { -// for (ByteBuffer buffer : packet.sendBuffers) { -// bufferConsumer0.accept(buffer); -// } -// } -// }); -// return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; -// } else { - CompletableFuture future = null; - if (single) { - for (Serializable userid : userids) { - WebSocket websocket = websockets.get(userid); - if (websocket == null) continue; - future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); - } - } else { - for (Serializable userid : userids) { - List list = websockets2.get(userid); - if (list == null) continue; - for (WebSocket websocket : list) { - future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); - } - } - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - //} - } - - @Comment("给指定WebSocket连接用户发起操作指令") - public CompletableFuture broadcastLocalAction(final WebSocketAction action) { - CompletableFuture future = null; - if (single) { - for (WebSocket websocket : websockets.values()) { - future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); - } - } else { - for (List list : websockets2.values()) { - for (WebSocket websocket : list) { - future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); - } - } - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - } - - @Comment("给指定用户组发送操作") - public CompletableFuture sendLocalAction(final WebSocketAction action, final Stream userids) { - Object[] array = userids.toArray(); - Serializable[] ss = new Serializable[array.length]; - for (int i = 0; i < array.length; i++) { - ss[i] = (Serializable) array[i]; - } - return WebSocketEngine.this.sendLocalAction(action, ss); - } - - @Comment("给指定用户组发送操作") - public CompletableFuture sendLocalAction(final WebSocketAction action, final Serializable... userids) { - CompletableFuture future = null; - if (single) { - for (Serializable userid : userids) { - WebSocket websocket = websockets.get(userid); - if (websocket == null) continue; - future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); - } - } else { - for (Serializable userid : userids) { - List list = websockets2.get(userid); - if (list == null) continue; - for (WebSocket websocket : list) { - future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); - } - } - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - } - - @Comment("获取最大连接数") - public int getLocalWsmaxconns() { - return this.wsmaxconns; - } - - @Comment("连接数是否达到上限") - public boolean isLocalConnLimited() { - if (this.wsmaxconns < 1) return false; - return currconns.get() >= this.wsmaxconns; - } - - @Comment("获取所有连接") - public Collection getLocalWebSockets() { - if (single) return websockets.values(); - List list = new ArrayList<>(); - websockets2.values().forEach(x -> list.addAll(x)); - return list; - } - - @Comment("获取所有连接") - public void forEachLocalWebSocket(Consumer consumer) { - if (consumer == null) return; - if (single) { - websockets.values().stream().forEach(consumer); - } else { - websockets2.values().forEach(x -> x.stream().forEach(consumer)); - } - } - - @Comment("获取当前连接总数") - public int getLocalWebSocketSize() { - if (single) return websockets.size(); - return (int) websockets2.values().stream().mapToInt(sublist -> sublist.size()).count(); - } - - @Comment("获取当前用户总数") - public Set getLocalUserSet() { - return single ? new LinkedHashSet<>(websockets.keySet()) : new LinkedHashSet<>(websockets2.keySet()); - } - - @Comment("获取当前用户总数") - public int getLocalUserSize() { - return single ? websockets.size() : websockets2.size(); - } - - @Comment("适用于单用户单连接模式") - public WebSocket findLocalWebSocket(Serializable userid) { - if (single) return websockets.get(userid); - List list = websockets2.get(userid); - return (list == null || list.isEmpty()) ? null : list.get(list.size() - 1); - } - - @Comment("适用于单用户多连接模式") - public Stream getLocalWebSockets(Serializable userid) { - if (single) { - WebSocket websocket = websockets.get(userid); - return websocket == null ? Stream.empty() : Stream.of(websocket); - } else { - List list = websockets2.get(userid); - return list == null ? Stream.empty() : list.stream(); - } - } - - public boolean existsLocalWebSocket(Serializable userid) { - return single ? websockets.containsKey(userid) : websockets2.containsKey(userid); - } - - public String getEngineid() { - return engineid; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import static org.redkale.net.http.WebSocketServlet.DEFAILT_LIVEINTERVAL; +import java.io.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.*; +import java.util.stream.*; +import org.redkale.convert.Convert; +import org.redkale.net.Cryptor; +import static org.redkale.net.http.WebSocket.RETCODE_GROUP_EMPTY; +import static org.redkale.net.http.WebSocketServlet.*; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class WebSocketEngine { + + @Comment("全局自增长ID, 为了确保在一个进程里多个WebSocketEngine定时发送ping时不会同时进行") + private static final AtomicInteger sequence = new AtomicInteger(); + + @Comment("Engine自增长序号ID") + private final int index; + + @Comment("当前WebSocket对应的Engine") + private final String engineid; + + @Comment("当前WebSocket对应的Node") + protected final WebSocketNode node; + + //HttpContext + protected final HttpContext context; + + //Convert + protected final Convert sendConvert; + + @Comment("是否单用户单连接") + protected final boolean single; + + @Comment("在线用户ID对应的WebSocket组,用于单用户单连接模式") + private final Map websockets = new ConcurrentHashMap<>(); + + @Comment("在线用户ID对应的WebSocket组,用于单用户多连接模式") + private final Map> websockets2 = new ConcurrentHashMap<>(); + + @Comment("当前连接数") + protected final AtomicInteger currconns = new AtomicInteger(); + + @Comment("用于PING的定时器") + private ScheduledThreadPoolExecutor scheduler; + + @Comment("日志") + protected final Logger logger; + + @Comment("PING的间隔秒数") + protected int liveinterval; + + @Comment("最大连接数, 为0表示无限制") + protected int wsmaxconns; + + @Comment("操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8)") + protected int wsthreads; + + @Comment("最大消息体长度, 小于1表示无限制") + protected int wsmaxbody; + + @Comment("接收客户端的分包(last=false)消息时是否自动合并包") + protected boolean mergemsg = true; + + @Comment("加密解密器") + protected Cryptor cryptor; + + protected WebSocketEngine(String engineid, boolean single, HttpContext context, int liveinterval, int wsmaxconns, + int wsthreads, int wsmaxbody, boolean mergemsg, Cryptor cryptor, WebSocketNode node, Convert sendConvert, Logger logger) { + this.engineid = engineid; + this.single = single; + this.context = context; + this.sendConvert = sendConvert; + this.node = node; + this.liveinterval = liveinterval; + this.wsmaxconns = wsmaxconns; + this.wsthreads = wsthreads; + this.wsmaxbody = wsmaxbody; + this.mergemsg = mergemsg; + this.cryptor = cryptor; + this.logger = logger; + this.index = sequence.getAndIncrement(); + } + + void init(AnyValue conf) { + AnyValue props = conf; + if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties"); + this.liveinterval = props == null ? (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval) : props.getIntValue(WEBPARAM__LIVEINTERVAL, (liveinterval < 0 ? DEFAILT_LIVEINTERVAL : liveinterval)); + if (liveinterval <= 0) return; + if (props != null) this.wsmaxconns = props.getIntValue(WEBPARAM__WSMAXCONNS, this.wsmaxconns); + if (props != null) this.wsthreads = props.getIntValue(WEBPARAM__WSTHREADS, this.wsthreads); + if (props != null) this.wsmaxbody = props.getIntValue(WEBPARAM__WSMAXBODY, this.wsmaxbody); + if (scheduler != null) return; + this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { + final Thread t = new Thread(r, "Redkale-" + engineid + "-WebSocket-LiveInterval-Thread"); + t.setDaemon(true); + return t; + }); + long delay = (liveinterval - System.currentTimeMillis() / 1000 % liveinterval) + index * 5; + final int intervalms = liveinterval * 1000; + scheduler.scheduleWithFixedDelay(() -> { + try { + long now = System.currentTimeMillis(); + getLocalWebSockets().stream().filter(x -> ((now - x.getLastReadTime()) > intervalms && (now - x.getLastSendTime()) > intervalms)).forEach(x -> x.sendPing()); + } catch (Throwable t) { + logger.log(Level.SEVERE, "WebSocketEngine schedule(interval=" + liveinterval + "s) ping error", t); + } + }, delay, liveinterval, TimeUnit.SECONDS); + if (logger.isLoggable(Level.FINEST)) logger.finest(this.getClass().getSimpleName() + "(" + engineid + ")" + " start keeplive(wsmaxconns:" + wsmaxconns + ", delay:" + delay + "s, interval:" + liveinterval + "s) scheduler executor"); + } + + void destroy(AnyValue conf) { + if (scheduler != null) scheduler.shutdownNow(); + } + + @Comment("添加WebSocket") + CompletableFuture addLocal(WebSocket socket) { + if (single) { + currconns.incrementAndGet(); + websockets.put(socket._userid, socket); + } else { //非线程安全, 在常规场景中无需锁 + List list = websockets2.get(socket._userid); + if (list == null) { + list = new CopyOnWriteArrayList<>(); + websockets2.put(socket._userid, list); + } + currconns.incrementAndGet(); + list.add(socket); + } + if (node != null) return node.connect(socket._userid); + return null; + } + + @Comment("从WebSocketEngine删除指定WebSocket") + CompletableFuture removeLocalThenDisconnect(WebSocket socket) { + Serializable userid = socket._userid; + if (userid == null) return null; //尚未登录成功 + if (single) { + currconns.decrementAndGet(); + websockets.remove(userid); + if (node != null) return node.disconnect(userid); + } else { //非线程安全, 在常规场景中无需锁 + List list = websockets2.get(userid); + if (list != null) { + currconns.decrementAndGet(); + list.remove(socket); + if (list.isEmpty()) { + websockets2.remove(userid); + return node.disconnect(userid); + } + } + } + return null; + } + + @Comment("更改WebSocket的userid") + CompletableFuture changeLocalUserid(WebSocket socket, final Serializable newuserid) { + if (newuserid == null) throw new NullPointerException("newuserid is null"); + final Serializable olduserid = socket._userid; + socket._userid = newuserid; + if (single) { + websockets.remove(olduserid); + websockets.put(newuserid, socket); + } else { //非线程安全, 在常规场景中无需锁 + List oldlist = websockets2.get(olduserid); + if (oldlist != null) { + oldlist.remove(socket); + if (oldlist.isEmpty()) websockets2.remove(olduserid); + } + List newlist = websockets2.get(newuserid); + if (newlist == null) { + newlist = new CopyOnWriteArrayList<>(); + websockets2.put(newuserid, newlist); + } + newlist.add(socket); + } + if (node != null) return node.changeUserid(olduserid, newuserid); + return CompletableFuture.completedFuture(null); + } + + @Comment("强制关闭本地用户的WebSocket") + public int forceCloseLocalWebSocket(Serializable userid) { + if (single) { + WebSocket ws = websockets.get(userid); + if (ws == null) return 0; + ws.close(); + return 1; + } + List list = websockets2.get(userid); + if (list == null || list.isEmpty()) return 0; + List list2 = new ArrayList<>(list); + for (WebSocket ws : list2) { + ws.close(); + } + return list2.size(); + } + + @Comment("给所有连接用户发送消息") + public CompletableFuture broadcastLocalMessage(final Object message, final boolean last) { + return WebSocketEngine.this.broadcastLocalMessage((Predicate) null, message, last); + } + + @Comment("给指定WebSocket连接用户发送消息") + public CompletableFuture broadcastLocalMessage(final WebSocketRange wsrange, final Object message, final boolean last) { + Predicate predicate = wsrange == null ? null : (ws) -> ws.predicate(wsrange); + return WebSocketEngine.this.broadcastLocalMessage(predicate, message, last); + } + + @Comment("给指定WebSocket连接用户发送消息") + public CompletableFuture broadcastLocalMessage(final Predicate predicate, final Object message, final boolean last) { + if (message instanceof CompletableFuture) { + return ((CompletableFuture) message).thenCompose((json) -> WebSocketEngine.this.broadcastLocalMessage(predicate, json, last)); + } +// final boolean more = (!(message instanceof WebSocketPacket) || ((WebSocketPacket) message).sendBuffers == null); +// if (more) { +// Supplier bufferSupplier = null; +// Consumer bufferConsumer = null; +// //此处的WebSocketPacket只能是包含payload或bytes内容的,不能包含sendConvert、sendJson、sendBuffers +// final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message +// : ((message == null || message instanceof CharSequence || message instanceof byte[]) +// ? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last)); +// //packet.setSendBuffers(packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor)); +// CompletableFuture future = null; +// if (single) { +// for (WebSocket websocket : websockets.values()) { +// if (predicate != null && !predicate.test(websocket)) continue; +// if (bufferSupplier == null) { +// bufferSupplier = websocket.getBufferSupplier(); +// bufferConsumer = websocket.getBufferConsumer(); +// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); +// } +// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); +// } +// } else { +// for (List list : websockets2.values()) { +// for (WebSocket websocket : list) { +// if (predicate != null && !predicate.test(websocket)) continue; +// if (bufferSupplier == null) { +// bufferSupplier = websocket.getBufferSupplier(); +// bufferConsumer = websocket.getBufferConsumer(); +// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); +// } +// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); +// } +// } +// } +// final Consumer bufferConsumer0 = bufferConsumer; +// if (future != null) future.whenComplete((rs, ex) -> { +// if (packet.sendBuffers != null && bufferConsumer0 != null) { +// for (ByteBuffer buffer : packet.sendBuffers) { +// bufferConsumer0.accept(buffer); +// } +// } +// }); +// return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; +// } else { + CompletableFuture future = null; + if (single) { + for (WebSocket websocket : websockets.values()) { + if (predicate != null && !predicate.test(websocket)) continue; + future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); + } + } else { + for (List list : websockets2.values()) { + for (WebSocket websocket : list) { + if (predicate != null && !predicate.test(websocket)) continue; + future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); + } + } + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + //} + } + + @Comment("给指定用户组发送消息") + public CompletableFuture sendLocalMessage(final Object message, final boolean last, final Stream userids) { + Object[] array = userids.toArray(); + Serializable[] ss = new Serializable[array.length]; + for (int i = 0; i < array.length; i++) { + ss[i] = (Serializable) array[i]; + } + return WebSocketEngine.this.sendLocalMessage(message, last, ss); + } + + @Comment("给指定用户组发送消息") + public CompletableFuture sendLocalMessage(final Object message, final boolean last, final Serializable... userids) { + if (message instanceof CompletableFuture) { + return ((CompletableFuture) message).thenCompose((json) -> WebSocketEngine.this.sendLocalMessage(json, last, userids)); + } +// final boolean more = userids.length > 1; +// if (more) { +// Supplier bufferSupplier = null; +// Consumer bufferConsumer = null; +// //此处的WebSocketPacket只能是包含payload或bytes内容的,不能包含sendConvert、sendJson、sendBuffers +// final WebSocketPacket packet = (message instanceof WebSocketPacket) ? (WebSocketPacket) message +// : ((message == null || message instanceof CharSequence || message instanceof byte[]) +// ? new WebSocketPacket((Serializable) message, last) : new WebSocketPacket(this.sendConvert, message, last)); +// //packet.encode(context.getBufferSupplier(), context.getBufferConsumer(), cryptor); +// CompletableFuture future = null; +// if (single) { +// for (Serializable userid : userids) { +// WebSocket websocket = websockets.get(userid); +// if (websocket == null) continue; +// if (bufferSupplier == null) { +// bufferSupplier = websocket.getBufferSupplier(); +// bufferConsumer = websocket.getBufferConsumer(); +// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); +// } +// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); +// } +// } else { +// for (Serializable userid : userids) { +// List list = websockets2.get(userid); +// if (list == null) continue; +// for (WebSocket websocket : list) { +// if (bufferSupplier == null) { +// bufferSupplier = websocket.getBufferSupplier(); +// bufferConsumer = websocket.getBufferConsumer(); +// packet.encodePacket(bufferSupplier, bufferConsumer, cryptor); +// } +// future = future == null ? websocket.sendPacket(packet) : future.thenCombine(websocket.sendPacket(packet), (a, b) -> a | (Integer) b); +// } +// } +// } +// final Consumer bufferConsumer0 = bufferConsumer; +// if (future != null) future.whenComplete((rs, ex) -> { +// if (packet.sendBuffers != null && bufferConsumer0 != null) { +// for (ByteBuffer buffer : packet.sendBuffers) { +// bufferConsumer0.accept(buffer); +// } +// } +// }); +// return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; +// } else { + CompletableFuture future = null; + if (single) { + for (Serializable userid : userids) { + WebSocket websocket = websockets.get(userid); + if (websocket == null) continue; + future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); + } + } else { + for (Serializable userid : userids) { + List list = websockets2.get(userid); + if (list == null) continue; + for (WebSocket websocket : list) { + future = future == null ? websocket.send(message, last) : future.thenCombine(websocket.send(message, last), (a, b) -> a | (Integer) b); + } + } + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + //} + } + + @Comment("给指定WebSocket连接用户发起操作指令") + public CompletableFuture broadcastLocalAction(final WebSocketAction action) { + CompletableFuture future = null; + if (single) { + for (WebSocket websocket : websockets.values()) { + future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); + } + } else { + for (List list : websockets2.values()) { + for (WebSocket websocket : list) { + future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); + } + } + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + } + + @Comment("给指定用户组发送操作") + public CompletableFuture sendLocalAction(final WebSocketAction action, final Stream userids) { + Object[] array = userids.toArray(); + Serializable[] ss = new Serializable[array.length]; + for (int i = 0; i < array.length; i++) { + ss[i] = (Serializable) array[i]; + } + return WebSocketEngine.this.sendLocalAction(action, ss); + } + + @Comment("给指定用户组发送操作") + public CompletableFuture sendLocalAction(final WebSocketAction action, final Serializable... userids) { + CompletableFuture future = null; + if (single) { + for (Serializable userid : userids) { + WebSocket websocket = websockets.get(userid); + if (websocket == null) continue; + future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); + } + } else { + for (Serializable userid : userids) { + List list = websockets2.get(userid); + if (list == null) continue; + for (WebSocket websocket : list) { + future = future == null ? websocket.action(action) : future.thenCombine(websocket.action(action), (a, b) -> a | (Integer) b); + } + } + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + } + + @Comment("获取WebSocketNode对象") + public WebSocketNode getWebSocketNode() { + return node; + } + + @Comment("获取最大连接数") + public int getLocalWsmaxconns() { + return this.wsmaxconns; + } + + @Comment("连接数是否达到上限") + public boolean isLocalConnLimited() { + if (this.wsmaxconns < 1) return false; + return currconns.get() >= this.wsmaxconns; + } + + @Comment("获取所有连接") + public Collection getLocalWebSockets() { + if (single) return websockets.values(); + List list = new ArrayList<>(); + websockets2.values().forEach(x -> list.addAll(x)); + return list; + } + + @Comment("获取所有连接") + public void forEachLocalWebSocket(Consumer consumer) { + if (consumer == null) return; + if (single) { + websockets.values().stream().forEach(consumer); + } else { + websockets2.values().forEach(x -> x.stream().forEach(consumer)); + } + } + + @Comment("获取当前连接总数") + public int getLocalWebSocketSize() { + if (single) return websockets.size(); + return (int) websockets2.values().stream().mapToInt(sublist -> sublist.size()).count(); + } + + @Comment("获取当前用户总数") + public Set getLocalUserSet() { + return single ? new LinkedHashSet<>(websockets.keySet()) : new LinkedHashSet<>(websockets2.keySet()); + } + + @Comment("获取当前用户总数") + public int getLocalUserSize() { + return single ? websockets.size() : websockets2.size(); + } + + @Comment("适用于单用户单连接模式") + public WebSocket findLocalWebSocket(Serializable userid) { + if (single) return websockets.get(userid); + List list = websockets2.get(userid); + return (list == null || list.isEmpty()) ? null : list.get(list.size() - 1); + } + + @Comment("适用于单用户多连接模式") + public Stream getLocalWebSockets(Serializable userid) { + if (single) { + WebSocket websocket = websockets.get(userid); + return websocket == null ? Stream.empty() : Stream.of(websocket); + } else { + List list = websockets2.get(userid); + return list == null ? Stream.empty() : list.stream(); + } + } + + public boolean existsLocalWebSocket(Serializable userid) { + return single ? websockets.containsKey(userid) : websockets2.containsKey(userid); + } + + public String getEngineid() { + return engineid; + } +} diff --git a/src/org/redkale/net/http/WebSocketNode.java b/src/main/java/org/redkale/net/http/WebSocketNode.java similarity index 97% rename from src/org/redkale/net/http/WebSocketNode.java rename to src/main/java/org/redkale/net/http/WebSocketNode.java index 567d6f146..3c7c0819f 100644 --- a/src/org/redkale/net/http/WebSocketNode.java +++ b/src/main/java/org/redkale/net/http/WebSocketNode.java @@ -1,1019 +1,1022 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import static org.redkale.net.http.WebSocket.*; -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import java.util.stream.*; -import javax.annotation.*; -import org.redkale.boot.*; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.mq.MessageAgent; -import org.redkale.service.*; -import org.redkale.source.*; -import org.redkale.util.*; - -/** - * 注: 部署了WebSocketNodeService就必然要配置SNCP协议的Server,不然无法做到WebSocketNode.sendMessage方法的有效性 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class WebSocketNode { - - @Comment("存储用户ID的key前缀") - public static final String WS_SOURCE_KEY_USERID_PREFIX = "sncpws_uid:"; - - @Comment("存储当前SNCP节点列表的key") - public static final String WS_SOURCE_KEY_NODES = "sncpws_nodes"; - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - //"SNCP_ADDR" 如果不是分布式(没有SNCP) 值为null - @Resource(name = Application.RESNAME_SNCP_ADDR) - protected InetSocketAddress localSncpAddress; //为SncpServer的服务address - - protected WebSocketAddress wsNodeAddress; - - protected String name; - - //如果不是分布式(没有SNCP) 值为null - @RpcRemote - protected WebSocketNode remoteNode; - - @Resource(name = "$_sendconvert") - protected Convert sendConvert; - - //存放所有用户分布在节点上的队列信息,Set 为 sncpnode 的集合, key: groupid - //集合包含 localSncpAddress - //如果不是分布式(没有SNCP),source 将不会被用到 - @Resource(name = "$") - protected CacheSource source; - - //当前节点的本地WebSocketEngine - protected WebSocketEngine localEngine; - - protected MessageAgent messageAgent; - - protected Semaphore semaphore; - - private int tryAcquireSeconds = 12; - - public void init(AnyValue conf) { - this.tryAcquireSeconds = Integer.getInteger("WebSocketNode.tryAcquireSeconds", 12); - - if (localEngine != null) { - int wsthreads = localEngine.wsthreads; - if (wsthreads == 0) wsthreads = Runtime.getRuntime().availableProcessors() * 8; - if (wsthreads > 0) this.semaphore = new Semaphore(wsthreads); - } - String mqtopic = this.messageAgent == null ? null : this.messageAgent.generateSncpReqTopic((Service) this); - if (mqtopic != null || this.localSncpAddress != null) { - this.wsNodeAddress = new WebSocketAddress(mqtopic, localSncpAddress); - } - if (source != null && wsNodeAddress != null) { - source.appendSetItem(WS_SOURCE_KEY_NODES, WebSocketAddress.class, this.wsNodeAddress); - } - } - - public void destroy(AnyValue conf) { - } - - @Local - public final Semaphore getSemaphore() { - return semaphore; - } - - @Local - public final MessageAgent getMessageAgent() { - return messageAgent; - } - - @Local - protected void postDestroy(AnyValue conf) { - if (this.localEngine == null) return; - //关掉所有本地本地WebSocket - this.localEngine.getLocalWebSockets().forEach(g -> g.close()); - if (source != null && wsNodeAddress != null) { - source.removeSetItem(WS_SOURCE_KEY_NODES, WebSocketAddress.class, this.wsNodeAddress); - } - } - - protected abstract CompletableFuture> getWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid); - - protected abstract CompletableFuture sendMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids); - - protected abstract CompletableFuture broadcastMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last); - - protected abstract CompletableFuture sendAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable... userids); - - protected abstract CompletableFuture broadcastAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action); - - protected abstract CompletableFuture getUserSize(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress); - - protected abstract CompletableFuture connect(Serializable userid, WebSocketAddress wsaddr); - - protected abstract CompletableFuture disconnect(Serializable userid, WebSocketAddress wsaddr); - - protected abstract CompletableFuture changeUserid(Serializable fromuserid, Serializable touserid, WebSocketAddress wsaddr); - - protected abstract CompletableFuture existsWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress); - - protected abstract CompletableFuture forceCloseWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress); - - //-------------------------------------------------------------------------------- - final CompletableFuture connect(final Serializable userid) { - if (logger.isLoggable(Level.FINEST)) logger.finest(wsNodeAddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); - return connect(userid, wsNodeAddress); - } - - final CompletableFuture disconnect(final Serializable userid) { - if (logger.isLoggable(Level.FINEST)) logger.finest(wsNodeAddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); - return disconnect(userid, wsNodeAddress); - } - - final CompletableFuture changeUserid(Serializable olduserid, final Serializable newuserid) { - if (logger.isLoggable(Level.FINEST)) logger.finest(wsNodeAddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); - return changeUserid(olduserid, newuserid, wsNodeAddress); - } - - public final String getName() { - return name; - } - - //-------------------------------------------------------------------------------- - /** - * 获取目标地址
    - * 该方法仅供内部调用 - * - * @param topic RpcTargetTopic - * @param targetAddress InetSocketAddress - * @param userid Serializable - * - * @return 客户端地址列表 - */ - protected CompletableFuture> remoteWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) { - if (remoteNode == null) return CompletableFuture.completedFuture(null); - try { - return remoteNode.getWebSocketAddresses(topic, targetAddress, userid); - } catch (Exception e) { - logger.log(Level.WARNING, "remote " + targetAddress + " websocket getOnlineRemoteAddresses error", e); - return CompletableFuture.completedFuture(null); - } - } - - /** - * 获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表
    - * WebSocketAddress 为 SNCP节点地址 - * - * @param userid Serializable - * - * @return 地址列表 - */ - public CompletableFuture> getRpcNodeAddresses(final Serializable userid) { - if (this.source != null) { - tryAcquireSemaphore(); - CompletableFuture> result = this.source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); - if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore()); - return result; - } - List rs = new ArrayList<>(); - rs.add(this.wsNodeAddress); - return CompletableFuture.completedFuture(rs); - } - - /** - * 获取在线用户的详细连接信息
    - * Map.key 为 SNCP节点地址, 含值为null的key表示没有分布式 - * Map.value 为 用户客户端的IP - * - * @param userid Serializable - * - * @return 地址集合 - */ - public CompletableFuture>> getRpcNodeWebSocketAddresses(final Serializable userid) { - CompletableFuture> sncpFuture = getRpcNodeAddresses(userid); - return sncpFuture.thenCompose((Collection addrs) -> { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>()); - CompletableFuture>> future = null; - for (final WebSocketAddress nodeAddress : addrs) { - CompletableFuture>> mapFuture = getWebSocketAddresses(nodeAddress.getTopic(), nodeAddress.getAddr(), userid) - .thenCompose((List list) -> CompletableFuture.completedFuture(Utility.ofMap(nodeAddress, list))); - future = future == null ? mapFuture : future.thenCombine(mapFuture, (a, b) -> Utility.merge(a, b)); - } - return future == null ? CompletableFuture.completedFuture(new HashMap<>()) : future; - }); - } - -// public CompletableFuture getUserSize() { -// if (this.localEngine != null && this.source == null) { -// return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); -// } -// tryAcquireSemaphore(); -// CompletableFuture> listFuture = this.source.queryKeysStartsWithAsync(WS_SOURCE_KEY_USERID_PREFIX); -// CompletableFuture rs = listFuture.thenApply(v -> v.size()); -// if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); -// return rs; -// } - /** - * 获取在线用户总数 - * - * - * @return boolean - */ - @Local - public CompletableFuture getUserSize() { - if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 - return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); - } - CompletableFuture localFuture = this.localEngine == null ? null : CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); - tryAcquireSemaphore(); - CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_NODES, WebSocketAddress.class); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket getUserSize on " + addrs); - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.getUserSize(addr.getTopic(), addr.getAddr()) - : future.thenCombine(remoteNode.getUserSize(addr.getTopic(), addr.getAddr()), (a, b) -> a + b); - } - return future == null ? CompletableFuture.completedFuture(0) : future; - }); - return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a + b); - } - - /** - * 获取在线用户总数 - * - * - * @return boolean - */ - public CompletableFuture> getUserSet() { - if (this.localEngine != null && this.source == null) { - return CompletableFuture.completedFuture(new LinkedHashSet<>(this.localEngine.getLocalUserSet().stream().map(x -> String.valueOf(x)).collect(Collectors.toList()))); - } - tryAcquireSemaphore(); - CompletableFuture> listFuture = this.source.queryKeysStartsWithAsync(WS_SOURCE_KEY_USERID_PREFIX); - CompletableFuture> rs = listFuture.thenApply(v -> new LinkedHashSet<>(v.stream().map(x -> x.substring(WS_SOURCE_KEY_USERID_PREFIX.length())).collect(Collectors.toList()))); - if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); - return rs; - } - - /** - * 判断指定用户是否WebSocket在线 - * - * @param userid Serializable - * - * @return boolean - */ - @Local - public CompletableFuture existsWebSocket(final Serializable userid) { - if (userid instanceof WebSocketUserAddress) return existsWebSocket((WebSocketUserAddress) userid); - CompletableFuture localFuture = null; - if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid)); - if (this.source == null || this.remoteNode == null) { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); - //没有CacheSource就不会有分布式节点 - return localFuture == null ? CompletableFuture.completedFuture(false) : localFuture; - } - //远程节点关闭 - tryAcquireSemaphore(); - CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { - //if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr()) - : future.thenCombine(remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(false) : future; - }); - return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); - } - - /** - * 判断指定用户是否WebSocket在线 - * - * @param userAddress WebSocketUserAddress - * - * @return boolean - */ - @Local - public CompletableFuture existsWebSocket(final WebSocketUserAddress userAddress) { - if (this.localEngine != null && localEngine.existsLocalWebSocket(userAddress.userid())) return CompletableFuture.completedFuture(true); - if (this.source == null || this.remoteNode == null) { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); - //没有CacheSource就不会有分布式节点 - return CompletableFuture.completedFuture(false); - } - Collection addrs = userAddress.addresses(); - if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值 - if (userAddress.address() != null) { - if (addrs == null) addrs = new ArrayList<>(); - addrs.add(userAddress.address()); - } - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr()) - : future.thenCombine(remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr()), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(false) : future; - } - - /** - * 强制关闭用户WebSocket - * - * @param userid Serializable - * - * @return int - */ - @Local - public CompletableFuture forceCloseWebSocket(final Serializable userid) { - return forceCloseWebSocket(userid, (WebSocketUserAddress) null); - } - - /** - * 强制关闭用户WebSocket - * - * @param userAddress WebSocketUserAddress - * - * @return int - */ - @Local - public CompletableFuture forceCloseWebSocket(final WebSocketUserAddress userAddress) { - return forceCloseWebSocket(null, userAddress); - } - - private CompletableFuture forceCloseWebSocket(final Serializable userid, final WebSocketUserAddress userAddress) { - CompletableFuture localFuture = null; - if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid())); - if (this.source == null || this.remoteNode == null) { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); - //没有CacheSource就不会有分布式节点 - return localFuture == null ? CompletableFuture.completedFuture(0) : localFuture; - } - //远程节点关闭 - CompletableFuture> addrsFuture; - if (userAddress == null) { - tryAcquireSemaphore(); - addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - } else { - Collection addrs = userAddress.addresses(); - if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值 - if (userAddress.address() != null) { - if (addrs == null) addrs = new ArrayList<>(); - addrs.add(userAddress.address()); - } - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); - addrsFuture = CompletableFuture.completedFuture(addrs); - } - CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr()) - : future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a + b); - } - return future == null ? CompletableFuture.completedFuture(0) : future; - }); - return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a + b); - } - - //-------------------------------------------------------------------------------- - /** - * 获取本地的WebSocketEngine,没有则返回null - * - * - * @return WebSocketEngine - */ - @Local - public final WebSocketEngine getLocalWebSocketEngine() { - return this.localEngine; - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param message 消息内容 - * @param useridOrAddrs Stream - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture sendMessage(Object message, final Stream useridOrAddrs) { - return sendMessage((Convert) null, message, true, useridOrAddrs); - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param message 消息内容 - * @param useridOrAddrs Serializable[] - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture sendMessage(Object message, final Serializable... useridOrAddrs) { - return sendMessage((Convert) null, message, true, useridOrAddrs); - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param convert Convert - * @param message 消息内容 - * @param useridOrAddrs Stream - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture sendMessage(final Convert convert, Object message, final Stream useridOrAddrs) { - return sendMessage(convert, message, true, useridOrAddrs); - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param convert Convert - * @param message 消息内容 - * @param useridOrAddrs Serializable[] - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture sendMessage(final Convert convert, Object message, final Serializable... useridOrAddrs) { - return sendMessage(convert, message, true, useridOrAddrs); - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param message 消息内容 - * @param last 是否最后一条 - * @param useridOrAddrs Stream - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture sendMessage(final Object message, final boolean last, final Stream useridOrAddrs) { - return sendMessage((Convert) null, message, last, useridOrAddrs); - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param message 消息内容 - * @param last 是否最后一条 - * @param useridOrAddrs Serializable[] - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture sendMessage(final Object message, final boolean last, final Serializable... useridOrAddrs) { - return sendMessage((Convert) null, message, last, useridOrAddrs); - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param convert Convert - * @param message0 消息内容 - * @param last 是否最后一条 - * @param userids Stream - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final Stream userids) { - Object[] array = userids.toArray(); - Serializable[] ss = new Serializable[array.length]; - for (int i = 0; i < array.length; i++) { - ss[i] = (Serializable) array[i]; - } - return sendMessage(convert, message0, last, ss); - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param convert Convert - * @param message0 消息内容 - * @param last 是否最后一条 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) { - if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - if (userids[0] instanceof WebSocketUserAddress) { - WebSocketUserAddress[] useraddrs = new WebSocketUserAddress[userids.length]; - for (int i = 0; i < useraddrs.length; i++) { - useraddrs[i] = (WebSocketUserAddress) userids[i]; - } - return sendMessage(convert, message0, last, useraddrs); - } - if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids)); - final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(WebSocketPacket.FrameType.TEXT, ((TextConvert) convert).convertToBytes(message0), last) : new WebSocketPacket(WebSocketPacket.FrameType.BINARY, ((BinaryConvert) convert).convertToBytes(message0), last)); - if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 - return this.localEngine.sendLocalMessage(message, last, userids); - } - final Object remoteMessage = formatRemoteMessage(message); - CompletableFuture rsfuture; - if (userids.length == 1) { - rsfuture = sendOneUserMessage(remoteMessage, last, userids[0]); - } else { - String[] keys = new String[userids.length]; - final Map keyuser = new HashMap<>(); - for (int i = 0; i < userids.length; i++) { - keys[i] = WS_SOURCE_KEY_USERID_PREFIX + userids[i]; - keyuser.put(keys[i], userids[i]); - } - tryAcquireSemaphore(); - CompletableFuture>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - rsfuture = addrsFuture.thenCompose((Map> addrs) -> { - if (addrs == null || addrs.isEmpty()) { - if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node "); - return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - } - Map> addrUsers = new HashMap<>(); - addrs.forEach((key, as) -> { - for (WebSocketAddress a : as) { - addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key)); - } - }); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found message-addr-userids: " + addrUsers); - } - CompletableFuture future = null; - for (Map.Entry> en : addrUsers.entrySet()) { - Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); - future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids) - : future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - }); - } - return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture; - } - - /** - * 向指定用户发送消息,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param convert Convert - * @param message0 消息内容 - * @param last 是否最后一条 - * @param useraddrs WebSocketUserAddress[] - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final WebSocketUserAddress... useraddrs) { - if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, useraddrs)); - final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); - if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 - return this.localEngine.sendLocalMessage(message, last, userAddressToUserids(useraddrs)); - } - - final Object remoteMessage = formatRemoteMessage(message); - final Map> addrUsers = userAddressToAddrMap(useraddrs); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found message-addr-userids: " + addrUsers); - } - CompletableFuture future = null; - for (Map.Entry> en : addrUsers.entrySet()) { - Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); - future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids) - : future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - - } - - protected CompletableFuture sendOneUserMessage(final Object message, final boolean last, final Serializable userid) { - if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneUserMessage(msg, last, userid)); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("websocket want send message {userid:" + userid + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); - } - CompletableFuture localFuture = null; - if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid); - if (this.source == null || this.remoteNode == null) { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); - //没有CacheSource就不会有分布式节点 - return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; - } - //远程节点发送消息 - final Object remoteMessage = formatRemoteMessage(message); - tryAcquireSemaphore(); - CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { - if (addrs == null || addrs.isEmpty()) { - if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); - return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - } - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + wsNodeAddress + ") found userid:" + userid + " on " + addrs); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid) - : future.thenCombine(remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - }); - return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); - } - - protected CompletableFuture sendOneAddrMessage(final WebSocketAddress addr, final Object message, final boolean last, final Serializable... userids) { - if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneAddrMessage(addr, msg, last, userids)); - if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的 - logger.finest("websocket want send message {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); - } - if (Objects.equals(addr, this.wsNodeAddress)) { - return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids); - } - if (this.source == null || this.remoteNode == null) { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); - //没有CacheSource就不会有分布式节点 - return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - } - final Object remoteMessage = formatRemoteMessage(message); - return remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userids); - } - - protected Serializable[] userAddressToUserids(WebSocketUserAddress... useraddrs) { - if (useraddrs == null || useraddrs.length == 1) return new Serializable[0]; - Set set = new HashSet<>(); - for (WebSocketUserAddress userAddress : useraddrs) { - set.add(userAddress.userid()); - } - return set.toArray(new Serializable[set.size()]); - } - - protected Map> userAddressToAddrMap(WebSocketUserAddress... useraddrs) { - final Map> addrUsers = new HashMap<>(); - for (WebSocketUserAddress userAddress : useraddrs) { - if (userAddress.address() != null) { - addrUsers.computeIfAbsent(userAddress.address(), k -> new ArrayList<>()).add(userAddress.userid()); - } - if (userAddress.addresses() != null) { - for (WebSocketAddress addr : userAddress.addresses()) { - if (addr != null) { - addrUsers.computeIfAbsent(addr, k -> new ArrayList<>()).add(userAddress.userid()); - } - } - } - } - return addrUsers; - } - - /** - * 广播消息, 给所有人发消息 - * - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture broadcastMessage(final Object message) { - return broadcastMessage((Convert) null, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message) { - return broadcastMessage(wsrange, (Convert) null, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param convert Convert - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture broadcastMessage(final Convert convert, final Object message) { - return broadcastMessage(convert, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param convert Convert - * @param message 消息内容 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) { - return broadcastMessage(wsrange, convert, message, true); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param message 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture broadcastMessage(final Object message, final boolean last) { - return broadcastMessage((Convert) null, message, last); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param message 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) { - return broadcastMessage(wsrange, (Convert) null, message, last); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param convert Convert - * @param message0 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public final CompletableFuture broadcastMessage(final Convert convert, final Object message0, final boolean last) { - return broadcastMessage((WebSocketRange) null, convert, message0, last); - } - - /** - * 广播消息, 给所有人发消息 - * - * @param wsrange 过滤条件 - * @param convert Convert - * @param message0 消息内容 - * @param last 是否最后一条 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) { - if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last)); - final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); - if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 - return this.localEngine.broadcastLocalMessage(wsrange, message, last); - } - final Object remoteMessage = formatRemoteMessage(message); - CompletableFuture localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalMessage(wsrange, message, last); - tryAcquireSemaphore(); - CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_NODES, WebSocketAddress.class); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs); - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last) - : future.thenCombine(remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(0) : future; - }); - return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); - } - - /** - * 广播操作, 给所有人发操作 - * - * @param action 操作参数 - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public CompletableFuture broadcastAction(final WebSocketAction action) { - if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 - return this.localEngine.broadcastLocalAction(action); - } - CompletableFuture localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalAction(action); - tryAcquireSemaphore(); - CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_NODES, WebSocketAddress.class); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast action (" + action + ") on " + addrs); - if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action) - : future.thenCombine(remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(0) : future; - }); - return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); - } - - /** - * 向指定用户发送操作,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param action 操作参数 - * @param userids Serializable[] - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public CompletableFuture sendAction(final WebSocketAction action, final Serializable... userids) { - if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - if (userids[0] instanceof WebSocketUserAddress) { - WebSocketUserAddress[] useraddrs = new WebSocketUserAddress[userids.length]; - for (int i = 0; i < useraddrs.length; i++) { - useraddrs[i] = (WebSocketUserAddress) userids[i]; - } - return sendAction(action, useraddrs); - } - if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 - return this.localEngine.sendLocalAction(action, userids); - } - CompletableFuture rsfuture; - if (userids.length == 1) { - rsfuture = sendOneUserAction(action, userids[0]); - } else { - String[] keys = new String[userids.length]; - final Map keyuser = new HashMap<>(); - for (int i = 0; i < userids.length; i++) { - keys[i] = WS_SOURCE_KEY_USERID_PREFIX + userids[i]; - keyuser.put(keys[i], userids[i]); - } - tryAcquireSemaphore(); - CompletableFuture>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - rsfuture = addrsFuture.thenCompose((Map> addrs) -> { - if (addrs == null || addrs.isEmpty()) { - if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node "); - return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - } - Map> addrUsers = new HashMap<>(); - addrs.forEach((key, as) -> { - for (WebSocketAddress a : as) { - addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key)); - } - }); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found action-userid-addrs: " + addrUsers); - } - CompletableFuture future = null; - for (Map.Entry> en : addrUsers.entrySet()) { - Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); - future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids) - : future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - }); - } - return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture; - } - - /** - * 向指定用户发送操作,先发送本地连接,再发送远程连接
    - * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 - * - * @param action 操作参数 - * @param useraddrs WebSocketUserAddress[] - * - * @return 为0表示成功, 其他值表示部分发送异常 - */ - @Local - public CompletableFuture sendAction(final WebSocketAction action, final WebSocketUserAddress... useraddrs) { - if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 - return this.localEngine.sendLocalAction(action, userAddressToUserids(useraddrs)); - } - - final Map> addrUsers = userAddressToAddrMap(useraddrs); - if (logger.isLoggable(Level.FINEST)) { - logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found action-userid-addrs: " + addrUsers); - } - CompletableFuture future = null; - for (Map.Entry> en : addrUsers.entrySet()) { - Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); - future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids) - : future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - } - - protected CompletableFuture sendOneUserAction(final WebSocketAction action, final Serializable userid) { - if (logger.isLoggable(Level.FINEST)) { - logger.finest("websocket want send action {userid:" + userid + ", action:" + action + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); - } - CompletableFuture localFuture = null; - if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid); - if (this.source == null || this.remoteNode == null) { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); - //没有CacheSource就不会有分布式节点 - return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; - } - //远程节点发送操作 - tryAcquireSemaphore(); - CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); - if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); - CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { - if (addrs == null || addrs.isEmpty()) { - if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); - return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - } - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs); - CompletableFuture future = null; - for (WebSocketAddress addr : addrs) { - if (addr == null || addr.equals(wsNodeAddress)) continue; - future = future == null ? remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid) - : future.thenCombine(remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid), (a, b) -> a | b); - } - return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; - }); - return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); - } - - protected CompletableFuture sendOneAddrAction(final WebSocketAddress addr, final WebSocketAction action, final Serializable... userids) { - if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的 - logger.finest("websocket want send action {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", action:" + action + " from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); - } - if (Objects.equals(addr, this.wsNodeAddress)) { - return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids); - } - if (this.source == null || this.remoteNode == null) { - if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); - //没有CacheSource就不会有分布式节点 - return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - } - return remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userids); - } - - protected Object formatRemoteMessage(Object message) { - if (message instanceof WebSocketPacket) return message; - if (message instanceof byte[]) return message; - if (message instanceof CharSequence) return message; - if (sendConvert instanceof TextConvert) ((TextConvert) sendConvert).convertTo(message); - if (sendConvert instanceof BinaryConvert) ((BinaryConvert) sendConvert).convertTo(message); - return JsonConvert.root().convertTo(message); - } - - protected boolean tryAcquireSemaphore() { - if (this.semaphore == null) return true; - try { - return this.semaphore.tryAcquire(tryAcquireSeconds, TimeUnit.SECONDS); - } catch (Exception e) { - return false; - } - } - - protected void releaseSemaphore() { - if (this.semaphore != null) this.semaphore.release(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import static org.redkale.net.http.WebSocket.*; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.*; +import java.util.stream.*; +import javax.annotation.*; +import org.redkale.boot.*; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.mq.MessageAgent; +import org.redkale.service.*; +import org.redkale.source.*; +import org.redkale.util.*; + +/** + * 注: 部署了WebSocketNodeService就必然要配置SNCP协议的Server,不然无法做到WebSocketNode.sendMessage方法的有效性 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class WebSocketNode { + + @Comment("存储用户ID的key前缀") + public static final String WS_SOURCE_KEY_USERID_PREFIX = "sncpws_uid:"; + + @Comment("存储当前SNCP节点列表的key") + public static final String WS_SOURCE_KEY_NODES = "sncpws_nodes"; + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + //"SNCP_ADDR" 如果不是分布式(没有SNCP) 值为null + @Resource(name = Application.RESNAME_SNCP_ADDR) + protected InetSocketAddress localSncpAddress; //为SncpServer的服务address + + protected WebSocketAddress wsNodeAddress; + + protected String name; + + //如果不是分布式(没有SNCP) 值为null + @RpcRemote + protected WebSocketNode remoteNode; + + @Resource(name = "$_sendconvert") + protected Convert sendConvert; + + //存放所有用户分布在节点上的队列信息,Set 为 sncpnode 的集合, key: groupid + //集合包含 localSncpAddress + //如果不是分布式(没有SNCP),source 将不会被用到 + @Resource(name = "$") + protected CacheSource source; + + //当前节点的本地WebSocketEngine + protected WebSocketEngine localEngine; + + protected MessageAgent messageAgent; + + protected Semaphore semaphore; + + private int tryAcquireSeconds = 12; + + public void init(AnyValue conf) { + this.tryAcquireSeconds = Integer.getInteger("redkale.http.websocket.tryAcquireSeconds", 12); + + if (localEngine != null) { + int wsthreads = localEngine.wsthreads; + if (wsthreads == 0) wsthreads = Utility.cpus() * 8; + if (wsthreads > 0) this.semaphore = new Semaphore(wsthreads); + } + String mqtopic = this.messageAgent == null ? null : this.messageAgent.generateSncpReqTopic((Service) this); + if (mqtopic != null || this.localSncpAddress != null) { + this.wsNodeAddress = new WebSocketAddress(mqtopic, localSncpAddress); + } + if (source != null && this.wsNodeAddress == null) { //非分布式模式 + this.wsNodeAddress = new WebSocketAddress(mqtopic, new InetSocketAddress("127.0.0.1", 27)); + } + if (source != null && wsNodeAddress != null) { + source.appendSetItem(WS_SOURCE_KEY_NODES, WebSocketAddress.class, this.wsNodeAddress); + } + } + + public void destroy(AnyValue conf) { + } + + @Local + public final Semaphore getSemaphore() { + return semaphore; + } + + @Local + public final MessageAgent getMessageAgent() { + return messageAgent; + } + + @Local + protected void postDestroy(AnyValue conf) { + if (this.localEngine == null) return; + //关掉所有本地本地WebSocket + this.localEngine.getLocalWebSockets().forEach(g -> g.close()); + if (source != null && wsNodeAddress != null) { + source.removeSetItem(WS_SOURCE_KEY_NODES, WebSocketAddress.class, this.wsNodeAddress); + } + } + + protected abstract CompletableFuture> getWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid); + + protected abstract CompletableFuture sendMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids); + + protected abstract CompletableFuture broadcastMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketRange wsrange, Object message, boolean last); + + protected abstract CompletableFuture sendAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action, Serializable... userids); + + protected abstract CompletableFuture broadcastAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, WebSocketAction action); + + protected abstract CompletableFuture getUserSize(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress); + + protected abstract CompletableFuture connect(Serializable userid, WebSocketAddress wsaddr); + + protected abstract CompletableFuture disconnect(Serializable userid, WebSocketAddress wsaddr); + + protected abstract CompletableFuture changeUserid(Serializable fromuserid, Serializable touserid, WebSocketAddress wsaddr); + + protected abstract CompletableFuture existsWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress); + + protected abstract CompletableFuture forceCloseWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress); + + //-------------------------------------------------------------------------------- + final CompletableFuture connect(final Serializable userid) { + if (logger.isLoggable(Level.FINEST)) logger.finest(wsNodeAddress + " receive websocket connect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); + return connect(userid, wsNodeAddress); + } + + final CompletableFuture disconnect(final Serializable userid) { + if (logger.isLoggable(Level.FINEST)) logger.finest(wsNodeAddress + " receive websocket disconnect event (" + userid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); + return disconnect(userid, wsNodeAddress); + } + + final CompletableFuture changeUserid(Serializable olduserid, final Serializable newuserid) { + if (logger.isLoggable(Level.FINEST)) logger.finest(wsNodeAddress + " receive websocket changeUserid event (from " + olduserid + " to " + newuserid + " on " + (this.localEngine == null ? null : this.localEngine.getEngineid()) + ")."); + return changeUserid(olduserid, newuserid, wsNodeAddress); + } + + public final String getName() { + return name; + } + + //-------------------------------------------------------------------------------- + /** + * 获取目标地址
    + * 该方法仅供内部调用 + * + * @param topic RpcTargetTopic + * @param targetAddress InetSocketAddress + * @param userid Serializable + * + * @return 客户端地址列表 + */ + protected CompletableFuture> remoteWebSocketAddresses(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Serializable userid) { + if (remoteNode == null) return CompletableFuture.completedFuture(null); + try { + return remoteNode.getWebSocketAddresses(topic, targetAddress, userid); + } catch (Exception e) { + logger.log(Level.WARNING, "remote " + targetAddress + " websocket getOnlineRemoteAddresses error", e); + return CompletableFuture.completedFuture(null); + } + } + + /** + * 获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表
    + * WebSocketAddress 为 SNCP节点地址 + * + * @param userid Serializable + * + * @return 地址列表 + */ + public CompletableFuture> getRpcNodeAddresses(final Serializable userid) { + if (this.source != null) { + tryAcquireSemaphore(); + CompletableFuture> result = this.source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); + if (semaphore != null) result.whenComplete((r, e) -> releaseSemaphore()); + return result; + } + List rs = new ArrayList<>(); + rs.add(this.wsNodeAddress); + return CompletableFuture.completedFuture(rs); + } + + /** + * 获取在线用户的详细连接信息
    + * Map.key 为 SNCP节点地址, 含值为null的key表示没有分布式 + * Map.value 为 用户客户端的IP + * + * @param userid Serializable + * + * @return 地址集合 + */ + public CompletableFuture>> getRpcNodeWebSocketAddresses(final Serializable userid) { + CompletableFuture> sncpFuture = getRpcNodeAddresses(userid); + return sncpFuture.thenCompose((Collection addrs) -> { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(new HashMap<>()); + CompletableFuture>> future = null; + for (final WebSocketAddress nodeAddress : addrs) { + CompletableFuture>> mapFuture = getWebSocketAddresses(nodeAddress.getTopic(), nodeAddress.getAddr(), userid) + .thenCompose((List list) -> CompletableFuture.completedFuture(Utility.ofMap(nodeAddress, list))); + future = future == null ? mapFuture : future.thenCombine(mapFuture, (a, b) -> Utility.merge(a, b)); + } + return future == null ? CompletableFuture.completedFuture(new HashMap<>()) : future; + }); + } + +// public CompletableFuture getUserSize() { +// if (this.localEngine != null && this.source == null) { +// return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); +// } +// tryAcquireSemaphore(); +// CompletableFuture> listFuture = this.source.queryKeysStartsWithAsync(WS_SOURCE_KEY_USERID_PREFIX); +// CompletableFuture rs = listFuture.thenApply(v -> v.size()); +// if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); +// return rs; +// } + /** + * 获取在线用户总数 + * + * + * @return boolean + */ + @Local + public CompletableFuture getUserSize() { + if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 + return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); + } + CompletableFuture localFuture = this.localEngine == null ? null : CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); + tryAcquireSemaphore(); + CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_NODES, WebSocketAddress.class); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket getUserSize on " + addrs); + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.getUserSize(addr.getTopic(), addr.getAddr()) + : future.thenCombine(remoteNode.getUserSize(addr.getTopic(), addr.getAddr()), (a, b) -> a + b); + } + return future == null ? CompletableFuture.completedFuture(0) : future; + }); + return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a + b); + } + + /** + * 获取在线用户总数 + * + * + * @return boolean + */ + public CompletableFuture> getUserSet() { + if (this.localEngine != null && this.source == null) { + return CompletableFuture.completedFuture(new LinkedHashSet<>(this.localEngine.getLocalUserSet().stream().map(x -> String.valueOf(x)).collect(Collectors.toList()))); + } + tryAcquireSemaphore(); + CompletableFuture> listFuture = this.source.queryKeysStartsWithAsync(WS_SOURCE_KEY_USERID_PREFIX); + CompletableFuture> rs = listFuture.thenApply(v -> new LinkedHashSet<>(v.stream().map(x -> x.substring(WS_SOURCE_KEY_USERID_PREFIX.length())).collect(Collectors.toList()))); + if (semaphore != null) rs.whenComplete((r, e) -> releaseSemaphore()); + return rs; + } + + /** + * 判断指定用户是否WebSocket在线 + * + * @param userid Serializable + * + * @return boolean + */ + @Local + public CompletableFuture existsWebSocket(final Serializable userid) { + if (userid instanceof WebSocketUserAddress) return existsWebSocket((WebSocketUserAddress) userid); + CompletableFuture localFuture = null; + if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid)); + if (this.source == null || this.remoteNode == null) { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); + //没有CacheSource就不会有分布式节点 + return localFuture == null ? CompletableFuture.completedFuture(false) : localFuture; + } + //远程节点关闭 + tryAcquireSemaphore(); + CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { + //if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr()) + : future.thenCombine(remoteNode.existsWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(false) : future; + }); + return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); + } + + /** + * 判断指定用户是否WebSocket在线 + * + * @param userAddress WebSocketUserAddress + * + * @return boolean + */ + @Local + public CompletableFuture existsWebSocket(final WebSocketUserAddress userAddress) { + if (this.localEngine != null && localEngine.existsLocalWebSocket(userAddress.userid())) return CompletableFuture.completedFuture(true); + if (this.source == null || this.remoteNode == null) { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); + //没有CacheSource就不会有分布式节点 + return CompletableFuture.completedFuture(false); + } + Collection addrs = userAddress.addresses(); + if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值 + if (userAddress.address() != null) { + if (addrs == null) addrs = new ArrayList<>(); + addrs.add(userAddress.address()); + } + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(false); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr()) + : future.thenCombine(remoteNode.existsWebSocket(userAddress.userid(), addr.getTopic(), addr.getAddr()), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(false) : future; + } + + /** + * 强制关闭用户WebSocket + * + * @param userid Serializable + * + * @return int + */ + @Local + public CompletableFuture forceCloseWebSocket(final Serializable userid) { + return forceCloseWebSocket(userid, (WebSocketUserAddress) null); + } + + /** + * 强制关闭用户WebSocket + * + * @param userAddress WebSocketUserAddress + * + * @return int + */ + @Local + public CompletableFuture forceCloseWebSocket(final WebSocketUserAddress userAddress) { + return forceCloseWebSocket(null, userAddress); + } + + private CompletableFuture forceCloseWebSocket(final Serializable userid, final WebSocketUserAddress userAddress) { + CompletableFuture localFuture = null; + if (this.localEngine != null) localFuture = CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userAddress == null ? userid : userAddress.userid())); + if (this.source == null || this.remoteNode == null) { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); + //没有CacheSource就不会有分布式节点 + return localFuture == null ? CompletableFuture.completedFuture(0) : localFuture; + } + //远程节点关闭 + CompletableFuture> addrsFuture; + if (userAddress == null) { + tryAcquireSemaphore(); + addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + } else { + Collection addrs = userAddress.addresses(); + if (addrs != null) addrs = new ArrayList<>(addrs); //不能修改参数内部值 + if (userAddress.address() != null) { + if (addrs == null) addrs = new ArrayList<>(); + addrs.add(userAddress.address()); + } + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); + addrsFuture = CompletableFuture.completedFuture(addrs); + } + CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket found userid:" + userid + " on " + addrs); + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr()) + : future.thenCombine(remoteNode.forceCloseWebSocket(userid, addr.getTopic(), addr.getAddr()), (a, b) -> a + b); + } + return future == null ? CompletableFuture.completedFuture(0) : future; + }); + return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a + b); + } + + //-------------------------------------------------------------------------------- + /** + * 获取本地的WebSocketEngine,没有则返回null + * + * + * @return WebSocketEngine + */ + @Local + public final WebSocketEngine getLocalWebSocketEngine() { + return this.localEngine; + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param message 消息内容 + * @param useridOrAddrs Stream + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture sendMessage(Object message, final Stream useridOrAddrs) { + return sendMessage((Convert) null, message, true, useridOrAddrs); + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param message 消息内容 + * @param useridOrAddrs Serializable[] + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture sendMessage(Object message, final Serializable... useridOrAddrs) { + return sendMessage((Convert) null, message, true, useridOrAddrs); + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param convert Convert + * @param message 消息内容 + * @param useridOrAddrs Stream + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture sendMessage(final Convert convert, Object message, final Stream useridOrAddrs) { + return sendMessage(convert, message, true, useridOrAddrs); + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param convert Convert + * @param message 消息内容 + * @param useridOrAddrs Serializable[] + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture sendMessage(final Convert convert, Object message, final Serializable... useridOrAddrs) { + return sendMessage(convert, message, true, useridOrAddrs); + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param message 消息内容 + * @param last 是否最后一条 + * @param useridOrAddrs Stream + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture sendMessage(final Object message, final boolean last, final Stream useridOrAddrs) { + return sendMessage((Convert) null, message, last, useridOrAddrs); + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param message 消息内容 + * @param last 是否最后一条 + * @param useridOrAddrs Serializable[] + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture sendMessage(final Object message, final boolean last, final Serializable... useridOrAddrs) { + return sendMessage((Convert) null, message, last, useridOrAddrs); + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param convert Convert + * @param message0 消息内容 + * @param last 是否最后一条 + * @param userids Stream + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final Stream userids) { + Object[] array = userids.toArray(); + Serializable[] ss = new Serializable[array.length]; + for (int i = 0; i < array.length; i++) { + ss[i] = (Serializable) array[i]; + } + return sendMessage(convert, message0, last, ss); + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param convert Convert + * @param message0 消息内容 + * @param last 是否最后一条 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final Serializable... userids) { + if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + if (userids[0] instanceof WebSocketUserAddress) { + WebSocketUserAddress[] useraddrs = new WebSocketUserAddress[userids.length]; + for (int i = 0; i < useraddrs.length; i++) { + useraddrs[i] = (WebSocketUserAddress) userids[i]; + } + return sendMessage(convert, message0, last, useraddrs); + } + if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, userids)); + final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(WebSocketPacket.FrameType.TEXT, ((TextConvert) convert).convertToBytes(message0), last) : new WebSocketPacket(WebSocketPacket.FrameType.BINARY, ((BinaryConvert) convert).convertToBytes(message0), last)); + if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 + return this.localEngine.sendLocalMessage(message, last, userids); + } + final Object remoteMessage = formatRemoteMessage(message); + CompletableFuture rsfuture; + if (userids.length == 1) { + rsfuture = sendOneUserMessage(remoteMessage, last, userids[0]); + } else { + String[] keys = new String[userids.length]; + final Map keyuser = new HashMap<>(); + for (int i = 0; i < userids.length; i++) { + keys[i] = WS_SOURCE_KEY_USERID_PREFIX + userids[i]; + keyuser.put(keys[i], userids[i]); + } + tryAcquireSemaphore(); + CompletableFuture>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + rsfuture = addrsFuture.thenCompose((Map> addrs) -> { + if (addrs == null || addrs.isEmpty()) { + if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node "); + return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + } + Map> addrUsers = new HashMap<>(); + addrs.forEach((key, as) -> { + for (WebSocketAddress a : as) { + addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key)); + } + }); + if (logger.isLoggable(Level.FINEST)) { + logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found message-addr-userids: " + addrUsers); + } + CompletableFuture future = null; + for (Map.Entry> en : addrUsers.entrySet()) { + Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); + future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids) + : future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + }); + } + return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture; + } + + /** + * 向指定用户发送消息,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param convert Convert + * @param message0 消息内容 + * @param last 是否最后一条 + * @param useraddrs WebSocketUserAddress[] + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public CompletableFuture sendMessage(final Convert convert, final Object message0, final boolean last, final WebSocketUserAddress... useraddrs) { + if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> sendMessage(convert, msg, last, useraddrs)); + final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); + if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 + return this.localEngine.sendLocalMessage(message, last, userAddressToUserids(useraddrs)); + } + + final Object remoteMessage = formatRemoteMessage(message); + final Map> addrUsers = userAddressToAddrMap(useraddrs); + if (logger.isLoggable(Level.FINEST)) { + logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found message-addr-userids: " + addrUsers); + } + CompletableFuture future = null; + for (Map.Entry> en : addrUsers.entrySet()) { + Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); + future = future == null ? sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids) + : future.thenCombine(sendOneAddrMessage(en.getKey(), remoteMessage, last, oneaddrUserids), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + + } + + protected CompletableFuture sendOneUserMessage(final Object message, final boolean last, final Serializable userid) { + if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneUserMessage(msg, last, userid)); + if (logger.isLoggable(Level.FINEST)) { + logger.finest("websocket want send message {userid:" + userid + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); + } + CompletableFuture localFuture = null; + if (this.localEngine != null) localFuture = localEngine.sendLocalMessage(message, last, userid); + if (this.source == null || this.remoteNode == null) { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); + //没有CacheSource就不会有分布式节点 + return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; + } + //远程节点发送消息 + final Object remoteMessage = formatRemoteMessage(message); + tryAcquireSemaphore(); + CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { + if (addrs == null || addrs.isEmpty()) { + if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); + return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + } + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + wsNodeAddress + ") found userid:" + userid + " on " + addrs); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid) + : future.thenCombine(remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userid), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + }); + return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); + } + + protected CompletableFuture sendOneAddrMessage(final WebSocketAddress addr, final Object message, final boolean last, final Serializable... userids) { + if (message instanceof CompletableFuture) return ((CompletableFuture) message).thenApply(msg -> sendOneAddrMessage(addr, msg, last, userids)); + if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的 + logger.finest("websocket want send message {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", content:" + (message instanceof WebSocketPacket ? ((WebSocketPacket) message).toSimpleString() : (message instanceof CharSequence ? message : JsonConvert.root().convertTo(message))) + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); + } + if (Objects.equals(addr, this.wsNodeAddress)) { + return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalMessage(message, last, userids); + } + if (this.source == null || this.remoteNode == null) { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); + //没有CacheSource就不会有分布式节点 + return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + } + final Object remoteMessage = formatRemoteMessage(message); + return remoteNode.sendMessage(addr.getTopic(), addr.getAddr(), remoteMessage, last, userids); + } + + protected Serializable[] userAddressToUserids(WebSocketUserAddress... useraddrs) { + if (useraddrs == null || useraddrs.length == 1) return new Serializable[0]; + Set set = new HashSet<>(); + for (WebSocketUserAddress userAddress : useraddrs) { + set.add(userAddress.userid()); + } + return set.toArray(new Serializable[set.size()]); + } + + protected Map> userAddressToAddrMap(WebSocketUserAddress... useraddrs) { + final Map> addrUsers = new HashMap<>(); + for (WebSocketUserAddress userAddress : useraddrs) { + if (userAddress.address() != null) { + addrUsers.computeIfAbsent(userAddress.address(), k -> new ArrayList<>()).add(userAddress.userid()); + } + if (userAddress.addresses() != null) { + for (WebSocketAddress addr : userAddress.addresses()) { + if (addr != null) { + addrUsers.computeIfAbsent(addr, k -> new ArrayList<>()).add(userAddress.userid()); + } + } + } + } + return addrUsers; + } + + /** + * 广播消息, 给所有人发消息 + * + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture broadcastMessage(final Object message) { + return broadcastMessage((Convert) null, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message) { + return broadcastMessage(wsrange, (Convert) null, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param convert Convert + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture broadcastMessage(final Convert convert, final Object message) { + return broadcastMessage(convert, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param convert Convert + * @param message 消息内容 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message) { + return broadcastMessage(wsrange, convert, message, true); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param message 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture broadcastMessage(final Object message, final boolean last) { + return broadcastMessage((Convert) null, message, last); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param message 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message, final boolean last) { + return broadcastMessage(wsrange, (Convert) null, message, last); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param convert Convert + * @param message0 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public final CompletableFuture broadcastMessage(final Convert convert, final Object message0, final boolean last) { + return broadcastMessage((WebSocketRange) null, convert, message0, last); + } + + /** + * 广播消息, 给所有人发消息 + * + * @param wsrange 过滤条件 + * @param convert Convert + * @param message0 消息内容 + * @param last 是否最后一条 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message0, final boolean last) { + if (message0 instanceof CompletableFuture) return ((CompletableFuture) message0).thenApply(msg -> broadcastMessage(wsrange, convert, msg, last)); + final Object message = (convert == null || message0 instanceof WebSocketPacket) ? message0 : ((convert instanceof TextConvert) ? new WebSocketPacket(((TextConvert) convert).convertTo(message0), last) : new WebSocketPacket(((BinaryConvert) convert).convertTo(message0), last)); + if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 + return this.localEngine.broadcastLocalMessage(wsrange, message, last); + } + final Object remoteMessage = formatRemoteMessage(message); + CompletableFuture localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalMessage(wsrange, message, last); + tryAcquireSemaphore(); + CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_NODES, WebSocketAddress.class); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast message (" + remoteMessage + ") on " + addrs); + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last) + : future.thenCombine(remoteNode.broadcastMessage(addr.getTopic(), addr.getAddr(), wsrange, remoteMessage, last), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(0) : future; + }); + return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); + } + + /** + * 广播操作, 给所有人发操作 + * + * @param action 操作参数 + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public CompletableFuture broadcastAction(final WebSocketAction action) { + if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 + return this.localEngine.broadcastLocalAction(action); + } + CompletableFuture localFuture = this.localEngine == null ? null : this.localEngine.broadcastLocalAction(action); + tryAcquireSemaphore(); + CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_NODES, WebSocketAddress.class); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket broadcast action (" + action + ") on " + addrs); + if (addrs == null || addrs.isEmpty()) return CompletableFuture.completedFuture(0); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action) + : future.thenCombine(remoteNode.broadcastAction(addr.getTopic(), addr.getAddr(), action), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(0) : future; + }); + return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); + } + + /** + * 向指定用户发送操作,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param action 操作参数 + * @param userids Serializable[] + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public CompletableFuture sendAction(final WebSocketAction action, final Serializable... userids) { + if (userids == null || userids.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + if (userids[0] instanceof WebSocketUserAddress) { + WebSocketUserAddress[] useraddrs = new WebSocketUserAddress[userids.length]; + for (int i = 0; i < useraddrs.length; i++) { + useraddrs[i] = (WebSocketUserAddress) userids[i]; + } + return sendAction(action, useraddrs); + } + if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 + return this.localEngine.sendLocalAction(action, userids); + } + CompletableFuture rsfuture; + if (userids.length == 1) { + rsfuture = sendOneUserAction(action, userids[0]); + } else { + String[] keys = new String[userids.length]; + final Map keyuser = new HashMap<>(); + for (int i = 0; i < userids.length; i++) { + keys[i] = WS_SOURCE_KEY_USERID_PREFIX + userids[i]; + keyuser.put(keys[i], userids[i]); + } + tryAcquireSemaphore(); + CompletableFuture>> addrsFuture = source.getCollectionMapAsync(true, WebSocketAddress.class, keys); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + rsfuture = addrsFuture.thenCompose((Map> addrs) -> { + if (addrs == null || addrs.isEmpty()) { + if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userids:" + JsonConvert.root().convertTo(userids) + " on any node "); + return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + } + Map> addrUsers = new HashMap<>(); + addrs.forEach((key, as) -> { + for (WebSocketAddress a : as) { + addrUsers.computeIfAbsent(a, k -> new ArrayList<>()).add(keyuser.get(key)); + } + }); + if (logger.isLoggable(Level.FINEST)) { + logger.finest("websocket(localaddr=" + localSncpAddress + ", userids=" + JsonConvert.root().convertTo(userids) + ") found action-userid-addrs: " + addrUsers); + } + CompletableFuture future = null; + for (Map.Entry> en : addrUsers.entrySet()) { + Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); + future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids) + : future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + }); + } + return rsfuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : rsfuture; + } + + /** + * 向指定用户发送操作,先发送本地连接,再发送远程连接
    + * 如果当前WebSocketNode是远程模式,此方法只发送远程连接 + * + * @param action 操作参数 + * @param useraddrs WebSocketUserAddress[] + * + * @return 为0表示成功, 其他值表示部分发送异常 + */ + @Local + public CompletableFuture sendAction(final WebSocketAction action, final WebSocketUserAddress... useraddrs) { + if (useraddrs == null || useraddrs.length < 1) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + if (this.localEngine != null && this.source == null) { //本地模式且没有分布式 + return this.localEngine.sendLocalAction(action, userAddressToUserids(useraddrs)); + } + + final Map> addrUsers = userAddressToAddrMap(useraddrs); + if (logger.isLoggable(Level.FINEST)) { + logger.finest("websocket(localaddr=" + localSncpAddress + ", useraddrs=" + JsonConvert.root().convertTo(useraddrs) + ") found action-userid-addrs: " + addrUsers); + } + CompletableFuture future = null; + for (Map.Entry> en : addrUsers.entrySet()) { + Serializable[] oneaddrUserids = en.getValue().toArray(new Serializable[en.getValue().size()]); + future = future == null ? sendOneAddrAction(en.getKey(), action, oneaddrUserids) + : future.thenCombine(sendOneAddrAction(en.getKey(), action, oneaddrUserids), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + } + + protected CompletableFuture sendOneUserAction(final WebSocketAction action, final Serializable userid) { + if (logger.isLoggable(Level.FINEST)) { + logger.finest("websocket want send action {userid:" + userid + ", action:" + action + "} from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); + } + CompletableFuture localFuture = null; + if (this.localEngine != null) localFuture = localEngine.sendLocalAction(action, userid); + if (this.source == null || this.remoteNode == null) { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); + //没有CacheSource就不会有分布式节点 + return localFuture == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localFuture; + } + //远程节点发送操作 + tryAcquireSemaphore(); + CompletableFuture> addrsFuture = source.getCollectionAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class); + if (semaphore != null) addrsFuture.whenComplete((r, e) -> releaseSemaphore()); + CompletableFuture remoteFuture = addrsFuture.thenCompose((Collection addrs) -> { + if (addrs == null || addrs.isEmpty()) { + if (logger.isLoggable(Level.FINER)) logger.finer("websocket not found userid:" + userid + " on any node "); + return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + } + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket(localaddr=" + localSncpAddress + ") found userid:" + userid + " on " + addrs); + CompletableFuture future = null; + for (WebSocketAddress addr : addrs) { + if (addr == null || addr.equals(wsNodeAddress)) continue; + future = future == null ? remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid) + : future.thenCombine(remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userid), (a, b) -> a | b); + } + return future == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : future; + }); + return localFuture == null ? remoteFuture : localFuture.thenCombine(remoteFuture, (a, b) -> a | b); + } + + protected CompletableFuture sendOneAddrAction(final WebSocketAddress addr, final WebSocketAction action, final Serializable... userids) { + if (logger.isLoggable(Level.FINEST) && this.localEngine == null) { //只打印远程模式的 + logger.finest("websocket want send action {userids:" + JsonConvert.root().convertTo(userids) + ", sncpaddr:" + addr + ", action:" + action + " from locale node to " + ((this.localEngine != null) ? "locale" : "remote") + " engine"); + } + if (Objects.equals(addr, this.wsNodeAddress)) { + return this.localEngine == null ? CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY) : localEngine.sendLocalAction(action, userids); + } + if (this.source == null || this.remoteNode == null) { + if (logger.isLoggable(Level.FINEST)) logger.finest("websocket " + (this.remoteNode == null ? (this.source == null ? "remote and source" : "remote") : "source") + " node is null"); + //没有CacheSource就不会有分布式节点 + return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + } + return remoteNode.sendAction(addr.getTopic(), addr.getAddr(), action, userids); + } + + protected Object formatRemoteMessage(Object message) { + if (message instanceof WebSocketPacket) return message; + if (message instanceof byte[]) return message; + if (message instanceof CharSequence) return message; + if (sendConvert instanceof TextConvert) ((TextConvert) sendConvert).convertTo(message); + if (sendConvert instanceof BinaryConvert) ((BinaryConvert) sendConvert).convertTo(message); + return JsonConvert.root().convertTo(message); + } + + protected boolean tryAcquireSemaphore() { + if (this.semaphore == null) return true; + try { + return this.semaphore.tryAcquire(tryAcquireSeconds, TimeUnit.SECONDS); + } catch (Exception e) { + return false; + } + } + + protected void releaseSemaphore() { + if (this.semaphore != null) this.semaphore.release(); + } +} diff --git a/src/org/redkale/net/http/WebSocketPacket.java b/src/main/java/org/redkale/net/http/WebSocketPacket.java similarity index 96% rename from src/org/redkale/net/http/WebSocketPacket.java rename to src/main/java/org/redkale/net/http/WebSocketPacket.java index 4cc22bc0e..5b468eaba 100644 --- a/src/org/redkale/net/http/WebSocketPacket.java +++ b/src/main/java/org/redkale/net/http/WebSocketPacket.java @@ -1,123 +1,123 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.*; -import java.nio.charset.StandardCharsets; -import org.redkale.net.http.WebSocketPacket.FrameType; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class WebSocketPacket { - - public static final Object MESSAGE_NIL = new Object(); - - static final WebSocketPacket NONE = new WebSocketPacket(); - - public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]); - - public static enum MessageType { - STRING, BYTES, OBJECT; - } - - public static enum FrameType { - - SERIES(0x00), TEXT(0x01), BINARY(0x02), CLOSE(0x08), PING(0x09), PONG(0x0A); - - private final int value; - - private FrameType(int v) { - this.value = v; - } - - public int getValue() { - return value; - } - - public static FrameType valueOf(int v) { - switch (v) { - case 0x00: return SERIES; - case 0x01: return TEXT; - case 0x02: return BINARY; - case 0x08: return CLOSE; - case 0x09: return PING; - case 0x0A: return PONG; - default: return null; - } - } - } - - protected FrameType type; - - protected byte[] payload; - - protected boolean last = true; - - public WebSocketPacket() { - } - - public WebSocketPacket(FrameType type, byte[] data) { - this(type, data, true); - } - - public WebSocketPacket(FrameType type, byte[] data, boolean last) { - this.type = type; - this.payload = data; - this.last = last; - } - - public WebSocketPacket(Serializable message, boolean last) { - boolean bin = message != null && message.getClass() == byte[].class; - if (bin) { - this.type = FrameType.BINARY; - this.payload = (byte[]) message; - } else { - this.type = FrameType.TEXT; - this.payload = String.valueOf(message).getBytes(StandardCharsets.UTF_8); - } - this.last = last; - } - - public byte[] getPayload() { - return payload; - } - - public boolean isLast() { - return last; - } - - public FrameType getType() { - return type; - } - - public void setType(FrameType type) { - this.type = type; - } - - public void setPayload(byte[] payload) { - this.payload = payload; - } - - public void setLast(boolean last) { - this.last = last; - } - - public String toSimpleString() { - if (payload == null) return null; - return type == FrameType.TEXT ? new String(payload, StandardCharsets.UTF_8) : ("bytes(" + payload.length + ")"); - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + ", payload=" + toSimpleString() + "]"; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import org.redkale.net.http.WebSocketPacket.FrameType; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class WebSocketPacket { + + public static final Object MESSAGE_NIL = new Object(); + + static final WebSocketPacket NONE = new WebSocketPacket(); + + public static final WebSocketPacket DEFAULT_PING_PACKET = new WebSocketPacket(FrameType.PING, new byte[0]); + + public static enum MessageType { + STRING, BYTES, OBJECT; + } + + public static enum FrameType { + + SERIES(0x00), TEXT(0x01), BINARY(0x02), CLOSE(0x08), PING(0x09), PONG(0x0A); + + private final int value; + + private FrameType(int v) { + this.value = v; + } + + public int getValue() { + return value; + } + + public static FrameType valueOf(int v) { + switch (v) { + case 0x00: return SERIES; + case 0x01: return TEXT; + case 0x02: return BINARY; + case 0x08: return CLOSE; + case 0x09: return PING; + case 0x0A: return PONG; + default: return null; + } + } + } + + protected FrameType type; + + protected byte[] payload; + + protected boolean last = true; + + public WebSocketPacket() { + } + + public WebSocketPacket(FrameType type, byte[] data) { + this(type, data, true); + } + + public WebSocketPacket(FrameType type, byte[] data, boolean last) { + this.type = type; + this.payload = data; + this.last = last; + } + + public WebSocketPacket(Serializable message, boolean last) { + boolean bin = message != null && message.getClass() == byte[].class; + if (bin) { + this.type = FrameType.BINARY; + this.payload = (byte[]) message; + } else { + this.type = FrameType.TEXT; + this.payload = String.valueOf(message).getBytes(StandardCharsets.UTF_8); + } + this.last = last; + } + + public byte[] getPayload() { + return payload; + } + + public boolean isLast() { + return last; + } + + public FrameType getType() { + return type; + } + + public void setType(FrameType type) { + this.type = type; + } + + public void setPayload(byte[] payload) { + this.payload = payload; + } + + public void setLast(boolean last) { + this.last = last; + } + + public String toSimpleString() { + if (payload == null) return null; + return type == FrameType.TEXT ? new String(payload, StandardCharsets.UTF_8) : ("bytes(" + payload.length + ")"); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "[type=" + type + ", last=" + last + ", payload=" + toSimpleString() + "]"; + } + +} diff --git a/src/org/redkale/net/http/WebSocketParam.java b/src/main/java/org/redkale/net/http/WebSocketParam.java similarity index 96% rename from src/org/redkale/net/http/WebSocketParam.java rename to src/main/java/org/redkale/net/http/WebSocketParam.java index 6fdd99bc4..d68db806b 100644 --- a/src/org/redkale/net/http/WebSocketParam.java +++ b/src/main/java/org/redkale/net/http/WebSocketParam.java @@ -1,48 +1,48 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Array; -import java.util.Arrays; - -/** - * - * 供WebSocket.preOnMessage 方法获取RestWebSocket里OnMessage方法的参数
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface WebSocketParam { - - public T getValue(String name); - - public String[] getNames(); - - public Annotation[] getAnnotations(); - - default T getAnnotation(Class annotationClass) { - for (Annotation ann : getAnnotations()) { - if (ann.getClass() == annotationClass) return (T) ann; - } - return null; - } - - default T[] getAnnotationsByType(Class annotationClass) { - Annotation[] annotations = getAnnotations(); - if (annotations == null) return (T[]) Array.newInstance(annotationClass, 0); - T[] news = (T[]) Array.newInstance(annotationClass, annotations.length); - int index = 0; - for (Annotation ann : annotations) { - if (ann.getClass() == annotationClass) { - news[index++] = (T) ann; - } - } - if (index < 1) return (T[]) Array.newInstance(annotationClass, 0); - return Arrays.copyOf(news, index); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Array; +import java.util.Arrays; + +/** + * + * 供WebSocket.preOnMessage 方法获取RestWebSocket里OnMessage方法的参数
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface WebSocketParam { + + public T getValue(String name); + + public String[] getNames(); + + public Annotation[] getAnnotations(); + + default T getAnnotation(Class annotationClass) { + for (Annotation ann : getAnnotations()) { + if (ann.getClass() == annotationClass) return (T) ann; + } + return null; + } + + default T[] getAnnotationsByType(Class annotationClass) { + Annotation[] annotations = getAnnotations(); + if (annotations == null) return (T[]) Array.newInstance(annotationClass, 0); + T[] news = (T[]) Array.newInstance(annotationClass, annotations.length); + int index = 0; + for (Annotation ann : annotations) { + if (ann.getClass() == annotationClass) { + news[index++] = (T) ann; + } + } + if (index < 1) return (T[]) Array.newInstance(annotationClass, 0); + return Arrays.copyOf(news, index); + } +} diff --git a/src/org/redkale/net/http/WebSocketRange.java b/src/main/java/org/redkale/net/http/WebSocketRange.java similarity index 95% rename from src/org/redkale/net/http/WebSocketRange.java rename to src/main/java/org/redkale/net/http/WebSocketRange.java index 70ab73147..558bc3fe3 100644 --- a/src/org/redkale/net/http/WebSocketRange.java +++ b/src/main/java/org/redkale/net/http/WebSocketRange.java @@ -1,70 +1,70 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.Serializable; -import java.util.Map; -import org.redkale.convert.json.JsonConvert; - -/** - * WebSocket.broadcastMessage时的过滤条件 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class WebSocketRange implements Serializable { - - protected String wskey; - - protected Map attach; - - public WebSocketRange() { - } - - public WebSocketRange(String wskey) { - this.wskey = wskey; - } - - public WebSocketRange(String wskey, Map attach) { - this.wskey = wskey; - this.attach = attach; - } - - public boolean containsAttach(String key) { - return this.attach == null ? false : this.attach.containsKey(key); - } - - public String getAttach(String key) { - return this.attach == null ? null : this.attach.get(key); - } - - public String getAttach(String key, String defval) { - return this.attach == null ? defval : this.attach.getOrDefault(key, defval); - } - - public String getWskey() { - return wskey; - } - - public void setWskey(String wskey) { - this.wskey = wskey; - } - - public Map getAttach() { - return attach; - } - - public void setAttach(Map attach) { - this.attach = attach; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.Serializable; +import java.util.Map; +import org.redkale.convert.json.JsonConvert; + +/** + * WebSocket.broadcastMessage时的过滤条件 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class WebSocketRange implements Serializable { + + protected String wskey; + + protected Map attach; + + public WebSocketRange() { + } + + public WebSocketRange(String wskey) { + this.wskey = wskey; + } + + public WebSocketRange(String wskey, Map attach) { + this.wskey = wskey; + this.attach = attach; + } + + public boolean containsAttach(String key) { + return this.attach == null ? false : this.attach.containsKey(key); + } + + public String getAttach(String key) { + return this.attach == null ? null : this.attach.get(key); + } + + public String getAttach(String key, String defval) { + return this.attach == null ? defval : this.attach.getOrDefault(key, defval); + } + + public String getWskey() { + return wskey; + } + + public void setWskey(String wskey) { + this.wskey = wskey; + } + + public Map getAttach() { + return attach; + } + + public void setAttach(Map attach) { + this.attach = attach; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/src/org/redkale/net/http/WebSocketReadHandler.java b/src/main/java/org/redkale/net/http/WebSocketReadHandler.java similarity index 97% rename from src/org/redkale/net/http/WebSocketReadHandler.java rename to src/main/java/org/redkale/net/http/WebSocketReadHandler.java index f400d3b2d..f6b9ea269 100644 --- a/src/org/redkale/net/http/WebSocketReadHandler.java +++ b/src/main/java/org/redkale/net/http/WebSocketReadHandler.java @@ -1,363 +1,363 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.util.logging.*; -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; -import java.util.logging.Level; -import org.redkale.convert.Convert; -import static org.redkale.net.http.WebSocket.*; -import org.redkale.net.http.WebSocketPacket.FrameType; -import org.redkale.util.ByteArray; - -/** - * - * @author zhangjx - */ -public class WebSocketReadHandler implements CompletionHandler { - - protected final HttpContext context; - - protected final WebSocket webSocket; - - protected final BiConsumer restMessageConsumer; //主要供RestWebSocket使用 - - protected final Logger logger; - - protected final boolean debug; - - protected final List currPackets = new ArrayList<>(); - - protected ByteArray currSeriesMergeMessageBytes; - - protected FrameType currSeriesMergeMessageType; - - protected final ByteArray halfFrameBytes = new ByteArray(); - - protected byte halfFrameOpcode; - - protected byte halfFrameCrcode; - - protected byte[] halfFrameMasks; - - protected int halfFrameStart; - - protected int halfFrameLength = -1; - - public WebSocketReadHandler(HttpContext context, WebSocket webSocket, BiConsumer messageConsumer) { - this.context = context; - this.restMessageConsumer = messageConsumer; - this.webSocket = webSocket; - this.logger = context.getLogger(); - this.debug = context.getLogger().isLoggable(Level.FINEST); - } - - public void startRead() { - CompletableFuture connectFuture = webSocket.onConnected(); - if (connectFuture == null) { - webSocket._channel.read(this); - } else { - connectFuture.whenComplete((r, t) -> { - webSocket._channel.read(this); - }); - } - } - - /** - * 消息解码
    - * - * 0 1 2 3 - * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - * +-+-+-+-+-------+-+-------------+-------------------------------+ - * |F|R|R|R| opcode|M| Payload len | Extended payload length | - * |I|S|S|S| (4) |A| (7) | (16/64) | - * |N|V|V|V| |S| | (if payload len==126/127) | - * | |1|2|3| |K| | | - * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + - * | Extended payload length continued, if payload len == 127 | - * + - - - - - - - - - - - - - - - +-------------------------------+ - * | |Masking-key, if MASK set to 1 | - * +-------------------------------+-------------------------------+ - * | Masking-key (continued) | Payload Data | - * +-------------------------------- - - - - - - - - - - - - - - - + - * : Payload Data continued : - * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - * | Payload Data continued | - * +-----------------------------------------------------------------------+ - * - * @param realbuf ByteBuffer - * - */ - protected void readDecode(final ByteBuffer realbuf) { - if (debug && realbuf.remaining() > 6) logger.log(Level.FINEST, "read websocket message's length = " + realbuf.remaining()); - if (!realbuf.hasRemaining()) return; - ByteBuffer buffer = realbuf; - byte frameOpcode; - byte frameCrcode; - byte[] frameMasks; - int frameLength; - //System.out.println("realbuf读到的长度: " + realbuf.remaining() + ", halfFrameBytes=" + (halfFrameBytes == null ? -1 : halfFrameBytes.length())); - if (halfFrameBytes.length() > 0) { //存在半包 - int remain = realbuf.remaining(); - if (halfFrameLength == -1) { - int cha = 2 - halfFrameBytes.length(); - if (remain < cha) { //还是不够2字节 - halfFrameBytes.put(realbuf); - return; - } - final byte opcode0 = halfFrameBytes.get(0); //第一个字节 - final byte crcode0 = halfFrameBytes.get(1); //第二个字节 - byte lengthCode = crcode0; - final boolean masked = (lengthCode & 0x80) == 0x80; - if (masked) lengthCode ^= 0x80; //mask - int minLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 8)) + (masked ? 4 : 0); - cha = minLength + 2 - halfFrameBytes.length(); - if (remain < cha) { //还不够读取长度值和mask - halfFrameBytes.put(realbuf); - return; - } - int length; - if (lengthCode <= 0x7D) { //125 长度<=125 - length = lengthCode; - } else if (lengthCode == 0x7E) {//0x7E=126 长度:126~65535 - length = (int) realbuf.getChar(); - } else if (lengthCode == 0x7F) {//0x7E=127 长度>65535 - length = (int) realbuf.getLong(); - } else { - throw new RuntimeException("read webSocket packet lengthCode (" + (int) lengthCode + ") error"); - } - byte[] masks0 = null; - if (masked) { - masks0 = new byte[4]; - realbuf.get(masks0); - } - this.halfFrameOpcode = opcode0; - this.halfFrameCrcode = crcode0; - this.halfFrameMasks = masks0; - this.halfFrameStart = 2 + minLength; - this.halfFrameLength = length; - } - //此时必然有halfFrameLength - int bulen = halfFrameLength + halfFrameStart - halfFrameBytes.length(); //还差多少字节 - if (bulen > remain) { //不够,继续读取 - halfFrameBytes.put(realbuf); - return; - } - halfFrameBytes.put(realbuf, bulen); - //此时halfFrameBytes是完整的frame数据 - buffer = ByteBuffer.wrap(halfFrameBytes.content(), halfFrameStart, halfFrameBytes.length()); - - frameOpcode = this.halfFrameOpcode; - frameCrcode = this.halfFrameCrcode; - frameMasks = this.halfFrameMasks; - frameLength = this.halfFrameLength; - - this.halfFrameBytes.clear(); - this.halfFrameLength = -1; - } else { //第一次就只有几个字节buffer - int remain = realbuf.remaining(); - if (remain < 2) { - this.halfFrameBytes.put(realbuf); - return; - } - final byte opcode0 = realbuf.get(); //第一个字节 - final byte crcode0 = realbuf.get(); //第二个字节 - byte lengthCode = crcode0; - final boolean masked = (lengthCode & 0x80) == 0x80; - if (masked) lengthCode ^= 0x80; //mask - int minLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 8)) + (masked ? 4 : 0); - if (remain < minLength + 2) { //还不够读取长度值 - this.halfFrameBytes.put(opcode0, crcode0); - this.halfFrameBytes.put(realbuf); - return; - } - int length; - if (lengthCode <= 0x7D) { //125 长度<=125 - length = lengthCode; - } else if (lengthCode == 0x7E) {//0x7E=126 长度:126~65535 - length = (int) realbuf.getChar(); - } else if (lengthCode == 0x7F) {//0x7E=127 长度>65535 - length = (int) realbuf.getLong(); - } else { - throw new RuntimeException("read webSocket packet lengthCode (" + (int) lengthCode + ") error"); - } - byte[] masks0 = null; - if (masked) { - masks0 = new byte[4]; - realbuf.get(masks0); - } - int bulen = length + minLength + 2; //还差多少字节 - if (bulen > remain) { //不够,继续读取 - this.halfFrameBytes.put(opcode0, crcode0); - if (lengthCode <= 0x7D) { //125 长度<=125 - this.halfFrameBytes.put((byte) length); - } else if (lengthCode == 0x7E) {//0x7E=126 长度:126~65535 - this.halfFrameBytes.putChar((char) length); - } else if (lengthCode == 0x7F) {//0x7E=127 长度>65535 - this.halfFrameBytes.putLong((long) length); - } - if (masks0 != null) this.halfFrameBytes.put(masks0); - this.halfFrameBytes.put(realbuf); - this.halfFrameOpcode = opcode0; - this.halfFrameCrcode = crcode0; - this.halfFrameMasks = masks0; - this.halfFrameStart = 2 + minLength; - this.halfFrameLength = length; - return; - } - frameOpcode = opcode0; - frameCrcode = crcode0; - frameMasks = masks0; - frameLength = length; - } - - final boolean last = (frameOpcode & 0B1000_0000) != 0; - final FrameType type = FrameType.valueOf(frameOpcode & 0B0000_1111); - //0x00 表示一个后续帧 - //0x01 表示一个文本帧 - //0x02 表示一个二进制帧 - //0x03-07 为以后的非控制帧保留 - //0x8 表示一个连接关闭 - //0x9 表示一个ping - //0xA 表示一个pong - //0x0B-0F 为以后的控制帧保留 - final boolean control = (frameOpcode & 0B0000_1000) != 0; //是否控制帧 - // this.receiveCompress = !control && webSocket.inflater != null && (opcode & 0B0100_0000) != 0; //rsv1 为 1 - - if (type == FrameType.CLOSE) { - if (debug) logger.log(Level.FINEST, " receive close command from websocket client"); - } - if (type == null) { - logger.log(Level.SEVERE, " receive unknown frametype(opcode=" + (frameOpcode & 0B0000_1111) + ") from websocket client"); - } - final boolean checkrsv = false;//暂时不校验 - if (checkrsv && (frameOpcode & 0B0111_0000) != 0) { - if (debug) logger.log(Level.FINE, "rsv1 rsv2 rsv3 must be 0, but not (" + frameOpcode + ")"); - return; //rsv1 rsv2 rsv3 must be 0 - } - byte[] content = new byte[frameLength]; - if (frameLength > 0) { - buffer.get(content); - int mi = 0; - if (frameMasks != null) { - for (int i = 0; i < content.length; i++) { - content[i] = (byte) (content[i] ^ frameMasks[mi++ % 4]); - } - } - } - if (!last && (type == FrameType.TEXT || type == FrameType.BINARY)) { - this.currSeriesMergeMessageBytes = new ByteArray(); - this.currSeriesMergeMessageBytes.put(content); - this.currSeriesMergeMessageType = type; - } else if (type == FrameType.SERIES) { - this.currSeriesMergeMessageBytes.put(content); - } else if (last && this.currSeriesMergeMessageBytes != null) { - this.currSeriesMergeMessageBytes.put(content); - byte[] bs = this.currSeriesMergeMessageBytes.getBytes(); - FrameType t = this.currSeriesMergeMessageType; - this.currSeriesMergeMessageBytes = null; - this.currSeriesMergeMessageType = null; - currPackets.add(new WebSocketPacket(t, bs, last)); - } else { - currPackets.add(new WebSocketPacket(type, content, last)); - } - buffer = realbuf; - while (buffer.hasRemaining()) { - readDecode(realbuf); - } - } - - @Override - public void completed(Integer count, ByteBuffer readBuffer) { - if (count < 1) { - if (debug) logger.log(Level.FINEST, "WebSocket(" + webSocket + ") abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds"); - webSocket.kill(CLOSECODE_ILLPACKET, "read buffer count is " + count); - return; - } - try { - webSocket.lastReadTime = System.currentTimeMillis(); - currPackets.clear(); - - readBuffer.flip(); - readDecode(readBuffer); - readBuffer.clear(); - webSocket._channel.setReadBuffer(readBuffer); - try { - //消息处理 - for (final WebSocketPacket packet : currPackets) { - if (packet.type == FrameType.TEXT) { - try { - Convert convert = webSocket.getTextConvert(); - if (restMessageConsumer != null) { //主要供RestWebSocket使用 - restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageTextType, packet.getPayload())); - } else { - webSocket.onMessage(packet.getPayload() == null ? null : new String(packet.getPayload(), StandardCharsets.UTF_8), packet.last); - } - } catch (Throwable e) { - logger.log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e); - } - } else if (packet.type == FrameType.BINARY) { - try { - Convert convert = webSocket.getBinaryConvert(); - if (restMessageConsumer != null) { //主要供RestWebSocket使用 - restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageTextType, packet.getPayload())); - } else { - webSocket.onMessage(packet.getPayload(), packet.last); - } - } catch (Throwable e) { - logger.log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e); - } - } else if (packet.type == FrameType.PING) { - try { - webSocket.onPing(packet.getPayload()); - } catch (Exception e) { - logger.log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e); - } - } else if (packet.type == FrameType.PONG) { - try { - //if (debug) logger.log(Level.FINEST, "WebSocket onMessage by PONG FrameType : " + packet); - webSocket.onPong(packet.getPayload()); - } catch (Exception e) { - logger.log(Level.SEVERE, "WebSocket(" + webSocket + ") onPong error (" + packet + ")", e); - } - } else if (packet.type == FrameType.CLOSE) { - webSocket.initiateClosed = true; - if (debug) logger.log(Level.FINEST, "WebSocket(" + webSocket + ") onMessage by CLOSE FrameType : " + packet); - webSocket.kill(CLOSECODE_CLIENTCLOSE, "received CLOSE frame-type message"); - return; - } else { - logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage by unknown FrameType : " + packet); - webSocket.kill(CLOSECODE_ILLPACKET, "received unknown frame-type message"); - return; - } - } - } catch (Throwable t) { - logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage error", t); - } - webSocket._channel.read(this); - } catch (Exception e) { - logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage by received error", e); - webSocket.kill(CLOSECODE_WSEXCEPTION, "websocket-received error"); - } - } - - @Override - public void failed(Throwable exc, ByteBuffer attachment2) { - if (webSocket.initiateClosed) return; - if (exc != null) { - if (debug) context.getLogger().log(Level.FINEST, "WebSocket(" + webSocket + ") read WebSocketPacket failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc); - webSocket.kill(CLOSECODE_WSEXCEPTION, "read websocket-packet failed"); - } else { - webSocket.kill(CLOSECODE_WSEXCEPTION, "decode websocket-packet error"); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.util.logging.*; +import java.nio.ByteBuffer; +import java.nio.channels.CompletionHandler; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiConsumer; +import java.util.logging.Level; +import org.redkale.convert.Convert; +import static org.redkale.net.http.WebSocket.*; +import org.redkale.net.http.WebSocketPacket.FrameType; +import org.redkale.util.ByteArray; + +/** + * + * @author zhangjx + */ +public class WebSocketReadHandler implements CompletionHandler { + + protected final HttpContext context; + + protected final WebSocket webSocket; + + protected final BiConsumer restMessageConsumer; //主要供RestWebSocket使用 + + protected final Logger logger; + + protected final boolean debug; + + protected final List currPackets = new ArrayList<>(); + + protected ByteArray currSeriesMergeMessageBytes; + + protected FrameType currSeriesMergeMessageType; + + protected final ByteArray halfFrameBytes = new ByteArray(); + + protected byte halfFrameOpcode; + + protected byte halfFrameCrcode; + + protected byte[] halfFrameMasks; + + protected int halfFrameStart; + + protected int halfFrameLength = -1; + + public WebSocketReadHandler(HttpContext context, WebSocket webSocket, BiConsumer messageConsumer) { + this.context = context; + this.restMessageConsumer = messageConsumer; + this.webSocket = webSocket; + this.logger = context.getLogger(); + this.debug = context.getLogger().isLoggable(Level.FINEST); + } + + public void startRead() { + CompletableFuture connectFuture = webSocket.onConnected(); + if (connectFuture == null) { + webSocket._channel.read(this); + } else { + connectFuture.whenComplete((r, t) -> { + webSocket._channel.read(this); + }); + } + } + + /** + * 消息解码
    + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-------+-+-------------+-------------------------------+ + * |F|R|R|R| opcode|M| Payload len | Extended payload length | + * |I|S|S|S| (4) |A| (7) | (16/64) | + * |N|V|V|V| |S| | (if payload len==126/127) | + * | |1|2|3| |K| | | + * +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + * | Extended payload length continued, if payload len == 127 | + * + - - - - - - - - - - - - - - - +-------------------------------+ + * | |Masking-key, if MASK set to 1 | + * +-------------------------------+-------------------------------+ + * | Masking-key (continued) | Payload Data | + * +-------------------------------- - - - - - - - - - - - - - - - + + * : Payload Data continued : + * + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + * | Payload Data continued | + * +-----------------------------------------------------------------------+ + * + * @param realbuf ByteBuffer + * + */ + protected void readDecode(final ByteBuffer realbuf) { + if (debug && realbuf.remaining() > 6) logger.log(Level.FINEST, "read websocket message's length = " + realbuf.remaining()); + if (!realbuf.hasRemaining()) return; + ByteBuffer buffer = realbuf; + byte frameOpcode; + byte frameCrcode; + byte[] frameMasks; + int frameLength; + //System.out.println("realbuf读到的长度: " + realbuf.remaining() + ", halfFrameBytes=" + (halfFrameBytes == null ? -1 : halfFrameBytes.length())); + if (halfFrameBytes.length() > 0) { //存在半包 + int remain = realbuf.remaining(); + if (halfFrameLength == -1) { + int cha = 2 - halfFrameBytes.length(); + if (remain < cha) { //还是不够2字节 + halfFrameBytes.put(realbuf); + return; + } + final byte opcode0 = halfFrameBytes.get(0); //第一个字节 + final byte crcode0 = halfFrameBytes.get(1); //第二个字节 + byte lengthCode = crcode0; + final boolean masked = (lengthCode & 0x80) == 0x80; + if (masked) lengthCode ^= 0x80; //mask + int minLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 8)) + (masked ? 4 : 0); + cha = minLength + 2 - halfFrameBytes.length(); + if (remain < cha) { //还不够读取长度值和mask + halfFrameBytes.put(realbuf); + return; + } + int length; + if (lengthCode <= 0x7D) { //125 长度<=125 + length = lengthCode; + } else if (lengthCode == 0x7E) {//0x7E=126 长度:126~65535 + length = (int) realbuf.getChar(); + } else if (lengthCode == 0x7F) {//0x7E=127 长度>65535 + length = (int) realbuf.getLong(); + } else { + throw new RuntimeException("read webSocket packet lengthCode (" + (int) lengthCode + ") error"); + } + byte[] masks0 = null; + if (masked) { + masks0 = new byte[4]; + realbuf.get(masks0); + } + this.halfFrameOpcode = opcode0; + this.halfFrameCrcode = crcode0; + this.halfFrameMasks = masks0; + this.halfFrameStart = 2 + minLength; + this.halfFrameLength = length; + } + //此时必然有halfFrameLength + int bulen = halfFrameLength + halfFrameStart - halfFrameBytes.length(); //还差多少字节 + if (bulen > remain) { //不够,继续读取 + halfFrameBytes.put(realbuf); + return; + } + halfFrameBytes.put(realbuf, bulen); + //此时halfFrameBytes是完整的frame数据 + buffer = ByteBuffer.wrap(halfFrameBytes.content(), halfFrameStart, halfFrameBytes.length()); + + frameOpcode = this.halfFrameOpcode; + frameCrcode = this.halfFrameCrcode; + frameMasks = this.halfFrameMasks; + frameLength = this.halfFrameLength; + + this.halfFrameBytes.clear(); + this.halfFrameLength = -1; + } else { //第一次就只有几个字节buffer + int remain = realbuf.remaining(); + if (remain < 2) { + this.halfFrameBytes.put(realbuf); + return; + } + final byte opcode0 = realbuf.get(); //第一个字节 + final byte crcode0 = realbuf.get(); //第二个字节 + byte lengthCode = crcode0; + final boolean masked = (lengthCode & 0x80) == 0x80; + if (masked) lengthCode ^= 0x80; //mask + int minLength = ((lengthCode <= 0x7D) ? 0 : (lengthCode == 0x7E ? 2 : 8)) + (masked ? 4 : 0); + if (remain < minLength + 2) { //还不够读取长度值 + this.halfFrameBytes.put(opcode0, crcode0); + this.halfFrameBytes.put(realbuf); + return; + } + int length; + if (lengthCode <= 0x7D) { //125 长度<=125 + length = lengthCode; + } else if (lengthCode == 0x7E) {//0x7E=126 长度:126~65535 + length = (int) realbuf.getChar(); + } else if (lengthCode == 0x7F) {//0x7E=127 长度>65535 + length = (int) realbuf.getLong(); + } else { + throw new RuntimeException("read webSocket packet lengthCode (" + (int) lengthCode + ") error"); + } + byte[] masks0 = null; + if (masked) { + masks0 = new byte[4]; + realbuf.get(masks0); + } + int bulen = length + minLength + 2; //还差多少字节 + if (bulen > remain) { //不够,继续读取 + this.halfFrameBytes.put(opcode0, crcode0); + if (lengthCode <= 0x7D) { //125 长度<=125 + this.halfFrameBytes.put((byte) length); + } else if (lengthCode == 0x7E) {//0x7E=126 长度:126~65535 + this.halfFrameBytes.putChar((char) length); + } else if (lengthCode == 0x7F) {//0x7E=127 长度>65535 + this.halfFrameBytes.putLong((long) length); + } + if (masks0 != null) this.halfFrameBytes.put(masks0); + this.halfFrameBytes.put(realbuf); + this.halfFrameOpcode = opcode0; + this.halfFrameCrcode = crcode0; + this.halfFrameMasks = masks0; + this.halfFrameStart = 2 + minLength; + this.halfFrameLength = length; + return; + } + frameOpcode = opcode0; + frameCrcode = crcode0; + frameMasks = masks0; + frameLength = length; + } + + final boolean last = (frameOpcode & 0B1000_0000) != 0; + final FrameType type = FrameType.valueOf(frameOpcode & 0B0000_1111); + //0x00 表示一个后续帧 + //0x01 表示一个文本帧 + //0x02 表示一个二进制帧 + //0x03-07 为以后的非控制帧保留 + //0x8 表示一个连接关闭 + //0x9 表示一个ping + //0xA 表示一个pong + //0x0B-0F 为以后的控制帧保留 + final boolean control = (frameOpcode & 0B0000_1000) != 0; //是否控制帧 + // this.receiveCompress = !control && webSocket.inflater != null && (opcode & 0B0100_0000) != 0; //rsv1 为 1 + + if (type == FrameType.CLOSE) { + if (debug) logger.log(Level.FINEST, " receive close command from websocket client"); + } + if (type == null) { + logger.log(Level.SEVERE, " receive unknown frametype(opcode=" + (frameOpcode & 0B0000_1111) + ") from websocket client"); + } + final boolean checkrsv = false;//暂时不校验 + if (checkrsv && (frameOpcode & 0B0111_0000) != 0) { + if (debug) logger.log(Level.FINE, "rsv1 rsv2 rsv3 must be 0, but not (" + frameOpcode + ")"); + return; //rsv1 rsv2 rsv3 must be 0 + } + byte[] content = new byte[frameLength]; + if (frameLength > 0) { + buffer.get(content); + int mi = 0; + if (frameMasks != null) { + for (int i = 0; i < content.length; i++) { + content[i] = (byte) (content[i] ^ frameMasks[mi++ % 4]); + } + } + } + if (!last && (type == FrameType.TEXT || type == FrameType.BINARY)) { + this.currSeriesMergeMessageBytes = new ByteArray(); + this.currSeriesMergeMessageBytes.put(content); + this.currSeriesMergeMessageType = type; + } else if (type == FrameType.SERIES) { + this.currSeriesMergeMessageBytes.put(content); + } else if (last && this.currSeriesMergeMessageBytes != null) { + this.currSeriesMergeMessageBytes.put(content); + byte[] bs = this.currSeriesMergeMessageBytes.getBytes(); + FrameType t = this.currSeriesMergeMessageType; + this.currSeriesMergeMessageBytes = null; + this.currSeriesMergeMessageType = null; + currPackets.add(new WebSocketPacket(t, bs, last)); + } else { + currPackets.add(new WebSocketPacket(type, content, last)); + } + buffer = realbuf; + while (buffer.hasRemaining()) { + readDecode(realbuf); + } + } + + @Override + public void completed(Integer count, ByteBuffer readBuffer) { + if (count < 1) { + if (debug) logger.log(Level.FINEST, "WebSocket(" + webSocket + ") abort on read buffer count, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds"); + webSocket.kill(CLOSECODE_ILLPACKET, "read buffer count is " + count); + return; + } + try { + webSocket.lastReadTime = System.currentTimeMillis(); + currPackets.clear(); + + readBuffer.flip(); + readDecode(readBuffer); + readBuffer.clear(); + webSocket._channel.setReadBuffer(readBuffer); + try { + //消息处理 + for (final WebSocketPacket packet : currPackets) { + if (packet.type == FrameType.TEXT) { + try { + Convert convert = webSocket.getTextConvert(); + if (restMessageConsumer != null) { //主要供RestWebSocket使用 + restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageTextType, packet.getPayload())); + } else { + webSocket.onMessage(packet.getPayload() == null ? null : new String(packet.getPayload(), StandardCharsets.UTF_8), packet.last); + } + } catch (Throwable e) { + logger.log(Level.SEVERE, "WebSocket onTextMessage error (" + packet + ")", e); + } + } else if (packet.type == FrameType.BINARY) { + try { + Convert convert = webSocket.getBinaryConvert(); + if (restMessageConsumer != null) { //主要供RestWebSocket使用 + restMessageConsumer.accept(webSocket, convert.convertFrom(webSocket._messageTextType, packet.getPayload())); + } else { + webSocket.onMessage(packet.getPayload(), packet.last); + } + } catch (Throwable e) { + logger.log(Level.SEVERE, "WebSocket onBinaryMessage error (" + packet + ")", e); + } + } else if (packet.type == FrameType.PING) { + try { + webSocket.onPing(packet.getPayload()); + } catch (Exception e) { + logger.log(Level.SEVERE, "WebSocket onPing error (" + packet + ")", e); + } + } else if (packet.type == FrameType.PONG) { + try { + //if (debug) logger.log(Level.FINEST, "WebSocket onMessage by PONG FrameType : " + packet); + webSocket.onPong(packet.getPayload()); + } catch (Exception e) { + logger.log(Level.SEVERE, "WebSocket(" + webSocket + ") onPong error (" + packet + ")", e); + } + } else if (packet.type == FrameType.CLOSE) { + webSocket.initiateClosed = true; + if (debug) logger.log(Level.FINEST, "WebSocket(" + webSocket + ") onMessage by CLOSE FrameType : " + packet); + webSocket.kill(CLOSECODE_CLIENTCLOSE, "received CLOSE frame-type message"); + return; + } else { + logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage by unknown FrameType : " + packet); + webSocket.kill(CLOSECODE_ILLPACKET, "received unknown frame-type message"); + return; + } + } + } catch (Throwable t) { + logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage error", t); + } + webSocket._channel.read(this); + } catch (Exception e) { + logger.log(Level.WARNING, "WebSocket(" + webSocket + ") onMessage by received error", e); + webSocket.kill(CLOSECODE_WSEXCEPTION, "websocket-received error"); + } + } + + @Override + public void failed(Throwable exc, ByteBuffer attachment2) { + if (webSocket.initiateClosed) return; + if (exc != null) { + if (debug) context.getLogger().log(Level.FINEST, "WebSocket(" + webSocket + ") read WebSocketPacket failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc); + webSocket.kill(CLOSECODE_WSEXCEPTION, "read websocket-packet failed"); + } else { + webSocket.kill(CLOSECODE_WSEXCEPTION, "decode websocket-packet error"); + } + } + +} diff --git a/src/org/redkale/net/http/WebSocketServlet.java b/src/main/java/org/redkale/net/http/WebSocketServlet.java similarity index 96% rename from src/org/redkale/net/http/WebSocketServlet.java rename to src/main/java/org/redkale/net/http/WebSocketServlet.java index 9a8f9eea3..601554351 100644 --- a/src/org/redkale/net/http/WebSocketServlet.java +++ b/src/main/java/org/redkale/net/http/WebSocketServlet.java @@ -1,382 +1,387 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.CompletionHandler; -import java.security.*; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.*; -import java.util.logging.*; -import java.util.zip.*; -import javax.annotation.*; -import org.redkale.convert.Convert; -import org.redkale.mq.MessageAgent; -import org.redkale.net.Cryptor; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - *

    - * 当WebSocketServlet接收一个TCP连接后,进行协议判断,如果成功就会创建一个WebSocket。
    - *
    - *                                    WebSocketServlet
    - *                                            |
    - *                                            |
    - *                                   WebSocketEngine
    - *                                    WebSocketNode
    - *                                   /             \
    - *                                 /                \
    - *                               /                   \
    - *                     WebSocket1                 WebSocket2
    - *
    - * 
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class WebSocketServlet extends HttpServlet implements Resourcable { - - @Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒") - public static final String WEBPARAM__LIVEINTERVAL = "liveinterval"; - - @Comment("WebScoket服务器最大连接数,为0表示无限制") - public static final String WEBPARAM__WSMAXCONNS = "wsmaxconns"; - - @Comment("WebScoket服务器操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8)") - public static final String WEBPARAM__WSTHREADS = "wsthreads"; - - @Comment("最大消息体长度, 小于1表示无限制") - public static final String WEBPARAM__WSMAXBODY = "wsmaxbody"; - - @Comment("接收客户端的分包(last=false)消息时是否自动合并包") - public static final String WEBPARAM__WSMERGEMSG = "wsmergemsg"; - - @Comment("加密解密器") - public static final String WEBPARAM__CRYPTOR = "cryptor"; - - @Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒") - public static final int DEFAILT_LIVEINTERVAL = 15; - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - private final MessageDigest digest = getMessageDigest(); - - private final BiConsumer restMessageConsumer = createRestOnMessageConsumer(); - - protected Type messageTextType; //RestWebSocket时会被修改 - - //同RestWebSocket.single - protected boolean single = true; //是否单用户单连接 - - //同RestWebSocket.liveinterval - protected int liveinterval = DEFAILT_LIVEINTERVAL; - - //同RestWebSocket.wsmaxconns - protected int wsmaxconns = 0; - - //同RestWebSocket.wsthreads - protected int wsthreads = 0; - - //同RestWebSocket.wsmaxbody - protected int wsmaxbody = 32 * 1024; - - //同RestWebSocket.anyuser - protected boolean anyuser = false; - - //同RestWebSocket.mergemsg - protected boolean mergemsg = true; - - //同RestWebSocket.cryptor, 变量名不可改, 被Rest.createRestWebSocketServlet用到 - protected Cryptor cryptor; - - protected boolean permessageDeflate = false; - - protected MessageAgent messageAgent; - - @Resource(name = "jsonconvert") - protected Convert jsonConvert; - - @Resource(name = "$_textconvert") - protected Convert textConvert; - - @Resource(name = "$_binaryconvert") - protected Convert binaryConvert; - - @Resource(name = "$_sendconvert") - protected Convert sendConvert; - - @Resource(name = "$") - protected WebSocketNode node; - - @Resource(name = "SERVER_RESFACTORY") - protected ResourceFactory resourceFactory; - - protected WebSocketServlet() { - Type msgtype = String.class; - try { - for (Method method : this.getClass().getDeclaredMethods()) { - if (!method.getName().equals("createWebSocket")) continue; - if (method.getParameterCount() > 0) continue; - Type rt = TypeToken.getGenericType(method.getGenericReturnType(), this.getClass()); - if (rt instanceof ParameterizedType) { - msgtype = ((ParameterizedType) rt).getActualTypeArguments()[1]; - } - if (msgtype == Object.class) msgtype = String.class; - break; - } - } catch (Exception e) { - logger.warning(this.getClass().getName() + " not designate text message type on createWebSocket Method"); - } - this.messageTextType = msgtype; - } - - @Override - final void preInit(HttpContext context, AnyValue conf) { - if (this.textConvert == null) this.textConvert = jsonConvert; - if (this.sendConvert == null) this.sendConvert = jsonConvert; - InetSocketAddress addr = context.getServerAddress(); - if (this.node == null) this.node = createWebSocketNode(); - if (this.node == null) { //没有部署SNCP,即不是分布式 - this.node = new WebSocketNodeService(); - if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName()); - } - if (this.node.sendConvert == null) this.node.sendConvert = this.sendConvert; - if (this.messageAgent != null) this.node.messageAgent = this.messageAgent; - { - AnyValue props = conf; - if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties"); - if (props != null) { - String cryptorClass = props.getValue(WEBPARAM__CRYPTOR); - if (cryptorClass != null && !cryptorClass.isEmpty()) { - try { - this.cryptor = (Cryptor) Thread.currentThread().getContextClassLoader().loadClass(cryptorClass).getDeclaredConstructor().newInstance(); - if (resourceFactory != null && this.cryptor != null) resourceFactory.inject(this.cryptor); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - } - } - //存在WebSocketServlet,则此WebSocketNode必须是本地模式Service - this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", - this.single, context, liveinterval, wsmaxconns, wsthreads, wsmaxbody, mergemsg, this.cryptor, this.node, this.sendConvert, logger); - this.node.init(conf); - this.node.localEngine.init(conf); - - } - - @Override - final void postDestroy(HttpContext context, AnyValue conf) { - this.node.postDestroy(conf); - super.destroy(context, conf); - this.node.localEngine.destroy(conf); - } - - @Override - public String resourceName() { - return this.getClass().getSimpleName().replace("_Dyn", "").toLowerCase().replaceAll("websocket.*$", "").replaceAll("servlet.*$", ""); - } - - @Override - public final void execute(final HttpRequest request, final HttpResponse response) throws IOException { - final boolean debug = logger.isLoggable(Level.FINEST); - if (!request.isWebSocket()) { - if (debug) logger.finest("WebSocket connect abort, (Not GET Method) or (Connection != Upgrade) or (Upgrade != websocket). request=" + request); - response.finish(true); - return; - } - final String key = request.getHeader("Sec-WebSocket-Key"); - if (key == null) { - if (debug) logger.finest("WebSocket connect abort, Not found Sec-WebSocket-Key header. request=" + request); - response.finish(true); - return; - } - if (this.node.localEngine.isLocalConnLimited()) { - if (debug) logger.finest("WebSocket connections limit, wsmaxconns=" + this.node.localEngine.getLocalWsmaxconns()); - response.finish(true); - return; - } - final WebSocket webSocket = this.createWebSocket(); - webSocket._engine = this.node.localEngine; - webSocket._channel = response.getChannel(); - webSocket._messageTextType = this.messageTextType; - webSocket._textConvert = textConvert; - webSocket._binaryConvert = binaryConvert; - webSocket._sendConvert = sendConvert; - webSocket._remoteAddress = request.getRemoteAddress(); - webSocket._remoteAddr = request.getRemoteAddr(); - webSocket._sncpAddress = this.node.localSncpAddress; - if (this.permessageDeflate && request.getHeader("Sec-WebSocket-Extensions", "").contains("permessage-deflate")) { - webSocket.deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true); - webSocket.inflater = new Inflater(true); - } - initRestWebSocket(webSocket); - CompletableFuture sessionFuture = webSocket.onOpen(request); - if (sessionFuture == null) { - if (debug) logger.finest("WebSocket connect abort, Not found sessionid. request=" + request); - response.finish(true); - return; - } - sessionFuture.whenComplete((sessionid, ex) -> { - if ((sessionid == null && webSocket.delayPackets == null) || ex != null) { - if (debug || ex != null) logger.log(ex == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Not found sessionid or occur error. request=" + request, ex); - response.finish(true); - return; - } - //onOpen成功或者存在delayPackets - webSocket._sessionid = sessionid; - request.setKeepAlive(true); - byte[] bytes = (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes(); - synchronized (digest) { - bytes = digest.digest(bytes); - } - response.setStatus(101); - response.setHeader("Connection", "Upgrade"); - response.addHeader("Upgrade", "websocket"); - response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes)); - if (webSocket.deflater != null) response.addHeader("Sec-WebSocket-Extensions", "permessage-deflate"); - - response.sendBody((ByteBuffer) null, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachment) { - webSocket._readHandler = new WebSocketReadHandler(response.getContext(), webSocket, restMessageConsumer); - webSocket._writeHandler = new WebSocketWriteHandler(response.getContext(), webSocket); - Runnable createUseridHandler = () -> { - CompletableFuture userFuture = webSocket.createUserid(); - if (userFuture == null) { - if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request); - response.finish(true); - return; - } - userFuture.whenComplete((userid, ex2) -> { - if ((userid == null && webSocket.delayPackets == null) || ex2 != null) { - if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2); - response.finish(true); - return; - } - Runnable runHandler = () -> { - webSocket._userid = userid; - if (single && !anyuser) { - WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, nex) -> { - if (rs) { - CompletableFuture rcFuture = webSocket.onSingleRepeatConnect(); - Consumer task = (oldkilled) -> { - if (oldkilled) { - WebSocketServlet.this.node.localEngine.addLocal(webSocket); - response.removeChannel(); - webSocket._readHandler.startRead(); -// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer); -// webSocket._runner = runner; -// runner.run(); //context.runAsync(runner); - response.finish(true); - } else { //关闭新连接 - response.finish(true); - } - }; - if (rcFuture == null) { - task.accept(false); - } else { - rcFuture.whenComplete((r, e) -> { - if (e != null) { - response.finish(true); - } else { - task.accept(r); - } - }); - } - } else { - WebSocketServlet.this.node.localEngine.addLocal(webSocket); - response.removeChannel(); - webSocket._readHandler.startRead(); -// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer); -// webSocket._runner = runner; -// runner.run(); //context.runAsync(runner); - response.finish(true); - } - }); - } else { - WebSocketServlet.this.node.localEngine.addLocal(webSocket); - response.removeChannel(); - webSocket._readHandler.startRead(); -// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer); -// webSocket._runner = runner; -// runner.run(); //context.runAsync(runner); - response.finish(true); - } - }; - if (webSocket.delayPackets != null) { //存在待发送的消息 - List delayPackets = webSocket.delayPackets; - webSocket.delayPackets = null; - CompletableFuture cf = webSocket._writeHandler.send(delayPackets.toArray(new WebSocketPacket[delayPackets.size()])); - cf.whenComplete((Integer v, Throwable t) -> { - if (userid == null || t != null) { - if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t); - response.finish(true); - } else { - runHandler.run(); - } - }); - } else { - runHandler.run(); - } - }); - }; - if (webSocket.delayPackets != null) { //存在待发送的消息 - List delayPackets = webSocket.delayPackets; - webSocket.delayPackets = null; - CompletableFuture cf = webSocket._writeHandler.send(delayPackets.toArray(new WebSocketPacket[delayPackets.size()])); - cf.whenComplete((Integer v, Throwable t) -> { - if (sessionid == null || t != null) { - if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t); - response.finish(true); - } else { - createUseridHandler.run(); - } - }); - } else { - createUseridHandler.run(); - } - } - - @Override - public void failed(Throwable exc, Void attachment) { - logger.log(Level.FINEST, "WebSocket connect abort, Response send abort. request = " + request, exc); - response.finish(true); - } - }); - }); - } - - protected abstract WebSocket createWebSocket(); - - protected WebSocketNode createWebSocketNode() { - return null; - } - - protected void initRestWebSocket(WebSocket websocket) { //设置WebSocket中的@Resource资源 - } - - protected BiConsumer createRestOnMessageConsumer() { - return null; - } - - private static MessageDigest getMessageDigest() { - try { - return MessageDigest.getInstance("SHA-1"); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.CompletionHandler; +import java.security.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.*; +import java.util.logging.*; +import java.util.zip.*; +import javax.annotation.*; +import org.redkale.boot.Application; +import org.redkale.convert.Convert; +import org.redkale.mq.MessageAgent; +import org.redkale.net.Cryptor; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + *

    + * 当WebSocketServlet接收一个TCP连接后,进行协议判断,如果成功就会创建一个WebSocket。
    + *
    + *                                    WebSocketServlet
    + *                                            |
    + *                                            |
    + *                                   WebSocketEngine
    + *                                    WebSocketNode
    + *                                   /             \
    + *                                 /                \
    + *                               /                   \
    + *                     WebSocket1                 WebSocket2
    + *
    + * 
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class WebSocketServlet extends HttpServlet implements Resourcable { + + @Comment("WebScoket服务器给客户端进行ping操作的间隔时间, 单位: 秒") + public static final String WEBPARAM__LIVEINTERVAL = "liveinterval"; + + @Comment("WebScoket服务器最大连接数,为0表示无限制") + public static final String WEBPARAM__WSMAXCONNS = "wsmaxconns"; + + @Comment("WebScoket服务器操作WebSocketNode对应CacheSource并发数, 为-1表示无限制,为0表示系统默认值(CPU*8)") + public static final String WEBPARAM__WSTHREADS = "wsthreads"; + + @Comment("最大消息体长度, 小于1表示无限制") + public static final String WEBPARAM__WSMAXBODY = "wsmaxbody"; + + @Comment("接收客户端的分包(last=false)消息时是否自动合并包") + public static final String WEBPARAM__WSMERGEMSG = "wsmergemsg"; + + @Comment("加密解密器") + public static final String WEBPARAM__CRYPTOR = "cryptor"; + + @Comment("WebScoket服务器给客户端进行ping操作的默认间隔时间, 单位: 秒") + public static final int DEFAILT_LIVEINTERVAL = 15; + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + private final MessageDigest digest = getMessageDigest(); + + private final BiConsumer restMessageConsumer = createRestOnMessageConsumer(); + + protected Type messageTextType; //RestWebSocket时会被修改 + + //同RestWebSocket.single + protected boolean single = true; //是否单用户单连接 + + //同RestWebSocket.liveinterval + protected int liveinterval = DEFAILT_LIVEINTERVAL; + + //同RestWebSocket.wsmaxconns + protected int wsmaxconns = 0; + + //同RestWebSocket.wsthreads + protected int wsthreads = 0; + + //同RestWebSocket.wsmaxbody + protected int wsmaxbody = 32 * 1024; + + //同RestWebSocket.anyuser + protected boolean anyuser = false; + + //同RestWebSocket.mergemsg + protected boolean mergemsg = true; + + //同RestWebSocket.cryptor, 变量名不可改, 被Rest.createRestWebSocketServlet用到 + protected Cryptor cryptor; + + protected boolean permessageDeflate = false; + + protected MessageAgent messageAgent; + + @Resource(name = "jsonconvert") + protected Convert jsonConvert; + + @Resource(name = "$_textconvert") + protected Convert textConvert; + + @Resource(name = "$_binaryconvert") + protected Convert binaryConvert; + + @Resource(name = "$_sendconvert") + protected Convert sendConvert; + + @Resource(name = "$") + protected WebSocketNode node; + + @Resource(name = "SERVER_RESFACTORY") + protected ResourceFactory resourceFactory; + + protected WebSocketServlet() { + Type msgtype = String.class; + try { + for (Method method : this.getClass().getDeclaredMethods()) { + if (!method.getName().equals("createWebSocket")) continue; + if (method.getParameterCount() > 0) continue; + Type rt = TypeToken.getGenericType(method.getGenericReturnType(), this.getClass()); + if (rt instanceof ParameterizedType) { + msgtype = ((ParameterizedType) rt).getActualTypeArguments()[1]; + } + if (msgtype == Object.class) msgtype = String.class; + break; + } + } catch (Exception e) { + logger.warning(this.getClass().getName() + " not designate text message type on createWebSocket Method"); + } + this.messageTextType = msgtype; + } + + @Override + final void preInit(Application application, HttpContext context, AnyValue conf) { + if (this.textConvert == null) this.textConvert = jsonConvert; + if (this.sendConvert == null) this.sendConvert = jsonConvert; + InetSocketAddress addr = context.getServerAddress(); + if (this.node == null) this.node = createWebSocketNode(); + if (this.node == null) { //没有部署SNCP,即不是分布式 + this.node = new WebSocketNodeService(); + if (logger.isLoggable(Level.WARNING)) logger.warning("Not found WebSocketNode, create a default value for " + getClass().getName()); + } + if (this.node.sendConvert == null) this.node.sendConvert = this.sendConvert; + if (this.messageAgent != null) this.node.messageAgent = this.messageAgent; + { + AnyValue props = conf; + if (conf != null && conf.getAnyValue("properties") != null) props = conf.getAnyValue("properties"); + if (props != null) { + String cryptorClass = props.getValue(WEBPARAM__CRYPTOR); + if (cryptorClass != null && !cryptorClass.isEmpty()) { + try { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass(cryptorClass); + this.cryptor = (Cryptor) clazz.getDeclaredConstructor().newInstance(); + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, cryptorClass); + if (resourceFactory != null && this.cryptor != null) resourceFactory.inject(this.cryptor); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + if (application != null && application.isCompileMode()) return; + //存在WebSocketServlet,则此WebSocketNode必须是本地模式Service + this.node.localEngine = new WebSocketEngine("WebSocketEngine-" + addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", + this.single, context, liveinterval, wsmaxconns, wsthreads, wsmaxbody, mergemsg, this.cryptor, this.node, this.sendConvert, logger); + this.node.init(conf); + this.node.localEngine.init(conf); + + } + + @Override + final void postDestroy(Application application, HttpContext context, AnyValue conf) { + this.node.postDestroy(conf); + super.destroy(context, conf); + if (application != null && application.isCompileMode()) return; + this.node.localEngine.destroy(conf); + } + + @Override + public String resourceName() { + return this.getClass().getSimpleName().replace("_Dyn", "").toLowerCase().replaceAll("websocket.*$", "").replaceAll("servlet.*$", ""); + } + + @Override + public final void execute(final HttpRequest request, final HttpResponse response) throws IOException { + final boolean debug = logger.isLoggable(Level.FINEST); + if (!request.isWebSocket()) { + if (debug) logger.finest("WebSocket connect abort, (Not GET Method) or (Connection != Upgrade) or (Upgrade != websocket). request=" + request); + response.finish(true); + return; + } + final String key = request.getHeader("Sec-WebSocket-Key"); + if (key == null) { + if (debug) logger.finest("WebSocket connect abort, Not found Sec-WebSocket-Key header. request=" + request); + response.finish(true); + return; + } + if (this.node.localEngine.isLocalConnLimited()) { + if (debug) logger.finest("WebSocket connections limit, wsmaxconns=" + this.node.localEngine.getLocalWsmaxconns()); + response.finish(true); + return; + } + final WebSocket webSocket = this.createWebSocket(); + webSocket._engine = this.node.localEngine; + webSocket._channel = response.getChannel(); + webSocket._messageTextType = this.messageTextType; + webSocket._textConvert = textConvert; + webSocket._binaryConvert = binaryConvert; + webSocket._sendConvert = sendConvert; + webSocket._remoteAddress = request.getRemoteAddress(); + webSocket._remoteAddr = request.getRemoteAddr(); + webSocket._sncpAddress = this.node.localSncpAddress; + if (this.permessageDeflate && request.getHeader("Sec-WebSocket-Extensions", "").contains("permessage-deflate")) { + webSocket.deflater = new Deflater(Deflater.DEFAULT_COMPRESSION, true); + webSocket.inflater = new Inflater(true); + } + initRestWebSocket(webSocket); + CompletableFuture sessionFuture = webSocket.onOpen(request); + if (sessionFuture == null) { + if (debug) logger.finest("WebSocket connect abort, Not found sessionid. request=" + request); + response.finish(true); + return; + } + sessionFuture.whenComplete((sessionid, ex) -> { + if ((sessionid == null && webSocket.delayPackets == null) || ex != null) { + if (debug || ex != null) logger.log(ex == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Not found sessionid or occur error. request=" + request, ex); + response.finish(true); + return; + } + //onOpen成功或者存在delayPackets + webSocket._sessionid = sessionid; + request.setKeepAlive(true); + byte[] bytes = (key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes(); + synchronized (digest) { + bytes = digest.digest(bytes); + } + response.setStatus(101); + response.setHeader("Connection", "Upgrade"); + response.addHeader("Upgrade", "websocket"); + response.addHeader("Sec-WebSocket-Accept", Base64.getEncoder().encodeToString(bytes)); + if (webSocket.deflater != null) response.addHeader("Sec-WebSocket-Extensions", "permessage-deflate"); + + response.sendBody((ByteBuffer) null, new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachment) { + webSocket._readHandler = new WebSocketReadHandler(response.getContext(), webSocket, restMessageConsumer); + webSocket._writeHandler = new WebSocketWriteHandler(response.getContext(), webSocket); + Runnable createUseridHandler = () -> { + CompletableFuture userFuture = webSocket.createUserid(); + if (userFuture == null) { + if (debug) logger.finest("WebSocket connect abort, Create userid abort. request = " + request); + response.finish(true); + return; + } + userFuture.whenComplete((userid, ex2) -> { + if ((userid == null && webSocket.delayPackets == null) || ex2 != null) { + if (debug || ex2 != null) logger.log(ex2 == null ? Level.FINEST : Level.FINE, "WebSocket connect abort, Create userid abort. request = " + request, ex2); + response.finish(true); + return; + } + Runnable runHandler = () -> { + webSocket._userid = userid; + if (single && !anyuser) { + WebSocketServlet.this.node.existsWebSocket(userid).whenComplete((rs, nex) -> { + if (rs) { + CompletableFuture rcFuture = webSocket.onSingleRepeatConnect(); + Consumer task = (oldkilled) -> { + if (oldkilled) { + WebSocketServlet.this.node.localEngine.addLocal(webSocket); + response.removeChannel(); + webSocket._readHandler.startRead(); +// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer); +// webSocket._runner = runner; +// runner.run(); //context.runAsync(runner); + response.finish(true); + } else { //关闭新连接 + response.finish(true); + } + }; + if (rcFuture == null) { + task.accept(false); + } else { + rcFuture.whenComplete((r, e) -> { + if (e != null) { + response.finish(true); + } else { + task.accept(r); + } + }); + } + } else { + WebSocketServlet.this.node.localEngine.addLocal(webSocket); + response.removeChannel(); + webSocket._readHandler.startRead(); +// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer); +// webSocket._runner = runner; +// runner.run(); //context.runAsync(runner); + response.finish(true); + } + }); + } else { + WebSocketServlet.this.node.localEngine.addLocal(webSocket); + response.removeChannel(); + webSocket._readHandler.startRead(); +// WebSocketRunner runner = new WebSocketRunner(context, webSocket, restMessageConsumer); +// webSocket._runner = runner; +// runner.run(); //context.runAsync(runner); + response.finish(true); + } + }; + if (webSocket.delayPackets != null) { //存在待发送的消息 + List delayPackets = webSocket.delayPackets; + webSocket.delayPackets = null; + CompletableFuture cf = webSocket._writeHandler.send(delayPackets.toArray(new WebSocketPacket[delayPackets.size()])); + cf.whenComplete((Integer v, Throwable t) -> { + if (userid == null || t != null) { + if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t); + response.finish(true); + } else { + runHandler.run(); + } + }); + } else { + runHandler.run(); + } + }); + }; + if (webSocket.delayPackets != null) { //存在待发送的消息 + List delayPackets = webSocket.delayPackets; + webSocket.delayPackets = null; + CompletableFuture cf = webSocket._writeHandler.send(delayPackets.toArray(new WebSocketPacket[delayPackets.size()])); + cf.whenComplete((Integer v, Throwable t) -> { + if (sessionid == null || t != null) { + if (t != null) logger.log(Level.FINEST, "WebSocket connect abort, Response send delayPackets abort. request = " + request, t); + response.finish(true); + } else { + createUseridHandler.run(); + } + }); + } else { + createUseridHandler.run(); + } + } + + @Override + public void failed(Throwable exc, Void attachment) { + logger.log(Level.FINEST, "WebSocket connect abort, Response send abort. request = " + request, exc); + response.finish(true); + } + }); + }); + } + + protected abstract WebSocket createWebSocket(); + + protected WebSocketNode createWebSocketNode() { + return null; + } + + protected void initRestWebSocket(WebSocket websocket) { //设置WebSocket中的@Resource资源 + } + + protected BiConsumer createRestOnMessageConsumer() { + return null; + } + + private static MessageDigest getMessageDigest() { + try { + return MessageDigest.getInstance("SHA-1"); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/org/redkale/net/http/WebSocketUserAddress.java b/src/main/java/org/redkale/net/http/WebSocketUserAddress.java similarity index 96% rename from src/org/redkale/net/http/WebSocketUserAddress.java rename to src/main/java/org/redkale/net/http/WebSocketUserAddress.java index 7055d5154..8fbe8cbe1 100644 --- a/src/org/redkale/net/http/WebSocketUserAddress.java +++ b/src/main/java/org/redkale/net/http/WebSocketUserAddress.java @@ -1,120 +1,120 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.io.Serializable; -import java.net.InetSocketAddress; -import java.util.Collection; -import org.redkale.convert.json.JsonConvert; - -/** - * userid 与 sncpaddress组合对象 - * - * - * @author zhangjx - */ -public interface WebSocketUserAddress extends Serializable { - - Serializable userid(); - - WebSocketAddress address(); - - Collection addresses(); - - public static WebSocketUserAddress create(WebSocketUserAddress userAddress) { - return new SimpleWebSocketUserAddress(userAddress); - } - - public static WebSocketUserAddress createTopic(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) { - return new SimpleWebSocketUserAddress(userid, mqtopic, sncpAddress); - } - - public static WebSocketUserAddress create(Serializable userid, WebSocketAddress address) { - return new SimpleWebSocketUserAddress(userid, address); - } - - public static WebSocketUserAddress create(Serializable userid, Collection addresses) { - return new SimpleWebSocketUserAddress(userid, addresses); - } - - public static class SimpleWebSocketUserAddress implements WebSocketUserAddress { - - private Serializable userid; - - private WebSocketAddress address; - - private Collection addresses; - - public SimpleWebSocketUserAddress() { - } - - public SimpleWebSocketUserAddress(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) { - this.userid = userid; - this.address = new WebSocketAddress(mqtopic, sncpAddress); - } - - public SimpleWebSocketUserAddress(Serializable userid, WebSocketAddress address) { - this.userid = userid; - this.address = address; - } - - public SimpleWebSocketUserAddress(Serializable userid, Collection addresses) { - this.userid = userid; - this.addresses = addresses; - } - - public SimpleWebSocketUserAddress(WebSocketUserAddress userAddress) { - if (userAddress == null) return; - this.userid = userAddress.userid(); - this.address = userAddress.address(); - this.addresses = userAddress.addresses(); - } - - @Override - public Serializable userid() { - return userid; - } - - @Override - public WebSocketAddress address() { - return address; - } - - @Override - public Collection addresses() { - return addresses; - } - - public Serializable getUserid() { - return userid; - } - - public void setUserid(Serializable userid) { - this.userid = userid; - } - - public WebSocketAddress getAddress() { - return address; - } - - public void setAddress(WebSocketAddress address) { - this.address = address; - } - - public Collection getAddresses() { - return addresses; - } - - public void setAddresses(Collection addresses) { - this.addresses = addresses; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.Serializable; +import java.net.InetSocketAddress; +import java.util.Collection; +import org.redkale.convert.json.JsonConvert; + +/** + * userid 与 sncpaddress组合对象 + * + * + * @author zhangjx + */ +public interface WebSocketUserAddress extends Serializable { + + Serializable userid(); + + WebSocketAddress address(); + + Collection addresses(); + + public static WebSocketUserAddress create(WebSocketUserAddress userAddress) { + return new SimpleWebSocketUserAddress(userAddress); + } + + public static WebSocketUserAddress createTopic(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) { + return new SimpleWebSocketUserAddress(userid, mqtopic, sncpAddress); + } + + public static WebSocketUserAddress create(Serializable userid, WebSocketAddress address) { + return new SimpleWebSocketUserAddress(userid, address); + } + + public static WebSocketUserAddress create(Serializable userid, Collection addresses) { + return new SimpleWebSocketUserAddress(userid, addresses); + } + + public static class SimpleWebSocketUserAddress implements WebSocketUserAddress { + + private Serializable userid; + + private WebSocketAddress address; + + private Collection addresses; + + public SimpleWebSocketUserAddress() { + } + + public SimpleWebSocketUserAddress(Serializable userid, String mqtopic, InetSocketAddress sncpAddress) { + this.userid = userid; + this.address = new WebSocketAddress(mqtopic, sncpAddress); + } + + public SimpleWebSocketUserAddress(Serializable userid, WebSocketAddress address) { + this.userid = userid; + this.address = address; + } + + public SimpleWebSocketUserAddress(Serializable userid, Collection addresses) { + this.userid = userid; + this.addresses = addresses; + } + + public SimpleWebSocketUserAddress(WebSocketUserAddress userAddress) { + if (userAddress == null) return; + this.userid = userAddress.userid(); + this.address = userAddress.address(); + this.addresses = userAddress.addresses(); + } + + @Override + public Serializable userid() { + return userid; + } + + @Override + public WebSocketAddress address() { + return address; + } + + @Override + public Collection addresses() { + return addresses; + } + + public Serializable getUserid() { + return userid; + } + + public void setUserid(Serializable userid) { + this.userid = userid; + } + + public WebSocketAddress getAddress() { + return address; + } + + public void setAddress(WebSocketAddress address) { + this.address = address; + } + + public Collection getAddresses() { + return addresses; + } + + public void setAddresses(Collection addresses) { + this.addresses = addresses; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/org/redkale/net/http/WebSocketWriteHandler.java b/src/main/java/org/redkale/net/http/WebSocketWriteHandler.java similarity index 97% rename from src/org/redkale/net/http/WebSocketWriteHandler.java rename to src/main/java/org/redkale/net/http/WebSocketWriteHandler.java index 198f14e45..cef3f1e89 100644 --- a/src/org/redkale/net/http/WebSocketWriteHandler.java +++ b/src/main/java/org/redkale/net/http/WebSocketWriteHandler.java @@ -1,132 +1,132 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.util.*; -import java.util.logging.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.nio.channels.CompletionHandler; -import org.redkale.util.ByteArray; -import static org.redkale.net.http.WebSocket.*; - -/** - * - * @author zhangjx - */ -public class WebSocketWriteHandler implements CompletionHandler { - - protected final HttpContext context; - - protected final WebSocket webSocket; - - protected final AtomicBoolean writePending = new AtomicBoolean(); - - protected final ByteArray writeArray = new ByteArray(); - - protected final List> respList = new ArrayList(); - - protected final ConcurrentLinkedDeque> requestQueue = new ConcurrentLinkedDeque(); - - public WebSocketWriteHandler(HttpContext context, WebSocket webSocket) { - this.context = context; - this.webSocket = webSocket; - } - - public CompletableFuture send(WebSocketPacket... packets) { - WebSocketFuture future = new WebSocketFuture<>(packets); - if (writePending.compareAndSet(false, true)) { - respList.clear(); - respList.add(future); - writeArray.clear(); - for (WebSocketPacket p : packets) { - writeEncode(p); - } - webSocket._channel.write(writeArray, this); - } else { - requestQueue.offer(future); - } - return future; - } - - @Override - public void completed(Integer result, Void attachment) { - webSocket.lastSendTime = System.currentTimeMillis(); - for (WebSocketFuture future : respList) { - future.complete(0); - } - respList.clear(); - writeArray.clear(); - WebSocketFuture req; - while ((req = requestQueue.poll()) != null) { - respList.add(req); - for (WebSocketPacket p : req.packets) { - writeEncode(p); - } - } - if (writeArray.isEmpty()) { - if (!writePending.compareAndSet(true, false)) { - completed(0, attachment); - } - } else { - webSocket._channel.write(writeArray, this); - } - } - - @Override - public void failed(Throwable exc, Void attachment) { - WebSocketFuture req; - try { - while ((req = requestQueue.poll()) != null) { - req.completeExceptionally(exc); - } - for (WebSocketFuture future : respList) { - future.completeExceptionally(exc); - } - respList.clear(); - } catch (Exception e) { - } - webSocket.kill(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler"); - if (exc != null && context.getLogger().isLoggable(Level.FINER)) { - context.getLogger().log(Level.FINER, "WebSocket sendMessage on CompletionHandler failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc); - } - } - - //消息编码 - protected void writeEncode(final WebSocketPacket packet) { - final ByteArray array = writeArray; - final byte opcode = (byte) (packet.type.getValue() | 0x80); - final byte[] content = packet.getPayload(); - final int len = content.length; - if (len <= 0x7D) { //125 - array.put(opcode); - array.put((byte) len); - } else if (len <= 0xFFFF) { // 65535 - array.put(opcode); - array.put((byte) 0x7E); //126 - array.putChar((char) len); - } else { - array.put(opcode); - array.put((byte) 0x7F); //127 - array.putLong(len); - } - array.put(content); - } - - protected static class WebSocketFuture extends CompletableFuture { - - protected WebSocketPacket[] packets; - - public WebSocketFuture() { - super(); - } - - public WebSocketFuture(WebSocketPacket... packets) { - super(); - this.packets = packets; - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.util.*; +import java.util.logging.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.nio.channels.CompletionHandler; +import org.redkale.util.ByteArray; +import static org.redkale.net.http.WebSocket.*; + +/** + * + * @author zhangjx + */ +public class WebSocketWriteHandler implements CompletionHandler { + + protected final HttpContext context; + + protected final WebSocket webSocket; + + protected final AtomicBoolean writePending = new AtomicBoolean(); + + protected final ByteArray writeArray = new ByteArray(); + + protected final List> respList = new ArrayList(); + + protected final ConcurrentLinkedDeque> requestQueue = new ConcurrentLinkedDeque(); + + public WebSocketWriteHandler(HttpContext context, WebSocket webSocket) { + this.context = context; + this.webSocket = webSocket; + } + + public CompletableFuture send(WebSocketPacket... packets) { + WebSocketFuture future = new WebSocketFuture<>(packets); + if (writePending.compareAndSet(false, true)) { + respList.clear(); + respList.add(future); + writeArray.clear(); + for (WebSocketPacket p : packets) { + writeEncode(p); + } + webSocket._channel.write(writeArray, this); + } else { + requestQueue.offer(future); + } + return future; + } + + @Override + public void completed(Integer result, Void attachment) { + webSocket.lastSendTime = System.currentTimeMillis(); + for (WebSocketFuture future : respList) { + future.complete(0); + } + respList.clear(); + writeArray.clear(); + WebSocketFuture req; + while ((req = requestQueue.poll()) != null) { + respList.add(req); + for (WebSocketPacket p : req.packets) { + writeEncode(p); + } + } + if (writeArray.isEmpty()) { + if (!writePending.compareAndSet(true, false)) { + completed(0, attachment); + } + } else { + webSocket._channel.write(writeArray, this); + } + } + + @Override + public void failed(Throwable exc, Void attachment) { + WebSocketFuture req; + try { + while ((req = requestQueue.poll()) != null) { + req.completeExceptionally(exc); + } + for (WebSocketFuture future : respList) { + future.completeExceptionally(exc); + } + respList.clear(); + } catch (Exception e) { + } + webSocket.kill(RETCODE_SENDEXCEPTION, "websocket send message failed on CompletionHandler"); + if (exc != null && context.getLogger().isLoggable(Level.FINER)) { + context.getLogger().log(Level.FINER, "WebSocket sendMessage on CompletionHandler failed, force to close channel, live " + (System.currentTimeMillis() - webSocket.getCreatetime()) / 1000 + " seconds", exc); + } + } + + //消息编码 + protected void writeEncode(final WebSocketPacket packet) { + final ByteArray array = writeArray; + final byte opcode = (byte) (packet.type.getValue() | 0x80); + final byte[] content = packet.getPayload(); + final int len = content.length; + if (len <= 0x7D) { //125 + array.put(opcode); + array.put((byte) len); + } else if (len <= 0xFFFF) { // 65535 + array.put(opcode); + array.put((byte) 0x7E); //126 + array.putChar((char) len); + } else { + array.put(opcode); + array.put((byte) 0x7F); //127 + array.putLong(len); + } + array.put(content); + } + + protected static class WebSocketFuture extends CompletableFuture { + + protected WebSocketPacket[] packets; + + public WebSocketFuture() { + super(); + } + + public WebSocketFuture(WebSocketPacket... packets) { + super(); + this.packets = packets; + } + } +} diff --git a/src/org/redkale/net/http/package-info.java b/src/main/java/org/redkale/net/http/package-info.java similarity index 95% rename from src/org/redkale/net/http/package-info.java rename to src/main/java/org/redkale/net/http/package-info.java index 3d749a21f..d1c471d5a 100644 --- a/src/org/redkale/net/http/package-info.java +++ b/src/main/java/org/redkale/net/http/package-info.java @@ -1,4 +1,4 @@ -/** - * HTTP协议包,提供HTTP协议服务器 - */ -package org.redkale.net.http; +/** + * HTTP协议包,提供HTTP协议服务器 + */ +package org.redkale.net.http; diff --git a/src/org/redkale/net/package-info.java b/src/main/java/org/redkale/net/package-info.java similarity index 94% rename from src/org/redkale/net/package-info.java rename to src/main/java/org/redkale/net/package-info.java index 5f10d5ca0..51589834c 100644 --- a/src/org/redkale/net/package-info.java +++ b/src/main/java/org/redkale/net/package-info.java @@ -1,4 +1,4 @@ -/** - * 网络TCP/UDP基础服务包 - */ -package org.redkale.net; +/** + * 网络TCP/UDP基础服务包 + */ +package org.redkale.net; diff --git a/src/org/redkale/net/sncp/Sncp.java b/src/main/java/org/redkale/net/sncp/Sncp.java similarity index 84% rename from src/org/redkale/net/sncp/Sncp.java rename to src/main/java/org/redkale/net/sncp/Sncp.java index 00f76942e..5e91538ca 100644 --- a/src/org/redkale/net/sncp/Sncp.java +++ b/src/main/java/org/redkale/net/sncp/Sncp.java @@ -1,870 +1,830 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import org.redkale.asm.MethodDebugVisitor; -import java.lang.annotation.Annotation; -import java.lang.reflect.*; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.security.*; -import java.util.*; -import javax.annotation.Resource; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; -import org.redkale.asm.*; -import static org.redkale.asm.Opcodes.*; -import org.redkale.asm.Type; -import org.redkale.mq.MessageAgent; -import org.redkale.net.TransportFactory; -import org.redkale.net.http.WebSocketNode; -import org.redkale.net.sncp.SncpClient.SncpAction; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - * Service Node Communicate Protocol - * 生成Service的本地模式或远程模式Service-Class的工具类 - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class Sncp { - - public static final ByteBuffer PING_BUFFER = ByteBuffer.wrap("PING".getBytes()).asReadOnlyBuffer(); - - public static final ByteBuffer PONG_BUFFER = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer(); - - static final String FIELDPREFIX = "_redkale"; - - static final String LOCALPREFIX = "_DynLocal"; - - static final String REMOTEPREFIX = "_DynRemote"; - - private static final MessageDigest md5; - - static { //64进制 - MessageDigest d = null; - try { - d = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException ex) { - ex.printStackTrace(); - } - md5 = d; - } - - private Sncp() { - } - - public static DLong hash(final java.lang.reflect.Method method) { - if (method == null) return DLong.ZERO; - StringBuilder sb = new StringBuilder(); //不能使用method.toString() 因为包含declaringClass信息导致接口与实现类的方法hash不一致 - sb.append(method.getReturnType().getName()).append(' '); - sb.append(method.getName()); - sb.append('('); - boolean first = true; - for (Class pt : method.getParameterTypes()) { - if (!first) sb.append(','); - sb.append(pt.getName()); - first = false; - } - sb.append(')'); - return hash(sb.toString()); - } - - /** - * 对类名或者name字符串进行hash。 - * - * @param name String - * - * @return hash值 - */ - public static DLong hash(final String name) { - if (name == null || name.isEmpty()) return DLong.ZERO; - byte[] bytes = name.trim().getBytes(); - synchronized (md5) { - bytes = md5.digest(bytes); - } - return DLong.create(bytes); - } - - public static boolean isRemote(Service service) { - SncpDyn dyn = service.getClass().getAnnotation(SncpDyn.class); - return dyn != null && dyn.remote(); - } - - public static boolean isSncpDyn(Service service) { - return service.getClass().getAnnotation(SncpDyn.class) != null; - } - - public static int getVersion(Service service) { - if (service == null) return -1; - Version ver = service.getClass().getAnnotation(Version.class); - return ver == null ? -1 : ver.value(); - } - - public static String getResourceName(Service service) { - if (service == null) return null; - Resource res = service.getClass().getAnnotation(Resource.class); - return res == null ? null : res.name(); - } - - public static Class getServiceType(Service service) { - ResourceType rt = service.getClass().getAnnotation(ResourceType.class); - return rt == null ? service.getClass() : rt.value(); - } - - public static Class getResourceType(Service service) { - if (service == null) return null; - ResourceType type = service.getClass().getAnnotation(ResourceType.class); - return type == null ? getServiceType(service) : type.value(); - } - - public static AnyValue getConf(Service service) { - if (service == null) return null; - try { - Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_conf"); - ts.setAccessible(true); - return (AnyValue) ts.get(service); - } catch (Exception e) { - throw new RuntimeException(service + " not found " + FIELDPREFIX + "_conf"); - } - } - - public static SncpClient getSncpClient(Service service) { - if (service == null) return null; - try { - Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_client"); - ts.setAccessible(true); - return (SncpClient) ts.get(service); - } catch (Exception e) { - throw new RuntimeException(service + " not found " + FIELDPREFIX + "_client"); - } - } - - public static MessageAgent getMessageAgent(Service service) { - if (service == null) return null; - try { - Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_messageagent"); - ts.setAccessible(true); - return (MessageAgent) ts.get(service); - } catch (Exception e) { - throw new RuntimeException(service + " not found " + FIELDPREFIX + "_messageagent"); - } - } - - public static void setMessageAgent(Service service, MessageAgent messageAgent) { - if (service == null) return; - try { - Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_messageagent"); - ts.setAccessible(true); - ts.set(service, messageAgent); - if (service instanceof WebSocketNode) { - Field c = WebSocketNode.class.getDeclaredField("messageAgent"); - c.setAccessible(true); - c.set(service, messageAgent); - } - } catch (Exception e) { - throw new RuntimeException(service + " not found " + FIELDPREFIX + "_messageagent"); - } - } - - public static boolean updateTransport(Service service, - final TransportFactory transportFactory, String name, String protocol, InetSocketAddress clientAddress, - final Set groups, final Collection addresses) { - if (!isSncpDyn(service)) return false; - SncpClient client = getSncpClient(service); - client.setRemoteGroups(groups); - if (client.getRemoteGroupTransport() != null) { - client.getRemoteGroupTransport().updateRemoteAddresses(addresses); - } else { - client.setRemoteGroupTransport(transportFactory.createTransport(name, protocol, clientAddress, addresses)); - } - return true; - } - - static void checkAsyncModifier(Class param, Method method) { - if (param == CompletionHandler.class) return; - if (Modifier.isFinal(param.getModifiers())) { - throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} cannot final modifier"); - } - if (!Modifier.isPublic(param.getModifiers())) { - throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} must be public modifier"); - } - if (param.isInterface()) return; - boolean constructorflag = false; - for (Constructor c : param.getDeclaredConstructors()) { - if (c.getParameterCount() == 0) { - int mod = c.getModifiers(); - if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) { - constructorflag = true; - break; - } - } - } - if (param.getDeclaredConstructors().length == 0) constructorflag = true; - if (!constructorflag) throw new RuntimeException(param + " must have a empty parameter Constructor"); - for (Method m : param.getMethods()) { - if (m.getName().equals("completed") && Modifier.isFinal(m.getModifiers())) { - throw new RuntimeException(param + "'s completed method cannot final modifier"); - } else if (m.getName().equals("failed") && Modifier.isFinal(m.getModifiers())) { - throw new RuntimeException(param + "'s failed method cannot final modifier"); - } else if (m.getName().equals("sncp_getParams") && Modifier.isFinal(m.getModifiers())) { - throw new RuntimeException(param + "'s sncp_getParams method cannot final modifier"); - } else if (m.getName().equals("sncp_setParams") && Modifier.isFinal(m.getModifiers())) { - throw new RuntimeException(param + "'s sncp_setParams method cannot final modifier"); - } else if (m.getName().equals("sncp_setFuture") && Modifier.isFinal(m.getModifiers())) { - throw new RuntimeException(param + "'s sncp_setFuture method cannot final modifier"); - } else if (m.getName().equals("sncp_getFuture") && Modifier.isFinal(m.getModifiers())) { - throw new RuntimeException(param + "'s sncp_getFuture method cannot final modifier"); - } - } - } - - public static String toSimpleString(final Service service, int maxNameLength, int maxClassNameLength) { - StringBuilder sb = new StringBuilder(); - sb.append(isRemote(service) ? "RemoteService" : "LocalService "); - int len; - Class type = getResourceType(service); - String name = getResourceName(service); - sb.append("(type= ").append(type.getName()); - len = maxClassNameLength - type.getName().length(); - for (int i = 0; i < len; i++) { - sb.append(' '); - } - sb.append(", name='").append(name).append("'"); - for (int i = 0; i < maxNameLength - name.length(); i++) { - sb.append(' '); - } - sb.append(")"); - return sb.toString(); - } - - /** - *

    -     * public class TestService implements Service{
    -     *
    -     *      public String findSomeThing(){
    -     *          return "hello";
    -     *      }
    -     *
    -     *      @RpcMultiRun(selfrun = false)
    -     *      public void createSomeThing(TestBean bean){
    -     *          //do something
    -     *      }
    -     *
    -     *      @RpcMultiRun
    -     *      public String updateSomeThing(String id){
    -     *          return "hello" + id;
    -     *      }
    -     * }
    -     * 
    - * - *
    -     * @Resource(name = "")
    -     * @SncpDyn(remote = false)
    -     * @ResourceType(TestService.class)
    -     * public final class _DynLocalTestService extends TestService{
    -     *
    -     *      private AnyValue _redkale_conf;
    -     *
    -     *      private SncpClient _redkale_client;
    -     *
    -     *      @Override
    -     *      public String toString() {
    -     *          return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
    -     *      }
    -     * }
    -     * 
    - * - * 创建Service的本地模式Class - * - * @param Service子类 - * @param classLoader ClassLoader - * @param name 资源名 - * @param serviceImplClass Service类 - * - * @return Service实例 - */ - @SuppressWarnings("unchecked") - protected static Class createLocalServiceClass(ClassLoader classLoader, final String name, final Class serviceImplClass) { - if (serviceImplClass == null) return null; - if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass; - ResourceFactory.checkResourceName(name); - int mod = serviceImplClass.getModifiers(); - if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceImplClass; - if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceImplClass; - final List methods = SncpClient.parseMethod(serviceImplClass); - final String supDynName = serviceImplClass.getName().replace('.', '/'); - final String clientName = SncpClient.class.getName().replace('.', '/'); - final String resDesc = Type.getDescriptor(Resource.class); - final String clientDesc = Type.getDescriptor(SncpClient.class); - final String anyValueDesc = Type.getDescriptor(AnyValue.class); - final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); - ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; - String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName(); - if (!name.isEmpty()) { - boolean normal = true; - for (char ch : name.toCharArray()) { - if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) normal = false; - } - if (!normal) throw new RuntimeException(serviceImplClass + "'s resource name is illegal, must be 0-9 _ a-z A-Z"); - newDynName += "_" + (normal ? name : hash(name)); - } - try { - return (Class) loader.loadClass(newDynName.replace('/', '.')); - } catch (Throwable ex) { - } - //------------------------------------------------------------------------------ - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodDebugVisitor mv; - AnnotationVisitor av0; - - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); - { - av0 = cw.visitAnnotation(resDesc, true); - av0.visit("name", name); - av0.visitEnd(); - } - { - av0 = cw.visitAnnotation(sncpDynDesc, true); - av0.visit("remote", Boolean.FALSE); - av0.visitEnd(); - } - { //给新类加上 原有的Annotation - for (Annotation ann : serviceImplClass.getAnnotations()) { - if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue; - visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); - } - } - { - av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true); - ResourceType rty = serviceImplClass.getAnnotation(ResourceType.class); - av0.visit("value", Type.getType(Type.getDescriptor(rty == null ? serviceImplClass : rty.value()))); - av0.visitEnd(); - } - { - fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null); - fv.visitEnd(); - } - { - fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null); - fv.visitEnd(); - } - { - fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_messageagent", Type.getDescriptor(MessageAgent.class), null, null); - fv.visitEnd(); - } - { //构造函数 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { // toString() - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); - Label l1 = new Label(); - mv.visitJumpInsn(IFNONNULL, l1); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false); - Label l2 = new Label(); - mv.visitJumpInsn(GOTO, l2); - mv.visitLabel(l1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); - mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "toSimpleString", "()Ljava/lang/String;", false); - mv.visitLabel(l2); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - cw.visitEnd(); - byte[] bytes = cw.toByteArray(); - Class newClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - return (Class) newClazz; - } - - private static void visitAnnotation(final AnnotationVisitor av, final Annotation ann) { - try { - for (Method anm : ann.annotationType().getMethods()) { - final String mname = anm.getName(); - if ("equals".equals(mname) || "hashCode".equals(mname) || "toString".equals(mname) || "annotationType".equals(mname)) continue; - final Object r = anm.invoke(ann); - if (r instanceof String[]) { - AnnotationVisitor av1 = av.visitArray(mname); - for (String item : (String[]) r) { - av1.visit(null, item); - } - av1.visitEnd(); - } else if (r instanceof Class[]) { - AnnotationVisitor av1 = av.visitArray(mname); - for (Class item : (Class[]) r) { - av1.visit(null, Type.getType(item)); - } - av1.visitEnd(); - } else if (r instanceof Enum[]) { - AnnotationVisitor av1 = av.visitArray(mname); - for (Enum item : (Enum[]) r) { - av1.visitEnum(null, Type.getDescriptor(item.getClass()), ((Enum) item).name()); - } - av1.visitEnd(); - } else if (r instanceof Annotation[]) { - AnnotationVisitor av1 = av.visitArray(mname); - for (Annotation item : (Annotation[]) r) { - visitAnnotation(av1.visitAnnotation(null, Type.getDescriptor(((Annotation) item).annotationType())), item); - } - av1.visitEnd(); - } else if (r instanceof Class) { - av.visit(mname, Type.getType((Class) r)); - } else if (r instanceof Enum) { - av.visitEnum(mname, Type.getDescriptor(r.getClass()), ((Enum) r).name()); - } else if (r instanceof Annotation) { - visitAnnotation(av.visitAnnotation(null, Type.getDescriptor(((Annotation) r).annotationType())), (Annotation) r); - } else { - av.visit(mname, r); - } - } - av.visitEnd(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static T createSimpleLocalService(final Class serviceImplClass, final MessageAgent messageAgent, - final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) { - return createLocalService(null, "", serviceImplClass, messageAgent, ResourceFactory.root(), transportFactory, clientSncpAddress, Utility.ofSet(groups), null); - } - - /** - * - * 创建本地模式Service实例 - * - * @param Service泛型 - * @param classLoader ClassLoader - * @param name 资源名 - * @param serviceImplClass Service类 - * @param messageAgent MQ管理器 - * @param resourceFactory ResourceFactory - * @param transportFactory TransportFactory - * @param clientSncpAddress 本地IP地址 - * @param groups 所有的组节点,包含自身 - * @param conf 启动配置项 - * - * @return Service的本地模式实例 - */ - @SuppressWarnings("unchecked") - public static T createLocalService( - final ClassLoader classLoader, - final String name, - final Class serviceImplClass, - final MessageAgent messageAgent, - final ResourceFactory resourceFactory, - final TransportFactory transportFactory, - final InetSocketAddress clientSncpAddress, - final Set groups, - final AnyValue conf) { - try { - final Class newClazz = createLocalServiceClass(classLoader, name, serviceImplClass); - T service = (T) newClazz.getDeclaredConstructor().newInstance(); - //-------------------------------------- - Service remoteService = null; - { - Class loop = newClazz; - do { - for (Field field : loop.getDeclaredFields()) { - int mod = field.getModifiers(); - if (Modifier.isFinal(mod) || Modifier.isStatic(mod)) continue; - if (field.getAnnotation(RpcRemote.class) == null) continue; - if (!field.getType().isAssignableFrom(newClazz)) continue; - field.setAccessible(true); - if (remoteService == null && clientSncpAddress != null) { - remoteService = createRemoteService(classLoader, name, serviceImplClass, messageAgent, transportFactory, clientSncpAddress, groups, conf); - } - if (remoteService != null) field.set(service, remoteService); - } - } while ((loop = loop.getSuperclass()) != Object.class); - } - SncpClient client = null; - { - try { - Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); - c.setAccessible(true); - client = new SncpClient(name, serviceImplClass, service, messageAgent, transportFactory, false, newClazz, clientSncpAddress); - c.set(service, client); - if (transportFactory != null) transportFactory.addSncpService(service); - } catch (NoSuchFieldException ne) { - ne.printStackTrace(); - } - } - if (messageAgent != null) { - Field c = newClazz.getDeclaredField(FIELDPREFIX + "_messageagent"); - c.setAccessible(true); - c.set(service, messageAgent); - } - if (client == null) return service; - { - Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf"); - c.setAccessible(true); - c.set(service, conf); - if (service instanceof WebSocketNode) { - c = WebSocketNode.class.getDeclaredField("messageAgent"); - c.setAccessible(true); - c.set(service, messageAgent); - } - } - return service; - } catch (RuntimeException rex) { - throw rex; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public static T createSimpleRemoteService(final Class serviceImplClass, final MessageAgent messageAgent, - final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) { - return createRemoteService(null, "", serviceImplClass, messageAgent, transportFactory, clientSncpAddress, Utility.ofSet(groups), null); - } - - /** - *
    -     * @Resource(name = "")
    -     * @SncpDyn(remote = true)
    -     * @ResourceType(TestService.class)
    -     * public final class _DynRemoteTestService extends TestService{
    -     *
    -     *      private AnyValue _redkale_conf;
    -     *
    -     *      private SncpClient _redkale_client;
    -     *
    -     *      @Override
    -     *      public void createSomeThing(TestBean bean){
    -     *          _redkale_client.remote(0, bean);
    -     *      }
    -     *
    -     *      @Override
    -     *      public String findSomeThing(){
    -     *          return _redkale_client.remote(1);
    -     *      }
    -     *
    -     *      @Override
    -     *      public String updateSomeThing(String id){
    -     *          return  _redkale_client.remote(2, id);
    -     *      }
    -     * }
    -     * 
    - * - * 创建远程模式的Service实例 - * - * @param Service泛型 - * @param classLoader ClassLoader - * @param name 资源名 - * @param serviceTypeOrImplClass Service类 - * @param messageAgent MQ管理器 - * @param transportFactory TransportFactory - * @param clientAddress 本地IP地址 - * @param groups0 所有的组节点,包含自身 - * @param conf 启动配置项 - * - * @return Service的远程模式实例 - */ - @SuppressWarnings("unchecked") - - public static T createRemoteService( - final ClassLoader classLoader, - final String name, - final Class serviceTypeOrImplClass, - final MessageAgent messageAgent, - final TransportFactory transportFactory, - final InetSocketAddress clientAddress, - final Set groups0, - final AnyValue conf) { - if (serviceTypeOrImplClass == null) return null; - if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null; - final Set groups = groups0 == null ? new HashSet<>() : groups0; - ResourceFactory.checkResourceName(name); - int mod = serviceTypeOrImplClass.getModifiers(); - boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceTypeOrImplClass.isInterface()); - if (!java.lang.reflect.Modifier.isPublic(mod)) return null; - final String supDynName = serviceTypeOrImplClass.getName().replace('.', '/'); - final String clientName = SncpClient.class.getName().replace('.', '/'); - final String resDesc = Type.getDescriptor(Resource.class); - final String clientDesc = Type.getDescriptor(SncpClient.class); - final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); - final String anyValueDesc = Type.getDescriptor(AnyValue.class); - ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; - String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName(); - try { - Class newClazz = loader.loadClass(newDynName.replace('/', '.')); - T service = (T) newClazz.getDeclaredConstructor().newInstance(); - SncpClient client = new SncpClient(name, serviceTypeOrImplClass, service, messageAgent, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); - client.setRemoteGroups(groups); - if (transportFactory != null) client.setRemoteGroupTransport(transportFactory.loadTransport(clientAddress, groups)); - Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); - c.setAccessible(true); - c.set(service, client); - if (messageAgent != null) { - Field m = newClazz.getDeclaredField(FIELDPREFIX + "_messageagent"); - m.setAccessible(true); - m.set(service, messageAgent); - if (service instanceof WebSocketNode) { - c = WebSocketNode.class.getDeclaredField("messageAgent"); - c.setAccessible(true); - c.set(service, messageAgent); - } - } - if (transportFactory != null) transportFactory.addSncpService(service); - return service; - } catch (Throwable ex) { - } - //------------------------------------------------------------------------------ - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodDebugVisitor mv; - AnnotationVisitor av0; - - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, serviceTypeOrImplClass.isInterface() ? new String[]{supDynName} : null); - { - av0 = cw.visitAnnotation(resDesc, true); - av0.visit("name", name); - av0.visitEnd(); - } - { - av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true); - ResourceType rty = serviceTypeOrImplClass.getAnnotation(ResourceType.class); - av0.visit("value", Type.getType(Type.getDescriptor(rty == null ? serviceTypeOrImplClass : rty.value()))); - av0.visitEnd(); - } - { - av0 = cw.visitAnnotation(sncpDynDesc, true); - av0.visit("remote", Boolean.TRUE); - av0.visitEnd(); - } - { //给新类加上 原有的Annotation - for (Annotation ann : serviceTypeOrImplClass.getAnnotations()) { - if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue; - visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); - } - } - { - fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null); - fv.visitEnd(); - } - { - fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null); - fv.visitEnd(); - } - { - fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_messageagent", Type.getDescriptor(MessageAgent.class), null, null); - fv.visitEnd(); - } - { //构造函数 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //init - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "init", "(" + anyValueDesc + ")V", null, null)); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 2); - mv.visitEnd(); - } - { //stop - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "stop", "(" + anyValueDesc + ")V", null, null)); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 2); - mv.visitEnd(); - } - { //destroy - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "destroy", "(" + anyValueDesc + ")V", null, null)); - mv.visitInsn(RETURN); - mv.visitMaxs(0, 2); - mv.visitEnd(); - } - { // toString() - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); - Label l1 = new Label(); - mv.visitJumpInsn(IFNONNULL, l1); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); - mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false); - Label l2 = new Label(); - mv.visitJumpInsn(GOTO, l2); - mv.visitLabel(l1); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); - mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "toSimpleString", "()Ljava/lang/String;", false); - mv.visitLabel(l2); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - int i = -1; - for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) { - final int index = ++i; - final java.lang.reflect.Method method = entry.method; - { - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null)); - //mv.setDebug(true); - { //给参数加上 Annotation - final Annotation[][] anns = method.getParameterAnnotations(); - for (int k = 0; k < anns.length; k++) { - for (Annotation ann : anns[k]) { - visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann); - } - } - } - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); - - pushInt(mv, index); - - { //传参数 - int paramlen = entry.paramTypes.length; - pushInt(mv, paramlen); - mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); - java.lang.reflect.Type[] paramtypes = entry.paramTypes; - int insn = 0; - for (int j = 0; j < paramtypes.length; j++) { - final java.lang.reflect.Type pt = paramtypes[j]; - mv.visitInsn(DUP); - insn++; - pushInt(mv, j); - if (pt instanceof Class && ((Class) pt).isPrimitive()) { - if (pt == long.class) { - mv.visitVarInsn(LLOAD, insn++); - } else if (pt == float.class) { - mv.visitVarInsn(FLOAD, insn++); - } else if (pt == double.class) { - mv.visitVarInsn(DLOAD, insn++); - } else { - mv.visitVarInsn(ILOAD, insn); - } - Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance((Class) pt, 1), 0).getClass(); - mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor((Class) pt) + ")" + Type.getDescriptor(bigclaz), false); - } else { - mv.visitVarInsn(ALOAD, insn); - } - mv.visitInsn(AASTORE); - } - } - - mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "remote", "(I[Ljava/lang/Object;)Ljava/lang/Object;", false); - //mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); - if (method.getGenericReturnType() == void.class) { - mv.visitInsn(POP); - mv.visitInsn(RETURN); - } else { - Class returnclz = method.getReturnType(); - Class bigPrimitiveClass = returnclz.isPrimitive() ? java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(returnclz, 1), 0).getClass() : returnclz; - mv.visitTypeInsn(CHECKCAST, (returnclz.isPrimitive() ? bigPrimitiveClass : returnclz).getName().replace('.', '/')); - if (returnclz.isPrimitive()) { - String bigPrimitiveName = bigPrimitiveClass.getName().replace('.', '/'); - try { - java.lang.reflect.Method pm = bigPrimitiveClass.getMethod(returnclz.getSimpleName() + "Value"); - mv.visitMethodInsn(INVOKEVIRTUAL, bigPrimitiveName, pm.getName(), Type.getMethodDescriptor(pm), false); - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - if (returnclz == long.class) { - mv.visitInsn(LRETURN); - } else if (returnclz == float.class) { - mv.visitInsn(FRETURN); - } else if (returnclz == double.class) { - mv.visitInsn(DRETURN); - } else { - mv.visitInsn(IRETURN); - } - } else { - mv.visitInsn(ARETURN); - } - } - mv.visitMaxs(20, 20); - mv.visitEnd(); - } - } - cw.visitEnd(); - byte[] bytes = cw.toByteArray(); - Class newClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - T service = (T) newClazz.getDeclaredConstructor().newInstance(); - SncpClient client = new SncpClient(name, serviceTypeOrImplClass, service, messageAgent, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); - client.setRemoteGroups(groups); - if (transportFactory != null) client.setRemoteGroupTransport(transportFactory.loadTransport(clientAddress, groups)); - { - Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); - c.setAccessible(true); - c.set(service, client); - } - if (messageAgent != null) { - Field c = newClazz.getDeclaredField(FIELDPREFIX + "_messageagent"); - c.setAccessible(true); - c.set(service, messageAgent); - if (service instanceof WebSocketNode) { - c = WebSocketNode.class.getDeclaredField("messageAgent"); - c.setAccessible(true); - c.set(service, messageAgent); - } - } - { - Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf"); - c.setAccessible(true); - c.set(service, conf); - } - if (transportFactory != null) transportFactory.addSncpService(service); - return service; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - - } - - private static void pushInt(MethodDebugVisitor mv, int num) { - if (num < 6) { - mv.visitInsn(ICONST_0 + num); - } else if (num <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, num); - } else if (num <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, num); - } else { - mv.visitLdcInsn(num); - } - } - - private static void pushInt(MethodVisitor mv, int num) { - if (num < 6) { - mv.visitInsn(ICONST_0 + num); - } else if (num <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, num); - } else if (num <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, num); - } else { - mv.visitLdcInsn(num); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import org.redkale.asm.MethodDebugVisitor; +import java.lang.annotation.Annotation; +import java.lang.reflect.*; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.CompletionHandler; +import java.security.*; +import java.util.*; +import javax.annotation.Resource; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import org.redkale.asm.*; +import static org.redkale.asm.Opcodes.*; +import org.redkale.asm.Type; +import org.redkale.mq.MessageAgent; +import org.redkale.net.TransportFactory; +import org.redkale.net.http.WebSocketNode; +import org.redkale.net.sncp.SncpClient.SncpAction; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * Service Node Communicate Protocol + * 生成Service的本地模式或远程模式Service-Class的工具类 + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class Sncp { + + public static final ByteBuffer PING_BUFFER = ByteBuffer.wrap("PING".getBytes()).asReadOnlyBuffer(); + + public static final ByteBuffer PONG_BUFFER = ByteBuffer.wrap("PONG".getBytes()).asReadOnlyBuffer(); + + static final String FIELDPREFIX = "_redkale"; + + private static final MessageDigest md5; + + static { //64进制 + MessageDigest d = null; + try { + d = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + ex.printStackTrace(); + } + md5 = d; + } + + private Sncp() { + } + + public static DLong hash(final java.lang.reflect.Method method) { + if (method == null) return DLong.ZERO; + StringBuilder sb = new StringBuilder(); //不能使用method.toString() 因为包含declaringClass信息导致接口与实现类的方法hash不一致 + sb.append(method.getReturnType().getName()).append(' '); + sb.append(method.getName()); + sb.append('('); + boolean first = true; + for (Class pt : method.getParameterTypes()) { + if (!first) sb.append(','); + sb.append(pt.getName()); + first = false; + } + sb.append(')'); + return hash(sb.toString()); + } + + /** + * 对类名或者name字符串进行hash。 + * + * @param name String + * + * @return hash值 + */ + public static DLong hash(final String name) { + if (name == null || name.isEmpty()) return DLong.ZERO; + byte[] bytes = name.trim().getBytes(); + synchronized (md5) { + bytes = md5.digest(bytes); + } + return DLong.create(bytes); + } + + public static boolean isRemote(Service service) { + SncpDyn dyn = service.getClass().getAnnotation(SncpDyn.class); + return dyn != null && dyn.remote(); + } + + public static boolean isSncpDyn(Service service) { + return service.getClass().getAnnotation(SncpDyn.class) != null; + } + + public static int getVersion(Service service) { + if (service == null) return -1; + Version ver = service.getClass().getAnnotation(Version.class); + return ver == null ? -1 : ver.value(); + } + + public static String getResourceName(Service service) { + if (service == null) return null; + Resource res = service.getClass().getAnnotation(Resource.class); + return res == null ? null : res.name(); + } + + public static Class getServiceType(Service service) { + ResourceType rt = service.getClass().getAnnotation(ResourceType.class); + return rt == null ? service.getClass() : rt.value(); + } + + public static Class getResourceType(Service service) { + if (service == null) return null; + ResourceType type = service.getClass().getAnnotation(ResourceType.class); + return type == null ? getServiceType(service) : type.value(); + } + + public static AnyValue getConf(Service service) { + if (service == null || !isSncpDyn(service)) return null; + try { + Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_conf"); + ts.setAccessible(true); + return (AnyValue) ts.get(service); + } catch (Exception e) { + throw new RuntimeException(service + " not found " + FIELDPREFIX + "_conf"); + } + } + + public static SncpClient getSncpClient(Service service) { + if (service == null || !isSncpDyn(service)) return null; + try { + Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_client"); + ts.setAccessible(true); + return (SncpClient) ts.get(service); + } catch (Exception e) { + throw new RuntimeException(service + " not found " + FIELDPREFIX + "_client"); + } + } + + public static MessageAgent getMessageAgent(Service service) { + if (service == null || !isSncpDyn(service)) return null; + try { + Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_messageagent"); + ts.setAccessible(true); + return (MessageAgent) ts.get(service); + } catch (Exception e) { + throw new RuntimeException(service + " not found " + FIELDPREFIX + "_messageagent"); + } + } + + public static void setMessageAgent(Service service, MessageAgent messageAgent) { + if (service == null || !isSncpDyn(service)) return; + try { + Field ts = service.getClass().getDeclaredField(FIELDPREFIX + "_messageagent"); + ts.setAccessible(true); + ts.set(service, messageAgent); + if (service instanceof WebSocketNode) { + Field c = WebSocketNode.class.getDeclaredField("messageAgent"); + c.setAccessible(true); + c.set(service, messageAgent); + } + } catch (Exception e) { + throw new RuntimeException(service + " not found " + FIELDPREFIX + "_messageagent"); + } + } + + public static boolean updateTransport(Service service, + final TransportFactory transportFactory, String name, String protocol, InetSocketAddress clientAddress, + final Set groups, final Collection addresses) { + if (!isSncpDyn(service)) return false; + SncpClient client = getSncpClient(service); + client.setRemoteGroups(groups); + if (client.getRemoteGroupTransport() != null) { + client.getRemoteGroupTransport().updateRemoteAddresses(addresses); + } else { + client.setRemoteGroupTransport(transportFactory.createTransport(name, protocol, clientAddress, addresses)); + } + return true; + } + + static void checkAsyncModifier(Class param, Method method) { + if (param == CompletionHandler.class) return; + if (Modifier.isFinal(param.getModifiers())) { + throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} cannot final modifier"); + } + if (!Modifier.isPublic(param.getModifiers())) { + throw new RuntimeException("CompletionHandler Type Parameter on {" + method + "} must be public modifier"); + } + if (param.isInterface()) return; + boolean constructorflag = false; + RedkaleClassLoader.putReflectionDeclaredConstructors(param, param.getName()); + for (Constructor c : param.getDeclaredConstructors()) { + if (c.getParameterCount() == 0) { + int mod = c.getModifiers(); + if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) { + constructorflag = true; + break; + } + } + } + if (param.getDeclaredConstructors().length == 0) constructorflag = true; + if (!constructorflag) throw new RuntimeException(param + " must have a empty parameter Constructor"); + for (Method m : param.getMethods()) { + if (m.getName().equals("completed") && Modifier.isFinal(m.getModifiers())) { + throw new RuntimeException(param + "'s completed method cannot final modifier"); + } else if (m.getName().equals("failed") && Modifier.isFinal(m.getModifiers())) { + throw new RuntimeException(param + "'s failed method cannot final modifier"); + } else if (m.getName().equals("sncp_getParams") && Modifier.isFinal(m.getModifiers())) { + throw new RuntimeException(param + "'s sncp_getParams method cannot final modifier"); + } else if (m.getName().equals("sncp_setParams") && Modifier.isFinal(m.getModifiers())) { + throw new RuntimeException(param + "'s sncp_setParams method cannot final modifier"); + } else if (m.getName().equals("sncp_setFuture") && Modifier.isFinal(m.getModifiers())) { + throw new RuntimeException(param + "'s sncp_setFuture method cannot final modifier"); + } else if (m.getName().equals("sncp_getFuture") && Modifier.isFinal(m.getModifiers())) { + throw new RuntimeException(param + "'s sncp_getFuture method cannot final modifier"); + } + } + } + + public static String toSimpleString(final Service service, int maxNameLength, int maxTypeLength) { + StringBuilder sb = new StringBuilder(); + sb.append(isRemote(service) ? "RemoteService" : "LocalService "); + int len; + Class type = getResourceType(service); + String name = getResourceName(service); + sb.append("(type= ").append(type.getName()); + len = maxTypeLength - type.getName().length(); + for (int i = 0; i < len; i++) { + sb.append(' '); + } + sb.append(", name='").append(name).append("'"); + for (int i = 0; i < maxNameLength - name.length(); i++) { + sb.append(' '); + } + sb.append(")"); + return sb.toString(); + } + + /** + *
    +     * public class TestService implements Service{
    +     *
    +     *      public String findSomeThing(){
    +     *          return "hello";
    +     *      }
    +     *
    +     *      @RpcMultiRun(selfrun = false)
    +     *      public void createSomeThing(TestBean bean){
    +     *          //do something
    +     *      }
    +     *
    +     *      @RpcMultiRun
    +     *      public String updateSomeThing(String id){
    +     *          return "hello" + id;
    +     *      }
    +     * }
    +     * 
    + * + *
    +     * @Resource(name = "")
    +     * @SncpDyn(remote = false)
    +     * @ResourceType(TestService.class)
    +     * public final class _DynLocalTestService extends TestService{
    +     *
    +     *      private AnyValue _redkale_conf;
    +     *
    +     *      private SncpClient _redkale_client;
    +     *
    +     *      @Override
    +     *      public String toString() {
    +     *          return _redkale_selfstring == null ? super.toString() : _redkale_selfstring;
    +     *      }
    +     * }
    +     * 
    + * + * 创建Service的本地模式Class + * + * @param Service子类 + * @param classLoader ClassLoader + * @param name 资源名 + * @param serviceImplClass Service类 + * + * @return Service实例 + */ + @SuppressWarnings("unchecked") + protected static Class createLocalServiceClass(ClassLoader classLoader, final String name, final Class serviceImplClass) { + if (serviceImplClass == null) return null; + if (!Service.class.isAssignableFrom(serviceImplClass)) return serviceImplClass; + ResourceFactory.checkResourceName(name); + int mod = serviceImplClass.getModifiers(); + if (!java.lang.reflect.Modifier.isPublic(mod)) return serviceImplClass; + if (java.lang.reflect.Modifier.isAbstract(mod)) return serviceImplClass; + final String supDynName = serviceImplClass.getName().replace('.', '/'); + final String clientName = SncpClient.class.getName().replace('.', '/'); + final String resDesc = Type.getDescriptor(Resource.class); + final String clientDesc = Type.getDescriptor(SncpClient.class); + final String anyValueDesc = Type.getDescriptor(AnyValue.class); + final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); + ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; + //String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + LOCALPREFIX + serviceImplClass.getSimpleName(); + String newDynName = "org/redkaledyn/service/local/_DynLocalService__" + serviceImplClass.getName().replace('.', '_').replace('$', '_'); + if (!name.isEmpty()) { + boolean normal = true; + for (char ch : name.toCharArray()) { + if (!((ch >= '0' && ch <= '9') || ch == '_' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) normal = false; + } + if (!normal) throw new RuntimeException(serviceImplClass + "'s resource name is illegal, must be 0-9 _ a-z A-Z"); + newDynName += "_" + (normal ? name : hash(name)); + } + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + return (Class) (clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz); + } catch (ClassNotFoundException e) { + } catch (Throwable t) { + t.printStackTrace(); + } + //------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + AnnotationVisitor av0; + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); + { + av0 = cw.visitAnnotation(resDesc, true); + av0.visit("name", name); + av0.visitEnd(); + } + { + av0 = cw.visitAnnotation(sncpDynDesc, true); + av0.visit("remote", Boolean.FALSE); + av0.visitEnd(); + } + { //给新类加上 原有的Annotation + for (Annotation ann : serviceImplClass.getAnnotations()) { + if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue; + MethodDebugVisitor.visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); + } + } + { + av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true); + ResourceType rty = serviceImplClass.getAnnotation(ResourceType.class); + av0.visit("value", Type.getType(Type.getDescriptor(rty == null ? serviceImplClass : rty.value()))); + av0.visitEnd(); + } + { + fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_messageagent", Type.getDescriptor(MessageAgent.class), null, null); + fv.visitEnd(); + } + { //构造函数 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { // toString() + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); + Label l1 = new Label(); + mv.visitJumpInsn(IFNONNULL, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false); + Label l2 = new Label(); + mv.visitJumpInsn(GOTO, l2); + mv.visitLabel(l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); + mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "toSimpleString", "()Ljava/lang/String;", false); + mv.visitLabel(l2); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionPublicClasses(newDynName.replace('/', '.')); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + try { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf"); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), c); + c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), c); + c = newClazz.getDeclaredField(FIELDPREFIX + "_messageagent"); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), c); + } catch (Exception e) { + } + return (Class) newClazz; + } + + public static T createSimpleLocalService(final Class serviceImplClass, final MessageAgent messageAgent, + final ResourceFactory resourceFactory, final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) { + return createLocalService(null, "", serviceImplClass, messageAgent, resourceFactory, transportFactory, clientSncpAddress, Utility.ofSet(groups), null); + } + + /** + * + * 创建本地模式Service实例 + * + * @param Service泛型 + * @param classLoader ClassLoader + * @param name 资源名 + * @param serviceImplClass Service类 + * @param messageAgent MQ管理器 + * @param resourceFactory ResourceFactory + * @param transportFactory TransportFactory + * @param clientSncpAddress 本地IP地址 + * @param groups 所有的组节点,包含自身 + * @param conf 启动配置项 + * + * @return Service的本地模式实例 + */ + @SuppressWarnings("unchecked") + public static T createLocalService( + final RedkaleClassLoader classLoader, + final String name, + final Class serviceImplClass, + final MessageAgent messageAgent, + final ResourceFactory resourceFactory, + final TransportFactory transportFactory, + final InetSocketAddress clientSncpAddress, + final Set groups, + final AnyValue conf) { + try { + final Class newClazz = createLocalServiceClass(classLoader, name, serviceImplClass); + T service = (T) newClazz.getDeclaredConstructor().newInstance(); + //-------------------------------------- + Service remoteService = null; + { + Class loop = newClazz; + do { + RedkaleClassLoader.putReflectionDeclaredFields(loop.getName()); + for (Field field : loop.getDeclaredFields()) { + int mod = field.getModifiers(); + if (Modifier.isFinal(mod) || Modifier.isStatic(mod)) continue; + if (field.getAnnotation(RpcRemote.class) == null) continue; + if (!field.getType().isAssignableFrom(newClazz)) continue; + field.setAccessible(true); + RedkaleClassLoader.putReflectionField(loop.getName(), field); + if (remoteService == null && clientSncpAddress != null) { + remoteService = createRemoteService(classLoader, name, serviceImplClass, messageAgent, transportFactory, clientSncpAddress, groups, conf); + } + if (remoteService != null) field.set(service, remoteService); + } + } while ((loop = loop.getSuperclass()) != Object.class); + } + SncpClient client = null; + { + try { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); + c.setAccessible(true); + client = new SncpClient(name, serviceImplClass, service, messageAgent, transportFactory, false, newClazz, clientSncpAddress); + c.set(service, client); + if (transportFactory != null) transportFactory.addSncpService(service); + } catch (NoSuchFieldException ne) { + ne.printStackTrace(); + } + } + if (messageAgent != null) { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_messageagent"); + c.setAccessible(true); + c.set(service, messageAgent); + } + if (client == null) return service; + { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf"); + c.setAccessible(true); + c.set(service, conf); + if (service instanceof WebSocketNode) { + c = WebSocketNode.class.getDeclaredField("messageAgent"); + c.setAccessible(true); + c.set(service, messageAgent); + } + } + return service; + } catch (RuntimeException rex) { + throw rex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static T createSimpleRemoteService(final Class serviceImplClass, final MessageAgent messageAgent, + final TransportFactory transportFactory, final InetSocketAddress clientSncpAddress, final String... groups) { + return createRemoteService(null, "", serviceImplClass, messageAgent, transportFactory, clientSncpAddress, Utility.ofSet(groups), null); + } + + /** + *
    +     * @Resource(name = "")
    +     * @SncpDyn(remote = true)
    +     * @ResourceType(TestService.class)
    +     * public final class _DynRemoteTestService extends TestService{
    +     *
    +     *      private AnyValue _redkale_conf;
    +     *
    +     *      private SncpClient _redkale_client;
    +     *
    +     *      @Override
    +     *      public void createSomeThing(TestBean bean){
    +     *          _redkale_client.remote(0, bean);
    +     *      }
    +     *
    +     *      @Override
    +     *      public String findSomeThing(){
    +     *          return _redkale_client.remote(1);
    +     *      }
    +     *
    +     *      @Override
    +     *      public String updateSomeThing(String id){
    +     *          return  _redkale_client.remote(2, id);
    +     *      }
    +     * }
    +     * 
    + * + * 创建远程模式的Service实例 + * + * @param Service泛型 + * @param classLoader ClassLoader + * @param name 资源名 + * @param serviceTypeOrImplClass Service类 + * @param messageAgent MQ管理器 + * @param transportFactory TransportFactory + * @param clientAddress 本地IP地址 + * @param groups0 所有的组节点,包含自身 + * @param conf 启动配置项 + * + * @return Service的远程模式实例 + */ + @SuppressWarnings("unchecked") + + public static T createRemoteService( + final ClassLoader classLoader, + final String name, + final Class serviceTypeOrImplClass, + final MessageAgent messageAgent, + final TransportFactory transportFactory, + final InetSocketAddress clientAddress, + final Set groups0, + final AnyValue conf) { + if (serviceTypeOrImplClass == null) return null; + if (!Service.class.isAssignableFrom(serviceTypeOrImplClass)) return null; + final Set groups = groups0 == null ? new HashSet<>() : groups0; + ResourceFactory.checkResourceName(name); + final int mod = serviceTypeOrImplClass.getModifiers(); + boolean realed = !(java.lang.reflect.Modifier.isAbstract(mod) || serviceTypeOrImplClass.isInterface()); + if (!java.lang.reflect.Modifier.isPublic(mod)) return null; + final String supDynName = serviceTypeOrImplClass.getName().replace('.', '/'); + final String clientName = SncpClient.class.getName().replace('.', '/'); + final String resDesc = Type.getDescriptor(Resource.class); + final String clientDesc = Type.getDescriptor(SncpClient.class); + final String sncpDynDesc = Type.getDescriptor(SncpDyn.class); + final String anyValueDesc = Type.getDescriptor(AnyValue.class); + final ClassLoader loader = classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader; + //final String newDynName = supDynName.substring(0, supDynName.lastIndexOf('/') + 1) + REMOTEPREFIX + serviceTypeOrImplClass.getSimpleName(); + final String newDynName = "org/redkaledyn/service/remote/_DynRemoteService__" + serviceTypeOrImplClass.getName().replace('.', '_').replace('$', '_'); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + Class newClazz = clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz; + T service = (T) newClazz.getDeclaredConstructor().newInstance(); + SncpClient client = new SncpClient(name, serviceTypeOrImplClass, service, messageAgent, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); + client.setRemoteGroups(groups); + if (transportFactory != null) client.setRemoteGroupTransport(transportFactory.loadTransport(clientAddress, groups)); + { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); + c.setAccessible(true); + c.set(service, client); + } + if (messageAgent != null) { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_messageagent"); + c.setAccessible(true); + c.set(service, messageAgent); + if (service instanceof WebSocketNode) { + c = WebSocketNode.class.getDeclaredField("messageAgent"); + c.setAccessible(true); + c.set(service, messageAgent); + } + } + { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf"); + c.setAccessible(true); + c.set(service, conf); + } + if (transportFactory != null) transportFactory.addSncpService(service); + return service; + } catch (Throwable ex) { + } + //------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + AnnotationVisitor av0; + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, serviceTypeOrImplClass.isInterface() ? new String[]{supDynName} : null); + { + av0 = cw.visitAnnotation(resDesc, true); + av0.visit("name", name); + av0.visitEnd(); + } + { + av0 = cw.visitAnnotation(Type.getDescriptor(ResourceType.class), true); + ResourceType rty = serviceTypeOrImplClass.getAnnotation(ResourceType.class); + av0.visit("value", Type.getType(Type.getDescriptor(rty == null ? serviceTypeOrImplClass : rty.value()))); + av0.visitEnd(); + } + { + av0 = cw.visitAnnotation(sncpDynDesc, true); + av0.visit("remote", Boolean.TRUE); + av0.visitEnd(); + } + { //给新类加上 原有的Annotation + for (Annotation ann : serviceTypeOrImplClass.getAnnotations()) { + if (ann instanceof Resource || ann instanceof SncpDyn || ann instanceof ResourceType) continue; + MethodDebugVisitor.visitAnnotation(cw.visitAnnotation(Type.getDescriptor(ann.annotationType()), true), ann); + } + } + { + fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_conf", anyValueDesc, null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_client", clientDesc, null, null); + fv.visitEnd(); + } + { + fv = cw.visitField(ACC_PRIVATE, FIELDPREFIX + "_messageagent", Type.getDescriptor(MessageAgent.class), null, null); + fv.visitEnd(); + } + { //构造函数 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, serviceTypeOrImplClass.isInterface() ? "java/lang/Object" : supDynName, "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //init + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "init", "(" + anyValueDesc + ")V", null, null)); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 2); + mv.visitEnd(); + } + { //stop + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "stop", "(" + anyValueDesc + ")V", null, null)); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 2); + mv.visitEnd(); + } + { //destroy + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "destroy", "(" + anyValueDesc + ")V", null, null)); + mv.visitInsn(RETURN); + mv.visitMaxs(0, 2); + mv.visitEnd(); + } + { // toString() + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); + Label l1 = new Label(); + mv.visitJumpInsn(IFNONNULL, l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getName", "()Ljava/lang/String;", false); + Label l2 = new Label(); + mv.visitJumpInsn(GOTO, l2); + mv.visitLabel(l1); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); + mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "toSimpleString", "()Ljava/lang/String;", false); + mv.visitLabel(l2); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + int i = -1; + for (final SncpAction entry : SncpClient.getSncpActions(realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass)) { + final int index = ++i; + final java.lang.reflect.Method method = entry.method; + { + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null)); + //mv.setDebug(true); + { //给参数加上 Annotation + final Annotation[][] anns = method.getParameterAnnotations(); + for (int k = 0; k < anns.length; k++) { + for (Annotation ann : anns[k]) { + MethodDebugVisitor.visitAnnotation(mv.visitParameterAnnotation(k, Type.getDescriptor(ann.annotationType()), true), ann); + } + } + } + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, FIELDPREFIX + "_client", clientDesc); + + MethodDebugVisitor.pushInt(mv, index); + + { //传参数 + int paramlen = entry.paramTypes.length; + MethodDebugVisitor.pushInt(mv, paramlen); + mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + java.lang.reflect.Type[] paramtypes = entry.paramTypes; + int insn = 0; + for (int j = 0; j < paramtypes.length; j++) { + final java.lang.reflect.Type pt = paramtypes[j]; + mv.visitInsn(DUP); + insn++; + MethodDebugVisitor.pushInt(mv, j); + if (pt instanceof Class && ((Class) pt).isPrimitive()) { + if (pt == long.class) { + mv.visitVarInsn(LLOAD, insn++); + } else if (pt == float.class) { + mv.visitVarInsn(FLOAD, insn++); + } else if (pt == double.class) { + mv.visitVarInsn(DLOAD, insn++); + } else { + mv.visitVarInsn(ILOAD, insn); + } + Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance((Class) pt, 1), 0).getClass(); + mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor((Class) pt) + ")" + Type.getDescriptor(bigclaz), false); + } else { + mv.visitVarInsn(ALOAD, insn); + } + mv.visitInsn(AASTORE); + } + } + + mv.visitMethodInsn(INVOKEVIRTUAL, clientName, "remote", "(I[Ljava/lang/Object;)Ljava/lang/Object;", false); + //mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); + if (method.getGenericReturnType() == void.class) { + mv.visitInsn(POP); + mv.visitInsn(RETURN); + } else { + Class returnclz = method.getReturnType(); + Class bigPrimitiveClass = returnclz.isPrimitive() ? java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(returnclz, 1), 0).getClass() : returnclz; + mv.visitTypeInsn(CHECKCAST, (returnclz.isPrimitive() ? bigPrimitiveClass : returnclz).getName().replace('.', '/')); + if (returnclz.isPrimitive()) { + String bigPrimitiveName = bigPrimitiveClass.getName().replace('.', '/'); + try { + java.lang.reflect.Method pm = bigPrimitiveClass.getMethod(returnclz.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, bigPrimitiveName, pm.getName(), Type.getMethodDescriptor(pm), false); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + if (returnclz == long.class) { + mv.visitInsn(LRETURN); + } else if (returnclz == float.class) { + mv.visitInsn(FRETURN); + } else if (returnclz == double.class) { + mv.visitInsn(DRETURN); + } else { + mv.visitInsn(IRETURN); + } + } else { + mv.visitInsn(ARETURN); + } + } + mv.visitMaxs(20, 20); + mv.visitEnd(); + } + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionPublicConstructors(newClazz, newDynName.replace('/', '.')); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + try { + T service = (T) newClazz.getDeclaredConstructor().newInstance(); + SncpClient client = new SncpClient(name, serviceTypeOrImplClass, service, messageAgent, transportFactory, true, realed ? createLocalServiceClass(loader, name, serviceTypeOrImplClass) : serviceTypeOrImplClass, clientAddress); + client.setRemoteGroups(groups); + if (transportFactory != null) client.setRemoteGroupTransport(transportFactory.loadTransport(clientAddress, groups)); + { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_client"); + c.setAccessible(true); + c.set(service, client); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), c); + } + if (messageAgent != null) { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_messageagent"); + c.setAccessible(true); + c.set(service, messageAgent); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), c); + if (service instanceof WebSocketNode) { + c = WebSocketNode.class.getDeclaredField("messageAgent"); + c.setAccessible(true); + c.set(service, messageAgent); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), c); + } + } + { + Field c = newClazz.getDeclaredField(FIELDPREFIX + "_conf"); + c.setAccessible(true); + c.set(service, conf); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), c); + } + if (transportFactory != null) transportFactory.addSncpService(service); + return service; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + + } +} diff --git a/src/org/redkale/net/sncp/SncpAsyncHandler.java b/src/main/java/org/redkale/net/sncp/SncpAsyncHandler.java similarity index 90% rename from src/org/redkale/net/sncp/SncpAsyncHandler.java rename to src/main/java/org/redkale/net/sncp/SncpAsyncHandler.java index c80d1d3f0..0a6164ca0 100644 --- a/src/org/redkale/net/sncp/SncpAsyncHandler.java +++ b/src/main/java/org/redkale/net/sncp/SncpAsyncHandler.java @@ -1,315 +1,323 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import org.redkale.asm.MethodDebugVisitor; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.CompletableFuture; -import java.util.logging.Logger; -import java.util.logging.Level; -import org.redkale.asm.*; -import static org.redkale.asm.Opcodes.*; -import org.redkale.convert.bson.*; -import org.redkale.net.sncp.SncpDynServlet.SncpServletAction; -import org.redkale.util.*; - -/** - * 异步回调函数
    - * - * public class _DyncSncpAsyncHandler extends XXXAsyncHandler implements SncpAsyncHandler - * - * - *
    -         *
    -         * 考虑点:
    -         *      1、CompletionHandler子类是接口,且还有其他多个方法
    -         *      2、CompletionHandler子类是类, 需要继承,且必须有空参数构造函数
    -         *      3、CompletionHandler子类无论是接口还是类,都可能存在其他泛型
    -         *
    -         *  public class XXXAsyncHandler_DyncSncpAsyncHandler_4323 extends XXXAsyncHandler implements SncpAsyncHandler {
    -         *
    -         *      private SncpAsyncHandler sncphandler;
    -         *
    -         *      private CompletableFuture sncpfuture;
    -         *
    -         *      @ConstructorParameters({"sncphandler"})
    -         *      public XXXAsyncHandler_DyncSncpAsyncHandler_4323(SncpAsyncHandler sncphandler) {
    -         *          super();
    -         *          this.sncphandler = sncphandler;
    -         *      }
    -         *
    -         *      @Override
    -         *      public void completed(Object result, Object attachment) {
    -         *          sncphandler.completed(result, attachment);
    -         *      }
    -         *
    -         *      @Override
    -         *      public void failed(Throwable exc, Object attachment) {
    -         *          sncphandler.failed(exc, attachment);
    -         *      }
    -         *
    -         *      @Override
    -         *      public Object[] sncp_getParams() {
    -         *          return sncphandler.sncp_getParams();
    -         *      }
    -         *
    -         *      @Override
    -         *      public void sncp_setParams(Object... params) {
    -         *          sncphandler.sncp_setParams(params);
    -         *      }
    -         *
    -         *      @Override
    -         *      public void sncp_setFuture(CompletableFuture future) {
    -         *          this.sncpfuture = future;
    -         *      }
    -         *
    -         *      @Override
    -         *      public CompletableFuture sncp_getFuture() {
    -         *          return this.sncpfuture;
    -         *      }
    -         *  }
    -         *
    -         * 
    - * - * @param handlerClass CompletionHandler类型或子类 - * - * @return Creator - */ - public static Creator createCreator(Class handlerClass) { - //------------------------------------------------------------- - final boolean handlerinterface = handlerClass.isInterface(); - final String handlerClassName = handlerClass.getName().replace('.', '/'); - final String sncpHandlerName = SncpAsyncHandler.class.getName().replace('.', '/'); - final String cpDesc = Type.getDescriptor(ConstructorParameters.class); - final String sncpHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class); - final String sncpFutureDesc = Type.getDescriptor(CompletableFuture.class); - final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + SncpAsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000); - - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - FieldVisitor fv; - MethodDebugVisitor mv; - AnnotationVisitor av0; - cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName, sncpHandlerName} : new String[]{sncpHandlerName}); - - { //handler 属性 - fv = cw.visitField(ACC_PRIVATE, "sncphandler", sncpHandlerDesc, null, null); - fv.visitEnd(); - } - { //future 属性 - fv = cw.visitField(ACC_PRIVATE, "sncpfuture", sncpFutureDesc, null, null); - fv.visitEnd(); - } - {//构造方法 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "(" + sncpHandlerDesc + ")V", null, null)); - //mv.setDebug(true); - { - av0 = mv.visitAnnotation(cpDesc, true); - { - AnnotationVisitor av1 = av0.visitArray("value"); - av1.visit(null, "sncphandler"); - av1.visitEnd(); - } - av0.visitEnd(); - } - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "", "()V", false); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(PUTFIELD, newDynName, "sncphandler", sncpHandlerDesc); - mv.visitInsn(RETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - - for (java.lang.reflect.Method method : handlerClass.getMethods()) { // - if ("completed".equals(method.getName()) && method.getParameterCount() == 2) { - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) { - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) { - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null)); - Class returnType = method.getReturnType(); - if (returnType == void.class) { - mv.visitInsn(RETURN); - mv.visitMaxs(0, 1); - } else if (returnType.isPrimitive()) { - mv.visitInsn(ICONST_0); - if (returnType == long.class) { - mv.visitInsn(LRETURN); - mv.visitMaxs(2, 1); - } else if (returnType == float.class) { - mv.visitInsn(FRETURN); - mv.visitMaxs(2, 1); - } else if (returnType == double.class) { - mv.visitInsn(DRETURN); - mv.visitMaxs(2, 1); - } else { - mv.visitInsn(IRETURN); - mv.visitMaxs(1, 1); - } - } else { - mv.visitInsn(ACONST_NULL); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - } - mv.visitEnd(); - } - } - { // sncp_getParams - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getParams", "()[Ljava/lang/Object;", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); - mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_getParams", "()[Ljava/lang/Object;", true); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { // sncp_setParams - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "sncp_setParams", "([Ljava/lang/Object;)V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true); - mv.visitInsn(RETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - { // sncp_setFuture - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_setFuture", "(" + sncpFutureDesc + ")V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(PUTFIELD, newDynName, "sncpfuture", sncpFutureDesc); - mv.visitInsn(RETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - { // sncp_getFuture - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getFuture", "()" + sncpFutureDesc, null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "sncpfuture", sncpFutureDesc); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - cw.visitEnd(); - byte[] bytes = cw.toByteArray(); - Class newHandlerClazz = (Class) new ClassLoader(handlerClass.getClassLoader()) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - return Creator.create(newHandlerClazz); - } - - } - - public static class DefaultSncpAsyncHandler implements SncpAsyncHandler { - - //为了在回调函数中调用_callParameter方法 - protected Object[] params; - - protected SncpServletAction action; - - protected BsonReader in; - - protected BsonWriter out; - - protected SncpRequest request; - - protected SncpResponse response; - - protected CompletableFuture future; - - protected Logger logger; - - public DefaultSncpAsyncHandler(Logger logger, SncpServletAction action, BsonReader in, BsonWriter out, SncpRequest request, SncpResponse response) { - this.logger = logger; - this.action = action; - this.in = in; - this.out = out; - this.request = request; - this.response = response; - } - - @Override - public void completed(Object result, Object attachment) { - try { - action._callParameter(out, sncp_getParams()); - action.convert.convertTo(out, Object.class, result); - response.finish(0, out); - } catch (Exception e) { - failed(e, attachment); - } finally { - action.convert.offerBsonReader(in); - action.convert.offerBsonWriter(out); - } - } - - @Override - public void failed(Throwable exc, Object attachment) { - response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", exc); - response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null); - } - - @Override - public Object[] sncp_getParams() { - return params; - } - - @Override - public void sncp_setParams(Object... params) { - this.params = params; - this.request.sncp_setParams(this.action, this.logger, params); - } - - @Override - public void sncp_setFuture(CompletableFuture future) { - this.future = future; - } - - @Override - public CompletableFuture sncp_getFuture() { - return this.future; - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import org.redkale.asm.MethodDebugVisitor; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.CompletableFuture; +import java.util.logging.Logger; +import java.util.logging.Level; +import org.redkale.asm.*; +import static org.redkale.asm.Opcodes.*; +import org.redkale.convert.bson.*; +import org.redkale.net.sncp.SncpDynServlet.SncpServletAction; +import org.redkale.util.*; + +/** + * 异步回调函数
    + * + * public class _DyncSncpAsyncHandler extends XXXAsyncHandler implements SncpAsyncHandler + * + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 结果对象的泛型 + * @param 附件对象的泛型 + */ +public interface SncpAsyncHandler extends CompletionHandler { + + public Object[] sncp_getParams(); + + public void sncp_setParams(Object... params); + + public void sncp_setFuture(CompletableFuture future); + + public CompletableFuture sncp_getFuture(); + + static class Factory { + + /** + *

    +         *
    +         * 考虑点:
    +         *      1、CompletionHandler子类是接口,且还有其他多个方法
    +         *      2、CompletionHandler子类是类, 需要继承,且必须有空参数构造函数
    +         *      3、CompletionHandler子类无论是接口还是类,都可能存在其他泛型
    +         *
    +         *  public class XXXAsyncHandler_DynSncpAsyncHandler extends XXXAsyncHandler implements SncpAsyncHandler {
    +         *
    +         *      private SncpAsyncHandler sncphandler;
    +         *
    +         *      private CompletableFuture sncpfuture;
    +         *
    +         *      @ConstructorParameters({"sncphandler"})
    +         *      public XXXAsyncHandler_DynSncpAsyncHandler(SncpAsyncHandler sncphandler) {
    +         *          super();
    +         *          this.sncphandler = sncphandler;
    +         *      }
    +         *
    +         *      @Override
    +         *      public void completed(Object result, Object attachment) {
    +         *          sncphandler.completed(result, attachment);
    +         *      }
    +         *
    +         *      @Override
    +         *      public void failed(Throwable exc, Object attachment) {
    +         *          sncphandler.failed(exc, attachment);
    +         *      }
    +         *
    +         *      @Override
    +         *      public Object[] sncp_getParams() {
    +         *          return sncphandler.sncp_getParams();
    +         *      }
    +         *
    +         *      @Override
    +         *      public void sncp_setParams(Object... params) {
    +         *          sncphandler.sncp_setParams(params);
    +         *      }
    +         *
    +         *      @Override
    +         *      public void sncp_setFuture(CompletableFuture future) {
    +         *          this.sncpfuture = future;
    +         *      }
    +         *
    +         *      @Override
    +         *      public CompletableFuture sncp_getFuture() {
    +         *          return this.sncpfuture;
    +         *      }
    +         *  }
    +         *
    +         * 
    + * + * @param handlerClass CompletionHandler类型或子类 + * + * @return Creator + */ + public static Creator createCreator(Class handlerClass) { + //------------------------------------------------------------- + final boolean handlerinterface = handlerClass.isInterface(); + final String handlerClassName = handlerClass.getName().replace('.', '/'); + final String sncpHandlerName = SncpAsyncHandler.class.getName().replace('.', '/'); + final String cpDesc = Type.getDescriptor(ConstructorParameters.class); + final String sncpHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class); + final String sncpFutureDesc = Type.getDescriptor(CompletableFuture.class); + final String newDynName = "org/redkaledyn/sncp/handler/_Dyn" + SncpAsyncHandler.class.getSimpleName() + + "__" + handlerClass.getName().replace('.', '/').replace('$', '_'); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + Class newHandlerClazz = clz == null ? Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.')) : clz; + return Creator.create(newHandlerClazz); + } catch (Throwable ex) { + } + // ------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + AnnotationVisitor av0; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName, sncpHandlerName} : new String[]{sncpHandlerName}); + + { //handler 属性 + fv = cw.visitField(ACC_PRIVATE, "sncphandler", sncpHandlerDesc, null, null); + fv.visitEnd(); + } + { //future 属性 + fv = cw.visitField(ACC_PRIVATE, "sncpfuture", sncpFutureDesc, null, null); + fv.visitEnd(); + } + {//构造方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "(" + sncpHandlerDesc + ")V", null, null)); + //mv.setDebug(true); + { + av0 = mv.visitAnnotation(cpDesc, true); + { + AnnotationVisitor av1 = av0.visitArray("value"); + av1.visit(null, "sncphandler"); + av1.visitEnd(); + } + av0.visitEnd(); + } + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "", "()V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, newDynName, "sncphandler", sncpHandlerDesc); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + + for (java.lang.reflect.Method method : handlerClass.getMethods()) { // + if ("completed".equals(method.getName()) && method.getParameterCount() == 2) { + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) { + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) { + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null)); + Class returnType = method.getReturnType(); + if (returnType == void.class) { + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + } else if (returnType.isPrimitive()) { + mv.visitInsn(ICONST_0); + if (returnType == long.class) { + mv.visitInsn(LRETURN); + mv.visitMaxs(2, 1); + } else if (returnType == float.class) { + mv.visitInsn(FRETURN); + mv.visitMaxs(2, 1); + } else if (returnType == double.class) { + mv.visitInsn(DRETURN); + mv.visitMaxs(2, 1); + } else { + mv.visitInsn(IRETURN); + mv.visitMaxs(1, 1); + } + } else { + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + } + mv.visitEnd(); + } + } + { // sncp_getParams + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getParams", "()[Ljava/lang/Object;", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); + mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_getParams", "()[Ljava/lang/Object;", true); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { // sncp_setParams + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "sncp_setParams", "([Ljava/lang/Object;)V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "sncphandler", sncpHandlerDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEINTERFACE, sncpHandlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { // sncp_setFuture + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_setFuture", "(" + sncpFutureDesc + ")V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, newDynName, "sncpfuture", sncpFutureDesc); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + { // sncp_getFuture + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "sncp_getFuture", "()" + sncpFutureDesc, null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "sncpfuture", sncpFutureDesc); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class newClazz = (Class) new ClassLoader(handlerClass.getClassLoader()) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + return Creator.create(newClazz); + } + + } + + public static class DefaultSncpAsyncHandler implements SncpAsyncHandler { + + //为了在回调函数中调用_callParameter方法 + protected Object[] params; + + protected SncpServletAction action; + + protected BsonReader in; + + protected BsonWriter out; + + protected SncpRequest request; + + protected SncpResponse response; + + protected CompletableFuture future; + + protected Logger logger; + + public DefaultSncpAsyncHandler(Logger logger, SncpServletAction action, BsonReader in, BsonWriter out, SncpRequest request, SncpResponse response) { + this.logger = logger; + this.action = action; + this.in = in; + this.out = out; + this.request = request; + this.response = response; + } + + @Override + public void completed(Object result, Object attachment) { + try { + action._callParameter(out, sncp_getParams()); + action.convert.convertTo(out, Object.class, result); + response.finish(0, out); + } catch (Exception e) { + failed(e, attachment); + } finally { + action.convert.offerBsonReader(in); + action.convert.offerBsonWriter(out); + } + } + + @Override + public void failed(Throwable exc, Object attachment) { + response.getContext().getLogger().log(Level.INFO, "sncp execute error(" + request + ")", exc); + response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null); + } + + @Override + public Object[] sncp_getParams() { + return params; + } + + @Override + public void sncp_setParams(Object... params) { + this.params = params; + this.request.sncp_setParams(this.action, this.logger, params); + } + + @Override + public void sncp_setFuture(CompletableFuture future) { + this.future = future; + } + + @Override + public CompletableFuture sncp_getFuture() { + return this.future; + } + + } +} diff --git a/src/org/redkale/net/sncp/SncpClient.java b/src/main/java/org/redkale/net/sncp/SncpClient.java similarity index 97% rename from src/org/redkale/net/sncp/SncpClient.java rename to src/main/java/org/redkale/net/sncp/SncpClient.java index a54ac71ab..f63da8590 100644 --- a/src/org/redkale/net/sncp/SncpClient.java +++ b/src/main/java/org/redkale/net/sncp/SncpClient.java @@ -1,616 +1,622 @@ -/* - * To change this license header, choose License Headers reader Project Properties. - * To change this template file, choose Tools | Templates - * and open the template reader the editor. - */ -package org.redkale.net.sncp; - -import java.lang.annotation.*; -import java.lang.reflect.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import javax.annotation.Resource; -import org.redkale.convert.bson.*; -import org.redkale.convert.json.*; -import org.redkale.mq.*; -import org.redkale.net.*; -import static org.redkale.net.sncp.SncpRequest.*; -import org.redkale.service.*; -import org.redkale.util.*; -import org.redkale.service.RpcCall; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class SncpClient { - - protected static final Logger logger = Logger.getLogger(SncpClient.class.getSimpleName()); - - protected final JsonConvert convert = JsonFactory.root().getConvert(); - - protected final String name; - - protected final boolean remote; - - private final Class serviceClass; - - protected final InetSocketAddress clientSncpAddress; - - private final byte[] addrBytes; - - private final int addrPort; - - protected final DLong serviceid; - - protected final int serviceversion; - - protected final SncpAction[] actions; - - protected final MessageAgent messageAgent; - - protected final SncpMessageClient messageClient; - - protected final String topic; - - @Resource - protected JsonConvert jsonConvert; - - @Resource - protected BsonConvert bsonConvert; - - //远程模式, 可能为null - protected Set remoteGroups; - - //远程模式, 可能为null - protected Transport remoteGroupTransport; - - public SncpClient(final String serviceName, final Class serviceTypeOrImplClass, final T service, MessageAgent messageAgent, final TransportFactory factory, - final boolean remote, final Class serviceClass, final InetSocketAddress clientSncpAddress) { - this.remote = remote; - this.messageAgent = messageAgent; - this.messageClient = messageAgent == null ? null : messageAgent.getSncpMessageClient(); - this.topic = messageAgent == null ? null : messageAgent.generateSncpReqTopic(service); - Class tn = serviceTypeOrImplClass; - Version ver = tn.getAnnotation(Version.class); - this.serviceClass = serviceClass; - this.serviceversion = ver == null ? 0 : ver.value(); - this.clientSncpAddress = clientSncpAddress; - this.name = serviceName; - ResourceType rt = tn.getAnnotation(ResourceType.class); - if (rt != null) tn = rt.value(); - this.serviceid = Sncp.hash(tn.getName() + ':' + serviceName); - final List methodens = new ArrayList<>(); - //------------------------------------------------------------------------------ - for (java.lang.reflect.Method method : parseMethod(serviceClass)) { - methodens.add(new SncpAction(serviceClass, method, Sncp.hash(method))); - } - this.actions = methodens.toArray(new SncpAction[methodens.size()]); - this.addrBytes = clientSncpAddress == null ? new byte[4] : clientSncpAddress.getAddress().getAddress(); - this.addrPort = clientSncpAddress == null ? 0 : clientSncpAddress.getPort(); - if (this.addrBytes.length != 4) throw new RuntimeException("SNCP clientAddress only support IPv4"); - } - - static List getSncpActions(final Class serviceClass) { - final List actions = new ArrayList<>(); - //------------------------------------------------------------------------------ - for (java.lang.reflect.Method method : parseMethod(serviceClass)) { - actions.add(new SncpAction(serviceClass, method, Sncp.hash(method))); - } - return actions; - } - - public MessageAgent getMessageAgent() { - return messageAgent; - } - - public InetSocketAddress getClientAddress() { - return clientSncpAddress; - } - - public DLong getServiceid() { - return serviceid; - } - - public int getServiceversion() { - return serviceversion; - } - - public int getActionCount() { - return actions.length; - } - - public Set getRemoteGroups() { - return remoteGroups; - } - - public void setRemoteGroups(Set remoteGroups) { - this.remoteGroups = remoteGroups; - } - - public Transport getRemoteGroupTransport() { - return remoteGroupTransport; - } - - public void setRemoteGroupTransport(Transport remoteGroupTransport) { - this.remoteGroupTransport = remoteGroupTransport; - } - - @Override - public String toString() { - String service = serviceClass.getName(); - if (remote) service = service.replace(Sncp.LOCALPREFIX, Sncp.REMOTEPREFIX); - return this.getClass().getSimpleName() + "(service = " + service + ", serviceid = " + serviceid + ", serviceversion = " + serviceversion + ", name = '" + name - + "', address = " + (clientSncpAddress == null ? "" : (clientSncpAddress.getHostString() + ":" + clientSncpAddress.getPort())) - + ", actions.size = " + actions.length + ")"; - } - - public String toSimpleString() { //给Sncp产生的Service用 - String service = serviceClass.getName(); - if (remote) service = service.replace(Sncp.LOCALPREFIX, Sncp.REMOTEPREFIX); - return service + "(name = '" + name + "', serviceid = " + serviceid + ", serviceversion = " + serviceversion - + ", clientaddr = " + (clientSncpAddress == null ? "" : (clientSncpAddress.getHostString() + ":" + clientSncpAddress.getPort())) - + ((remoteGroups == null || remoteGroups.isEmpty()) ? "" : ", remoteGroups = " + remoteGroups) - + (remoteGroupTransport == null ? "" : ", remoteGroupTransport = " + Arrays.toString(remoteGroupTransport.getRemoteAddresses())) - + ", actions.size = " + actions.length + ")"; - } - - public static List parseMethod(final Class serviceClass) { - final List list = new ArrayList<>(); - final List multis = new ArrayList<>(); - final Map actionids = new HashMap<>(); - - for (final java.lang.reflect.Method method : serviceClass.getMethods()) { - if (method.isSynthetic()) continue; - if (Modifier.isStatic(method.getModifiers())) continue; - if (Modifier.isFinal(method.getModifiers())) continue; - if (method.getAnnotation(Local.class) != null) continue; - if (method.getName().equals("getClass") || method.getName().equals("toString")) continue; - if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue; - if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue; - if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) { - if (method.getName().equals("init") || method.getName().equals("stop") || method.getName().equals("destroy")) continue; - } - //if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue; - - DLong actionid = Sncp.hash(method); - Method old = actionids.get(actionid); - if (old != null) { - if (old.getDeclaringClass().equals(method.getDeclaringClass())) - throw new RuntimeException(serviceClass.getName() + " have one more same action(Method=" + method + ", " + old + ", actionid=" + actionid + ")"); - continue; - } - actionids.put(actionid, method); - if (method.getAnnotation(SncpDyn.class) != null) { - multis.add(method); - } else { - list.add(method); - } - } - multis.sort((m1, m2) -> m1.getAnnotation(SncpDyn.class).index() - m2.getAnnotation(SncpDyn.class).index()); - list.sort((Method o1, Method o2) -> { - if (!o1.getName().equals(o2.getName())) return o1.getName().compareTo(o2.getName()); - if (o1.getParameterCount() != o2.getParameterCount()) return o1.getParameterCount() - o2.getParameterCount(); - return 0; - }); - //带SncpDyn必须排在前面 - multis.addAll(list); - return multis; - } - - //只给远程模式调用的 - public T remote(final int index, final Object... params) { - final SncpAction action = actions[index]; - final CompletionHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (CompletionHandler) params[action.handlerFuncParamIndex] : null; - if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; - final BsonReader reader = bsonConvert.pollBsonReader(); - CompletableFuture future = remote0(handlerFunc, remoteGroupTransport, null, action, params); - if (action.boolReturnTypeFuture) { //与handlerFuncIndex互斥 - CompletableFuture result = action.futureCreator.create(); - future.whenComplete((v, e) -> { - try { - if (e != null) { - result.completeExceptionally(e); - } else { - reader.setBytes(v); - byte i; - while ((i = reader.readByte()) != 0) { - final Attribute attr = action.paramAttrs[i]; - attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader)); - } - Object rs = bsonConvert.convertFrom(Object.class, reader); - - result.complete(rs); - } - } catch (Exception exp) { - result.completeExceptionally(exp); - } finally { - bsonConvert.offerBsonReader(reader); - } - }); //需要获取 Executor - return (T) result; - } - if (handlerFunc != null) return null; - try { - reader.setBytes(future.get(5, TimeUnit.SECONDS)); - byte i; - while ((i = reader.readByte()) != 0) { - final Attribute attr = action.paramAttrs[i]; - attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader)); - } - return bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader); - } catch (RpcRemoteException re) { - throw re; - } catch (TimeoutException e) { - throw new RpcRemoteException(actions[index].method + " sncp remote timeout, params=" + JsonConvert.root().convertTo(params)); - } catch (InterruptedException | ExecutionException e) { - throw new RpcRemoteException(actions[index].method + " sncp remote error, params=" + JsonConvert.root().convertTo(params), e); - } finally { - bsonConvert.offerBsonReader(reader); - } - } - - private CompletableFuture remote0(final CompletionHandler handler, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) { - final Type[] myparamtypes = action.paramTypes; - final Class[] myparamclass = action.paramClass; - if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientSncpAddress; - if (bsonConvert == null) bsonConvert = BsonConvert.root(); - final BsonWriter writer = bsonConvert.pollBsonWriter(); // 将head写入 - writer.writeTo(DEFAULT_HEADER); - for (int i = 0; i < params.length; i++) { //params 可能包含: 3 个 boolean - BsonConvert bcc = bsonConvert; - if (params[i] instanceof org.redkale.service.RetResult) { - org.redkale.convert.Convert cc = ((org.redkale.service.RetResult) params[i]).convert(); - if (cc instanceof BsonConvert) bcc = (BsonConvert) cc; - } - bcc.convertTo(writer, CompletionHandler.class.isAssignableFrom(myparamclass[i]) ? CompletionHandler.class : myparamtypes[i], params[i]); - } - final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度 - final long seqid = System.nanoTime(); - final DLong actionid = action.actionid; - if (messageAgent != null) { //MQ模式 - final ByteArray reqbytes = writer.toByteArray(); - fillHeader(reqbytes, seqid, actionid, reqBodyLength); - String targetTopic = action.topicTargetParamIndex >= 0 ? (String) params[action.topicTargetParamIndex] : this.topic; - if (targetTopic == null) targetTopic = this.topic; - MessageRecord message = messageClient.createMessageRecord(targetTopic, null, reqbytes.getBytes()); - final String tt = targetTopic; - if (logger.isLoggable(Level.FINER)) { - message.attach(Utility.append(new Object[]{action.actionName()}, params)); - } else { - message.attach(params); - } - return messageClient.sendMessage(message).thenApply(msg -> { - if (msg == null || msg.getContent() == null) { - logger.log(Level.SEVERE, action.method + " sncp mq(params: " + convert.convertTo(params) + ", message: " + message + ") deal error, this.topic = " + this.topic + ", targetTopic = " + tt + ", result = " + msg); - return null; - } - ByteBuffer buffer = ByteBuffer.wrap(msg.getContent()); - checkResult(seqid, action, buffer); - - final int respBodyLength = buffer.getInt(); - final int retcode = buffer.getInt(); - if (retcode != 0) { - logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + "), params=" + JsonConvert.root().convertTo(params)); - throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")"); - } - byte[] body = new byte[respBodyLength]; - buffer.get(body, 0, respBodyLength); - return body; - }); - } - final SocketAddress addr = addr0 == null ? (action.addressTargetParamIndex >= 0 ? (SocketAddress) params[action.addressTargetParamIndex] : null) : addr0; - CompletableFuture connFuture = transport.pollConnection(addr); - return connFuture.thenCompose(conn0 -> { - final CompletableFuture future = new CompletableFuture(); - if (conn0 == null) { - future.completeExceptionally(new RpcRemoteException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect, params=" + JsonConvert.root().convertTo(params))); - return future; - } - if (!conn0.isOpen()) { - conn0.dispose(); - future.completeExceptionally(new RpcRemoteException("sncp " + conn0.getRemoteAddress() + " cannot connect, params=" + JsonConvert.root().convertTo(params))); - return future; - } - final AsyncConnection conn = conn0; - final ByteArray array = writer.toByteArray(); - fillHeader(array, seqid, actionid, reqBodyLength); - - conn.write(array, new CompletionHandler() { - - @Override - public void completed(Integer result, Void attachments) { - //----------------------- 读取返回结果 ------------------------------------- - conn.read(new CompletionHandler() { - - private byte[] body; - - private int received; - - @Override - public void completed(Integer count, ByteBuffer buffer) { - try { - if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读 - future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data, params=" + JsonConvert.root().convertTo(params))); - conn.offerBuffer(buffer); - transport.offerConnection(true, conn); - return; - } - if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全 - conn.setReadBuffer(buffer); - conn.read(this); - return; - } - buffer.flip(); - if (received > 0) { - int offset = this.received; - this.received += buffer.remaining(); - buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset)); - if (this.received < this.body.length) {// 数据仍然不全,需要继续读取 - buffer.clear(); - conn.setReadBuffer(buffer); - conn.read(this); - } else { - conn.offerBuffer(buffer); - success(); - } - return; - } - checkResult(seqid, action, buffer); - - final int respBodyLength = buffer.getInt(); - final int retcode = buffer.getInt(); - if (retcode != 0) { - logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + "), params=" + JsonConvert.root().convertTo(params)); - throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")"); - } - - if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取 - this.body = new byte[respBodyLength]; - this.received = buffer.remaining(); - buffer.get(body, 0, this.received); - buffer.clear(); - conn.setReadBuffer(buffer); - conn.read(this); - } else { - this.body = new byte[respBodyLength]; - buffer.get(body, 0, respBodyLength); - conn.offerBuffer(buffer); - success(); - } - } catch (Throwable e) { - future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote response error, params=" + JsonConvert.root().convertTo(params))); - transport.offerConnection(true, conn); - if (handler != null) { - final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; - handler.failed(e, handlerAttach); - } - logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error", e); - } - } - - @SuppressWarnings("unchecked") - public void success() { - future.complete(this.body); - transport.offerConnection(false, conn); - if (handler != null) { - final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; - final BsonReader reader = bsonConvert.pollBsonReader(); - try { - reader.setBytes(this.body); - int i; - while ((i = (reader.readByte() & 0xff)) != 0) { - final Attribute attr = action.paramAttrs[i]; - attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader)); - } - Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader); - handler.completed(rs, handlerAttach); - } catch (Exception e) { - handler.failed(e, handlerAttach); - } finally { - bsonConvert.offerBsonReader(reader); - } - } - } - - @Override - public void failed(Throwable exc, ByteBuffer attachment2) { - future.completeExceptionally(new RpcRemoteException(action.method + " sncp remote exec failed, params=" + JsonConvert.root().convertTo(params))); - conn.offerBuffer(attachment2); - transport.offerConnection(true, conn); - if (handler != null) { - final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; - handler.failed(exc, handlerAttach); - } - logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed, params=" + JsonConvert.root().convertTo(params), exc); - } - }); - } - - @Override - public void failed(Throwable exc, Void attachment) { - future.completeExceptionally(new RpcRemoteException(action.method + " sncp remote exec failed, params=" + JsonConvert.root().convertTo(params))); - transport.offerConnection(true, conn); - if (handler != null) { - final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; - handler.failed(exc, handlerAttach); - } - logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed, params=" + JsonConvert.root().convertTo(params), exc); - } - }); - return future; - }); - } - - private void checkResult(long seqid, final SncpAction action, ByteBuffer buffer) { - long rseqid = buffer.getLong(); - if (rseqid != seqid) throw new RuntimeException("sncp(" + action.method + ") response.seqid = " + seqid + ", but request.seqid =" + rseqid); - if (buffer.getChar() != HEADER_SIZE) throw new RuntimeException("sncp(" + action.method + ") buffer receive header.length not " + HEADER_SIZE); - DLong rserviceid = DLong.read(buffer); - if (!rserviceid.equals(this.serviceid)) throw new RuntimeException("sncp(" + action.method + ") response.serviceid = " + serviceid + ", but request.serviceid =" + rserviceid); - int version = buffer.getInt(); - if (version != this.serviceversion) throw new RuntimeException("sncp(" + action.method + ") response.serviceversion = " + serviceversion + ", but request.serviceversion =" + version); - DLong raction = DLong.read(buffer); - DLong actid = action.actionid; - if (!actid.equals(raction)) throw new RuntimeException("sncp(" + action.method + ") response.actionid = " + action.actionid + ", but request.actionid =(" + raction + ")"); - buffer.getInt(); //地址 - buffer.getChar(); //端口 - } - - private void fillHeader(ByteArray buffer, long seqid, DLong actionid, int bodyLength) { - //---------------------head---------------------------------- - int offset = 0; - buffer.putLong(offset, seqid); //序列号 - offset += 8; - buffer.putChar(offset, (char) HEADER_SIZE); //header长度 - offset += 2; - DLong.write(buffer, offset, this.serviceid); - offset += 16; - buffer.putInt(offset, this.serviceversion); - offset += 4; - DLong.write(buffer, offset, actionid); - offset += 16; - buffer.put(offset, addrBytes); - offset += addrBytes.length; //4 - buffer.putChar(offset, (char) this.addrPort); - offset += 2; - buffer.putInt(offset, bodyLength); //body长度 - offset += 4; - buffer.putInt(offset, 0); //结果码, 请求方固定传0 - //offset += 4; - } - -// private void fillHeader(ByteBuffer buffer, long seqid, DLong actionid, int bodyLength) { -// //---------------------head---------------------------------- -// final int currentpos = buffer.position(); -// buffer.position(0); -// buffer.putLong(seqid); //序列号 -// buffer.putChar((char) HEADER_SIZE); //header长度 -// DLong.write(buffer, this.serviceid); -// buffer.putInt(this.serviceversion); -// DLong.write(buffer, actionid); -// buffer.put(addrBytes); -// buffer.putChar((char) this.addrPort); -// buffer.putInt(bodyLength); //body长度 -// buffer.putInt(0); //结果码, 请求方固定传0 -// buffer.position(currentpos); -// } - protected static final class SncpAction { - - protected final DLong actionid; - - protected final Method method; - - protected final Type resultTypes; //void 必须设为 null - - protected final Type[] paramTypes; - - protected final Class[] paramClass; - - protected final Attribute[] paramAttrs; // 为null表示无RpcCall处理,index=0固定为null, 其他为参数标记的RpcCall回调方法 - - protected final int handlerFuncParamIndex; - - protected final int handlerAttachParamIndex; - - protected final int addressTargetParamIndex; - - protected final int addressSourceParamIndex; - - protected final int topicTargetParamIndex; - - protected final boolean boolReturnTypeFuture; // 返回结果类型是否为 CompletableFuture - - protected final Creator futureCreator; - - @SuppressWarnings("unchecked") - public SncpAction(final Class clazz, Method method, DLong actionid) { - this.actionid = actionid == null ? Sncp.hash(method) : actionid; - Type rt = TypeToken.getGenericType(method.getGenericReturnType(), clazz); - this.resultTypes = rt == void.class ? null : rt; - this.boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType()); - this.futureCreator = boolReturnTypeFuture ? Creator.create((Class) method.getReturnType()) : null; - this.paramTypes = TypeToken.getGenericType(method.getGenericParameterTypes(), clazz); - this.paramClass = method.getParameterTypes(); - this.method = method; - Annotation[][] anns = method.getParameterAnnotations(); - int tpoicAddrIndex = -1; - int targetAddrIndex = -1; - int sourceAddrIndex = -1; - int handlerAttachIndex = -1; - int handlerFuncIndex = -1; - boolean hasattr = false; - Attribute[] atts = new Attribute[paramTypes.length + 1]; - if (anns.length > 0) { - Class[] params = method.getParameterTypes(); - for (int i = 0; i < params.length; i++) { - if (CompletionHandler.class.isAssignableFrom(params[i])) { - if (boolReturnTypeFuture) { - throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture"); - } - if (handlerFuncIndex >= 0) { - throw new RuntimeException(method + " have more than one CompletionHandler type parameter"); - } - Sncp.checkAsyncModifier(params[i], method); - handlerFuncIndex = i; - break; - } - } - for (int i = 0; i < anns.length; i++) { - if (anns[i].length > 0) { - for (Annotation ann : anns[i]) { - if (ann.annotationType() == RpcAttachment.class) { - if (handlerAttachIndex >= 0) { - throw new RuntimeException(method + " have more than one @RpcAttachment parameter"); - } - handlerAttachIndex = i; - } else if (ann.annotationType() == RpcTargetAddress.class && SocketAddress.class.isAssignableFrom(params[i])) { - targetAddrIndex = i; - } else if (ann.annotationType() == RpcSourceAddress.class && SocketAddress.class.isAssignableFrom(params[i])) { - sourceAddrIndex = i; - } else if (ann.annotationType() == RpcTargetTopic.class && String.class.isAssignableFrom(params[i])) { - tpoicAddrIndex = i; - } - } - for (Annotation ann : anns[i]) { - if (ann.annotationType() == RpcCall.class) { - try { - atts[i + 1] = ((RpcCall) ann).value().getDeclaredConstructor().newInstance(); - hasattr = true; - } catch (Exception e) { - logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e); - } - break; - } - } - } - } - } - this.topicTargetParamIndex = tpoicAddrIndex; - this.addressTargetParamIndex = targetAddrIndex; - this.addressSourceParamIndex = sourceAddrIndex; - this.handlerFuncParamIndex = handlerFuncIndex; - this.handlerAttachParamIndex = handlerAttachIndex; - this.paramAttrs = hasattr ? atts : null; - if (this.handlerFuncParamIndex >= 0 && method.getReturnType() != void.class) { - throw new RuntimeException(method + " have CompletionHandler type parameter but return type is not void"); - } - } - - public String actionName() { - return method.getDeclaringClass().getSimpleName() + "." + method.getName(); - } - - @Override - public String toString() { - return "{" + actionid + "," + (method == null ? "null" : method.getName()) + "}"; - } - } -} +/* + * To change this license header, choose License Headers reader Project Properties. + * To change this template file, choose Tools | Templates + * and open the template reader the editor. + */ +package org.redkale.net.sncp; + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.*; +import javax.annotation.Resource; +import org.redkale.convert.bson.*; +import org.redkale.convert.json.*; +import org.redkale.mq.*; +import org.redkale.net.*; +import static org.redkale.net.sncp.SncpRequest.*; +import org.redkale.service.*; +import org.redkale.util.*; +import org.redkale.service.RpcCall; +import org.redkale.source.CacheSource; +import org.redkale.source.DataSource; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class SncpClient { + + protected static final Logger logger = Logger.getLogger(SncpClient.class.getSimpleName()); + + protected final JsonConvert convert = JsonFactory.root().getConvert(); + + protected final String name; + + protected final boolean remote; + + private final Class serviceClass; + + protected final InetSocketAddress clientSncpAddress; + + private final byte[] addrBytes; + + private final int addrPort; + + protected final DLong serviceid; + + protected final int serviceversion; + + protected final SncpAction[] actions; + + protected final MessageAgent messageAgent; + + protected final SncpMessageClient messageClient; + + protected final String topic; + + @Resource + protected JsonConvert jsonConvert; + + @Resource + protected BsonConvert bsonConvert; + + //远程模式, 可能为null + protected Set remoteGroups; + + //远程模式, 可能为null + protected Transport remoteGroupTransport; + + public SncpClient(final String serviceName, final Class serviceTypeOrImplClass, final T service, MessageAgent messageAgent, final TransportFactory factory, + final boolean remote, final Class serviceClass, final InetSocketAddress clientSncpAddress) { + this.remote = remote; + this.messageAgent = messageAgent; + this.messageClient = messageAgent == null ? null : messageAgent.getSncpMessageClient(); + this.topic = messageAgent == null ? null : messageAgent.generateSncpReqTopic(service); + Class tn = serviceTypeOrImplClass; + Version ver = tn.getAnnotation(Version.class); + this.serviceClass = serviceClass; + this.serviceversion = ver == null ? 0 : ver.value(); + this.clientSncpAddress = clientSncpAddress; + this.name = serviceName; + ResourceType rt = tn.getAnnotation(ResourceType.class); + if (rt != null) tn = rt.value(); + this.serviceid = Sncp.hash(tn.getName() + ':' + serviceName); + final List methodens = new ArrayList<>(); + //------------------------------------------------------------------------------ + for (java.lang.reflect.Method method : parseMethod(serviceClass)) { + methodens.add(new SncpAction(serviceClass, method, Sncp.hash(method))); + } + this.actions = methodens.toArray(new SncpAction[methodens.size()]); + this.addrBytes = clientSncpAddress == null ? new byte[4] : clientSncpAddress.getAddress().getAddress(); + this.addrPort = clientSncpAddress == null ? 0 : clientSncpAddress.getPort(); + if (this.addrBytes.length != 4) throw new RuntimeException("SNCP clientAddress only support IPv4"); + } + + static List getSncpActions(final Class serviceClass) { + final List actions = new ArrayList<>(); + //------------------------------------------------------------------------------ + for (java.lang.reflect.Method method : parseMethod(serviceClass)) { + actions.add(new SncpAction(serviceClass, method, Sncp.hash(method))); + } + return actions; + } + + public MessageAgent getMessageAgent() { + return messageAgent; + } + + public InetSocketAddress getClientAddress() { + return clientSncpAddress; + } + + public DLong getServiceid() { + return serviceid; + } + + public int getServiceversion() { + return serviceversion; + } + + public int getActionCount() { + return actions.length; + } + + public Set getRemoteGroups() { + return remoteGroups; + } + + public void setRemoteGroups(Set remoteGroups) { + this.remoteGroups = remoteGroups; + } + + public Transport getRemoteGroupTransport() { + return remoteGroupTransport; + } + + public void setRemoteGroupTransport(Transport remoteGroupTransport) { + this.remoteGroupTransport = remoteGroupTransport; + } + + @Override + public String toString() { + String service = serviceClass.getName(); + if (remote) service = service.replace("DynLocalService", "DynRemoteService"); + return this.getClass().getSimpleName() + "(service = " + service + ", serviceid = " + serviceid + ", serviceversion = " + serviceversion + ", name = '" + name + + "', address = " + (clientSncpAddress == null ? "" : (clientSncpAddress.getHostString() + ":" + clientSncpAddress.getPort())) + + ", actions.size = " + actions.length + ")"; + } + + public String toSimpleString() { //给Sncp产生的Service用 + if(DataSource.class.isAssignableFrom(serviceClass)||CacheSource.class.isAssignableFrom(serviceClass)){ + String service = serviceClass.getAnnotation(SncpDyn.class) ==null? serviceClass.getName() : serviceClass.getSuperclass().getSimpleName(); + return service + "(serviceid=" + serviceid + ", name='" + name + "', actions.size=" + actions.length + ")"; + } + String service = serviceClass.getAnnotation(SncpDyn.class) ==null? serviceClass.getName() : serviceClass.getSuperclass().getSimpleName(); + if (remote) service = service.replace("DynLocalService", "DynRemoteService"); + return service + "(name = '" + name + "', serviceid = " + serviceid + ", serviceversion = " + serviceversion + + ", clientaddr = " + (clientSncpAddress == null ? "" : (clientSncpAddress.getHostString() + ":" + clientSncpAddress.getPort())) + + ((remoteGroups == null || remoteGroups.isEmpty()) ? "" : ", remoteGroups = " + remoteGroups) + + (remoteGroupTransport == null ? "" : ", remoteGroupTransport = " + Arrays.toString(remoteGroupTransport.getRemoteAddresses())) + + ", actions.size = " + actions.length + ")"; + } + + public static List parseMethod(final Class serviceClass) { + final List list = new ArrayList<>(); + final List multis = new ArrayList<>(); + final Map actionids = new HashMap<>(); + for (final java.lang.reflect.Method method : serviceClass.getMethods()) { + if (method.isSynthetic()) continue; + if (Modifier.isStatic(method.getModifiers())) continue; + if (Modifier.isFinal(method.getModifiers())) continue; + if (method.getAnnotation(Local.class) != null) continue; + if (method.getName().equals("getClass") || method.getName().equals("toString")) continue; + if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue; + if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue; + if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) { + if (method.getName().equals("init") || method.getName().equals("stop") || method.getName().equals("destroy")) continue; + } + //if (onlySncpDyn && method.getAnnotation(SncpDyn.class) == null) continue; + + DLong actionid = Sncp.hash(method); + Method old = actionids.get(actionid); + if (old != null) { + if (old.getDeclaringClass().equals(method.getDeclaringClass())) + throw new RuntimeException(serviceClass.getName() + " have one more same action(Method=" + method + ", " + old + ", actionid=" + actionid + ")"); + continue; + } + actionids.put(actionid, method); + if (method.getAnnotation(SncpDyn.class) != null) { + multis.add(method); + } else { + list.add(method); + } + } + multis.sort((m1, m2) -> m1.getAnnotation(SncpDyn.class).index() - m2.getAnnotation(SncpDyn.class).index()); + list.sort((Method o1, Method o2) -> { + if (!o1.getName().equals(o2.getName())) return o1.getName().compareTo(o2.getName()); + if (o1.getParameterCount() != o2.getParameterCount()) return o1.getParameterCount() - o2.getParameterCount(); + return 0; + }); + //带SncpDyn必须排在前面 + multis.addAll(list); + return multis; + } + + //只给远程模式调用的 + public T remote(final int index, final Object... params) { + final SncpAction action = actions[index]; + final CompletionHandler handlerFunc = action.handlerFuncParamIndex >= 0 ? (CompletionHandler) params[action.handlerFuncParamIndex] : null; + if (action.handlerFuncParamIndex >= 0) params[action.handlerFuncParamIndex] = null; + final BsonReader reader = bsonConvert.pollBsonReader(); + CompletableFuture future = remote0(handlerFunc, remoteGroupTransport, null, action, params); + if (action.boolReturnTypeFuture) { //与handlerFuncIndex互斥 + CompletableFuture result = action.futureCreator.create(); + future.whenComplete((v, e) -> { + try { + if (e != null) { + result.completeExceptionally(e); + } else { + reader.setBytes(v); + byte i; + while ((i = reader.readByte()) != 0) { + final Attribute attr = action.paramAttrs[i]; + attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader)); + } + Object rs = bsonConvert.convertFrom(Object.class, reader); + + result.complete(rs); + } + } catch (Exception exp) { + result.completeExceptionally(exp); + } finally { + bsonConvert.offerBsonReader(reader); + } + }); //需要获取 Executor + return (T) result; + } + if (handlerFunc != null) return null; + try { + reader.setBytes(future.get(5, TimeUnit.SECONDS)); + byte i; + while ((i = reader.readByte()) != 0) { + final Attribute attr = action.paramAttrs[i]; + attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader)); + } + return bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader); + } catch (RpcRemoteException re) { + throw re; + } catch (TimeoutException e) { + throw new RpcRemoteException(actions[index].method + " sncp remote timeout, params=" + JsonConvert.root().convertTo(params)); + } catch (InterruptedException | ExecutionException e) { + throw new RpcRemoteException(actions[index].method + " sncp remote error, params=" + JsonConvert.root().convertTo(params), e); + } finally { + bsonConvert.offerBsonReader(reader); + } + } + + private CompletableFuture remote0(final CompletionHandler handler, final Transport transport, final SocketAddress addr0, final SncpAction action, final Object... params) { + final Type[] myparamtypes = action.paramTypes; + final Class[] myparamclass = action.paramClass; + if (action.addressSourceParamIndex >= 0) params[action.addressSourceParamIndex] = this.clientSncpAddress; + if (bsonConvert == null) bsonConvert = BsonConvert.root(); + final BsonWriter writer = bsonConvert.pollBsonWriter(); // 将head写入 + writer.writeTo(DEFAULT_HEADER); + for (int i = 0; i < params.length; i++) { //params 可能包含: 3 个 boolean + BsonConvert bcc = bsonConvert; + if (params[i] instanceof org.redkale.service.RetResult) { + org.redkale.convert.Convert cc = ((org.redkale.service.RetResult) params[i]).convert(); + if (cc instanceof BsonConvert) bcc = (BsonConvert) cc; + } + bcc.convertTo(writer, CompletionHandler.class.isAssignableFrom(myparamclass[i]) ? CompletionHandler.class : myparamtypes[i], params[i]); + } + final int reqBodyLength = writer.count() - HEADER_SIZE; //body总长度 + final long seqid = System.nanoTime(); + final DLong actionid = action.actionid; + if (messageAgent != null) { //MQ模式 + final ByteArray reqbytes = writer.toByteArray(); + fillHeader(reqbytes, seqid, actionid, reqBodyLength); + String targetTopic = action.topicTargetParamIndex >= 0 ? (String) params[action.topicTargetParamIndex] : this.topic; + if (targetTopic == null) targetTopic = this.topic; + MessageRecord message = messageClient.createMessageRecord(targetTopic, null, reqbytes.getBytes()); + final String tt = targetTopic; + if (logger.isLoggable(Level.FINER)) { + message.attach(Utility.append(new Object[]{action.actionName()}, params)); + } else { + message.attach(params); + } + return messageClient.sendMessage(message).thenApply(msg -> { + if (msg == null || msg.getContent() == null) { + logger.log(Level.SEVERE, action.method + " sncp mq(params: " + convert.convertTo(params) + ", message: " + message + ") deal error, this.topic = " + this.topic + ", targetTopic = " + tt + ", result = " + msg); + return null; + } + ByteBuffer buffer = ByteBuffer.wrap(msg.getContent()); + checkResult(seqid, action, buffer); + + final int respBodyLength = buffer.getInt(); + final int retcode = buffer.getInt(); + if (retcode != 0) { + logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + "), params=" + JsonConvert.root().convertTo(params)); + throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")"); + } + byte[] body = new byte[respBodyLength]; + buffer.get(body, 0, respBodyLength); + return body; + }); + } + final SocketAddress addr = addr0 == null ? (action.addressTargetParamIndex >= 0 ? (SocketAddress) params[action.addressTargetParamIndex] : null) : addr0; + CompletableFuture connFuture = transport.pollConnection(addr); + return connFuture.thenCompose(conn0 -> { + final CompletableFuture future = new CompletableFuture(); + if (conn0 == null) { + future.completeExceptionally(new RpcRemoteException("sncp " + (conn0 == null ? addr : conn0.getRemoteAddress()) + " cannot connect, params=" + JsonConvert.root().convertTo(params))); + return future; + } + if (!conn0.isOpen()) { + conn0.dispose(); + future.completeExceptionally(new RpcRemoteException("sncp " + conn0.getRemoteAddress() + " cannot connect, params=" + JsonConvert.root().convertTo(params))); + return future; + } + final AsyncConnection conn = conn0; + final ByteArray array = writer.toByteArray(); + fillHeader(array, seqid, actionid, reqBodyLength); + + conn.write(array, new CompletionHandler() { + + @Override + public void completed(Integer result, Void attachments) { + //----------------------- 读取返回结果 ------------------------------------- + conn.read(new CompletionHandler() { + + private byte[] body; + + private int received; + + @Override + public void completed(Integer count, ByteBuffer buffer) { + try { + if (count < 1 && buffer.remaining() == buffer.limit()) { //没有数据可读 + future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote no response data, params=" + JsonConvert.root().convertTo(params))); + conn.offerBuffer(buffer); + transport.offerConnection(true, conn); + return; + } + if (received < 1 && buffer.limit() < buffer.remaining() + HEADER_SIZE) { //header都没读全 + conn.setReadBuffer(buffer); + conn.read(this); + return; + } + buffer.flip(); + if (received > 0) { + int offset = this.received; + this.received += buffer.remaining(); + buffer.get(body, offset, Math.min(buffer.remaining(), this.body.length - offset)); + if (this.received < this.body.length) {// 数据仍然不全,需要继续读取 + buffer.clear(); + conn.setReadBuffer(buffer); + conn.read(this); + } else { + conn.offerBuffer(buffer); + success(); + } + return; + } + checkResult(seqid, action, buffer); + + final int respBodyLength = buffer.getInt(); + final int retcode = buffer.getInt(); + if (retcode != 0) { + logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + "), params=" + JsonConvert.root().convertTo(params)); + throw new RuntimeException("remote service(" + action.method + ") deal error (retcode=" + retcode + ", retinfo=" + SncpResponse.getRetCodeInfo(retcode) + ")"); + } + + if (respBodyLength > buffer.remaining()) { // 数据不全,需要继续读取 + this.body = new byte[respBodyLength]; + this.received = buffer.remaining(); + buffer.get(body, 0, this.received); + buffer.clear(); + conn.setReadBuffer(buffer); + conn.read(this); + } else { + this.body = new byte[respBodyLength]; + buffer.get(body, 0, respBodyLength); + conn.offerBuffer(buffer); + success(); + } + } catch (Throwable e) { + future.completeExceptionally(new RpcRemoteException(action.method + " sncp[" + conn.getRemoteAddress() + "] remote response error, params=" + JsonConvert.root().convertTo(params))); + transport.offerConnection(true, conn); + if (handler != null) { + final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; + handler.failed(e, handlerAttach); + } + logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") deal error", e); + } + } + + @SuppressWarnings("unchecked") + public void success() { + future.complete(this.body); + transport.offerConnection(false, conn); + if (handler != null) { + final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; + final BsonReader reader = bsonConvert.pollBsonReader(); + try { + reader.setBytes(this.body); + int i; + while ((i = (reader.readByte() & 0xff)) != 0) { + final Attribute attr = action.paramAttrs[i]; + attr.set(params[i - 1], bsonConvert.convertFrom(attr.genericType(), reader)); + } + Object rs = bsonConvert.convertFrom(action.handlerFuncParamIndex >= 0 ? Object.class : action.resultTypes, reader); + handler.completed(rs, handlerAttach); + } catch (Exception e) { + handler.failed(e, handlerAttach); + } finally { + bsonConvert.offerBsonReader(reader); + } + } + } + + @Override + public void failed(Throwable exc, ByteBuffer attachment2) { + future.completeExceptionally(new RpcRemoteException(action.method + " sncp remote exec failed, params=" + JsonConvert.root().convertTo(params))); + conn.offerBuffer(attachment2); + transport.offerConnection(true, conn); + if (handler != null) { + final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; + handler.failed(exc, handlerAttach); + } + logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote read exec failed, params=" + JsonConvert.root().convertTo(params), exc); + } + }); + } + + @Override + public void failed(Throwable exc, Void attachment) { + future.completeExceptionally(new RpcRemoteException(action.method + " sncp remote exec failed, params=" + JsonConvert.root().convertTo(params))); + transport.offerConnection(true, conn); + if (handler != null) { + final Object handlerAttach = action.handlerAttachParamIndex >= 0 ? params[action.handlerAttachParamIndex] : null; + handler.failed(exc, handlerAttach); + } + logger.log(Level.SEVERE, action.method + " sncp (params: " + convert.convertTo(params) + ") remote write exec failed, params=" + JsonConvert.root().convertTo(params), exc); + } + }); + return future; + }); + } + + private void checkResult(long seqid, final SncpAction action, ByteBuffer buffer) { + long rseqid = buffer.getLong(); + if (rseqid != seqid) throw new RuntimeException("sncp(" + action.method + ") response.seqid = " + seqid + ", but request.seqid =" + rseqid); + if (buffer.getChar() != HEADER_SIZE) throw new RuntimeException("sncp(" + action.method + ") buffer receive header.length not " + HEADER_SIZE); + DLong rserviceid = DLong.read(buffer); + if (!rserviceid.equals(this.serviceid)) throw new RuntimeException("sncp(" + action.method + ") response.serviceid = " + serviceid + ", but request.serviceid =" + rserviceid); + int version = buffer.getInt(); + if (version != this.serviceversion) throw new RuntimeException("sncp(" + action.method + ") response.serviceversion = " + serviceversion + ", but request.serviceversion =" + version); + DLong raction = DLong.read(buffer); + DLong actid = action.actionid; + if (!actid.equals(raction)) throw new RuntimeException("sncp(" + action.method + ") response.actionid = " + action.actionid + ", but request.actionid =(" + raction + ")"); + buffer.getInt(); //地址 + buffer.getChar(); //端口 + } + + private void fillHeader(ByteArray buffer, long seqid, DLong actionid, int bodyLength) { + //---------------------head---------------------------------- + int offset = 0; + buffer.putLong(offset, seqid); //序列号 + offset += 8; + buffer.putChar(offset, (char) HEADER_SIZE); //header长度 + offset += 2; + DLong.write(buffer, offset, this.serviceid); + offset += 16; + buffer.putInt(offset, this.serviceversion); + offset += 4; + DLong.write(buffer, offset, actionid); + offset += 16; + buffer.put(offset, addrBytes); + offset += addrBytes.length; //4 + buffer.putChar(offset, (char) this.addrPort); + offset += 2; + buffer.putInt(offset, bodyLength); //body长度 + offset += 4; + buffer.putInt(offset, 0); //结果码, 请求方固定传0 + //offset += 4; + } + +// private void fillHeader(ByteBuffer buffer, long seqid, DLong actionid, int bodyLength) { +// //---------------------head---------------------------------- +// final int currentpos = buffer.position(); +// buffer.position(0); +// buffer.putLong(seqid); //序列号 +// buffer.putChar((char) HEADER_SIZE); //header长度 +// DLong.write(buffer, this.serviceid); +// buffer.putInt(this.serviceversion); +// DLong.write(buffer, actionid); +// buffer.put(addrBytes); +// buffer.putChar((char) this.addrPort); +// buffer.putInt(bodyLength); //body长度 +// buffer.putInt(0); //结果码, 请求方固定传0 +// buffer.position(currentpos); +// } + protected static final class SncpAction { + + protected final DLong actionid; + + protected final Method method; + + protected final Type resultTypes; //void 必须设为 null + + protected final Type[] paramTypes; + + protected final Class[] paramClass; + + protected final Attribute[] paramAttrs; // 为null表示无RpcCall处理,index=0固定为null, 其他为参数标记的RpcCall回调方法 + + protected final int handlerFuncParamIndex; + + protected final int handlerAttachParamIndex; + + protected final int addressTargetParamIndex; + + protected final int addressSourceParamIndex; + + protected final int topicTargetParamIndex; + + protected final boolean boolReturnTypeFuture; // 返回结果类型是否为 CompletableFuture + + protected final Creator futureCreator; + + @SuppressWarnings("unchecked") + public SncpAction(final Class clazz, Method method, DLong actionid) { + this.actionid = actionid == null ? Sncp.hash(method) : actionid; + Type rt = TypeToken.getGenericType(method.getGenericReturnType(), clazz); + this.resultTypes = rt == void.class ? null : rt; + this.boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType()); + this.futureCreator = boolReturnTypeFuture ? Creator.create((Class) method.getReturnType()) : null; + this.paramTypes = TypeToken.getGenericType(method.getGenericParameterTypes(), clazz); + this.paramClass = method.getParameterTypes(); + this.method = method; + Annotation[][] anns = method.getParameterAnnotations(); + int tpoicAddrIndex = -1; + int targetAddrIndex = -1; + int sourceAddrIndex = -1; + int handlerAttachIndex = -1; + int handlerFuncIndex = -1; + boolean hasattr = false; + Attribute[] atts = new Attribute[paramTypes.length + 1]; + if (anns.length > 0) { + Class[] params = method.getParameterTypes(); + for (int i = 0; i < params.length; i++) { + if (CompletionHandler.class.isAssignableFrom(params[i])) { + if (boolReturnTypeFuture) { + throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture"); + } + if (handlerFuncIndex >= 0) { + throw new RuntimeException(method + " have more than one CompletionHandler type parameter"); + } + Sncp.checkAsyncModifier(params[i], method); + handlerFuncIndex = i; + break; + } + } + for (int i = 0; i < anns.length; i++) { + if (anns[i].length > 0) { + for (Annotation ann : anns[i]) { + if (ann.annotationType() == RpcAttachment.class) { + if (handlerAttachIndex >= 0) { + throw new RuntimeException(method + " have more than one @RpcAttachment parameter"); + } + handlerAttachIndex = i; + } else if (ann.annotationType() == RpcTargetAddress.class && SocketAddress.class.isAssignableFrom(params[i])) { + targetAddrIndex = i; + } else if (ann.annotationType() == RpcSourceAddress.class && SocketAddress.class.isAssignableFrom(params[i])) { + sourceAddrIndex = i; + } else if (ann.annotationType() == RpcTargetTopic.class && String.class.isAssignableFrom(params[i])) { + tpoicAddrIndex = i; + } + } + for (Annotation ann : anns[i]) { + if (ann.annotationType() == RpcCall.class) { + try { + atts[i + 1] = ((RpcCall) ann).value().getDeclaredConstructor().newInstance(); + RedkaleClassLoader.putReflectionDeclaredConstructors(((RpcCall) ann).value(), ((RpcCall) ann).value().getName()); + hasattr = true; + } catch (Exception e) { + logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e); + } + break; + } + } + } + } + } + this.topicTargetParamIndex = tpoicAddrIndex; + this.addressTargetParamIndex = targetAddrIndex; + this.addressSourceParamIndex = sourceAddrIndex; + this.handlerFuncParamIndex = handlerFuncIndex; + this.handlerAttachParamIndex = handlerAttachIndex; + this.paramAttrs = hasattr ? atts : null; + if (this.handlerFuncParamIndex >= 0 && method.getReturnType() != void.class) { + throw new RuntimeException(method + " have CompletionHandler type parameter but return type is not void"); + } + } + + public String actionName() { + return method.getDeclaringClass().getSimpleName() + "." + method.getName(); + } + + @Override + public String toString() { + return "{" + actionid + "," + (method == null ? "null" : method.getName()) + "}"; + } + } +} diff --git a/src/org/redkale/net/sncp/SncpContext.java b/src/main/java/org/redkale/net/sncp/SncpContext.java similarity index 95% rename from src/org/redkale/net/sncp/SncpContext.java rename to src/main/java/org/redkale/net/sncp/SncpContext.java index 5a58b914e..d23fee579 100644 --- a/src/org/redkale/net/sncp/SncpContext.java +++ b/src/main/java/org/redkale/net/sncp/SncpContext.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import org.redkale.net.*; - -/** - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class SncpContext extends Context { - - public SncpContext(SncpContextConfig config) { - super(config); - } - - public static class SncpContextConfig extends ContextConfig { - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import org.redkale.net.*; + +/** + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class SncpContext extends Context { + + public SncpContext(SncpContextConfig config) { + super(config); + } + + public static class SncpContextConfig extends ContextConfig { + + } +} diff --git a/src/org/redkale/net/sncp/SncpDyn.java b/src/main/java/org/redkale/net/sncp/SncpDyn.java similarity index 96% rename from src/org/redkale/net/sncp/SncpDyn.java rename to src/main/java/org/redkale/net/sncp/SncpDyn.java index d64de39f5..de4046f45 100644 --- a/src/org/redkale/net/sncp/SncpDyn.java +++ b/src/main/java/org/redkale/net/sncp/SncpDyn.java @@ -1,29 +1,29 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 修饰由SNCP协议动态生成的class、和method - * 本地模式:动态生成的_DynLocalXXXXService类会打上@SncpDyn(remote = false) 的注解 - * 远程模式:动态生成的_DynRemoteXXXService类会打上@SncpDyn(remote = true) 的注解 - * - *

    详情见: https://redkale.org - * @author zhangjx - */ -@Inherited -@Documented -@Target({METHOD, TYPE}) -@Retention(RUNTIME) -public @interface SncpDyn { - - boolean remote(); - - int index() default 0; //排列顺序, 主要用于Method -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 修饰由SNCP协议动态生成的class、和method + * 本地模式:动态生成的_DynLocalXXXXService类会打上@SncpDyn(remote = false) 的注解 + * 远程模式:动态生成的_DynRemoteXXXService类会打上@SncpDyn(remote = true) 的注解 + * + *

    详情见: https://redkale.org + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD, TYPE}) +@Retention(RUNTIME) +public @interface SncpDyn { + + boolean remote(); + + int index() default 0; //排列顺序, 主要用于Method +} diff --git a/src/org/redkale/net/sncp/SncpDynServlet.java b/src/main/java/org/redkale/net/sncp/SncpDynServlet.java similarity index 51% rename from src/org/redkale/net/sncp/SncpDynServlet.java rename to src/main/java/org/redkale/net/sncp/SncpDynServlet.java index c8411203c..a6e05c612 100644 --- a/src/org/redkale/net/sncp/SncpDynServlet.java +++ b/src/main/java/org/redkale/net/sncp/SncpDynServlet.java @@ -1,633 +1,670 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import org.redkale.asm.MethodDebugVisitor; -import static org.redkale.net.sncp.SncpRequest.DEFAULT_HEADER; -import java.io.*; -import java.lang.annotation.*; -import java.lang.reflect.*; -import java.nio.channels.CompletionHandler; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.*; -import javax.annotation.*; -import org.redkale.asm.*; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; -import static org.redkale.asm.Opcodes.*; -import org.redkale.asm.Type; -import org.redkale.convert.bson.*; -import org.redkale.net.sncp.SncpAsyncHandler.DefaultSncpAsyncHandler; -import org.redkale.service.*; -import org.redkale.util.*; -import org.redkale.service.RpcCall; - -/** - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class SncpDynServlet extends SncpServlet { - - private final AtomicInteger maxClassNameLength; - - private final AtomicInteger maxNameLength; - - private static final Logger logger = Logger.getLogger(SncpDynServlet.class.getSimpleName()); - - private final DLong serviceid; - - private final HashMap actions = new HashMap<>(); - - - public SncpDynServlet(final BsonConvert convert, final String serviceName, final Class serviceOrSourceType, final Service service, - final AtomicInteger maxClassNameLength, AtomicInteger maxNameLength) { - super(serviceName, serviceOrSourceType, service); - this.maxClassNameLength = maxClassNameLength; - this.maxNameLength = maxNameLength; - this.serviceid = Sncp.hash(type.getName() + ':' + serviceName); - Set actionids = new HashSet<>(); - for (java.lang.reflect.Method method : service.getClass().getMethods()) { - if (method.isSynthetic()) continue; - if (Modifier.isStatic(method.getModifiers())) continue; - if (Modifier.isFinal(method.getModifiers())) continue; - if (method.getName().equals("getClass") || method.getName().equals("toString")) continue; - if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue; - if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue; - if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) { - if (method.getName().equals("init") || method.getName().equals("stop") || method.getName().equals("destroy")) continue; - } - - final DLong actionid = Sncp.hash(method); - SncpServletAction action = SncpServletAction.create(service, actionid, method); - action.convert = convert; - if (actionids.contains(actionid)) { - throw new RuntimeException(type.getName() + " have action(Method=" + method + ", actionid=" + actionid + ") same to (" + actions.get(actionid).method + ")"); - } - actions.put(actionid, action); - actionids.add(actionid); - } - maxNameLength.set(Math.max(maxNameLength.get(), serviceName.length() + 1)); - maxClassNameLength.set(Math.max(maxClassNameLength.get(), type.getName().length())); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(this.getClass().getSimpleName()).append(" (type=").append(type.getName()); - int len = this.maxClassNameLength.get() - type.getName().length(); - for (int i = 0; i < len; i++) { - sb.append(' '); - } - sb.append(", serviceid=").append(serviceid).append(", name='").append(serviceName).append("'"); - for (int i = 0; i < this.maxNameLength.get() - serviceName.length(); i++) { - sb.append(' '); - } - sb.append(", actions.size=").append(actions.size() > 9 ? "" : " ").append(actions.size()).append(")"); - return sb.toString(); - } - - @Override - public DLong getServiceid() { - return serviceid; - } - - @Override - public int compareTo(SncpServlet o0) { - if (!(o0 instanceof SncpDynServlet)) return 1; - SncpDynServlet o = (SncpDynServlet) o0; - int rs = this.type.getName().compareTo(o.type.getName()); - if (rs == 0) rs = this.serviceName.compareTo(o.serviceName); - return rs; - } - - @Override - @SuppressWarnings("unchecked") - public void execute(SncpRequest request, SncpResponse response) throws IOException { - final SncpServletAction action = actions.get(request.getActionid()); - //logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method)); - if (action == null) { - response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid - } else { - BsonWriter out = action.convert.pollBsonWriter(); - out.writeTo(DEFAULT_HEADER); - BsonReader in = action.convert.pollBsonReader(); - SncpAsyncHandler handler = null; - try { - if (action.handlerFuncParamIndex >= 0) { - if (action.handlerFuncParamClass == CompletionHandler.class) { - handler = new DefaultSncpAsyncHandler(logger, action, in, out, request, response); - } else { - Creator creator = action.handlerCreator; - if (creator == null) { - creator = SncpAsyncHandler.Factory.createCreator(action.handlerFuncParamClass); - action.handlerCreator = creator; - } - handler = creator.create(new DefaultSncpAsyncHandler(logger, action, in, out, request, response)); - } - } else if (action.boolReturnTypeFuture) { - handler = new DefaultSncpAsyncHandler(logger, action, in, out, request, response); - } - in.setBytes(request.getBody()); - action.action(in, out, handler); - if (handler == null) { - response.finish(0, out); - action.convert.offerBsonReader(in); - action.convert.offerBsonWriter(out); - } else if (action.boolReturnTypeFuture) { - CompletableFuture future = handler.sncp_getFuture(); - if (future == null) { - action._callParameter(out, handler.sncp_getParams()); - action.convert.convertTo(out, Object.class, null); - } else { - Object[] sncpParams = handler.sncp_getParams(); - future.whenComplete((v, e) -> { - if (e != null) { - response.getContext().getLogger().log(Level.SEVERE, "sncp CompleteAsync error(" + request + ")", e); - response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null); - return; - } - action._callParameter(out, sncpParams); - action.convert.convertTo(out, Object.class, v); - response.finish(0, out); - action.convert.offerBsonReader(in); - action.convert.offerBsonWriter(out); - }); - } - } - } catch (Throwable t) { - response.getContext().getLogger().log(Level.SEVERE, "sncp execute error(" + request + ")", t); - response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null); - } - } - } - - public static abstract class SncpServletAction { - - public Method method; - - public Creator handlerCreator; - - @Resource - protected BsonConvert convert; - - protected org.redkale.util.Attribute[] paramAttrs; // 为null表示无RpcCall处理,index=0固定为null, 其他为参数标记的RpcCall回调方法 - - protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type, void的返回参数类型为null - - protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在CompletionHandler参数 - - protected boolean boolReturnTypeFuture = false; // 返回结果类型是否为 CompletableFuture - - protected Class handlerFuncParamClass; //CompletionHandler参数的类型 - - public abstract void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable; - - //只有同步方法才调用 (没有CompletionHandler、CompletableFuture) - public final void _callParameter(final BsonWriter out, final Object... params) { - if (paramAttrs != null) { - for (int i = 1; i < paramAttrs.length; i++) { - org.redkale.util.Attribute attr = paramAttrs[i]; - if (attr == null) continue; - out.writeByte((byte) i); - convert.convertTo(out, attr.genericType(), attr.get(params[i - 1])); - } - } - out.writeByte((byte) 0); - } - - public String actionName() { - return method.getDeclaringClass().getSimpleName() + "." + method.getName(); - } - - /** - *

    -         *  public class TestService implements Service {
    -         *
    -         *      public boolean change(TestBean bean, String name, int id) {
    -         *          return false;
    -         *      }
    -         *
    -         *      public void insert(CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
    -         *      }
    -         *
    -         *      public void update(long show, short v2, CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
    -         *      }
    -         *
    -         *      public CompletableFuture<String> changeName(TestBean bean, String name, int id) {
    -         *          return null;
    -         *      }
    -         * }
    -         *
    -         *
    -         * class DynActionTestService_change extends SncpServletAction {
    -         *
    -         *      public TestService service;
    -         *
    -         *      @Override
    -         *      public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
    -         *          TestBean arg1 = convert.convertFrom(paramTypes[1], in);
    -         *          String arg2 = convert.convertFrom(paramTypes[2], in);
    -         *          int arg3 = convert.convertFrom(paramTypes[3], in);
    -         *          Object rs = service.change(arg1, arg2, arg3);
    -         *          _callParameter(out, arg1, arg2, arg3);
    -         *          convert.convertTo(out, paramTypes[0], rs);
    -         *      }
    -         * }
    -         *
    -         * class DynActionTestService_insert extends SncpServletAction {
    -         *
    -         *      public TestService service;
    -         *
    -         *      @Override
    -         *      public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
    -         *          SncpAsyncHandler arg0 = handler;
    -         *          convert.convertFrom(CompletionHandler.class, in);
    -         *          TestBean arg1 = convert.convertFrom(paramTypes[2], in);
    -         *          String arg2 = convert.convertFrom(paramTypes[3], in);
    -         *          int arg3 = convert.convertFrom(paramTypes[4], in);
    -         *          handler.sncp_setParams(arg0, arg1, arg2, arg3);
    -         *          service.insert(arg0, arg1, arg2, arg3);
    -         *       }
    -         * }
    -         *
    -         * class DynActionTestService_update extends SncpServletAction {
    -         *
    -         *      public TestService service;
    -         *
    -         *      @Override
    -         *      public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
    -         *          long a1 = convert.convertFrom(paramTypes[1], in);
    -         *          short a2 = convert.convertFrom(paramTypes[2], in);
    -         *          SncpAsyncHandler a3 = handler;
    -         *          convert.convertFrom(CompletionHandler.class, in);
    -         *          TestBean arg1 = convert.convertFrom(paramTypes[4], in);
    -         *          String arg2 = convert.convertFrom(paramTypes[5], in);
    -         *          int arg3 = convert.convertFrom(paramTypes[6], in);
    -         *          handler.sncp_setParams(a1, a2, a3, arg1, arg2, arg3);
    -         *          service.update(a1, a2, a3, arg1, arg2, arg3);
    -         *      }
    -         * }
    -         *
    -         *
    -         * class DynActionTestService_changeName extends SncpServletAction {
    -         *
    -         *      public TestService service;
    -         *
    -         *      @Override
    -         *      public void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable {
    -         *          TestBean arg1 = convert.convertFrom(paramTypes[1], in);
    -         *          String arg2 = convert.convertFrom(paramTypes[2], in);
    -         *          int arg3 = convert.convertFrom(paramTypes[3], in);
    -         *          handler.sncp_setParams(arg1, arg2, arg3);
    -         *          CompletableFuture future = service.changeName(arg1, arg2, arg3);
    -         *          handler.sncp_setFuture(future);
    -         *      }
    -         * }
    -         *
    -         * 
    - * - * @param service Service - * @param actionid 操作ID - * @param method 方法 - * - * @return SncpServletAction - */ - @SuppressWarnings("unchecked") - public static SncpServletAction create(final Service service, final DLong actionid, final Method method) { - final Class serviceClass = service.getClass(); - final String supDynName = SncpServletAction.class.getName().replace('.', '/'); - final String serviceName = serviceClass.getName().replace('.', '/'); - final String convertName = BsonConvert.class.getName().replace('.', '/'); - final String handlerName = SncpAsyncHandler.class.getName().replace('.', '/'); - final String asyncHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class); - final String convertReaderDesc = Type.getDescriptor(BsonReader.class); - final String convertWriterDesc = Type.getDescriptor(BsonWriter.class); - final String serviceDesc = Type.getDescriptor(serviceClass); - final boolean boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType()); - String newDynName = serviceName.substring(0, serviceName.lastIndexOf('/') + 1) - + "DynAction" + serviceClass.getSimpleName() + "_" + method.getName() + "_" + actionid; - while (true) { - try { - Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.')); - newDynName += "_"; - } catch (Throwable ex) { - break; - } - } - //------------------------------------------------------------- - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodDebugVisitor mv; - - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); - - { - { - fv = cw.visitField(ACC_PUBLIC, "service", serviceDesc, null, null); - fv.visitEnd(); - } - fv.visitEnd(); - } - { // constructor方法 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - String convertFromDesc = "(Ljava/lang/reflect/Type;" + convertReaderDesc + ")Ljava/lang/Object;"; - try { - convertFromDesc = Type.getMethodDescriptor(BsonConvert.class.getMethod("convertFrom", java.lang.reflect.Type.class, BsonReader.class)); - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - int handlerFuncIndex = -1; - Class handlerFuncClass = null; - { // action方法 - mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "action", "(" + convertReaderDesc + convertWriterDesc + asyncHandlerDesc + ")V", null, new String[]{"java/lang/Throwable"})); - //mv.setDebug(true); - int iconst = ICONST_1; - int intconst = 1; - int store = 4; //action的参数个数+1 - final Class[] paramClasses = method.getParameterTypes(); - int[][] codes = new int[paramClasses.length][2]; - for (int i = 0; i < paramClasses.length; i++) { //反序列化方法的每个参数 - if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) { - if (boolReturnTypeFuture) { - throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture"); - } - if (handlerFuncIndex >= 0) { - throw new RuntimeException(method + " have more than one CompletionHandler type parameter"); - } - Sncp.checkAsyncModifier(paramClasses[i], method); - handlerFuncIndex = i; - handlerFuncClass = paramClasses[i]; - mv.visitVarInsn(ALOAD, 3); - mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/')); - mv.visitVarInsn(ASTORE, store); - codes[i] = new int[]{ALOAD, store}; - store++; - iconst++; - intconst++; - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); - mv.visitLdcInsn(Type.getType(Type.getDescriptor(CompletionHandler.class))); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); - mv.visitInsn(POP); - continue; - } - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;"); - - if (intconst < 6) { - mv.visitInsn(ICONST_0 + intconst); - } else if (iconst <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, intconst); - } else if (iconst <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, intconst); - } else { - mv.visitLdcInsn(intconst); - } - mv.visitInsn(AALOAD); - mv.visitVarInsn(ALOAD, 1); - - mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); - int load = ALOAD; - int v = 0; - if (paramClasses[i].isPrimitive()) { - int storecode = ISTORE; - load = ILOAD; - if (paramClasses[i] == long.class) { - storecode = LSTORE; - load = LLOAD; - v = 1; - } else if (paramClasses[i] == float.class) { - storecode = FSTORE; - load = FLOAD; - v = 1; - } else if (paramClasses[i] == double.class) { - storecode = DSTORE; - load = DLOAD; - v = 1; - } - Class bigPrimitiveClass = Array.get(Array.newInstance(paramClasses[i], 1), 0).getClass(); - String bigPrimitiveName = bigPrimitiveClass.getName().replace('.', '/'); - try { - Method pm = bigPrimitiveClass.getMethod(paramClasses[i].getSimpleName() + "Value"); - mv.visitTypeInsn(CHECKCAST, bigPrimitiveName); - mv.visitMethodInsn(INVOKEVIRTUAL, bigPrimitiveName, pm.getName(), Type.getMethodDescriptor(pm), false); - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - mv.visitVarInsn(storecode, store); - } else { - mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/')); - mv.visitVarInsn(ASTORE, store); // - } - codes[i] = new int[]{load, store}; - store += v; - iconst++; - intconst++; - store++; - } - if (boolReturnTypeFuture || handlerFuncIndex >= 0) { //调用SncpAsyncHandler.setParams(Object... params) - mv.visitVarInsn(ALOAD, 3); - if (paramClasses.length < 6) { - mv.visitInsn(ICONST_0 + paramClasses.length); - } else if (paramClasses.length <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, paramClasses.length); - } else if (paramClasses.length <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, paramClasses.length); - } else { - mv.visitLdcInsn(paramClasses.length); - } - - mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); - int insn = 3; //action的参数个数 - for (int j = 0; j < paramClasses.length; j++) { - final Class pt = paramClasses[j]; - mv.visitInsn(DUP); - insn++; - if (j < 6) { - mv.visitInsn(ICONST_0 + j); - } else if (j <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, j); - } else if (j <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, j); - } else { - mv.visitLdcInsn(j); - } - if (pt.isPrimitive()) { - if (pt == long.class) { - mv.visitVarInsn(LLOAD, insn++); - } else if (pt == float.class) { - mv.visitVarInsn(FLOAD, insn++); - } else if (pt == double.class) { - mv.visitVarInsn(DLOAD, insn++); - } else { - mv.visitVarInsn(ILOAD, insn); - } - Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass(); - mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false); - } else { - mv.visitVarInsn(ALOAD, insn); - } - mv.visitInsn(AASTORE); - } - mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true); - } - { //调用service - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "service", serviceDesc); - for (int[] j : codes) { - mv.visitVarInsn(j[0], j[1]); - } - mv.visitMethodInsn(INVOKEVIRTUAL, serviceName, method.getName(), Type.getMethodDescriptor(method), false); - } - - final Class returnClass = method.getReturnType(); - if (returnClass != void.class) { - if (returnClass.isPrimitive()) { - Class bigClass = Array.get(Array.newInstance(returnClass, 1), 0).getClass(); - try { - Method vo = bigClass.getMethod("valueOf", returnClass); - mv.visitMethodInsn(INVOKESTATIC, bigClass.getName().replace('.', '/'), vo.getName(), Type.getMethodDescriptor(vo), false); - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - } - mv.visitVarInsn(ASTORE, store); //11 - if (boolReturnTypeFuture) { - mv.visitVarInsn(ALOAD, 3); - mv.visitVarInsn(ALOAD, store); - mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setFuture", "(Ljava/util/concurrent/CompletableFuture;)V", true); - } - } - if (!boolReturnTypeFuture && handlerFuncIndex < 0) { //同步方法 - //------------------------- _callParameter 方法 -------------------------------- - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 2); - if (paramClasses.length < 6) { //参数总数量 - mv.visitInsn(ICONST_0 + paramClasses.length); - } else if (paramClasses.length <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, paramClasses.length); - } else if (paramClasses.length <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, paramClasses.length); - } else { - mv.visitLdcInsn(paramClasses.length); - } - mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); - int insn = 3;//action的参数个数 - for (int j = 0; j < paramClasses.length; j++) { - final Class pt = paramClasses[j]; - mv.visitInsn(DUP); - insn++; - if (j < 6) { - mv.visitInsn(ICONST_0 + j); - } else if (j <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, j); - } else if (j <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, j); - } else { - mv.visitLdcInsn(j); - } - if (pt.isPrimitive()) { - if (pt == long.class) { - mv.visitVarInsn(LLOAD, insn++); - } else if (pt == float.class) { - mv.visitVarInsn(FLOAD, insn++); - } else if (pt == double.class) { - mv.visitVarInsn(DLOAD, insn++); - } else { - mv.visitVarInsn(ILOAD, insn); - } - Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass(); - mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false); - } else { - mv.visitVarInsn(ALOAD, insn); - } - mv.visitInsn(AASTORE); - } - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_callParameter", "(" + convertWriterDesc + "[Ljava/lang/Object;)V", false); - } - //-------------------------直接返回 或者 调用convertTo方法 -------------------------------- - int maxStack = codes.length > 0 ? codes[codes.length - 1][1] : 1; - if (boolReturnTypeFuture || returnClass == void.class) { //返回 - mv.visitInsn(RETURN); - maxStack = 8; - } else { //同步方法调用 - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); - mv.visitVarInsn(ALOAD, 2); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;"); - mv.visitInsn(ICONST_0); - mv.visitInsn(AALOAD); - mv.visitVarInsn(ALOAD, store); - mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertTo", "(" + convertWriterDesc + "Ljava/lang/reflect/Type;Ljava/lang/Object;)V", false); - mv.visitInsn(RETURN); - store++; - } - mv.visitMaxs(maxStack, store); - mv.visitEnd(); - } - cw.visitEnd(); - - byte[] bytes = cw.toByteArray(); - Class newClazz = new ClassLoader(serviceClass.getClassLoader()) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - SncpServletAction instance = (SncpServletAction) newClazz.getDeclaredConstructor().newInstance(); - instance.method = method; - java.lang.reflect.Type[] ptypes = TypeToken.getGenericType(method.getGenericParameterTypes(), serviceClass); - java.lang.reflect.Type[] types = new java.lang.reflect.Type[ptypes.length + 1]; - java.lang.reflect.Type rt = TypeToken.getGenericType(method.getGenericReturnType(), serviceClass); - types[0] = rt; - System.arraycopy(ptypes, 0, types, 1, ptypes.length); - instance.paramTypes = types; - instance.handlerFuncParamIndex = handlerFuncIndex; - instance.handlerFuncParamClass = handlerFuncClass; - instance.boolReturnTypeFuture = boolReturnTypeFuture; - - org.redkale.util.Attribute[] atts = new org.redkale.util.Attribute[ptypes.length + 1]; - Annotation[][] anns = method.getParameterAnnotations(); - boolean hasattr = false; - for (int i = 0; i < anns.length; i++) { - if (anns[i].length > 0) { - for (Annotation ann : anns[i]) { - if (ann.annotationType() == RpcCall.class) { - try { - atts[i + 1] = ((RpcCall) ann).value().getDeclaredConstructor().newInstance(); - hasattr = true; - } catch (Exception e) { - logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e); - } - break; - } - } - } - } - if (hasattr) instance.paramAttrs = atts; - newClazz.getField("service").set(instance, service); - return instance; - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import org.redkale.asm.MethodDebugVisitor; +import static org.redkale.net.sncp.SncpRequest.DEFAULT_HEADER; +import java.io.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.nio.channels.CompletionHandler; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.*; +import javax.annotation.*; +import org.redkale.asm.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import static org.redkale.asm.Opcodes.*; +import org.redkale.asm.Type; +import org.redkale.convert.bson.*; +import org.redkale.net.sncp.SncpAsyncHandler.DefaultSncpAsyncHandler; +import org.redkale.service.*; +import org.redkale.util.*; +import org.redkale.service.RpcCall; + +/** + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class SncpDynServlet extends SncpServlet { + + private final AtomicInteger maxTypeLength; + + private final AtomicInteger maxNameLength; + + private static final Logger logger = Logger.getLogger(SncpDynServlet.class.getSimpleName()); + + private final DLong serviceid; + + private final HashMap actions = new HashMap<>(); + + public SncpDynServlet(final BsonConvert convert, final String serviceName, final Class serviceOrSourceType, final Service service, + final AtomicInteger maxTypeLength, AtomicInteger maxNameLength) { + super(serviceName, serviceOrSourceType, service); + this.maxTypeLength = maxTypeLength; + this.maxNameLength = maxNameLength; + this.serviceid = Sncp.hash(type.getName() + ':' + serviceName); + Set actionids = new HashSet<>(); + RedkaleClassLoader.putReflectionPublicMethods(service.getClass().getName()); + for (java.lang.reflect.Method method : service.getClass().getMethods()) { + if (method.isSynthetic()) continue; + if (Modifier.isStatic(method.getModifiers())) continue; + if (Modifier.isFinal(method.getModifiers())) continue; + if (method.getAnnotation(Local.class) != null) continue; + if (method.getName().equals("getClass") || method.getName().equals("toString")) continue; + if (method.getName().equals("equals") || method.getName().equals("hashCode")) continue; + if (method.getName().equals("notify") || method.getName().equals("notifyAll") || method.getName().equals("wait")) continue; + if (method.getParameterCount() == 1 && method.getParameterTypes()[0] == AnyValue.class) { + if (method.getName().equals("init") || method.getName().equals("stop") || method.getName().equals("destroy")) continue; + } + + final DLong actionid = Sncp.hash(method); + SncpServletAction action; + try { + action = SncpServletAction.create(service, actionid, method); + } catch (RuntimeException e) { + throw new RuntimeException(method + " create " + SncpServletAction.class.getSimpleName() + " error", e); + } + action.convert = convert; + if (actionids.contains(actionid)) { + throw new RuntimeException(type.getName() + " have action(Method=" + method + ", actionid=" + actionid + ") same to (" + actions.get(actionid).method + ")"); + } + actions.put(actionid, action); + actionids.add(actionid); + } + maxNameLength.set(Math.max(maxNameLength.get(), serviceName.length() + 1)); + maxTypeLength.set(Math.max(maxTypeLength.get(), type.getName().length())); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getSimpleName()).append(" (type=").append(type.getName()); + int len = this.maxTypeLength.get() - type.getName().length(); + for (int i = 0; i < len; i++) { + sb.append(' '); + } + sb.append(", serviceid=").append(serviceid).append(", name='").append(serviceName).append("'"); + for (int i = 0; i < this.maxNameLength.get() - serviceName.length(); i++) { + sb.append(' '); + } + sb.append(", actions.size=").append(actions.size() > 9 ? "" : " ").append(actions.size()).append(")"); + return sb.toString(); + } + + @Override + public DLong getServiceid() { + return serviceid; + } + + @Override + public int compareTo(SncpServlet o0) { + if (!(o0 instanceof SncpDynServlet)) return 1; + SncpDynServlet o = (SncpDynServlet) o0; + int rs = this.type.getName().compareTo(o.type.getName()); + if (rs == 0) rs = this.serviceName.compareTo(o.serviceName); + return rs; + } + + @Override + @SuppressWarnings("unchecked") + public void execute(SncpRequest request, SncpResponse response) throws IOException { + final SncpServletAction action = actions.get(request.getActionid()); + //logger.log(Level.FINEST, "sncpdyn.execute: " + request + ", " + (action == null ? "null" : action.method)); + if (action == null) { + response.finish(SncpResponse.RETCODE_ILLACTIONID, null); //无效actionid + } else { + BsonWriter out = action.convert.pollBsonWriter(); + out.writeTo(DEFAULT_HEADER); + BsonReader in = action.convert.pollBsonReader(); + SncpAsyncHandler handler = null; + try { + if (action.handlerFuncParamIndex >= 0) { + if (action.handlerFuncParamClass == CompletionHandler.class) { + handler = new DefaultSncpAsyncHandler(logger, action, in, out, request, response); + } else { + Creator creator = action.handlerCreator; + if (creator == null) { + creator = SncpAsyncHandler.Factory.createCreator(action.handlerFuncParamClass); + action.handlerCreator = creator; + } + handler = creator.create(new DefaultSncpAsyncHandler(logger, action, in, out, request, response)); + } + } else if (action.boolReturnTypeFuture) { + handler = new DefaultSncpAsyncHandler(logger, action, in, out, request, response); + } + in.setBytes(request.getBody()); + action.action(in, out, handler); + if (handler == null) { + response.finish(0, out); + action.convert.offerBsonReader(in); + action.convert.offerBsonWriter(out); + } else if (action.boolReturnTypeFuture) { + CompletableFuture future = handler.sncp_getFuture(); + if (future == null) { + action._callParameter(out, handler.sncp_getParams()); + action.convert.convertTo(out, Object.class, null); + } else { + Object[] sncpParams = handler.sncp_getParams(); + future.whenComplete((v, e) -> { + if (e != null) { + response.getContext().getLogger().log(Level.SEVERE, "sncp CompleteAsync error(" + request + ")", e); + response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null); + return; + } + action._callParameter(out, sncpParams); + action.convert.convertTo(out, Object.class, v); + response.finish(0, out); + action.convert.offerBsonReader(in); + action.convert.offerBsonWriter(out); + }); + } + } + } catch (Throwable t) { + response.getContext().getLogger().log(Level.SEVERE, "sncp execute error(" + request + ")", t); + response.finish(SncpResponse.RETCODE_THROWEXCEPTION, null); + } + } + } + + public static abstract class SncpServletAction { + + public Method method; + + public Creator handlerCreator; + + @Resource + protected BsonConvert convert; + + protected org.redkale.util.Attribute[] paramAttrs; // 为null表示无RpcCall处理,index=0固定为null, 其他为参数标记的RpcCall回调方法 + + protected java.lang.reflect.Type[] paramTypes; //index=0表示返回参数的type, void的返回参数类型为null + + protected int handlerFuncParamIndex = -1; //handlerFuncParamIndex>=0表示存在CompletionHandler参数 + + protected boolean boolReturnTypeFuture = false; // 返回结果类型是否为 CompletableFuture + + protected Class handlerFuncParamClass; //CompletionHandler参数的类型 + + public abstract void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable; + + //只有同步方法才调用 (没有CompletionHandler、CompletableFuture) + public final void _callParameter(final BsonWriter out, final Object... params) { + if (paramAttrs != null) { + for (int i = 1; i < paramAttrs.length; i++) { + org.redkale.util.Attribute attr = paramAttrs[i]; + if (attr == null) continue; + out.writeByte((byte) i); + convert.convertTo(out, attr.genericType(), attr.get(params[i - 1])); + } + } + out.writeByte((byte) 0); + } + + public String actionName() { + return method.getDeclaringClass().getSimpleName() + "." + method.getName(); + } + + /** + *
    +         *  public class TestService implements Service {
    +         *
    +         *      public boolean change(TestBean bean, String name, int id) {
    +         *          return false;
    +         *      }
    +         *
    +         *      public void insert(CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
    +         *      }
    +         *
    +         *      public void update(long show, short v2, CompletionHandler<Boolean, TestBean> handler, TestBean bean, String name, int id) {
    +         *      }
    +         *
    +         *      public CompletableFuture<String> changeName(TestBean bean, String name, int id) {
    +         *          return null;
    +         *      }
    +         * }
    +         *
    +         *
    +         * class DynActionTestService_change extends SncpServletAction {
    +         *
    +         *      public TestService service;
    +         *
    +         *      @Override
    +         *      public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
    +         *          TestBean arg1 = convert.convertFrom(paramTypes[1], in);
    +         *          String arg2 = convert.convertFrom(paramTypes[2], in);
    +         *          int arg3 = convert.convertFrom(paramTypes[3], in);
    +         *          Object rs = service.change(arg1, arg2, arg3);
    +         *          _callParameter(out, arg1, arg2, arg3);
    +         *          convert.convertTo(out, paramTypes[0], rs);
    +         *      }
    +         * }
    +         *
    +         * class DynActionTestService_insert extends SncpServletAction {
    +         *
    +         *      public TestService service;
    +         *
    +         *      @Override
    +         *      public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
    +         *          SncpAsyncHandler arg0 = handler;
    +         *          convert.convertFrom(CompletionHandler.class, in);
    +         *          TestBean arg1 = convert.convertFrom(paramTypes[2], in);
    +         *          String arg2 = convert.convertFrom(paramTypes[3], in);
    +         *          int arg3 = convert.convertFrom(paramTypes[4], in);
    +         *          handler.sncp_setParams(arg0, arg1, arg2, arg3);
    +         *          service.insert(arg0, arg1, arg2, arg3);
    +         *       }
    +         * }
    +         *
    +         * class DynActionTestService_update extends SncpServletAction {
    +         *
    +         *      public TestService service;
    +         *
    +         *      @Override
    +         *      public void action(BsonReader in, BsonWriter out, SncpAsyncHandler handler) throws Throwable {
    +         *          long a1 = convert.convertFrom(paramTypes[1], in);
    +         *          short a2 = convert.convertFrom(paramTypes[2], in);
    +         *          SncpAsyncHandler a3 = handler;
    +         *          convert.convertFrom(CompletionHandler.class, in);
    +         *          TestBean arg1 = convert.convertFrom(paramTypes[4], in);
    +         *          String arg2 = convert.convertFrom(paramTypes[5], in);
    +         *          int arg3 = convert.convertFrom(paramTypes[6], in);
    +         *          handler.sncp_setParams(a1, a2, a3, arg1, arg2, arg3);
    +         *          service.update(a1, a2, a3, arg1, arg2, arg3);
    +         *      }
    +         * }
    +         *
    +         *
    +         * class DynActionTestService_changeName extends SncpServletAction {
    +         *
    +         *      public TestService service;
    +         *
    +         *      @Override
    +         *      public void action(final BsonReader in, final BsonWriter out, final SncpAsyncHandler handler) throws Throwable {
    +         *          TestBean arg1 = convert.convertFrom(paramTypes[1], in);
    +         *          String arg2 = convert.convertFrom(paramTypes[2], in);
    +         *          int arg3 = convert.convertFrom(paramTypes[3], in);
    +         *          handler.sncp_setParams(arg1, arg2, arg3);
    +         *          CompletableFuture future = service.changeName(arg1, arg2, arg3);
    +         *          handler.sncp_setFuture(future);
    +         *      }
    +         * }
    +         *
    +         * 
    + * + * @param service Service + * @param actionid 操作ID + * @param method 方法 + * + * @return SncpServletAction + */ + @SuppressWarnings("unchecked") + public static SncpServletAction create(final Service service, final DLong actionid, final Method method) { + final Class serviceClass = service.getClass(); + final String supDynName = SncpServletAction.class.getName().replace('.', '/'); + final String serviceName = serviceClass.getName().replace('.', '/'); + final String convertName = BsonConvert.class.getName().replace('.', '/'); + final String handlerName = SncpAsyncHandler.class.getName().replace('.', '/'); + final String asyncHandlerDesc = Type.getDescriptor(SncpAsyncHandler.class); + final String convertReaderDesc = Type.getDescriptor(BsonReader.class); + final String convertWriterDesc = Type.getDescriptor(BsonWriter.class); + final String serviceDesc = Type.getDescriptor(serviceClass); + final boolean boolReturnTypeFuture = CompletableFuture.class.isAssignableFrom(method.getReturnType()); + final String newDynName = "org/redkaledyn/sncp/servlet/action/_DynSncpActionServlet__" + serviceClass.getName().replace('.', '_').replace('$', '_') + "__" + method.getName() + "__" + actionid; + + int handlerFuncIndex = -1; + Class handlerFuncClass = null; + Class newClazz = null; + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + newClazz = clz == null ? Thread.currentThread().getContextClassLoader().loadClass(newDynName.replace('/', '.')) : clz; + final Class[] paramClasses = method.getParameterTypes(); + for (int i = 0; i < paramClasses.length; i++) { //反序列化方法的每个参数 + if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) { + handlerFuncIndex = i; + handlerFuncClass = paramClasses[i]; + break; + } + } + } catch (Throwable ex) { + } + + final java.lang.reflect.Type[] originalParamTypes = TypeToken.getGenericType(method.getGenericParameterTypes(), serviceClass); + final java.lang.reflect.Type originalReturnType = TypeToken.getGenericType(method.getGenericReturnType(), serviceClass); + if (newClazz == null) { + //------------------------------------------------------------- + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); + { + { + fv = cw.visitField(ACC_PUBLIC, "service", serviceDesc, null, null); + fv.visitEnd(); + } + fv.visitEnd(); + } + { // constructor方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + String convertFromDesc = "(Ljava/lang/reflect/Type;" + convertReaderDesc + ")Ljava/lang/Object;"; + try { + convertFromDesc = Type.getMethodDescriptor(BsonConvert.class.getMethod("convertFrom", java.lang.reflect.Type.class, BsonReader.class)); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + { // action方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "action", "(" + convertReaderDesc + convertWriterDesc + asyncHandlerDesc + ")V", null, new String[]{"java/lang/Throwable"})); + //mv.setDebug(true); + int iconst = ICONST_1; + int intconst = 1; + int store = 4; //action的参数个数+1 + final Class[] paramClasses = method.getParameterTypes(); + int[][] codes = new int[paramClasses.length][2]; + for (int i = 0; i < paramClasses.length; i++) { //反序列化方法的每个参数 + if (CompletionHandler.class.isAssignableFrom(paramClasses[i])) { + if (boolReturnTypeFuture) { + throw new RuntimeException(method + " have both CompletionHandler and CompletableFuture"); + } + if (handlerFuncIndex >= 0) { + throw new RuntimeException(method + " have more than one CompletionHandler type parameter"); + } + Sncp.checkAsyncModifier(paramClasses[i], method); + handlerFuncIndex = i; + handlerFuncClass = paramClasses[i]; + mv.visitVarInsn(ALOAD, 3); + mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/')); + mv.visitVarInsn(ASTORE, store); + codes[i] = new int[]{ALOAD, store}; + store++; + iconst++; + intconst++; + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); + mv.visitLdcInsn(Type.getType(Type.getDescriptor(CompletionHandler.class))); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); + mv.visitInsn(POP); + continue; + } + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;"); + + if (intconst < 6) { + mv.visitInsn(ICONST_0 + intconst); + } else if (iconst <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, intconst); + } else if (iconst <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, intconst); + } else { + mv.visitLdcInsn(intconst); + } + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, 1); + + mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertFrom", convertFromDesc, false); + int load = ALOAD; + int v = 0; + if (paramClasses[i].isPrimitive()) { + int storecode = ISTORE; + load = ILOAD; + if (paramClasses[i] == long.class) { + storecode = LSTORE; + load = LLOAD; + v = 1; + } else if (paramClasses[i] == float.class) { + storecode = FSTORE; + load = FLOAD; + v = 1; + } else if (paramClasses[i] == double.class) { + storecode = DSTORE; + load = DLOAD; + v = 1; + } + Class bigPrimitiveClass = Array.get(Array.newInstance(paramClasses[i], 1), 0).getClass(); + String bigPrimitiveName = bigPrimitiveClass.getName().replace('.', '/'); + try { + Method pm = bigPrimitiveClass.getMethod(paramClasses[i].getSimpleName() + "Value"); + mv.visitTypeInsn(CHECKCAST, bigPrimitiveName); + mv.visitMethodInsn(INVOKEVIRTUAL, bigPrimitiveName, pm.getName(), Type.getMethodDescriptor(pm), false); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + mv.visitVarInsn(storecode, store); + } else { + mv.visitTypeInsn(CHECKCAST, paramClasses[i].getName().replace('.', '/')); + mv.visitVarInsn(ASTORE, store); // + } + codes[i] = new int[]{load, store}; + store += v; + iconst++; + intconst++; + store++; + } + if (boolReturnTypeFuture || handlerFuncIndex >= 0) { //调用SncpAsyncHandler.setParams(Object... params) + mv.visitVarInsn(ALOAD, 3); + if (paramClasses.length < 6) { + mv.visitInsn(ICONST_0 + paramClasses.length); + } else if (paramClasses.length <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, paramClasses.length); + } else if (paramClasses.length <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, paramClasses.length); + } else { + mv.visitLdcInsn(paramClasses.length); + } + + mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + int insn = 3; //action的参数个数 + for (int j = 0; j < paramClasses.length; j++) { + final Class pt = paramClasses[j]; + mv.visitInsn(DUP); + insn++; + if (j < 6) { + mv.visitInsn(ICONST_0 + j); + } else if (j <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, j); + } else if (j <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, j); + } else { + mv.visitLdcInsn(j); + } + if (pt.isPrimitive()) { + if (pt == long.class) { + mv.visitVarInsn(LLOAD, insn++); + } else if (pt == float.class) { + mv.visitVarInsn(FLOAD, insn++); + } else if (pt == double.class) { + mv.visitVarInsn(DLOAD, insn++); + } else { + mv.visitVarInsn(ILOAD, insn); + } + Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass(); + mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false); + } else { + mv.visitVarInsn(ALOAD, insn); + } + mv.visitInsn(AASTORE); + } + mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setParams", "([Ljava/lang/Object;)V", true); + } + { //调用service + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "service", serviceDesc); + for (int[] j : codes) { + mv.visitVarInsn(j[0], j[1]); + } + mv.visitMethodInsn(INVOKEVIRTUAL, serviceName, method.getName(), Type.getMethodDescriptor(method), false); + } + + final Class returnClass = method.getReturnType(); + if (returnClass != void.class) { + if (returnClass.isPrimitive()) { + Class bigClass = Array.get(Array.newInstance(returnClass, 1), 0).getClass(); + try { + Method vo = bigClass.getMethod("valueOf", returnClass); + mv.visitMethodInsn(INVOKESTATIC, bigClass.getName().replace('.', '/'), vo.getName(), Type.getMethodDescriptor(vo), false); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + mv.visitVarInsn(ASTORE, store); //11 + if (boolReturnTypeFuture) { + mv.visitVarInsn(ALOAD, 3); + mv.visitVarInsn(ALOAD, store); + mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "sncp_setFuture", "(Ljava/util/concurrent/CompletableFuture;)V", true); + } + } + if (!boolReturnTypeFuture && handlerFuncIndex < 0) { //同步方法 + //------------------------- _callParameter 方法 -------------------------------- + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 2); + if (paramClasses.length < 6) { //参数总数量 + mv.visitInsn(ICONST_0 + paramClasses.length); + } else if (paramClasses.length <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, paramClasses.length); + } else if (paramClasses.length <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, paramClasses.length); + } else { + mv.visitLdcInsn(paramClasses.length); + } + mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); + int insn = 3;//action的参数个数 + for (int j = 0; j < paramClasses.length; j++) { + final Class pt = paramClasses[j]; + mv.visitInsn(DUP); + insn++; + if (j < 6) { + mv.visitInsn(ICONST_0 + j); + } else if (j <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, j); + } else if (j <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, j); + } else { + mv.visitLdcInsn(j); + } + if (pt.isPrimitive()) { + if (pt == long.class) { + mv.visitVarInsn(LLOAD, insn++); + } else if (pt == float.class) { + mv.visitVarInsn(FLOAD, insn++); + } else if (pt == double.class) { + mv.visitVarInsn(DLOAD, insn++); + } else { + mv.visitVarInsn(ILOAD, insn); + } + Class bigclaz = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(pt, 1), 0).getClass(); + mv.visitMethodInsn(INVOKESTATIC, bigclaz.getName().replace('.', '/'), "valueOf", "(" + Type.getDescriptor(pt) + ")" + Type.getDescriptor(bigclaz), false); + } else { + mv.visitVarInsn(ALOAD, insn); + } + mv.visitInsn(AASTORE); + } + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "_callParameter", "(" + convertWriterDesc + "[Ljava/lang/Object;)V", false); + } + //-------------------------直接返回 或者 调用convertTo方法 -------------------------------- + int maxStack = codes.length > 0 ? codes[codes.length - 1][1] : 1; + if (boolReturnTypeFuture || returnClass == void.class) { //返回 + mv.visitInsn(RETURN); + maxStack = 8; + } else { //同步方法调用 + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "convert", Type.getDescriptor(BsonConvert.class)); + mv.visitVarInsn(ALOAD, 2); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "paramTypes", "[Ljava/lang/reflect/Type;"); + mv.visitInsn(ICONST_0); + mv.visitInsn(AALOAD); + mv.visitVarInsn(ALOAD, store); + mv.visitMethodInsn(INVOKEVIRTUAL, convertName, "convertTo", "(" + convertWriterDesc + "Ljava/lang/reflect/Type;Ljava/lang/Object;)V", false); + mv.visitInsn(RETURN); + store++; + } + mv.visitMaxs(maxStack, store); + mv.visitEnd(); + } + cw.visitEnd(); + + byte[] bytes = cw.toByteArray(); + newClazz = new ClassLoader(serviceClass.getClassLoader()) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + try { + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), newClazz.getField("service")); + } catch (Exception e) { + } + for (java.lang.reflect.Type t : originalParamTypes) { + if (t.toString().startsWith("java.lang.")) continue; + BsonFactory.root().loadDecoder(t); + } + if (originalReturnType != void.class && originalReturnType != Void.class) { + if (boolReturnTypeFuture && method.getReturnType() != method.getGenericReturnType()) { + java.lang.reflect.Type t = ((ParameterizedType) method.getGenericReturnType()).getActualTypeArguments()[0]; + if (t != Void.class && t != java.lang.reflect.Type.class) BsonFactory.root().loadEncoder(t); + } else { + try { + BsonFactory.root().loadEncoder(originalReturnType); + } catch (Exception e) { + System.err.println(method); + } + } + } + } + try { + SncpServletAction instance = (SncpServletAction) newClazz.getDeclaredConstructor().newInstance(); + instance.method = method; + java.lang.reflect.Type[] types = new java.lang.reflect.Type[originalParamTypes.length + 1]; + types[0] = originalReturnType; + System.arraycopy(originalParamTypes, 0, types, 1, originalParamTypes.length); + instance.paramTypes = types; + instance.handlerFuncParamIndex = handlerFuncIndex; + instance.handlerFuncParamClass = handlerFuncClass; + instance.boolReturnTypeFuture = boolReturnTypeFuture; + + org.redkale.util.Attribute[] atts = new org.redkale.util.Attribute[originalParamTypes.length + 1]; + Annotation[][] anns = method.getParameterAnnotations(); + boolean hasattr = false; + for (int i = 0; i < anns.length; i++) { + if (anns[i].length > 0) { + for (Annotation ann : anns[i]) { + if (ann.annotationType() == RpcCall.class) { + try { + atts[i + 1] = ((RpcCall) ann).value().getDeclaredConstructor().newInstance(); + RedkaleClassLoader.putReflectionDeclaredConstructors(((RpcCall) ann).value(), ((RpcCall) ann).value().getName()); + hasattr = true; + } catch (Exception e) { + logger.log(Level.SEVERE, RpcCall.class.getSimpleName() + ".attribute cannot a newInstance for" + method, e); + } + break; + } + } + } + } + if (hasattr) instance.paramAttrs = atts; + newClazz.getField("service").set(instance, service); + return instance; + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + } + +} diff --git a/src/org/redkale/net/sncp/SncpFilter.java b/src/main/java/org/redkale/net/sncp/SncpFilter.java similarity index 95% rename from src/org/redkale/net/sncp/SncpFilter.java rename to src/main/java/org/redkale/net/sncp/SncpFilter.java index 4ad0479c5..5de8f9ae4 100644 --- a/src/org/redkale/net/sncp/SncpFilter.java +++ b/src/main/java/org/redkale/net/sncp/SncpFilter.java @@ -1,16 +1,16 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import org.redkale.net.Filter; - -/** - * - * @author zhangjx - */ -public abstract class SncpFilter extends Filter { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import org.redkale.net.Filter; + +/** + * + * @author zhangjx + */ +public abstract class SncpFilter extends Filter { + +} diff --git a/src/org/redkale/net/sncp/SncpPrepareServlet.java b/src/main/java/org/redkale/net/sncp/SncpPrepareServlet.java similarity index 96% rename from src/org/redkale/net/sncp/SncpPrepareServlet.java rename to src/main/java/org/redkale/net/sncp/SncpPrepareServlet.java index 58500a1db..a02448fbb 100644 --- a/src/org/redkale/net/sncp/SncpPrepareServlet.java +++ b/src/main/java/org/redkale/net/sncp/SncpPrepareServlet.java @@ -1,80 +1,81 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import org.redkale.net.PrepareServlet; -import org.redkale.util.AnyValue; -import java.io.IOException; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class SncpPrepareServlet extends PrepareServlet { - - private final Object sncplock = new Object(); - - @Override - public void addServlet(SncpServlet servlet, Object attachment, AnyValue conf, DLong... mappings) { - synchronized (sncplock) { - for (SncpServlet s : getServlets()) { - if (s.service == servlet.service) throw new RuntimeException(s.service + " repeat addSncpServlet"); - } - setServletConf(servlet, conf); - putMapping(servlet.getServiceid(), servlet); - putServlet(servlet); - } - } - - public SncpServlet removeSncpServlet(Service service) { - SncpServlet rs = null; - synchronized (sncplock) { - for (SncpServlet servlet : getServlets()) { - if (servlet.service == service) { - rs = servlet; - break; - } - } - if (rs != null) { - removeMapping(rs); - removeServlet(rs); - } - } - return rs; - } - - @Override - public void init(SncpContext context, AnyValue config) { - super.init(context, config); //必须要执行 - getServlets().forEach(s -> s.init(context, getServletConf(s))); - } - - @Override - public void destroy(SncpContext context, AnyValue config) { - super.destroy(context, config); //必须要执行 - getServlets().forEach(s -> s.destroy(context, getServletConf(s))); - } - - @Override - public void execute(SncpRequest request, SncpResponse response) throws IOException { - if (request.isPing()) { - response.finish(false, Sncp.PONG_BUFFER.duplicate()); - return; - } - SncpServlet servlet = (SncpServlet) mappingServlet(request.getServiceid()); - if (servlet == null) { - response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid - } else { - servlet.execute(request, response); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import org.redkale.net.PrepareServlet; +import org.redkale.util.AnyValue; +import java.io.IOException; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class SncpPrepareServlet extends PrepareServlet { + + private final Object sncplock = new Object(); + + @Override + public void addServlet(SncpServlet servlet, Object attachment, AnyValue conf, DLong... mappings) { + synchronized (sncplock) { + for (SncpServlet s : getServlets()) { + if (s.service == servlet.service) throw new RuntimeException(s.service + " repeat addSncpServlet"); + } + setServletConf(servlet, conf); + putMapping(servlet.getServiceid(), servlet); + putServlet(servlet); + } + } + + public SncpServlet removeSncpServlet(Service service) { + SncpServlet rs = null; + synchronized (sncplock) { + for (SncpServlet servlet : getServlets()) { + if (servlet.service == service) { + rs = servlet; + break; + } + } + if (rs != null) { + removeMapping(rs); + removeServlet(rs); + } + } + return rs; + } + + @Override + public void init(SncpContext context, AnyValue config) { + if (application != null && application.isCompileMode()) return; + super.init(context, config); //必须要执行 + getServlets().forEach(s -> s.init(context, getServletConf(s))); + } + + @Override + public void destroy(SncpContext context, AnyValue config) { + super.destroy(context, config); //必须要执行 + getServlets().forEach(s -> s.destroy(context, getServletConf(s))); + } + + @Override + public void execute(SncpRequest request, SncpResponse response) throws IOException { + if (request.isPing()) { + response.finish(false, Sncp.PONG_BUFFER.duplicate()); + return; + } + SncpServlet servlet = (SncpServlet) mappingServlet(request.getServiceid()); + if (servlet == null) { + response.finish(SncpResponse.RETCODE_ILLSERVICEID, null); //无效serviceid + } else { + servlet.execute(request, response); + } + } + +} diff --git a/src/org/redkale/net/sncp/SncpRequest.java b/src/main/java/org/redkale/net/sncp/SncpRequest.java similarity index 96% rename from src/org/redkale/net/sncp/SncpRequest.java rename to src/main/java/org/redkale/net/sncp/SncpRequest.java index f5933104c..62df68b2d 100644 --- a/src/org/redkale/net/sncp/SncpRequest.java +++ b/src/main/java/org/redkale/net/sncp/SncpRequest.java @@ -1,175 +1,175 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import java.net.*; -import java.nio.*; -import java.util.logging.Level; -import java.util.logging.Logger; -import org.redkale.convert.bson.*; -import org.redkale.net.*; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class SncpRequest extends Request { - - public static final int HEADER_SIZE = 60; - - public static final byte[] DEFAULT_HEADER = new byte[HEADER_SIZE]; - - protected static final int READ_STATE_ROUTE = 1; - - protected static final int READ_STATE_HEADER = 2; - - protected static final int READ_STATE_BODY = 3; - - protected static final int READ_STATE_END = 4; - - protected final BsonConvert convert; - - private long seqid; - - protected int readState = READ_STATE_ROUTE; - - private int serviceversion; - - private DLong serviceid; - - private DLong actionid; - - private int bodylength; - - private int bodyoffset; - - private boolean ping; - - private byte[] body; - - private byte[] addrbytes = new byte[6]; - - protected SncpRequest(SncpContext context) { - super(context); - this.convert = context.getBsonConvert(); - } - - @Override - protected int readHeader(ByteBuffer buffer, Request last) { - if (buffer.remaining() == Sncp.PING_BUFFER.remaining()) { - if (buffer.hasRemaining()) buffer.get(new byte[buffer.remaining()]); - this.ping = true; //Sncp.PING_BUFFER - this.readState = READ_STATE_END; - return 0; - } - //---------------------head---------------------------------- - if (this.readState == READ_STATE_ROUTE) { - if (buffer.remaining() < HEADER_SIZE) return 1; //小于60 - this.seqid = buffer.getLong(); //8 - if (buffer.getChar() != HEADER_SIZE) { //2 - if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.length not " + HEADER_SIZE); - return -1; - } - this.serviceid = DLong.read(buffer); //16 - this.serviceversion = buffer.getInt(); //4 - this.actionid = DLong.read(buffer); //16 - buffer.get(addrbytes); //ipaddr //6 - this.bodylength = buffer.getInt(); //4 - - if (buffer.getInt() != 0) { //4 - if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.retcode not 0"); - return -1; - } - this.body = new byte[this.bodylength]; - this.readState = READ_STATE_BODY; - } - //---------------------body---------------------------------- - if (this.readState == READ_STATE_BODY) { - int len = Math.min(this.bodylength, buffer.remaining()); - buffer.get(body, 0, len); - this.bodyoffset = len; - int rs = bodylength - len; - if (rs == 0) this.readState = READ_STATE_END; - return rs; - } - return 0; - } - -// @Override -// protected int readBody(ByteBuffer buffer, int length) { -// final int framelen = buffer.remaining(); -// int len = Math.min(framelen, length); -// buffer.get(this.body, this.bodyoffset, len); -// this.bodyoffset += len; -// return len; -// } - @Override - protected void prepare() { - this.keepAlive = true; - } - - //被SncpAsyncHandler.sncp_setParams调用 - protected void sncp_setParams(SncpDynServlet.SncpServletAction action, Logger logger, Object... params) { - } - - @Override - public String toString() { - return SncpRequest.class.getSimpleName() + "{seqid=" + this.seqid - + ",serviceversion=" + this.serviceversion + ",serviceid=" + this.serviceid - + ",actionid=" + this.actionid + ",bodylength=" + this.bodylength - + ",bodyoffset=" + this.bodyoffset + ",remoteAddress=" + getRemoteAddress() + "}"; - } - - @Override - protected void recycle() { - this.seqid = 0; - this.readState = READ_STATE_ROUTE; - this.serviceid = null; - this.serviceversion = 0; - this.actionid = null; - this.bodylength = 0; - this.bodyoffset = 0; - this.body = null; - this.ping = false; - this.addrbytes[0] = 0; - super.recycle(); - } - - protected boolean isPing() { - return ping; - } - - public byte[] getBody() { - return body; - } - - public long getSeqid() { - return seqid; - } - - public int getServiceversion() { - return serviceversion; - } - - public DLong getServiceid() { - return serviceid; - } - - public DLong getActionid() { - return actionid; - } - - public InetSocketAddress getRemoteAddress() { - if (addrbytes[0] == 0) return null; - return new InetSocketAddress((0xff & addrbytes[0]) + "." + (0xff & addrbytes[1]) + "." + (0xff & addrbytes[2]) + "." + (0xff & addrbytes[3]), - ((0xff00 & (addrbytes[4] << 8)) | (0xff & addrbytes[5]))); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import java.net.*; +import java.nio.*; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.redkale.convert.bson.*; +import org.redkale.net.*; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class SncpRequest extends Request { + + public static final int HEADER_SIZE = 60; + + public static final byte[] DEFAULT_HEADER = new byte[HEADER_SIZE]; + + protected static final int READ_STATE_ROUTE = 1; + + protected static final int READ_STATE_HEADER = 2; + + protected static final int READ_STATE_BODY = 3; + + protected static final int READ_STATE_END = 4; + + protected final BsonConvert convert; + + private long seqid; + + protected int readState = READ_STATE_ROUTE; + + private int serviceversion; + + private DLong serviceid; + + private DLong actionid; + + private int bodylength; + + private int bodyoffset; + + private boolean ping; + + private byte[] body; + + private byte[] addrbytes = new byte[6]; + + protected SncpRequest(SncpContext context) { + super(context); + this.convert = context.getBsonConvert(); + } + + @Override + protected int readHeader(ByteBuffer buffer, Request last) { + if (buffer.remaining() == Sncp.PING_BUFFER.remaining()) { + if (buffer.hasRemaining()) buffer.get(new byte[buffer.remaining()]); + this.ping = true; //Sncp.PING_BUFFER + this.readState = READ_STATE_END; + return 0; + } + //---------------------head---------------------------------- + if (this.readState == READ_STATE_ROUTE) { + if (buffer.remaining() < HEADER_SIZE) return 1; //小于60 + this.seqid = buffer.getLong(); //8 + if (buffer.getChar() != HEADER_SIZE) { //2 + if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.length not " + HEADER_SIZE); + return -1; + } + this.serviceid = DLong.read(buffer); //16 + this.serviceversion = buffer.getInt(); //4 + this.actionid = DLong.read(buffer); //16 + buffer.get(addrbytes); //ipaddr //6 + this.bodylength = buffer.getInt(); //4 + + if (buffer.getInt() != 0) { //4 + if (context.getLogger().isLoggable(Level.FINEST)) context.getLogger().finest("sncp buffer header.retcode not 0"); + return -1; + } + this.body = new byte[this.bodylength]; + this.readState = READ_STATE_BODY; + } + //---------------------body---------------------------------- + if (this.readState == READ_STATE_BODY) { + int len = Math.min(this.bodylength, buffer.remaining()); + buffer.get(body, 0, len); + this.bodyoffset = len; + int rs = bodylength - len; + if (rs == 0) this.readState = READ_STATE_END; + return rs; + } + return 0; + } + +// @Override +// protected int readBody(ByteBuffer buffer, int length) { +// final int framelen = buffer.remaining(); +// int len = Math.min(framelen, length); +// buffer.get(this.body, this.bodyoffset, len); +// this.bodyoffset += len; +// return len; +// } + @Override + protected void prepare() { + this.keepAlive = true; + } + + //被SncpAsyncHandler.sncp_setParams调用 + protected void sncp_setParams(SncpDynServlet.SncpServletAction action, Logger logger, Object... params) { + } + + @Override + public String toString() { + return SncpRequest.class.getSimpleName() + "{seqid=" + this.seqid + + ",serviceversion=" + this.serviceversion + ",serviceid=" + this.serviceid + + ",actionid=" + this.actionid + ",bodylength=" + this.bodylength + + ",bodyoffset=" + this.bodyoffset + ",remoteAddress=" + getRemoteAddress() + "}"; + } + + @Override + protected void recycle() { + this.seqid = 0; + this.readState = READ_STATE_ROUTE; + this.serviceid = null; + this.serviceversion = 0; + this.actionid = null; + this.bodylength = 0; + this.bodyoffset = 0; + this.body = null; + this.ping = false; + this.addrbytes[0] = 0; + super.recycle(); + } + + protected boolean isPing() { + return ping; + } + + public byte[] getBody() { + return body; + } + + public long getSeqid() { + return seqid; + } + + public int getServiceversion() { + return serviceversion; + } + + public DLong getServiceid() { + return serviceid; + } + + public DLong getActionid() { + return actionid; + } + + public InetSocketAddress getRemoteAddress() { + if (addrbytes[0] == 0) return null; + return new InetSocketAddress((0xff & addrbytes[0]) + "." + (0xff & addrbytes[1]) + "." + (0xff & addrbytes[2]) + "." + (0xff & addrbytes[3]), + ((0xff00 & (addrbytes[4] << 8)) | (0xff & addrbytes[5]))); + } + +} diff --git a/src/org/redkale/net/sncp/SncpResponse.java b/src/main/java/org/redkale/net/sncp/SncpResponse.java similarity index 97% rename from src/org/redkale/net/sncp/SncpResponse.java rename to src/main/java/org/redkale/net/sncp/SncpResponse.java index ea3959b37..b6067be15 100644 --- a/src/org/redkale/net/sncp/SncpResponse.java +++ b/src/main/java/org/redkale/net/sncp/SncpResponse.java @@ -1,116 +1,116 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE; -import java.nio.*; -import org.redkale.convert.bson.*; -import org.redkale.net.*; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class SncpResponse extends Response { - - public static final int RETCODE_ILLSERVICEID = (1 << 1); //无效serviceid - - public static final int RETCODE_ILLSERVICEVER = (1 << 2); //无效serviceversion - - public static final int RETCODE_ILLACTIONID = (1 << 3); //无效actionid - - public static final int RETCODE_THROWEXCEPTION = (1 << 4); //内部异常 - - private final byte[] addrBytes; - - private final int addrPort; - - public static String getRetCodeInfo(int retcode) { - if (retcode == RETCODE_ILLSERVICEID) return "The serviceid is invalid"; - if (retcode == RETCODE_ILLSERVICEVER) return "The serviceversion is invalid"; - if (retcode == RETCODE_ILLACTIONID) return "The actionid is invalid"; - if (retcode == RETCODE_THROWEXCEPTION) return "Inner exception"; - return null; - } - - protected SncpResponse(SncpContext context, SncpRequest request) { - super(context, request); - this.addrBytes = context.getServerAddress().getAddress().getAddress(); - this.addrPort = context.getServerAddress().getPort(); - if (this.addrBytes.length != 4) throw new RuntimeException("SNCP serverAddress only support IPv4"); - } - - @Override - protected void prepare() { - super.prepare(); - } - - @Override - protected boolean recycle() { - return super.recycle(); - } - - @Override - protected void finish(boolean kill, ByteBuffer buffer) { - super.finish(kill, buffer); - } - - public void finish(final int retcode, final BsonWriter out) { - if (out == null) { - final ByteArray buffer = new ByteArray(SncpRequest.HEADER_SIZE); - fillHeader(buffer, 0, retcode); - finish(buffer); - return; - } - final int respBodyLength = out.count(); //body总长度 - final ByteArray array = out.toByteArray(); - fillHeader(array, respBodyLength - HEADER_SIZE, retcode); - finish(array); - } - - protected void fillHeader(ByteArray buffer, int bodyLength, int retcode) { - //---------------------head---------------------------------- - int offset = 0; - buffer.putLong(offset, request.getSeqid()); - offset += 8; - buffer.putChar(offset, (char) SncpRequest.HEADER_SIZE); - offset += 2; - DLong.write(buffer, offset, request.getServiceid()); - offset += 16; - buffer.putInt(offset, request.getServiceversion()); - offset += 4; - DLong.write(buffer, offset, request.getActionid()); - offset += 16; - buffer.put(offset, addrBytes); - offset += addrBytes.length; //4 - buffer.putChar(offset, (char) this.addrPort); - offset += 2; - buffer.putInt(offset, bodyLength); - offset += 4; - buffer.putInt(offset, retcode); - //offset += 4; - } - -// protected void fillHeader(ByteBuffer buffer, int bodyLength, int retcode) { -// //---------------------head---------------------------------- -// final int currentpos = buffer.position(); -// buffer.position(0); -// buffer.putLong(request.getSeqid()); -// buffer.putChar((char) SncpRequest.HEADER_SIZE); -// DLong.write(buffer, request.getServiceid()); -// buffer.putInt(request.getServiceversion()); -// DLong.write(buffer, request.getActionid()); -// buffer.put(addrBytes); -// buffer.putChar((char) this.addrPort); -// buffer.putInt(bodyLength); -// buffer.putInt(retcode); -// buffer.position(currentpos); -// } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import static org.redkale.net.sncp.SncpRequest.HEADER_SIZE; +import java.nio.*; +import org.redkale.convert.bson.*; +import org.redkale.net.*; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class SncpResponse extends Response { + + public static final int RETCODE_ILLSERVICEID = (1 << 1); //无效serviceid + + public static final int RETCODE_ILLSERVICEVER = (1 << 2); //无效serviceversion + + public static final int RETCODE_ILLACTIONID = (1 << 3); //无效actionid + + public static final int RETCODE_THROWEXCEPTION = (1 << 4); //内部异常 + + private final byte[] addrBytes; + + private final int addrPort; + + public static String getRetCodeInfo(int retcode) { + if (retcode == RETCODE_ILLSERVICEID) return "The serviceid is invalid"; + if (retcode == RETCODE_ILLSERVICEVER) return "The serviceversion is invalid"; + if (retcode == RETCODE_ILLACTIONID) return "The actionid is invalid"; + if (retcode == RETCODE_THROWEXCEPTION) return "Inner exception"; + return null; + } + + protected SncpResponse(SncpContext context, SncpRequest request) { + super(context, request); + this.addrBytes = context.getServerAddress().getAddress().getAddress(); + this.addrPort = context.getServerAddress().getPort(); + if (this.addrBytes.length != 4) throw new RuntimeException("SNCP serverAddress only support IPv4"); + } + + @Override + protected void prepare() { + super.prepare(); + } + + @Override + protected boolean recycle() { + return super.recycle(); + } + + @Override + protected void finish(boolean kill, ByteBuffer buffer) { + super.finish(kill, buffer); + } + + public void finish(final int retcode, final BsonWriter out) { + if (out == null) { + final ByteArray buffer = new ByteArray(SncpRequest.HEADER_SIZE); + fillHeader(buffer, 0, retcode); + finish(buffer); + return; + } + final int respBodyLength = out.count(); //body总长度 + final ByteArray array = out.toByteArray(); + fillHeader(array, respBodyLength - HEADER_SIZE, retcode); + finish(array); + } + + protected void fillHeader(ByteArray buffer, int bodyLength, int retcode) { + //---------------------head---------------------------------- + int offset = 0; + buffer.putLong(offset, request.getSeqid()); + offset += 8; + buffer.putChar(offset, (char) SncpRequest.HEADER_SIZE); + offset += 2; + DLong.write(buffer, offset, request.getServiceid()); + offset += 16; + buffer.putInt(offset, request.getServiceversion()); + offset += 4; + DLong.write(buffer, offset, request.getActionid()); + offset += 16; + buffer.put(offset, addrBytes); + offset += addrBytes.length; //4 + buffer.putChar(offset, (char) this.addrPort); + offset += 2; + buffer.putInt(offset, bodyLength); + offset += 4; + buffer.putInt(offset, retcode); + //offset += 4; + } + +// protected void fillHeader(ByteBuffer buffer, int bodyLength, int retcode) { +// //---------------------head---------------------------------- +// final int currentpos = buffer.position(); +// buffer.position(0); +// buffer.putLong(request.getSeqid()); +// buffer.putChar((char) SncpRequest.HEADER_SIZE); +// DLong.write(buffer, request.getServiceid()); +// buffer.putInt(request.getServiceversion()); +// DLong.write(buffer, request.getActionid()); +// buffer.put(addrBytes); +// buffer.putChar((char) this.addrPort); +// buffer.putInt(bodyLength); +// buffer.putInt(retcode); +// buffer.position(currentpos); +// } +} diff --git a/src/org/redkale/net/sncp/SncpServer.java b/src/main/java/org/redkale/net/sncp/SncpServer.java similarity index 71% rename from src/org/redkale/net/sncp/SncpServer.java rename to src/main/java/org/redkale/net/sncp/SncpServer.java index 49b6d369d..5134a5d72 100644 --- a/src/org/redkale/net/sncp/SncpServer.java +++ b/src/main/java/org/redkale/net/sncp/SncpServer.java @@ -1,154 +1,139 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import java.nio.ByteBuffer; -import java.util.List; -import java.util.concurrent.atomic.*; -import org.redkale.boot.Application; -import org.redkale.convert.bson.BsonFactory; -import org.redkale.net.*; -import org.redkale.net.sncp.SncpContext.SncpContextConfig; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * Service Node Communicate Protocol - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public class SncpServer extends Server { - - private final AtomicInteger maxClassNameLength = new AtomicInteger(); - - private final AtomicInteger maxNameLength = new AtomicInteger(); - - public SncpServer() { - this(null, System.currentTimeMillis(), null, ResourceFactory.root()); - } - - public SncpServer(ResourceFactory resourceFactory) { - this(null, System.currentTimeMillis(), null, resourceFactory); - } - - public SncpServer(Application application, long serverStartTime, AnyValue serconf, ResourceFactory resourceFactory) { - super(application, serverStartTime, netprotocol(serconf), resourceFactory, new SncpPrepareServlet()); - } - - private static String netprotocol(AnyValue serconf) { - if (serconf == null) return "TCP"; - String protocol = serconf.getValue("protocol", "").toUpperCase(); - if (protocol.endsWith(".UDP")) return "UDP"; - return "TCP"; - } - - @Override - public void init(AnyValue config) throws Exception { - super.init(config); - } - - public List getSncpServlets() { - return this.prepare.getServlets(); - } - - public List getSncpFilters() { - return this.prepare.getFilters(); - } - - /** - * 删除SncpFilter - * - * @param 泛型 - * @param filterClass SncpFilter类 - * - * @return SncpFilter - */ - public T removeSncpFilter(Class filterClass) { - return (T) this.prepare.removeFilter(filterClass); - } - - /** - * 添加SncpFilter - * - * @param filter SncpFilter - * @param conf AnyValue - * - * @return SncpServer - */ - public SncpServer addSncpFilter(SncpFilter filter, AnyValue conf) { - this.prepare.addFilter(filter, conf); - return this; - } - - /** - * 删除SncpServlet - * - * @param sncpService Service - * - * @return SncpServlet - */ - public SncpServlet removeSncpServlet(Service sncpService) { - return ((SncpPrepareServlet) this.prepare).removeSncpServlet(sncpService); - } - - public SncpDynServlet addSncpServlet(Service sncpService) { - if (!Sncp.isSncpDyn(sncpService)) return null; - SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), Sncp.getResourceName(sncpService), - Sncp.getResourceType(sncpService), sncpService, maxClassNameLength, maxNameLength); - this.prepare.addServlet(sds, null, Sncp.getConf(sncpService)); - return sds; - } - - @Override - @SuppressWarnings("unchecked") - protected SncpContext createContext(Application application) { - this.bufferCapacity = Math.max(this.bufferCapacity, 8 * 1024); - - final SncpContextConfig contextConfig = new SncpContextConfig(); - if (application != null) contextConfig.workExecutor = application.getWorkExecutor(); - contextConfig.serverStartTime = this.serverStartTime; - contextConfig.logger = this.logger; - contextConfig.sslContext = this.sslContext; - contextConfig.bufferCapacity = this.bufferCapacity; - contextConfig.maxconns = this.maxconns; - contextConfig.maxbody = this.maxbody; - contextConfig.charset = this.charset; - contextConfig.address = this.address; - contextConfig.prepare = this.prepare; - contextConfig.resourceFactory = this.resourceFactory; - contextConfig.aliveTimeoutSeconds = this.aliveTimeoutSeconds; - contextConfig.readTimeoutSeconds = this.readTimeoutSeconds; - contextConfig.writeTimeoutSeconds = this.writeTimeoutSeconds; - - return new SncpContext(contextConfig); - } - - @Override - protected ObjectPool createBufferPool(AtomicLong createCounter, AtomicLong cycleCounter, int bufferPoolSize) { - if (createCounter == null) createCounter = new AtomicLong(); - if (cycleCounter == null) cycleCounter = new AtomicLong(); - final int rcapacity = this.bufferCapacity; - ObjectPool bufferPool = ObjectPool.createSafePool(createCounter, cycleCounter, bufferPoolSize, - (Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> { - if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false; - e.clear(); - return true; - }); - return bufferPool; - } - - @Override - protected ObjectPool createResponsePool(AtomicLong createCounter, AtomicLong cycleCounter, int responsePoolSize) { - Creator creator = (Object... params) -> new SncpResponse(this.context, new SncpRequest(this.context)); - ObjectPool pool = ObjectPool.createSafePool(createCounter, cycleCounter, responsePoolSize, creator, (x) -> ((SncpResponse) x).prepare(), (x) -> ((SncpResponse) x).recycle()); - return pool; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import java.nio.ByteBuffer; +import java.util.List; +import java.util.concurrent.atomic.*; +import org.redkale.boot.Application; +import org.redkale.convert.bson.BsonFactory; +import org.redkale.net.*; +import org.redkale.net.sncp.SncpContext.SncpContextConfig; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * Service Node Communicate Protocol + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public class SncpServer extends Server { + + private final AtomicInteger maxTypeLength = new AtomicInteger(); + + private final AtomicInteger maxNameLength = new AtomicInteger(); + + public SncpServer() { + this(null, System.currentTimeMillis(), null, ResourceFactory.create()); + } + + public SncpServer(ResourceFactory resourceFactory) { + this(null, System.currentTimeMillis(), null, resourceFactory); + } + + public SncpServer(Application application, long serverStartTime, AnyValue serconf, ResourceFactory resourceFactory) { + super(application, serverStartTime, netprotocol(serconf), resourceFactory, new SncpPrepareServlet()); + } + + private static String netprotocol(AnyValue serconf) { + if (serconf == null) return "TCP"; + String protocol = serconf.getValue("protocol", "").toUpperCase(); + if (protocol.endsWith(".UDP")) return "UDP"; + return "TCP"; + } + + @Override + public void init(AnyValue config) throws Exception { + super.init(config); + } + + public List getSncpServlets() { + return this.prepare.getServlets(); + } + + public List getSncpFilters() { + return this.prepare.getFilters(); + } + + /** + * 删除SncpFilter + * + * @param 泛型 + * @param filterClass SncpFilter类 + * + * @return SncpFilter + */ + public T removeSncpFilter(Class filterClass) { + return (T) this.prepare.removeFilter(filterClass); + } + + /** + * 添加SncpFilter + * + * @param filter SncpFilter + * @param conf AnyValue + * + * @return SncpServer + */ + public SncpServer addSncpFilter(SncpFilter filter, AnyValue conf) { + this.prepare.addFilter(filter, conf); + return this; + } + + /** + * 删除SncpServlet + * + * @param sncpService Service + * + * @return SncpServlet + */ + public SncpServlet removeSncpServlet(Service sncpService) { + return ((SncpPrepareServlet) this.prepare).removeSncpServlet(sncpService); + } + + public SncpDynServlet addSncpServlet(Service sncpService) { + if (!Sncp.isSncpDyn(sncpService)) return null; + SncpDynServlet sds = new SncpDynServlet(BsonFactory.root().getConvert(), Sncp.getResourceName(sncpService), + Sncp.getResourceType(sncpService), sncpService, maxTypeLength, maxNameLength); + this.prepare.addServlet(sds, null, Sncp.getConf(sncpService)); + return sds; + } + + @Override + @SuppressWarnings("unchecked") + protected SncpContext createContext() { + this.bufferCapacity = Math.max(this.bufferCapacity, 8 * 1024); + + final SncpContextConfig contextConfig = new SncpContextConfig(); + initContextConfig(contextConfig); + + return new SncpContext(contextConfig); + } + + @Override + protected ObjectPool createBufferPool(LongAdder createCounter, LongAdder cycleCounter, int bufferPoolSize) { + final int rcapacity = this.bufferCapacity; + ObjectPool bufferPool = ObjectPool.createSafePool(createCounter, cycleCounter, bufferPoolSize, + (Object... params) -> ByteBuffer.allocateDirect(rcapacity), null, (e) -> { + if (e == null || e.isReadOnly() || e.capacity() != rcapacity) return false; + e.clear(); + return true; + }); + return bufferPool; + } + + @Override + protected ObjectPool createResponsePool(LongAdder createCounter, LongAdder cycleCounter, int responsePoolSize) { + Creator creator = (Object... params) -> new SncpResponse(this.context, new SncpRequest(this.context)); + ObjectPool pool = ObjectPool.createSafePool(createCounter, cycleCounter, responsePoolSize, creator, (x) -> ((SncpResponse) x).prepare(), (x) -> ((SncpResponse) x).recycle()); + return pool; + } + +} diff --git a/src/org/redkale/net/sncp/SncpServlet.java b/src/main/java/org/redkale/net/sncp/SncpServlet.java similarity index 96% rename from src/org/redkale/net/sncp/SncpServlet.java rename to src/main/java/org/redkale/net/sncp/SncpServlet.java index 41e9c2283..b027fed01 100644 --- a/src/org/redkale/net/sncp/SncpServlet.java +++ b/src/main/java/org/redkale/net/sncp/SncpServlet.java @@ -1,72 +1,72 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.sncp; - -import java.util.Objects; -import java.util.concurrent.*; -import org.redkale.net.*; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class SncpServlet extends Servlet implements Comparable { - - protected final Class type; - - protected final String serviceName; - - protected final Service service; - - protected SncpServlet(String serviceName, Class serviceOrSourceType, Service service) { - this.type = serviceOrSourceType; - this.service = service; - this.serviceName = serviceName; - } - - public Service getService() { - return service; - } - - public String getServiceName() { - return serviceName; - } - - public Class getServiceType() { - return type; - } - - public abstract DLong getServiceid(); - - protected ExecutorService getExecutor() { - Thread thread = Thread.currentThread(); - if (thread instanceof WorkThread) { - return ((WorkThread) thread).getWorkExecutor(); - } - return ForkJoinPool.commonPool(); - } - - @Override - public final boolean equals(Object obj) { - if (!(obj instanceof SncpServlet)) return false; - return Objects.equals(getServiceid(), ((SncpServlet) obj).getServiceid()); - } - - @Override - public final int hashCode() { - return Objects.hashCode(getServiceid()); - } - - @Override - public int compareTo(SncpServlet o) { - return 0; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.sncp; + +import java.util.Objects; +import java.util.concurrent.*; +import org.redkale.net.*; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class SncpServlet extends Servlet implements Comparable { + + protected final Class type; + + protected final String serviceName; + + protected final Service service; + + protected SncpServlet(String serviceName, Class serviceOrSourceType, Service service) { + this.type = serviceOrSourceType; + this.service = service; + this.serviceName = serviceName; + } + + public Service getService() { + return service; + } + + public String getServiceName() { + return serviceName; + } + + public Class getServiceType() { + return type; + } + + public abstract DLong getServiceid(); + + protected ExecutorService getExecutor() { + Thread thread = Thread.currentThread(); + if (thread instanceof WorkThread) { + return ((WorkThread) thread).getWorkExecutor(); + } + return ForkJoinPool.commonPool(); + } + + @Override + public final boolean equals(Object obj) { + if (!(obj instanceof SncpServlet)) return false; + return Objects.equals(getServiceid(), ((SncpServlet) obj).getServiceid()); + } + + @Override + public final int hashCode() { + return Objects.hashCode(getServiceid()); + } + + @Override + public int compareTo(SncpServlet o) { + return 0; + } +} diff --git a/src/org/redkale/net/sncp/package-info.java b/src/main/java/org/redkale/net/sncp/package-info.java similarity index 95% rename from src/org/redkale/net/sncp/package-info.java rename to src/main/java/org/redkale/net/sncp/package-info.java index 60085c7d0..2ab81a227 100644 --- a/src/org/redkale/net/sncp/package-info.java +++ b/src/main/java/org/redkale/net/sncp/package-info.java @@ -1,4 +1,4 @@ -/** - * SNCP协议包,提供SNCP协议服务器 - */ -package org.redkale.net.sncp; +/** + * SNCP协议包,提供SNCP协议服务器 + */ +package org.redkale.net.sncp; diff --git a/src/org/redkale/service/AbstractService.java b/src/main/java/org/redkale/service/AbstractService.java similarity index 94% rename from src/org/redkale/service/AbstractService.java rename to src/main/java/org/redkale/service/AbstractService.java index 1616b1a21..cb5ff71e7 100644 --- a/src/org/redkale/service/AbstractService.java +++ b/src/main/java/org/redkale/service/AbstractService.java @@ -1,67 +1,72 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.util.concurrent.*; -import javax.annotation.Resource; -import org.redkale.boot.Application; -import org.redkale.net.*; -import org.redkale.util.ThreadHashExecutor; - -/** - * - * @author zhangjx - */ -public abstract class AbstractService implements Service { - - @Resource(name = Application.RESNAME_APP_EXECUTOR) - private ExecutorService workExecutor; - - protected void runAsync(Runnable command) { - if (workExecutor != null) { - workExecutor.execute(command); - } else { - Thread thread = Thread.currentThread(); - if (thread instanceof WorkThread) { - ((WorkThread) thread).runAsync(command); - } else { - ForkJoinPool.commonPool().execute(command); - } - } - } - - protected void runAsync(int hash, Runnable command) { - if (workExecutor != null) { - if (workExecutor instanceof ThreadHashExecutor) { - ((ThreadHashExecutor) workExecutor).execute(hash, command); - } else { - Thread thread = Thread.currentThread(); - if (thread instanceof WorkThread) { - ((WorkThread) thread).runAsync(hash, command); - } else { - workExecutor.execute(command); - } - } - } else { - Thread thread = Thread.currentThread(); - if (thread instanceof WorkThread) { - ((WorkThread) thread).runAsync(hash, command); - } else { - ForkJoinPool.commonPool().execute(command); - } - } - } - - protected ExecutorService getExecutor() { - if (workExecutor != null) return workExecutor; - Thread thread = Thread.currentThread(); - if (thread instanceof WorkThread) { - ExecutorService e = ((WorkThread) thread).getWorkExecutor(); - if (e != null) return e; - } - return ForkJoinPool.commonPool(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.util.concurrent.*; +import javax.annotation.Resource; +import org.redkale.boot.Application; +import org.redkale.net.*; +import org.redkale.net.sncp.Sncp; +import org.redkale.util.ThreadHashExecutor; + +/** + * + * @author zhangjx + */ +public abstract class AbstractService implements Service { + + @Resource(name = Application.RESNAME_APP_EXECUTOR) + private ExecutorService workExecutor; + + protected Class serviceType() { + return Sncp.getServiceType(this); + } + + protected void runAsync(Runnable command) { + if (workExecutor != null) { + workExecutor.execute(command); + } else { + Thread thread = Thread.currentThread(); + if (thread instanceof WorkThread) { + ((WorkThread) thread).runAsync(command); + } else { + ForkJoinPool.commonPool().execute(command); + } + } + } + + protected void runAsync(int hash, Runnable command) { + if (workExecutor != null) { + if (workExecutor instanceof ThreadHashExecutor) { + ((ThreadHashExecutor) workExecutor).execute(hash, command); + } else { + Thread thread = Thread.currentThread(); + if (thread instanceof WorkThread) { + ((WorkThread) thread).runAsync(hash, command); + } else { + workExecutor.execute(command); + } + } + } else { + Thread thread = Thread.currentThread(); + if (thread instanceof WorkThread) { + ((WorkThread) thread).runAsync(hash, command); + } else { + ForkJoinPool.commonPool().execute(command); + } + } + } + + protected ExecutorService getExecutor() { + if (workExecutor != null) return workExecutor; + Thread thread = Thread.currentThread(); + if (thread instanceof WorkThread) { + ExecutorService e = ((WorkThread) thread).getWorkExecutor(); + if (e != null) return e; + } + return ForkJoinPool.commonPool(); + } +} diff --git a/src/org/redkale/service/Local.java b/src/main/java/org/redkale/service/Local.java similarity index 96% rename from src/org/redkale/service/Local.java rename to src/main/java/org/redkale/service/Local.java index 67933ca40..da5c22c4d 100644 --- a/src/org/redkale/service/Local.java +++ b/src/main/java/org/redkale/service/Local.java @@ -1,39 +1,39 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 本地模式注解。
    - * 声明为Local的Service只能以本地模式存在, 即使配置文件中配置成远程模式也将被忽略。
    - * Service里被标记为Local的public方法不会被重载。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE, METHOD, PARAMETER}) -@Retention(RUNTIME) -public @interface Local { - - /** - * 标记全局唯一性 - *

    - * 有些Service可能只能启动一个实例, 比如凌晨定时清除一些数据的Service, 在整个系统部署中应该只被部署一次 - * - * @since 2.1.0 - * @return boolean - */ - //boolean unique() default false; - - String comment() default ""; //备注描述 -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 本地模式注解。
    + * 声明为Local的Service只能以本地模式存在, 即使配置文件中配置成远程模式也将被忽略。
    + * Service里被标记为Local的public方法不会被重载。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE, METHOD, PARAMETER}) +@Retention(RUNTIME) +public @interface Local { + + /** + * 标记全局唯一性 + *

    + * 有些Service可能只能启动一个实例, 比如凌晨定时清除一些数据的Service, 在整个系统部署中应该只被部署一次 + * + * @since 2.1.0 + * @return boolean + */ + //boolean unique() default false; + + String comment() default ""; //备注描述 +} diff --git a/src/org/redkale/service/Persist.java b/src/main/java/org/redkale/service/Persist.java similarity index 96% rename from src/org/redkale/service/Persist.java rename to src/main/java/org/redkale/service/Persist.java index bd1e11d0b..9a72e657b 100644 --- a/src/org/redkale/service/Persist.java +++ b/src/main/java/org/redkale/service/Persist.java @@ -1,32 +1,32 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * Service类中临时缓存字段
    - * - * 注意: 被标记字段的数据必须是可序列化和反序列化的, 且字段不能是static的, 如果字段类型不是Map或Collection类型则不能修饰为final - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Target({FIELD}) -@Retention(RUNTIME) -public @interface Persist { - - /** - * 临时缓存的超时秒数,超过指定秒数的缓存数据将会被废弃, 0表示不超时, 默认超时值为60秒 - * - * @return int - */ - int timeout() default 60; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Service类中临时缓存字段
    + * + * 注意: 被标记字段的数据必须是可序列化和反序列化的, 且字段不能是static的, 如果字段类型不是Map或Collection类型则不能修饰为final + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Target({FIELD}) +@Retention(RUNTIME) +public @interface Persist { + + /** + * 临时缓存的超时秒数,超过指定秒数的缓存数据将会被废弃, 0表示不超时, 默认超时值为60秒 + * + * @return int + */ + int timeout() default 60; +} diff --git a/src/org/redkale/service/RetLabel.java b/src/main/java/org/redkale/service/RetLabel.java similarity index 90% rename from src/org/redkale/service/RetLabel.java rename to src/main/java/org/redkale/service/RetLabel.java index 4d5f60b86..cea8407c8 100644 --- a/src/org/redkale/service/RetLabel.java +++ b/src/main/java/org/redkale/service/RetLabel.java @@ -1,108 +1,114 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.io.*; -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.reflect.*; -import java.util.*; -import java.util.function.BiFunction; -import static org.redkale.boot.Application.*; - -/** - * 用于定义错误码的注解
    - * 结果码定义范围:
    - * // 10000001 - 19999999 预留给Redkale的核心包使用
    - * // 20000001 - 29999999 预留给Redkale的扩展包使用
    - * // 30000001 - 99999999 预留给Dev开发系统自身使用
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({FIELD}) -@Retention(RUNTIME) -@Repeatable(RetLabel.RetLabels.class) -public @interface RetLabel { - - String value(); - - String locale() default ""; - - @Inherited - @Documented - @Target({FIELD}) - @Retention(RUNTIME) - @interface RetLabels { - - RetLabel[] value(); - } - - public static interface RetInfoTransfer extends BiFunction { - - } - - public static abstract class RetLoader { - - public static Map> loadMap(Class clazz) { - final Map> rets = new LinkedHashMap<>(); - ServiceLoader loader = ServiceLoader.load(RetInfoTransfer.class); - Iterator it = loader.iterator(); - RetInfoTransfer func = it.hasNext() ? it.next() : null; - for (Field field : clazz.getFields()) { - if (!Modifier.isStatic(field.getModifiers())) continue; - if (field.getType() != int.class) continue; - RetLabel[] infos = field.getAnnotationsByType(RetLabel.class); - if (infos == null || infos.length == 0) continue; - int value; - try { - value = field.getInt(null); - } catch (Exception ex) { - ex.printStackTrace(); - continue; - } - for (RetLabel info : infos) { - rets.computeIfAbsent(info.locale(), (k) -> new LinkedHashMap<>()).put(value, func == null ? info.value() : func.apply(value, info.value())); - } - } - try { - File propPath = new File(System.getProperty(RESNAME_APP_CONF, new File(System.getProperty(RESNAME_APP_HOME, ""), "conf").getPath())); - if (propPath.isDirectory() && propPath.canRead()) { - final String prefix = clazz.getSimpleName().toLowerCase(); - for (File propFile : propPath.listFiles(f -> f.getName().startsWith(prefix) && f.getName().endsWith(".properties"))) { - if (propFile.isFile() && propFile.canRead()) { - String locale = propFile.getName().substring(prefix.length()).replaceAll("\\.\\d+", ""); - locale = locale.substring(0, locale.indexOf(".properties")); - Map defrets = rets.get(locale); - if (defrets != null) { - InputStreamReader in = new InputStreamReader(new FileInputStream(propFile), "UTF-8"); - Properties prop = new Properties(); - prop.load(in); - in.close(); - prop.forEach((k, v) -> { - int retcode = Integer.parseInt(k.toString()); - if (defrets.containsKey(retcode)) defrets.put(retcode, v.toString()); - }); - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } - return rets; - } - -// @Deprecated -// public static Map load(Class clazz) { -// return loadMap(clazz).computeIfAbsent("", (k) -> new LinkedHashMap<>()); -// } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import org.redkale.util.RedkaleClassLoader; + +import java.io.*; +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.reflect.*; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.BiFunction; +import static org.redkale.boot.Application.*; + +/** + * 用于定义错误码的注解
    + * 结果码定义范围:
    + * // 10000001 - 19999999 预留给Redkale的核心包使用
    + * // 20000001 - 29999999 预留给Redkale的扩展包使用
    + * // 30000001 - 99999999 预留给Dev开发系统自身使用
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +@Repeatable(RetLabel.RetLabels.class) +public @interface RetLabel { + + String value(); + + String locale() default ""; + + @Inherited + @Documented + @Target({FIELD}) + @Retention(RUNTIME) + @interface RetLabels { + + RetLabel[] value(); + } + + public static interface RetInfoTransfer extends BiFunction { + + } + + public static abstract class RetLoader { + + public static Map> loadMap(Class clazz) { + final Map> rets = new LinkedHashMap<>(); + ServiceLoader loader = ServiceLoader.load(RetInfoTransfer.class); + RedkaleClassLoader.putServiceLoader(RetInfoTransfer.class); + Iterator it = loader.iterator(); + RetInfoTransfer func = it.hasNext() ? it.next() : null; + if (func != null) RedkaleClassLoader.putReflectionPublicConstructors(func.getClass(), func.getClass().getName()); + RedkaleClassLoader.putReflectionPublicFields(clazz.getName()); + for (Field field : clazz.getFields()) { + if (!Modifier.isStatic(field.getModifiers())) continue; + if (field.getType() != int.class) continue; + RetLabel[] infos = field.getAnnotationsByType(RetLabel.class); + if (infos == null || infos.length == 0) continue; + int value; + try { + value = field.getInt(null); + } catch (Exception ex) { + ex.printStackTrace(); + continue; + } + for (RetLabel info : infos) { + rets.computeIfAbsent(info.locale(), (k) -> new LinkedHashMap<>()).put(value, func == null ? info.value() : func.apply(value, info.value())); + } + } + try { + File propPath = new File(System.getProperty(RESNAME_APP_CONF, new File(System.getProperty(RESNAME_APP_HOME, ""), "conf").getPath())); + if (propPath.isDirectory() && propPath.canRead()) { + final String prefix = clazz.getSimpleName().toLowerCase(); + for (File propFile : propPath.listFiles(f -> f.getName().startsWith(prefix) && f.getName().endsWith(".properties"))) { + if (propFile.isFile() && propFile.canRead()) { + String locale = propFile.getName().substring(prefix.length()).replaceAll("\\.\\d+", ""); + locale = locale.substring(0, locale.indexOf(".properties")); + Map defrets = rets.get(locale); + if (defrets != null) { + InputStreamReader in = new InputStreamReader(new FileInputStream(propFile), StandardCharsets.UTF_8); + Properties prop = new Properties(); + prop.load(in); + in.close(); + prop.forEach((k, v) -> { + int retcode = Integer.parseInt(k.toString()); + if (defrets.containsKey(retcode)) defrets.put(retcode, v.toString()); + }); + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return rets; + } + +// @Deprecated +// public static Map load(Class clazz) { +// return loadMap(clazz).computeIfAbsent("", (k) -> new LinkedHashMap<>()); +// } + } +} diff --git a/src/org/redkale/service/RetResult.java b/src/main/java/org/redkale/service/RetResult.java similarity index 95% rename from src/org/redkale/service/RetResult.java rename to src/main/java/org/redkale/service/RetResult.java index 1e968dd3c..ef9f0c12f 100644 --- a/src/org/redkale/service/RetResult.java +++ b/src/main/java/org/redkale/service/RetResult.java @@ -1,334 +1,344 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.io.Serializable; -import java.lang.reflect.Type; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.Function; -import org.redkale.convert.*; -import org.redkale.convert.json.*; -import org.redkale.util.*; - -/** - * 通用的结果对象,在常见的HTTP+JSON接口中返回的结果需要含结果码,错误信息,和实体对象。
    - * 结果码定义通常前四位为模块,后四位为操作。
    - * 结果码定义范围:
    - * // 10000001 - 19999999 预留给Redkale的核心包使用
    - * // 20000001 - 29999999 预留给Redkale的扩展包使用
    - * // 30000001 - 99999999 预留给Dev开发系统自身使用
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 结果对象的泛型 - */ -public class RetResult implements Serializable { - - public static final Type TYPE_RET_INTEGER = new TypeToken>() { - }.getType(); - - public static final Type TYPE_RET_LONG = new TypeToken>() { - }.getType(); - - public static final Type TYPE_RET_STRING = new TypeToken>() { - }.getType(); - - //success index = 1 - @ConvertColumn(index = 2) - protected int retcode; - - @ConvertColumn(index = 3) - protected String retinfo; - - @ConvertColumn(index = 4) - protected T result; - - @ConvertColumn(index = 5) - protected Map attach; - - protected Convert convert; - - public RetResult() { - } - - public RetResult(T result) { - this.result = result; - } - - public RetResult(Convert convert, T result) { - this.convert = convert; - this.result = result; - } - - public RetResult(int retcode) { - this.retcode = retcode; - } - - public RetResult(int retcode, String retinfo) { - this.retcode = retcode; - this.retinfo = retinfo; - } - - public RetResult(int retcode, String retinfo, T result) { - this.retcode = retcode; - this.retinfo = retinfo; - this.result = result; - } - - public Convert convert() { - return convert; - } - - public Convert clearConvert() { - Convert c = this.convert; - this.convert = null; - return c; - } - - public RetResult convert(Convert convert) { - this.convert = convert; - return this; - } - - public CompletableFuture> toFuture() { - return CompletableFuture.completedFuture(this); - } - - public CompletableFuture toAnyFuture() { - return CompletableFuture.completedFuture(this); - } - - public static RetResult success() { - return new RetResult(); - } - - public static RetResult success(T result) { - return new RetResult().result(result); - } - - public static CompletableFuture> successFuture() { - return CompletableFuture.completedFuture(new RetResult()); - } - - public static CompletableFuture> successFuture(T result) { - return CompletableFuture.completedFuture(new RetResult(result)); - } - - public static RetResult get(CompletableFuture> future, long timeout, TimeUnit unit) { - try { - return future.get(timeout, unit); - } catch (ExecutionException ex) { - throw new RuntimeException(ex.getCause()); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - public static RetResult> map(String... items) { - return new RetResult(Utility.ofMap(items)); - } - - public static RetResult> map(Object... items) { - return new RetResult(Utility.ofMap(items)); - } - - /** - * 清空result - * - * @param V - * - * @return RetResult - */ - public RetResult clearResult() { - this.result = null; - return (RetResult) this; - } - - /** - * 将RetResult<X> 转换成一个新的 RetResult<Y> - * - * @param 目标数据类型 - * @param mapper 转换函数 - * - * @return RetResult - * - * @since 2.1.0 - */ - public RetResult mapTo(Function mapper) { - return new RetResult<>(mapper.apply(this.result)).convert(this.convert).retcode(this.retcode).retinfo(this.retinfo).attach(this.attach); - } - - /** - * 同 setRetcode - * - * @param retcode retcode - * - * @return RetResult - */ - public RetResult retcode(int retcode) { - this.retcode = retcode; - return this; - } - - /** - * 同 setRetinfo - * - * @param retinfo retinfo - * - * @return RetResult - */ - public RetResult retinfo(String retinfo) { - this.retinfo = retinfo; - return this; - } - - /** - * 同 setResult - * - * @param result result - * - * @return RetResult - */ - public RetResult result(T result) { - this.result = result; - return this; - } - - /** - * 同 setAttach - * - * @param attach attach - * - * @return RetResult - */ - public RetResult attach(Map attach) { - this.attach = attach; - return this; - } - - /** - * attach添加元素 - * - * @param key String - * @param value String - * - * @return RetResult - */ - public RetResult attach(String key, Object value) { - if (this.attach == null) this.attach = new HashMap<>(); - boolean canstr = value != null && (value instanceof CharSequence || value instanceof Number || value.getClass().isPrimitive()); - this.attach.put(key, value == null ? null : (canstr ? String.valueOf(value) : JsonConvert.root().convertTo(value))); - return this; - } - - /** - * 清空attach - * - * - * @return RetResult - */ - public RetResult clearAttach() { - this.attach = null; - return this; - } - - /** - * 结果码 0表示成功、 非0表示错误 - * - * @return 结果码 - */ - public int getRetcode() { - return retcode; - } - - public void setRetcode(int retcode) { - this.retcode = retcode; - } - - /** - * 结果信息,通常retcode != 0时值为错误信息 - * - * @return 结果信息 - */ - public String getRetinfo() { - return retinfo; - } - - /** - * 设置结果信息 - * - * @param retinfo 结果信息 - */ - public void setRetinfo(String retinfo) { - this.retinfo = retinfo; - } - - /** - * 结果附件 - * - * @return 结果附件 - */ - public Map getAttach() { - return attach; - } - - /** - * 设置结果附件 - * - * @param attach Map - */ - public void setAttach(Map attach) { - this.attach = attach; - } - - /** - * 获取附件元素值 - * - * @param name 元素名 - * @param defValue 默认值 - * - * @return 结果值 - */ - public String getAttach(String name, String defValue) { - return attach == null ? defValue : attach.getOrDefault(name, defValue); - } - - /** - * 结果对象, 通常只有在retcode = 0时值才有效 - * - * @return 结果对象 - */ - public T getResult() { - return result; - } - - /** - * 设置结果对象 - * - * @param result T - */ - public void setResult(T result) { - this.result = result; - } - - /** - * 判断结果是否成功返回, retcode = 0 视为成功, 否则视为错误码 - * - * @return 是否成功 - */ - @ConvertColumn(index = 1) - public boolean isSuccess() { - return retcode == 0; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Function; +import javax.persistence.Column; +import org.redkale.convert.*; +import org.redkale.convert.json.*; +import org.redkale.util.*; + +/** + * 通用的结果对象,在常见的HTTP+JSON接口中返回的结果需要含结果码,错误信息,和实体对象。
    + * 结果码定义通常前四位为模块,后四位为操作。
    + * 结果码定义范围:
    + * // 10000001 - 19999999 预留给Redkale的核心包使用
    + * // 20000001 - 29999999 预留给Redkale的扩展包使用
    + * // 30000001 - 99999999 预留给Dev开发系统自身使用
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 结果对象的泛型 + */ +public class RetResult implements Serializable { + + public static final Type TYPE_RET_INTEGER = new TypeToken>() { + }.getType(); + + public static final Type TYPE_RET_LONG = new TypeToken>() { + }.getType(); + + public static final Type TYPE_RET_STRING = new TypeToken>() { + }.getType(); + + //success index = 1 + @ConvertColumn(index = 2) + @Column(nullable = false) + protected int retcode; + + @ConvertColumn(index = 3) + protected String retinfo; + + @ConvertColumn(index = 4) + protected T result; + + @ConvertColumn(index = 5) + @Deprecated //@since 2.5.0 + protected Map attach; + + @ConvertDisabled + protected Convert convert; + + public RetResult() { + } + + public RetResult(T result) { + this.result = result; + } + + public RetResult(Convert convert, T result) { + this.convert = convert; + this.result = result; + } + + public RetResult(int retcode) { + this.retcode = retcode; + } + + public RetResult(int retcode, String retinfo) { + this.retcode = retcode; + this.retinfo = retinfo; + } + + public RetResult(int retcode, String retinfo, T result) { + this.retcode = retcode; + this.retinfo = retinfo; + this.result = result; + } + + public Convert convert() { + return convert; + } + + public Convert clearConvert() { + Convert c = this.convert; + this.convert = null; + return c; + } + + public RetResult convert(Convert convert) { + this.convert = convert; + return this; + } + + public CompletableFuture> toFuture() { + return CompletableFuture.completedFuture(this); + } + + public CompletableFuture toAnyFuture() { + return CompletableFuture.completedFuture(this); + } + + public static RetResult success() { + return new RetResult(); + } + + public static RetResult success(T result) { + return new RetResult().result(result); + } + + public static CompletableFuture> successFuture() { + return CompletableFuture.completedFuture(new RetResult()); + } + + public static CompletableFuture> successFuture(T result) { + return CompletableFuture.completedFuture(new RetResult(result)); + } + + public static RetResult get(CompletableFuture> future, long timeout, TimeUnit unit) { + try { + return future.get(timeout, unit); + } catch (ExecutionException ex) { + throw new RuntimeException(ex.getCause()); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + public static RetResult> map(String... items) { + return new RetResult(Utility.ofMap(items)); + } + + public static RetResult> map(Object... items) { + return new RetResult(Utility.ofMap(items)); + } + + /** + * 清空result + * + * @param V + * + * @return RetResult + */ + public RetResult clearResult() { + this.result = null; + return (RetResult) this; + } + + /** + * 将RetResult<X> 转换成一个新的 RetResult<Y> + * + * @param 目标数据类型 + * @param mapper 转换函数 + * + * @return RetResult + * + * @since 2.1.0 + */ + public RetResult mapTo(Function mapper) { + return new RetResult<>(mapper.apply(this.result)).convert(this.convert).retcode(this.retcode).retinfo(this.retinfo).attach(this.attach); + } + + /** + * 同 setRetcode + * + * @param retcode retcode + * + * @return RetResult + */ + public RetResult retcode(int retcode) { + this.retcode = retcode; + return this; + } + + /** + * 同 setRetinfo + * + * @param retinfo retinfo + * + * @return RetResult + */ + public RetResult retinfo(String retinfo) { + this.retinfo = retinfo; + return this; + } + + /** + * 同 setResult + * + * @param result result + * + * @return RetResult + */ + public RetResult result(T result) { + this.result = result; + return this; + } + + /** + * 同 setAttach + * + * @param attach attach + * + * @return RetResult + */ + @Deprecated + public RetResult attach(Map attach) { + this.attach = attach; + return this; + } + + /** + * attach添加元素 + * + * @param key String + * @param value String + * + * @return RetResult + */ + @Deprecated + public RetResult attach(String key, Object value) { + if (this.attach == null) this.attach = new HashMap<>(); + boolean canstr = value != null && (value instanceof CharSequence || value instanceof Number || value.getClass().isPrimitive()); + this.attach.put(key, value == null ? null : (canstr ? String.valueOf(value) : JsonConvert.root().convertTo(value))); + return this; + } + + /** + * 清空attach + * + * + * @return RetResult + */ + @Deprecated + public RetResult clearAttach() { + this.attach = null; + return this; + } + + /** + * 结果码 0表示成功、 非0表示错误 + * + * @return 结果码 + */ + public int getRetcode() { + return retcode; + } + + public void setRetcode(int retcode) { + this.retcode = retcode; + } + + /** + * 结果信息,通常retcode != 0时值为错误信息 + * + * @return 结果信息 + */ + public String getRetinfo() { + return retinfo; + } + + /** + * 设置结果信息 + * + * @param retinfo 结果信息 + */ + public void setRetinfo(String retinfo) { + this.retinfo = retinfo; + } + + /** + * 结果附件 + * + * @return 结果附件 + */ + @Deprecated + public Map getAttach() { + return attach; + } + + /** + * 设置结果附件 + * + * @param attach Map + */ + @Deprecated + public void setAttach(Map attach) { + this.attach = attach; + } + + /** + * 获取附件元素值 + * + * @param name 元素名 + * @param defValue 默认值 + * + * @return 结果值 + */ + @Deprecated + public String getAttach(String name, String defValue) { + return attach == null ? defValue : attach.getOrDefault(name, defValue); + } + + /** + * 结果对象, 通常只有在retcode = 0时值才有效 + * + * @return 结果对象 + */ + public T getResult() { + return result; + } + + /** + * 设置结果对象 + * + * @param result T + */ + public void setResult(T result) { + this.result = result; + } + + /** + * 判断结果是否成功返回, retcode = 0 视为成功, 否则视为错误码 + * + * @return 是否成功 + */ + @ConvertColumn(index = 1) + public boolean isSuccess() { + return retcode == 0; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + +} diff --git a/src/org/redkale/service/RpcAttachment.java b/src/main/java/org/redkale/service/RpcAttachment.java similarity index 95% rename from src/org/redkale/service/RpcAttachment.java rename to src/main/java/org/redkale/service/RpcAttachment.java index d807f1163..96f86ae74 100644 --- a/src/org/redkale/service/RpcAttachment.java +++ b/src/main/java/org/redkale/service/RpcAttachment.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - -/** - * SNCP协议中用于CompletionHandler回调函数中的attach字段。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER}) -@Retention(RUNTIME) -public @interface RpcAttachment { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; + +/** + * SNCP协议中用于CompletionHandler回调函数中的attach字段。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER}) +@Retention(RUNTIME) +public @interface RpcAttachment { + +} diff --git a/src/org/redkale/service/RpcCall.java b/src/main/java/org/redkale/service/RpcCall.java similarity index 96% rename from src/org/redkale/service/RpcCall.java rename to src/main/java/org/redkale/service/RpcCall.java index b2aa7a6d4..18a7d3beb 100644 --- a/src/org/redkale/service/RpcCall.java +++ b/src/main/java/org/redkale/service/RpcCall.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import org.redkale.util.*; - -/** - * 参数回写, 当Service的方法需要更改参数对象内部的数据时,需要使用RpcCall - * - *

    详情见: https://redkale.org - * @author zhangjx - */ -@Inherited -@Documented -@Target({ElementType.PARAMETER}) -@Retention(RUNTIME) -public @interface RpcCall { - - Class value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import org.redkale.util.*; + +/** + * 参数回写, 当Service的方法需要更改参数对象内部的数据时,需要使用RpcCall + * + *

    详情见: https://redkale.org + * @author zhangjx + */ +@Inherited +@Documented +@Target({ElementType.PARAMETER}) +@Retention(RUNTIME) +public @interface RpcCall { + + Class value(); +} diff --git a/src/org/redkale/service/RpcCallArrayAttribute.java b/src/main/java/org/redkale/service/RpcCallArrayAttribute.java similarity index 96% rename from src/org/redkale/service/RpcCallArrayAttribute.java rename to src/main/java/org/redkale/service/RpcCallArrayAttribute.java index 633a473bc..d1c2ea5eb 100644 --- a/src/org/redkale/service/RpcCallArrayAttribute.java +++ b/src/main/java/org/redkale/service/RpcCallArrayAttribute.java @@ -1,63 +1,63 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.io.Serializable; -import java.lang.reflect.Array; -import org.redkale.util.Attribute; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - * @param 对象类型 - * @param 字段类型 - */ -@SuppressWarnings("unchecked") -public class RpcCallArrayAttribute implements Attribute { - - public static final RpcCallArrayAttribute instance = new RpcCallArrayAttribute(); - - @Override - public Class type() { - return (Class) Object.class; - } - - @Override - public Class declaringClass() { - return (Class) (Class) Object[].class; - } - - @Override - public String field() { - return ""; - } - - @Override - public F get(final T[] objs) { - if (objs == null || objs.length == 0) return null; - final Attribute attr = RpcCallAttribute.load(objs[0].getClass()); - final Object keys = Array.newInstance(attr.type(), objs.length); - for (int i = 0; i < objs.length; i++) { - Array.set(keys, i, attr.get(objs[i])); - } - return (F) keys; - } - - @Override - public void set(final T[] objs, final F keys) { - if (objs == null || objs.length == 0) return; - final Attribute attr = RpcCallAttribute.load(objs[0].getClass()); - for (int i = 0; i < objs.length; i++) { - attr.set(objs[i], (Serializable) Array.get(keys, i)); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.io.Serializable; +import java.lang.reflect.Array; +import org.redkale.util.Attribute; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + * @param 对象类型 + * @param 字段类型 + */ +@SuppressWarnings("unchecked") +public class RpcCallArrayAttribute implements Attribute { + + public static final RpcCallArrayAttribute instance = new RpcCallArrayAttribute(); + + @Override + public Class type() { + return (Class) Object.class; + } + + @Override + public Class declaringClass() { + return (Class) (Class) Object[].class; + } + + @Override + public String field() { + return ""; + } + + @Override + public F get(final T[] objs) { + if (objs == null || objs.length == 0) return null; + final Attribute attr = RpcCallAttribute.load(objs[0].getClass()); + final Object keys = Array.newInstance(attr.type(), objs.length); + for (int i = 0; i < objs.length; i++) { + Array.set(keys, i, attr.get(objs[i])); + } + return (F) keys; + } + + @Override + public void set(final T[] objs, final F keys) { + if (objs == null || objs.length == 0) return; + final Attribute attr = RpcCallAttribute.load(objs[0].getClass()); + for (int i = 0; i < objs.length; i++) { + attr.set(objs[i], (Serializable) Array.get(keys, i)); + } + } + +} diff --git a/src/org/redkale/service/RpcCallAttribute.java b/src/main/java/org/redkale/service/RpcCallAttribute.java similarity index 96% rename from src/org/redkale/service/RpcCallAttribute.java rename to src/main/java/org/redkale/service/RpcCallAttribute.java index e8e760e14..592342e5e 100644 --- a/src/org/redkale/service/RpcCallAttribute.java +++ b/src/main/java/org/redkale/service/RpcCallAttribute.java @@ -1,77 +1,77 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.io.Serializable; -import java.lang.reflect.*; -import java.util.concurrent.ConcurrentHashMap; -import org.redkale.util.Attribute; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class RpcCallAttribute implements Attribute { - - public static final RpcCallAttribute instance = new RpcCallAttribute(); - - private static final ConcurrentHashMap attributes = new ConcurrentHashMap<>(); - - static Attribute load(final Class clazz) { - Attribute rs = attributes.get(clazz); - if (rs != null) return rs; - synchronized (attributes) { - rs = attributes.get(clazz); - if (rs == null) { - Class cltmp = clazz; - do { - for (Field field : cltmp.getDeclaredFields()) { - try { - rs = Attribute.create(cltmp, field); - attributes.put(clazz, rs); - return rs; - } catch (RuntimeException e) { - } - } - } while ((cltmp = cltmp.getSuperclass()) != Object.class); - } - return rs; - } - } - - @Override - public Class type() { - return Serializable.class; - } - - @Override - public Class declaringClass() { - return Object.class; - } - - @Override - public String field() { - return ""; - } - - @Override - public Serializable get(final Object obj) { - if (obj == null) return null; - return load(obj.getClass()).get(obj); - } - - @Override - public void set(final Object obj, final Serializable key) { - if (obj == null) return; - load(obj.getClass()).set(obj, key); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.io.Serializable; +import java.lang.reflect.*; +import java.util.concurrent.ConcurrentHashMap; +import org.redkale.util.Attribute; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class RpcCallAttribute implements Attribute { + + public static final RpcCallAttribute instance = new RpcCallAttribute(); + + private static final ConcurrentHashMap attributes = new ConcurrentHashMap<>(); + + static Attribute load(final Class clazz) { + Attribute rs = attributes.get(clazz); + if (rs != null) return rs; + synchronized (attributes) { + rs = attributes.get(clazz); + if (rs == null) { + Class cltmp = clazz; + do { + for (Field field : cltmp.getDeclaredFields()) { + try { + rs = Attribute.create(cltmp, field); + attributes.put(clazz, rs); + return rs; + } catch (RuntimeException e) { + } + } + } while ((cltmp = cltmp.getSuperclass()) != Object.class); + } + return rs; + } + } + + @Override + public Class type() { + return Serializable.class; + } + + @Override + public Class declaringClass() { + return Object.class; + } + + @Override + public String field() { + return ""; + } + + @Override + public Serializable get(final Object obj) { + if (obj == null) return null; + return load(obj.getClass()).get(obj); + } + + @Override + public void set(final Object obj, final Serializable key) { + if (obj == null) return; + load(obj.getClass()).set(obj, key); + } + +} diff --git a/src/org/redkale/service/RpcRemote.java b/src/main/java/org/redkale/service/RpcRemote.java similarity index 96% rename from src/org/redkale/service/RpcRemote.java rename to src/main/java/org/redkale/service/RpcRemote.java index 0f1f500bd..042bd1455 100644 --- a/src/org/redkale/service/RpcRemote.java +++ b/src/main/java/org/redkale/service/RpcRemote.java @@ -1,24 +1,24 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 用于在 Service 中创建自身远程模式的对象 - * - *

    详情见: https://redkale.org - * @author zhangjx - */ -@Inherited -@Documented -@Target({FIELD}) -@Retention(RUNTIME) -public @interface RpcRemote { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 用于在 Service 中创建自身远程模式的对象 + * + *

    详情见: https://redkale.org + * @author zhangjx + */ +@Inherited +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +public @interface RpcRemote { + +} diff --git a/src/org/redkale/service/RpcRemoteException.java b/src/main/java/org/redkale/service/RpcRemoteException.java similarity index 95% rename from src/org/redkale/service/RpcRemoteException.java rename to src/main/java/org/redkale/service/RpcRemoteException.java index 61ee4a4a2..e4757a502 100644 --- a/src/org/redkale/service/RpcRemoteException.java +++ b/src/main/java/org/redkale/service/RpcRemoteException.java @@ -1,33 +1,33 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -/** - * 供RPC协议使用 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class RpcRemoteException extends RuntimeException { - - public RpcRemoteException() { - super(); - } - - public RpcRemoteException(String s) { - super(s); - } - - public RpcRemoteException(String message, Throwable cause) { - super(message, cause); - } - - public RpcRemoteException(Throwable cause) { - super(cause); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +/** + * 供RPC协议使用 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class RpcRemoteException extends RuntimeException { + + public RpcRemoteException() { + super(); + } + + public RpcRemoteException(String s) { + super(s); + } + + public RpcRemoteException(String message, Throwable cause) { + super(message, cause); + } + + public RpcRemoteException(Throwable cause) { + super(cause); + } +} diff --git a/src/org/redkale/service/RpcSourceAddress.java b/src/main/java/org/redkale/service/RpcSourceAddress.java similarity index 96% rename from src/org/redkale/service/RpcSourceAddress.java rename to src/main/java/org/redkale/service/RpcSourceAddress.java index 8fa45861b..5713fa3d8 100644 --- a/src/org/redkale/service/RpcSourceAddress.java +++ b/src/main/java/org/redkale/service/RpcSourceAddress.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * SNCP协议中标记为来源地址参数, 该注解只能标记在类型为SocketAddress或InetSocketAddress的参数上。 - * - * - * 详情见: https://redkale.org - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER}) -@Retention(RUNTIME) -public @interface RpcSourceAddress { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * SNCP协议中标记为来源地址参数, 该注解只能标记在类型为SocketAddress或InetSocketAddress的参数上。 + * + * + * 详情见: https://redkale.org + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER}) +@Retention(RUNTIME) +public @interface RpcSourceAddress { + +} diff --git a/src/org/redkale/service/RpcTargetAddress.java b/src/main/java/org/redkale/service/RpcTargetAddress.java similarity index 96% rename from src/org/redkale/service/RpcTargetAddress.java rename to src/main/java/org/redkale/service/RpcTargetAddress.java index 75abcb5de..c32ffaa43 100644 --- a/src/org/redkale/service/RpcTargetAddress.java +++ b/src/main/java/org/redkale/service/RpcTargetAddress.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * SNCP协议中标记为目标地址参数, 该注解只能标记在类型为SocketAddress或InetSocketAddress的参数上。 - * - * - * 详情见: https://redkale.org - * @author zhangjx - */ -@Inherited -@Documented -@Target({PARAMETER}) -@Retention(RUNTIME) -public @interface RpcTargetAddress { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * SNCP协议中标记为目标地址参数, 该注解只能标记在类型为SocketAddress或InetSocketAddress的参数上。 + * + * + * 详情见: https://redkale.org + * @author zhangjx + */ +@Inherited +@Documented +@Target({PARAMETER}) +@Retention(RUNTIME) +public @interface RpcTargetAddress { + +} diff --git a/src/org/redkale/service/RpcTargetTopic.java b/src/main/java/org/redkale/service/RpcTargetTopic.java similarity index 95% rename from src/org/redkale/service/RpcTargetTopic.java rename to src/main/java/org/redkale/service/RpcTargetTopic.java index b94cf8a25..a2fc66c45 100644 --- a/src/org/redkale/service/RpcTargetTopic.java +++ b/src/main/java/org/redkale/service/RpcTargetTopic.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.PARAMETER; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * SNCP协议中标记为目标topic参数, 该注解只能标记在类型为String的参数上。 - * - * - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -@Inherited -@Documented -@Target({PARAMETER}) -@Retention(RUNTIME) -public @interface RpcTargetTopic { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * SNCP协议中标记为目标topic参数, 该注解只能标记在类型为String的参数上。 + * + * + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +@Inherited +@Documented +@Target({PARAMETER}) +@Retention(RUNTIME) +public @interface RpcTargetTopic { + +} diff --git a/src/org/redkale/service/Service.java b/src/main/java/org/redkale/service/Service.java similarity index 97% rename from src/org/redkale/service/Service.java rename to src/main/java/org/redkale/service/Service.java index 69e65cf33..cdd5150ec 100644 --- a/src/org/redkale/service/Service.java +++ b/src/main/java/org/redkale/service/Service.java @@ -1,60 +1,60 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import org.redkale.util.*; - -/** - * 所有Service的实现类不得声明为final, 允许远程模式的public方法都不能声明为final。
    - * 注意: "$"是一个很特殊的Service.name值 。 被标记为@Resource(name = "$") 的Service的资源名与所属父Service的资源名一致。
    - * - *

    - * Service的资源类型
    - * 业务逻辑的Service通常有两种编写方式:
    - *    1、只写一个Service实现类。
    - *    2、先定义业务的Service接口或抽象类,再编写具体实现类。
    - * 第二种方式需要在具体实现类上使用@ResourceType指明资源注入的类型。
    - * 
    - * - *
    - * 异步方法:
    - * Service编写异步方法:
    - *    1、异步方法有且仅有一个类型为CompletionHandler的参数, 返回类型必须是void。若参数类型为CompletionHandler子类,必须保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
    - *    2、异步方法返回类型是CompletableFuture。
    - * 例如:
    - *      public void insertRecord(CompletionHandler<Integer, Record> handler, String name, @RpcAttachment Record record);
    - *
    - * 
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface Service { - - /** - * 该方法必须是可以重复调用, 当reload时需要重复调用init方法 - * 远程模式下该方法会重载成空方法 - * - * @param config 配置参数 - */ - default void init(AnyValue config) { - - } - - /** - * 进程退出时,调用Service销毁 - * 远程模式下该方法会重载成空方法 - * 注意: 在此方法内不能调用MessageClient.sendMessage 方法,因为Application关闭时会先destroy掉MessageClient - * - * @param config 配置参数 - */ - default void destroy(AnyValue config) { - - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import org.redkale.util.*; + +/** + * 所有Service的实现类不得声明为final, 允许远程模式的public方法都不能声明为final。
    + * 注意: "$"是一个很特殊的Service.name值 。 被标记为@Resource(name = "$") 的Service的资源名与所属父Service的资源名一致。
    + * + *

    + * Service的资源类型
    + * 业务逻辑的Service通常有两种编写方式:
    + *    1、只写一个Service实现类。
    + *    2、先定义业务的Service接口或抽象类,再编写具体实现类。
    + * 第二种方式需要在具体实现类上使用@ResourceType指明资源注入的类型。
    + * 
    + * + *
    + * 异步方法:
    + * Service编写异步方法:
    + *    1、异步方法有且仅有一个类型为CompletionHandler的参数, 返回类型必须是void。若参数类型为CompletionHandler子类,必须保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。
    + *    2、异步方法返回类型是CompletableFuture。
    + * 例如:
    + *      public void insertRecord(CompletionHandler<Integer, Record> handler, String name, @RpcAttachment Record record);
    + *
    + * 
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface Service { + + /** + * 该方法必须是可以重复调用, 当reload时需要重复调用init方法 + * 远程模式下该方法会重载成空方法 + * + * @param config 配置参数 + */ + default void init(AnyValue config) { + + } + + /** + * 进程退出时,调用Service销毁 + * 远程模式下该方法会重载成空方法 + * 注意: 在此方法内不能调用MessageClient.sendMessage 方法,因为Application关闭时会先destroy掉MessageClient + * + * @param config 配置参数 + */ + default void destroy(AnyValue config) { + + } + +} diff --git a/src/org/redkale/service/WebSocketNodeService.java b/src/main/java/org/redkale/service/WebSocketNodeService.java similarity index 97% rename from src/org/redkale/service/WebSocketNodeService.java rename to src/main/java/org/redkale/service/WebSocketNodeService.java index 21bbad7ae..4e5539d55 100644 --- a/src/org/redkale/service/WebSocketNodeService.java +++ b/src/main/java/org/redkale/service/WebSocketNodeService.java @@ -1,166 +1,166 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.service; - -import static org.redkale.net.http.WebSocket.*; -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.Level; -import org.redkale.net.http.*; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@AutoLoad(false) -@ResourceType(WebSocketNode.class) -public class WebSocketNodeService extends WebSocketNode implements Service { - - @Override - public void init(AnyValue conf) { - super.init(conf); - } - - @Override - public void destroy(AnyValue conf) { - super.destroy(conf); - } - - public final void setName(String name) { - this.name = name; - } - - @Override - public CompletableFuture> getWebSocketAddresses(@RpcTargetTopic String topic, final @RpcTargetAddress InetSocketAddress targetAddress, final Serializable groupid) { - if ((topic == null || !topic.equals(this.wsNodeAddress.getTopic())) && (localSncpAddress == null || !localSncpAddress.equals(targetAddress))) return remoteWebSocketAddresses(topic, targetAddress, groupid); - if (this.localEngine == null) return CompletableFuture.completedFuture(new ArrayList<>()); - final List rs = new ArrayList<>(); - this.localEngine.getLocalWebSockets(groupid).forEach(x -> rs.add(x.getRemoteAddr())); - return CompletableFuture.completedFuture(rs); - } - - @Override - public CompletableFuture sendMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids) { - if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - return this.localEngine.sendLocalMessage(message, last, userids); - } - - @Override - public CompletableFuture broadcastMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, final WebSocketRange wsrange, Object message, boolean last) { - if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - return this.localEngine.broadcastLocalMessage(wsrange, message, last); - } - - @Override - public CompletableFuture sendAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, final WebSocketAction action, Serializable... userids) { - if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - return this.localEngine.sendLocalAction(action, userids); - } - - @Override - public CompletableFuture broadcastAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, final WebSocketAction action) { - if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); - return this.localEngine.broadcastLocalAction(action); - } - - @Override - public CompletableFuture getUserSize(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress) { - if (this.localEngine == null) return CompletableFuture.completedFuture(0); - return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); - } - - /** - * 当用户连接到节点,需要更新到CacheSource - * - * @param userid Serializable - * @param wsaddr WebSocketAddress - * - * @return 无返回值 - */ - @Override - public CompletableFuture connect(Serializable userid, WebSocketAddress wsaddr) { - tryAcquireSemaphore(); - CompletableFuture future = source.appendSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class, wsaddr); - if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore()); - if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + wsaddr); - return future; - } - - /** - * 当用户从一个节点断掉了所有的连接,需要从CacheSource中删除 - * - * @param userid Serializable - * @param wsaddr WebSocketAddress - * - * @return 无返回值 - */ - @Override - public CompletableFuture disconnect(Serializable userid, WebSocketAddress wsaddr) { - tryAcquireSemaphore(); - CompletableFuture future = source.removeSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class, wsaddr); - if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore()); - if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + wsaddr); - return future.thenApply(v -> null); - } - - /** - * 更改用户ID,需要更新到CacheSource - * - * @param olduserid Serializable - * @param newuserid Serializable - * @param wsaddr WebSocketAddress - * - * @return 无返回值 - */ - @Override - public CompletableFuture changeUserid(Serializable olduserid, Serializable newuserid, WebSocketAddress wsaddr) { - tryAcquireSemaphore(); - CompletableFuture future = source.appendSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + newuserid, WebSocketAddress.class, wsaddr); - future = future.thenAccept((a) -> source.removeSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + olduserid, WebSocketAddress.class, wsaddr)); - if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore()); - if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + olduserid + " changeUserid to " + newuserid + " from " + wsaddr); - return future; - } - - /** - * 判断用户是否有WebSocket - * - * @param userid Serializable - * @param topic RpcTargetTopic - * @param targetAddress InetSocketAddress - * - * @return 无返回值 - */ - @Override - public CompletableFuture existsWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress) { - if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " existsWebSocket from " + targetAddress); - if (localEngine == null) return CompletableFuture.completedFuture(false); - return CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid)); - } - - /** - * 强制关闭用户的WebSocket - * - * @param userid Serializable - * @param topic RpcTargetTopic - * @param targetAddress InetSocketAddress - * - * @return 无返回值 - */ - @Override - public CompletableFuture forceCloseWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress) { - //不能从sncpNodeAddresses中移除,因为engine.forceCloseWebSocket 会调用到disconnect - if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " forceCloseWebSocket from " + targetAddress); - if (localEngine == null) return CompletableFuture.completedFuture(0); - return CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid)); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.service; + +import static org.redkale.net.http.WebSocket.*; +import java.io.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.logging.Level; +import org.redkale.net.http.*; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@AutoLoad(false) +@ResourceType(WebSocketNode.class) +public class WebSocketNodeService extends WebSocketNode implements Service { + + @Override + public void init(AnyValue conf) { + super.init(conf); + } + + @Override + public void destroy(AnyValue conf) { + super.destroy(conf); + } + + public final void setName(String name) { + this.name = name; + } + + @Override + public CompletableFuture> getWebSocketAddresses(@RpcTargetTopic String topic, final @RpcTargetAddress InetSocketAddress targetAddress, final Serializable groupid) { + if ((topic == null || !topic.equals(this.wsNodeAddress.getTopic())) && (localSncpAddress == null || !localSncpAddress.equals(targetAddress))) return remoteWebSocketAddresses(topic, targetAddress, groupid); + if (this.localEngine == null) return CompletableFuture.completedFuture(new ArrayList<>()); + final List rs = new ArrayList<>(); + this.localEngine.getLocalWebSockets(groupid).forEach(x -> rs.add(x.getRemoteAddr())); + return CompletableFuture.completedFuture(rs); + } + + @Override + public CompletableFuture sendMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, Object message, boolean last, Serializable... userids) { + if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + return this.localEngine.sendLocalMessage(message, last, userids); + } + + @Override + public CompletableFuture broadcastMessage(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, final WebSocketRange wsrange, Object message, boolean last) { + if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + return this.localEngine.broadcastLocalMessage(wsrange, message, last); + } + + @Override + public CompletableFuture sendAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, final WebSocketAction action, Serializable... userids) { + if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + return this.localEngine.sendLocalAction(action, userids); + } + + @Override + public CompletableFuture broadcastAction(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress, final WebSocketAction action) { + if (this.localEngine == null) return CompletableFuture.completedFuture(RETCODE_GROUP_EMPTY); + return this.localEngine.broadcastLocalAction(action); + } + + @Override + public CompletableFuture getUserSize(@RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress) { + if (this.localEngine == null) return CompletableFuture.completedFuture(0); + return CompletableFuture.completedFuture(this.localEngine.getLocalUserSize()); + } + + /** + * 当用户连接到节点,需要更新到CacheSource + * + * @param userid Serializable + * @param wsaddr WebSocketAddress + * + * @return 无返回值 + */ + @Override + public CompletableFuture connect(Serializable userid, WebSocketAddress wsaddr) { + tryAcquireSemaphore(); + CompletableFuture future = source.appendSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class, wsaddr); + if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore()); + if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " connect from " + wsaddr); + return future; + } + + /** + * 当用户从一个节点断掉了所有的连接,需要从CacheSource中删除 + * + * @param userid Serializable + * @param wsaddr WebSocketAddress + * + * @return 无返回值 + */ + @Override + public CompletableFuture disconnect(Serializable userid, WebSocketAddress wsaddr) { + tryAcquireSemaphore(); + CompletableFuture future = source.removeSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + userid, WebSocketAddress.class, wsaddr); + if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore()); + if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " disconnect from " + wsaddr); + return future.thenApply(v -> null); + } + + /** + * 更改用户ID,需要更新到CacheSource + * + * @param olduserid Serializable + * @param newuserid Serializable + * @param wsaddr WebSocketAddress + * + * @return 无返回值 + */ + @Override + public CompletableFuture changeUserid(Serializable olduserid, Serializable newuserid, WebSocketAddress wsaddr) { + tryAcquireSemaphore(); + CompletableFuture future = source.appendSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + newuserid, WebSocketAddress.class, wsaddr); + future = future.thenAccept((a) -> source.removeSetItemAsync(WS_SOURCE_KEY_USERID_PREFIX + olduserid, WebSocketAddress.class, wsaddr)); + if (semaphore != null) future.whenComplete((r, e) -> releaseSemaphore()); + if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + olduserid + " changeUserid to " + newuserid + " from " + wsaddr); + return future; + } + + /** + * 判断用户是否有WebSocket + * + * @param userid Serializable + * @param topic RpcTargetTopic + * @param targetAddress InetSocketAddress + * + * @return 无返回值 + */ + @Override + public CompletableFuture existsWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress) { + if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " existsWebSocket from " + targetAddress); + if (localEngine == null) return CompletableFuture.completedFuture(false); + return CompletableFuture.completedFuture(localEngine.existsLocalWebSocket(userid)); + } + + /** + * 强制关闭用户的WebSocket + * + * @param userid Serializable + * @param topic RpcTargetTopic + * @param targetAddress InetSocketAddress + * + * @return 无返回值 + */ + @Override + public CompletableFuture forceCloseWebSocket(Serializable userid, @RpcTargetTopic String topic, @RpcTargetAddress InetSocketAddress targetAddress) { + //不能从sncpNodeAddresses中移除,因为engine.forceCloseWebSocket 会调用到disconnect + if (logger.isLoggable(Level.FINEST)) logger.finest(WebSocketNodeService.class.getSimpleName() + ".event: " + userid + " forceCloseWebSocket from " + targetAddress); + if (localEngine == null) return CompletableFuture.completedFuture(0); + return CompletableFuture.completedFuture(localEngine.forceCloseLocalWebSocket(userid)); + } +} diff --git a/src/org/redkale/service/package-info.java b/src/main/java/org/redkale/service/package-info.java similarity index 94% rename from src/org/redkale/service/package-info.java rename to src/main/java/org/redkale/service/package-info.java index 0bf67060d..37b2e9cf7 100644 --- a/src/org/redkale/service/package-info.java +++ b/src/main/java/org/redkale/service/package-info.java @@ -1,4 +1,4 @@ -/** - * Service接口和模式配置包 - */ -package org.redkale.service; +/** + * Service接口和模式配置包 + */ +package org.redkale.service; diff --git a/src/main/java/org/redkale/source/AbstractDataSource.java b/src/main/java/org/redkale/source/AbstractDataSource.java new file mode 100644 index 000000000..f5c60b6a4 --- /dev/null +++ b/src/main/java/org/redkale/source/AbstractDataSource.java @@ -0,0 +1,905 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.*; +import java.util.stream.Stream; +import javax.persistence.Entity; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * DataSource的S抽象实现类
    + * 注意: 所有的操作只能作用在一张表上,不能同时变更多张表 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +@Local +@AutoLoad(false) +@SuppressWarnings("unchecked") +@ResourceType(DataSource.class) +public abstract class AbstractDataSource extends AbstractService implements DataSource, AutoCloseable, Resourcable { + + protected boolean isOnlyCache(EntityInfo info) { + return info.isVirtualEntity(); + } + + protected boolean isCacheUseable(FilterNode node, Function entityApplyer) { + return node.isCacheUseable(entityApplyer); + } + + protected Predicate createPredicate(FilterNode node, EntityCache cache) { + return node.createPredicate(cache); + } + + protected T getEntityValue(EntityInfo info, final SelectColumn sels, final EntityInfo.DataResultSetRow row) { + return sels == null ? info.getFullEntityValue(row) : info.getEntityValue(sels, row); + } + + protected T getEntityValue(EntityInfo info, final Attribute[] constructorAttrs, final Attribute[] unconstructorAttrs, final EntityInfo.DataResultSetRow row) { + return info.getEntityValue(constructorAttrs, unconstructorAttrs, row); + } + + protected String createSQLOrderby(EntityInfo info, Flipper flipper) { + return info.createSQLOrderby(flipper); + } + + protected Map getJoinTabalis(FilterNode node) { + return node == null ? null : node.getJoinTabalis(); + } + + protected EntityInfo loadEntityInfo(Class clazz, final boolean cacheForbidden, final Properties props, BiFunction> fullloader) { + return EntityInfo.load(clazz, cacheForbidden, props, this, fullloader); + } + + //检查对象是否都是同一个Entity类 + protected CompletableFuture checkEntity(String action, boolean async, T... entitys) { + if (entitys.length < 1) return null; + Class clazz = null; + for (T val : entitys) { + if (clazz == null) { + clazz = val.getClass(); + if (clazz.getAnnotation(Entity.class) == null) throw new RuntimeException("Entity Class " + clazz + " must be on annotation @Entity"); + continue; + } + if (clazz != val.getClass()) { + if (async) { + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(new RuntimeException("DataSource." + action + " must the same Class Entity, but diff is " + clazz + " and " + val.getClass())); + return future; + } + throw new RuntimeException("DataSource." + action + " must the same Class Entity, but diff is " + clazz + " and " + val.getClass()); + } + } + return null; + } + + @Override + public final int insert(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return 0; + return insert(entitys.toArray()); + } + + @Override + public final int insert(final Stream entitys) { + if (entitys == null) return 0; + return insert(entitys.toArray()); + } + + @Override + public final CompletableFuture insertAsync(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); + return insertAsync(entitys.toArray()); + } + + @Override + public final CompletableFuture insertAsync(final Stream entitys) { + if (entitys == null) return CompletableFuture.completedFuture(0); + return insertAsync(entitys.toArray()); + } + + @Override + public CompletableFuture clearTableAsync(final Class clazz) { + return clearTableAsync(clazz, (FilterNode) null); + } + + @Override + public int dropTable(Class clazz) { + return dropTable(clazz, (FilterNode) null); + } + + @Override + public CompletableFuture dropTableAsync(final Class clazz) { + return dropTableAsync(clazz, (FilterNode) null); + } + + /** + * 根据主键值更新对象的多个column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * @param values 字段值 + * + * @return 更新的数据条数 + */ + @Override + public int updateColumn(final Class clazz, final FilterNode node, final ColumnValue... values) { + return updateColumn(clazz, node, null, values); + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final ColumnValue... values) { + return updateColumnAsync(clazz, node, null, values); + } + + @Override + public int updateColumn(final T entity, final String... columns) { + return updateColumn(entity, SelectColumn.includes(columns)); + } + + @Override + public CompletableFuture updateColumnAsync(final T entity, final String... columns) { + return updateColumnAsync(entity, SelectColumn.includes(columns)); + } + + @Override + public int updateColumn(final T entity, final FilterNode node, final String... columns) { + return updateColumn(entity, node, SelectColumn.includes(columns)); + } + + @Override + public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final String... columns) { + return updateColumnAsync(entity, node, SelectColumn.includes(columns)); + } + + @Override + public Map getNumberMap(final Class entityClass, final FilterFuncColumn... columns) { + return getNumberMap(entityClass, (FilterNode) null, columns); + } + + @Override + public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterFuncColumn... columns) { + return getNumberMapAsync(entityClass, (FilterNode) null, columns); + } + + @Override + public Map getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { + return getNumberMap(entityClass, FilterNodeBean.createFilterNode(bean), columns); + } + + @Override + public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { + return getNumberMapAsync(entityClass, FilterNodeBean.createFilterNode(bean), columns); + } + + @Override + public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column) { + return getNumberResult(entityClass, func, null, column, (FilterNode) null); + } + + @Override + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column) { + return getNumberResultAsync(entityClass, func, null, column, (FilterNode) null); + } + + @Override + public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, FilterBean bean) { + return getNumberResult(entityClass, func, null, column, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterBean bean) { + return getNumberResultAsync(entityClass, func, null, column, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { + return getNumberResult(entityClass, func, null, column, node); + } + + @Override + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { + return getNumberResultAsync(entityClass, func, null, column, node); + } + + @Override + public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { + return getNumberResult(entityClass, func, defVal, column, (FilterNode) null); + } + + @Override + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { + return getNumberResultAsync(entityClass, func, defVal, column, (FilterNode) null); + } + + @Override + public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, FilterBean bean) { + return getNumberResult(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, FilterBean bean) { + return getNumberResultAsync(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); + } + + //------------------------ queryColumnMapCompose ------------------------ + @Override + public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { + return queryColumnMap(entityClass, keyColumn, func, funcColumn, (FilterNode) null); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { + return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, (FilterNode) null); + } + + @Override + public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { + return queryColumnMap(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { + return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { + return queryColumnMap(entityClass, funcNodes, groupByColumn, (FilterNode) null); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, (FilterNode) null); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { + return queryColumnMap(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { + return queryColumnMap(entityClass, funcNodes, groupByColumns, (FilterNode) null); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, (FilterNode) null); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { + return queryColumnMap(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); + } + + //----------------------------- findCompose ----------------------------- + /** + * 根据主键获取对象 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param pk 主键值 + * + * @return Entity对象 + */ + @Override + public T find(Class clazz, Serializable pk) { + return find(clazz, (SelectColumn) null, pk); + } + + @Override + public CompletableFuture findAsync(final Class clazz, final Serializable pk) { + return findAsync(clazz, (SelectColumn) null, pk); + } + + @Override + public T find(final Class clazz, final String column, final Serializable colval) { + return find(clazz, null, FilterNode.create(column, colval)); + } + + @Override + public CompletableFuture findAsync(final Class clazz, final String column, final Serializable colval) { + return findAsync(clazz, null, FilterNode.create(column, colval)); + } + + @Override + public T find(final Class clazz, final FilterBean bean) { + return find(clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture findAsync(final Class clazz, final FilterBean bean) { + return findAsync(clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public T find(final Class clazz, final FilterNode node) { + return find(clazz, null, node); + } + + @Override + public CompletableFuture findAsync(final Class clazz, final FilterNode node) { + return findAsync(clazz, null, node); + } + + @Override + public T find(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return find(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return findAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final Serializable pk) { + return findColumn(clazz, column, null, pk); + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable pk) { + return findColumnAsync(clazz, column, null, pk); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final FilterBean bean) { + return findColumn(clazz, column, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterBean bean) { + return findColumnAsync(clazz, column, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final FilterNode node) { + return findColumn(clazz, column, null, node); + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterNode node) { + return findColumnAsync(clazz, column, null, node); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { + return findColumn(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { + return findColumnAsync(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public boolean exists(final Class clazz, final FilterBean bean) { + return exists(clazz, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture existsAsync(final Class clazz, final FilterBean bean) { + return existsAsync(clazz, FilterNodeBean.createFilterNode(bean)); + } + + //-----------------------list set---------------------------- + @Override + public Set queryColumnSet(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { + return queryColumnSet(selectedColumn, clazz, null, FilterNode.create(column, colval)); + } + + @Override + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { + return queryColumnSetAsync(selectedColumn, clazz, null, FilterNode.create(column, colval)); + } + + @Override + public Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnSet(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnSetAsync(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnSet(selectedColumn, clazz, null, node); + } + + @Override + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnSetAsync(selectedColumn, clazz, null, node); + } + + @Override + public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public List queryColumnList(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { + return queryColumnList(selectedColumn, clazz, null, FilterNode.create(column, colval)); + } + + @Override + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { + return queryColumnListAsync(selectedColumn, clazz, null, FilterNode.create(column, colval)); + } + + @Override + public List queryColumnList(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnList(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnListAsync(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public List queryColumnList(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnList(selectedColumn, clazz, null, node); + } + + @Override + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnListAsync(selectedColumn, clazz, null, node); + } + + @Override + public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnList(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnListAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 根据指定参数查询对象某个字段的集合 + *

    + * @param Entity类的泛型 + * @param 字段值的类型 + * @param selectedColumn 字段名 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤Bean + * + * @return 字段集合 + */ + @Override + public Sheet queryColumnSheet(final String selectedColumn, Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSheet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSheetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param keyStream 主键Stream + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final Stream keyStream) { + return queryMap(clazz, null, keyStream); + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final Stream keyStream) { + return queryMapAsync(clazz, null, keyStream); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param bean FilterBean + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final FilterBean bean) { + return queryMap(clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final FilterBean bean) { + return queryMapAsync(clazz, null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param node FilterNode + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final FilterNode node) { + return queryMap(clazz, null, node); + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final FilterNode node) { + return queryMapAsync(clazz, null, node); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean FilterBean + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return queryMap(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return queryMapAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 根据指定字段值查询对象集合 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity对象的集合 + */ + @Override + public Set querySet(final Class clazz, final String column, final Serializable colval) { + return querySet(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final String column, final Serializable colval) { + return querySetAsync(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); + } + + @Override + public Set querySet(final Class clazz) { + return querySet(clazz, (SelectColumn) null, null, (FilterNode) null); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz) { + return querySetAsync(clazz, (SelectColumn) null, null, (FilterNode) null); + } + + /** + * 根据过滤对象FilterBean查询对象集合 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param bean 过滤Bean + * + * @return Entity对象集合 + */ + @Override + public Set querySet(final Class clazz, final FilterBean bean) { + return querySet(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final FilterBean bean) { + return querySetAsync(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Set querySet(final Class clazz, final FilterNode node) { + return querySet(clazz, (SelectColumn) null, null, node); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final FilterNode node) { + return querySetAsync(clazz, (SelectColumn) null, null, node); + } + + /** + * 根据过滤对象FilterBean查询对象集合, 对象只填充或排除SelectField指定的字段 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param selects 收集的字段 + * @param bean 过滤Bean + * + * @return Entity对象的集合 + */ + @Override + public Set querySet(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return querySet(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, SelectColumn selects, final FilterBean bean) { + return querySetAsync(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Set querySet(final Class clazz, final SelectColumn selects, final FilterNode node) { + return querySet(clazz, selects, null, node); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, SelectColumn selects, final FilterNode node) { + return querySetAsync(clazz, selects, null, node); + } + + @Override + public Set querySet(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { + return querySet(clazz, null, flipper, FilterNode.create(column, colval)); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { + return querySetAsync(clazz, null, flipper, FilterNode.create(column, colval)); + } + + @Override + public Set querySet(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySet(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySetAsync(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Set querySet(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySet(clazz, null, flipper, node); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySetAsync(clazz, null, flipper, node); + } + + @Override + public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 根据指定字段值查询对象集合 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity对象的集合 + */ + @Override + public List queryList(final Class clazz, final String column, final Serializable colval) { + return queryList(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final String column, final Serializable colval) { + return queryListAsync(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); + } + + @Override + public List queryList(final Class clazz) { + return queryList(clazz, (SelectColumn) null, null, (FilterNode) null); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz) { + return queryListAsync(clazz, (SelectColumn) null, null, (FilterNode) null); + } + + /** + * 根据过滤对象FilterBean查询对象集合 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param bean 过滤Bean + * + * @return Entity对象集合 + */ + @Override + public List queryList(final Class clazz, final FilterBean bean) { + return queryList(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final FilterBean bean) { + return queryListAsync(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public List queryList(final Class clazz, final FilterNode node) { + return queryList(clazz, (SelectColumn) null, null, node); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final FilterNode node) { + return queryListAsync(clazz, (SelectColumn) null, null, node); + } + + /** + * 根据过滤对象FilterBean查询对象集合, 对象只填充或排除SelectField指定的字段 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param selects 收集的字段 + * @param bean 过滤Bean + * + * @return Entity对象的集合 + */ + @Override + public List queryList(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return queryList(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, SelectColumn selects, final FilterBean bean) { + return queryListAsync(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public List queryList(final Class clazz, final SelectColumn selects, final FilterNode node) { + return queryList(clazz, selects, null, node); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, SelectColumn selects, final FilterNode node) { + return queryListAsync(clazz, selects, null, node); + } + + @Override + public List queryList(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { + return queryList(clazz, null, flipper, FilterNode.create(column, colval)); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { + return queryListAsync(clazz, null, flipper, FilterNode.create(column, colval)); + } + + @Override + public List queryList(final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryList(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryListAsync(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public List queryList(final Class clazz, final Flipper flipper, final FilterNode node) { + return queryList(clazz, null, flipper, node); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterNode node) { + return queryListAsync(clazz, null, flipper, node); + } + + @Override + public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return queryList(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return queryListAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + //-----------------------sheet---------------------------- + /** + * 根据过滤对象FilterBean和翻页对象Flipper查询一页的数据 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤Bean + * + * @return Entity对象的集合 + */ + @Override + public Sheet querySheet(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySheet(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySheetAsync(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public Sheet querySheet(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySheet(clazz, null, flipper, node); + } + + @Override + public CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySheetAsync(clazz, null, flipper, node); + } + + /** + * 根据过滤对象FilterBean和翻页对象Flipper查询一页的数据, 对象只填充或排除SelectField指定的字段 + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param selects 收集的字段集合 + * @param flipper 翻页对象 + * @param bean 过滤Bean + * + * @return Entity对象的集合 + */ + @Override + public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySheet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + @Override + public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySheetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + +} diff --git a/src/org/redkale/source/CacheMemorySource.java b/src/main/java/org/redkale/source/CacheMemorySource.java similarity index 81% rename from src/org/redkale/source/CacheMemorySource.java rename to src/main/java/org/redkale/source/CacheMemorySource.java index 634b67dc5..b616bdb81 100644 --- a/src/org/redkale/source/CacheMemorySource.java +++ b/src/main/java/org/redkale/source/CacheMemorySource.java @@ -1,1501 +1,1431 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.*; -import java.lang.reflect.Type; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.*; -import java.util.logging.*; -import javax.annotation.Resource; -import org.redkale.convert.Convert; -import org.redkale.convert.json.*; -import org.redkale.net.sncp.*; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - * CacheSource的默认实现--内存缓存 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Local -@AutoLoad(false) -@SuppressWarnings("unchecked") -@ResourceType(CacheSource.class) -public final class CacheMemorySource extends AbstractService implements CacheSource, Service, AutoCloseable, Resourcable { - - private static final Type STRING_ENTRY_TYPE = new TypeToken>() { - }.getType(); - - private static final Type LONG_ENTRY_TYPE = new TypeToken>() { - }.getType(); - - private static final Type ATOMIC_ENTRY_TYPE = new TypeToken>() { - }.getType(); - - private static final Type MAP_ENTRY_TYPE = new TypeToken>() { - }.getType(); - - @Resource(name = "APP_HOME") - private File home; - - @Resource - private JsonConvert defaultConvert; - - @Resource(name = "$_convert") - private JsonConvert convert; - - private boolean needStore; - - private Type objValueType; - - private ScheduledThreadPoolExecutor scheduler; - - private Consumer expireHandler; - - private final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - protected final ConcurrentHashMap> container = new ConcurrentHashMap<>(); - - protected final BiConsumer futureCompleteConsumer = (r, t) -> { - if (t != null) logger.log(Level.SEVERE, "CompletableFuture complete error", (Throwable) t); - }; - - public CacheMemorySource() { - } - - @Override - public final String getType() { - return "memory"; - } - - @Override - public String toString() { - return "CacheMemorySource(type=memory)"; - } - - @Override //ServiceLoader时判断配置是否符合当前实现类 - public boolean match(AnyValue config) { - return false; - } - - @Override - @SuppressWarnings("unchecked") - public void init(AnyValue conf) { - if (this.convert == null) this.convert = this.defaultConvert; - if (this.convert == null) this.convert = JsonConvert.root(); - final CacheMemorySource self = this; - AnyValue prop = conf == null ? null : conf.getAnyValue("properties"); - String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler"); - if (expireHandlerClass != null) { - try { - this.expireHandler = (Consumer) Thread.currentThread().getContextClassLoader().loadClass(expireHandlerClass).getDeclaredConstructor().newInstance(); - } catch (Throwable e) { - logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e); - } - } - if (scheduler == null) { - this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-" + self.getClass().getSimpleName() + "-Expirer-Thread"); - t.setDaemon(true); - return t; - }); - final List keys = new ArrayList<>(); - scheduler.scheduleWithFixedDelay(() -> { - try { - keys.clear(); - int now = (int) (System.currentTimeMillis() / 1000); - container.forEach((k, x) -> { - if (x.expireSeconds > 0 && (now > (x.lastAccessed + x.expireSeconds))) { - keys.add(x.key); - } - }); - for (String key : keys) { - CacheEntry entry = container.remove(key); - if (expireHandler != null && entry != null) expireHandler.accept(entry); - } - } catch (Throwable t) { - logger.log(Level.SEVERE, "CacheMemorySource schedule(interval=" + 10 + "s) error", t); - } - }, 10, 10, TimeUnit.SECONDS); - if (logger.isLoggable(Level.FINEST)) logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor"); - } - if (Sncp.isRemote(self)) return; - - boolean datasync = false; //是否从远程同步过数据 - //----------同步数据……----------- - // TODO - if (this.needStore && false) { //不存储 - try { - File store = home == null ? new File("cache/" + resourceName()) : new File(home, "cache/" + resourceName()); - if (!store.isFile() || !store.canRead()) return; - LineNumberReader reader = new LineNumberReader(new FileReader(store)); - if (this.objValueType == null) this.objValueType = Object.class; - final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType); - - String line; - while ((line = reader.readLine()) != null) { - if (line.isEmpty()) continue; - Type convertType = storeObjType; - if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.LONG)) { - convertType = LONG_ENTRY_TYPE; - } else if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.STRING)) { - convertType = STRING_ENTRY_TYPE; - } else if (line.startsWith("{\"cacheType\":\"" + CacheEntryType.ATOMIC)) { - convertType = ATOMIC_ENTRY_TYPE; - } else { - continue; - } - CacheEntry entry = convert.convertFrom(convertType, line); - if (entry.isExpired()) continue; - if (datasync && container.containsKey(entry.key)) continue; //已经同步了 - container.put(entry.key, entry); - } - reader.close(); - store.delete(); - } catch (Exception e) { - logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") load store file error ", e); - } - } - } - - public static void main(String[] args) throws Exception { - AnyValue.DefaultAnyValue conf = new AnyValue.DefaultAnyValue(); - conf.addValue("node", new AnyValue.DefaultAnyValue().addValue("addr", "127.0.0.1").addValue("port", "6379")); - - CacheMemorySource source = new CacheMemorySource(); - source.defaultConvert = JsonFactory.root().getConvert(); - - source.init(conf); - - System.out.println("------------------------------------"); - source.remove("key1"); - source.remove("key2"); - source.remove("300"); - source.setString("key1", "value1"); - source.setString("keystr1", "strvalue1"); - source.setLong("keylong1", 333L); - source.setString("300", "4000"); - source.getStringAndRefresh("key1", 3500); - System.out.println("[有值] 300 GET : " + source.get("300", String.class)); - System.out.println("[有值] key1 GET : " + source.get("key1", String.class)); - System.out.println("[无值] key2 GET : " + source.get("key2", String.class)); - System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L)); - System.out.println("[有值] key1 EXISTS : " + source.exists("key1")); - System.out.println("[无值] key2 EXISTS : " + source.exists("key2")); - - source.remove("keys3"); - source.appendStringListItem("keys3", "vals1"); - source.appendStringListItem("keys3", "vals2"); - System.out.println("-------- keys3 追加了两个值 --------"); - System.out.println("[两值] keys3 VALUES : " + source.getStringCollection("keys3")); - System.out.println("[有值] keys3 EXISTS : " + source.exists("keys3")); - source.removeStringListItem("keys3", "vals1"); - System.out.println("[一值] keys3 VALUES : " + source.getStringCollection("keys3")); - source.getStringCollectionAndRefresh("keys3", 3000); - - source.remove("sets3"); - source.appendStringSetItem("sets3", "setvals1"); - source.appendStringSetItem("sets3", "setvals2"); - source.appendStringSetItem("sets3", "setvals1"); - System.out.println("[两值] sets3 VALUES : " + source.getStringCollection("sets3")); - System.out.println("[有值] sets3 EXISTS : " + source.exists("sets3")); - source.removeStringSetItem("sets3", "setvals1"); - System.out.println("[一值] sets3 VALUES : " + source.getStringCollection("sets3")); - System.out.println("sets3 大小 : " + source.getCollectionSize("sets3")); - System.out.println("all keys: " + source.queryKeys()); - System.out.println("newnum 值 : " + source.incr("newnum")); - System.out.println("newnum 值 : " + source.decr("newnum")); - - source.remove("hmap1"); - source.hincr("map", "key1"); - System.out.println("map.key1 值 : " + source.hgetLong("map", "key1", -1)); - source.hmset("map", "key2", "haha", "key3", 333); - System.out.println("map.[key1,key2,key3] 值 : " + source.hmget("map", String.class, "key1", "key2", "key3")); - - System.out.println("------------------------------------"); - source.destroy(null); - source.init(null); - System.out.println("all keys: " + source.queryKeys()); - System.out.println("[有值] keylong1 GET : " + source.getLong("keylong1", 0L)); - } - - @Override - public void close() throws Exception { //给Application 关闭时调用 - destroy(null); - } - - @Override - public String resourceName() { - Resource res = this.getClass().getAnnotation(Resource.class); - return res == null ? "cachememory" : res.name(); - } - - @Override - public void destroy(AnyValue conf) { - if (scheduler != null) scheduler.shutdownNow(); - if (!this.needStore || Sncp.isRemote(this) || container.isEmpty()) return; - if (true) return; //不存储 - try { - File store = new File(home, "cache/" + resourceName()); - store.getParentFile().mkdirs(); - PrintStream stream = new PrintStream(store, "UTF-8"); - final Type storeObjType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType); - final Type storeSetType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType); - final Type storeListType = TypeToken.createParameterizedType(null, CacheEntry.class, objValueType); - Collection> entrys = container.values(); - for (CacheEntry entry : entrys) { - Type convertType = storeObjType; - if (entry.cacheType == CacheEntryType.LONG) { - convertType = LONG_ENTRY_TYPE; - } else if (entry.cacheType == CacheEntryType.STRING) { - convertType = STRING_ENTRY_TYPE; - } else if (entry.cacheType == CacheEntryType.ATOMIC) { - convertType = ATOMIC_ENTRY_TYPE; - } else if (entry.cacheType == CacheEntryType.OBJECT) { - convertType = storeObjType; - } else if (entry.cacheType == CacheEntryType.LONG_LIST) { - convertType = LONG_ENTRY_TYPE; - } else if (entry.cacheType == CacheEntryType.LONG_SET) { - convertType = LONG_ENTRY_TYPE; - } else if (entry.cacheType == CacheEntryType.STRING_LIST) { - convertType = STRING_ENTRY_TYPE; - } else if (entry.cacheType == CacheEntryType.STRING_SET) { - convertType = STRING_ENTRY_TYPE; - } else if (entry.cacheType == CacheEntryType.OBJECT_LIST) { - convertType = storeListType; - } else if (entry.cacheType == CacheEntryType.OBJECT_SET) { - convertType = storeSetType; - } else if (entry.cacheType == CacheEntryType.BYTES) { - convertType = byte[].class; - } - try { - stream.println(convert.convertTo(convertType, entry)); - } catch (Exception ee) { - System.err.println(storeSetType + "-----" + entry); - throw ee; - } - } - container.clear(); - stream.close(); - } catch (Exception e) { - logger.log(Level.SEVERE, CacheSource.class.getSimpleName() + "(" + resourceName() + ") store to file error ", e); - } - } - - //----------- hxxx -------------- - - @Override - public int hremove(final String key, String... fields) { - int count = 0; - CacheEntry entry = container.get(key); - if (entry == null || entry.mapValue == null) return 0; - for (String field : fields) { - if (entry.mapValue.remove(field) != null) count++; - } - return count; - } - - @Override - public List hkeys(final String key) { - List list = new ArrayList<>(); - CacheEntry entry = container.get(key); - if (entry == null || entry.mapValue == null) return list; - list.addAll(entry.mapValue.keySet()); - return list; - } - - @Override - public int hsize(final String key) { - CacheEntry entry = container.get(key); - if (entry == null || entry.mapValue == null) return 0; - return entry.mapValue.keySet().size(); - } - - @Override - public long hincr(final String key, String field) { - return hincr(key, field, 1); - } - - @Override - public long hincr(final String key, String field, long num) { - CacheEntry entry = container.get(key); - if (entry == null) { - synchronized (container) { - entry = container.get(key); - if (entry == null) { - ConcurrentHashMap map = new ConcurrentHashMap(); - map.put(field, new AtomicLong()); - entry = new CacheEntry(CacheEntryType.MAP, key, new AtomicLong(), null, null, map); - container.put(key, entry); - } - } - } - Serializable val = (Serializable) entry.mapValue.computeIfAbsent(field, f -> new AtomicLong()); - if (!(val instanceof AtomicLong)) { - synchronized (entry.mapValue) { - if (!(val instanceof AtomicLong)) { - if (val == null) { - val = new AtomicLong(); - } else { - val = new AtomicLong(((Number) val).longValue()); - } - entry.mapValue.put(field, val); - } - } - } - return ((AtomicLong) entry.mapValue.get(field)).addAndGet(num); - } - - @Override - public long hdecr(final String key, String field) { - return hincr(key, field, -1); - } - - @Override - public long hdecr(final String key, String field, long num) { - return hincr(key, field, -num); - } - - @Override - public boolean hexists(final String key, String field) { - if (key == null) return false; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired() || entry.mapValue == null) return false; - return entry.mapValue.contains(field); - } - - @Override - public void hset(final String key, final String field, final Convert convert, final T value) { - hset(CacheEntryType.MAP, key, field, value); - } - - @Override - public void hset(final String key, final String field, final Type type, final T value) { - hset(CacheEntryType.MAP, key, field, value); - } - - @Override - public void hset(final String key, final String field, final Convert convert, final Type type, final T value) { - hset(CacheEntryType.MAP, key, field, value); - } - - @Override - public void hsetString(final String key, final String field, final String value) { - hset(CacheEntryType.MAP, key, field, value); - } - - @Override - public void hsetLong(final String key, final String field, final long value) { - hset(CacheEntryType.MAP, key, field, value); - } - - @Override - public void hmset(final String key, final Serializable... values) { - for (int i = 0; i < values.length; i += 2) { - hset(CacheEntryType.MAP, key, (String) values[i], values[i + 1]); - } - } - - @Override - public List hmget(final String key, final Type type, final String... fields) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired() || entry.mapValue == null) return null; - List rs = new ArrayList<>(fields.length); - for (int i = 0; i < fields.length; i++) { - Serializable val = (Serializable) entry.mapValue.get(fields[i]); - if (type == String.class) { - rs.add(val == null ? null : (T) String.valueOf(val)); - } else { - rs.add((T) val); - } - } - return rs; - } - - @Override - public Map hmap(final String key, final Type type, int offset, int limit) { - return hmap(key, type, offset, limit, null); - } - - @Override - public Map hmap(final String key, final Type type, int offset, int limit, String pattern) { - if (key == null) return new HashMap(); - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired() || entry.mapValue == null) return new HashMap(); - return new HashMap(entry.mapValue); - } - - @Override - public T hget(final String key, final String field, final Type type) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired() || entry.mapValue == null) return null; - return (T) entry.mapValue.get(field); - } - - @Override - public String hgetString(final String key, final String field) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired() || entry.mapValue == null) return null; - return (String) entry.mapValue.get(field); - } - - @Override - public long hgetLong(final String key, final String field, long defValue) { - if (key == null) return defValue; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired() || entry.mapValue == null) return defValue; - return ((Number) entry.mapValue.getOrDefault(field, defValue)).longValue(); - } - //----------- hxxx -------------- - - @Override - public boolean exists(String key) { - if (key == null) return false; - CacheEntry entry = container.get(key); - if (entry == null) return false; - return !entry.isExpired(); - } - - @Override - public CompletableFuture existsAsync(final String key) { - return CompletableFuture.supplyAsync(() -> exists(key), getExecutor()); - } - - @Override - public T get(final String key, final Type type) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return null; - if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue)); - if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue)); - return (T) entry.objectValue; - } - - @Override - public String getString(String key) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return null; - return (String) entry.objectValue; - } - - @Override - public long getLong(String key, long defValue) { - if (key == null) return defValue; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return defValue; - return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue); - } - //----------- hxxx -------------- - - @Override - public CompletableFuture hremoveAsync(final String key, String... fields) { - return CompletableFuture.supplyAsync(() -> hremove(key, fields), getExecutor()); - } - - @Override - public CompletableFuture> hkeysAsync(final String key) { - return CompletableFuture.supplyAsync(() -> hkeys(key), getExecutor()); - } - - @Override - public CompletableFuture hsizeAsync(final String key) { - return CompletableFuture.supplyAsync(() -> hsize(key), getExecutor()); - } - - @Override - public CompletableFuture hincrAsync(final String key, String field) { - return CompletableFuture.supplyAsync(() -> hincr(key, field), getExecutor()); - } - - @Override - public CompletableFuture hincrAsync(final String key, String field, long num) { - return CompletableFuture.supplyAsync(() -> hincr(key, field, num), getExecutor()); - } - - @Override - public CompletableFuture hdecrAsync(final String key, String field) { - return CompletableFuture.supplyAsync(() -> hdecr(key, field), getExecutor()); - } - - @Override - public CompletableFuture hdecrAsync(final String key, String field, long num) { - return CompletableFuture.supplyAsync(() -> hdecr(key, field, num), getExecutor()); - } - - @Override - public CompletableFuture hexistsAsync(final String key, String field) { - return CompletableFuture.supplyAsync(() -> hexists(key, field), getExecutor()); - } - - @Override - public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final T value) { - return CompletableFuture.runAsync(() -> hset(key, field, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture hsetAsync(final String key, final String field, final Type type, final T value) { - return CompletableFuture.runAsync(() -> hset(key, field, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final Type type, final T value) { - return CompletableFuture.runAsync(() -> hset(key, field, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture hsetStringAsync(final String key, final String field, final String value) { - return CompletableFuture.runAsync(() -> hsetString(key, field, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture hsetLongAsync(final String key, final String field, final long value) { - return CompletableFuture.runAsync(() -> hsetLong(key, field, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture hmsetAsync(final String key, final Serializable... values) { - return CompletableFuture.runAsync(() -> hmset(key, values), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture> hmgetAsync(final String key, final Type type, final String... fields) { - return CompletableFuture.supplyAsync(() -> hmget(key, type, fields), getExecutor()); - } - - @Override - public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit) { - return CompletableFuture.supplyAsync(() -> hmap(key, type, offset, limit), getExecutor()); - } - - @Override - public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit, String pattern) { - return CompletableFuture.supplyAsync(() -> hmap(key, type, offset, limit, pattern), getExecutor()); - } - - @Override - public CompletableFuture hgetAsync(final String key, final String field, final Type type) { - return CompletableFuture.supplyAsync(() -> hget(key, field, type), getExecutor()); - } - - @Override - public CompletableFuture hgetStringAsync(final String key, final String field) { - return CompletableFuture.supplyAsync(() -> hgetString(key, field), getExecutor()); - } - - @Override - public CompletableFuture hgetLongAsync(final String key, final String field, long defValue) { - return CompletableFuture.supplyAsync(() -> hgetLong(key, field, defValue), getExecutor()); - } - - //----------- hxxx -------------- - @Override - public CompletableFuture getAsync(final String key, final Type type) { - return CompletableFuture.supplyAsync(() -> (T) get(key, type), getExecutor()); - } - - @Override - public CompletableFuture getStringAsync(final String key) { - return CompletableFuture.supplyAsync(() -> getString(key), getExecutor()); - } - - @Override - public CompletableFuture getLongAsync(final String key, long defValue) { - return CompletableFuture.supplyAsync(() -> getLong(key, defValue), getExecutor()); - } - - @Override - public T getAndRefresh(final String key, final int expireSeconds, final Type type) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return null; - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - entry.expireSeconds = expireSeconds; - if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue)); - if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue)); - return (T) entry.objectValue; - } - - @Override - @SuppressWarnings("unchecked") - public String getStringAndRefresh(String key, final int expireSeconds) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return null; - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - entry.expireSeconds = expireSeconds; - return (String) entry.objectValue; - } - - @Override - public long getLongAndRefresh(String key, final int expireSeconds, long defValue) { - if (key == null) return defValue; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return defValue; - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - entry.expireSeconds = expireSeconds; - return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue); - - } - - @Override - public CompletableFuture getAndRefreshAsync(final String key, final int expireSeconds, final Type type) { - return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds, type), getExecutor()); - } - - @Override - public CompletableFuture getStringAndRefreshAsync(final String key, final int expireSeconds) { - return CompletableFuture.supplyAsync(() -> getStringAndRefresh(key, expireSeconds), getExecutor()); - } - - @Override - public CompletableFuture getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue) { - return CompletableFuture.supplyAsync(() -> getLongAndRefresh(key, expireSeconds, defValue), getExecutor()); - } - - @Override - public void refresh(String key, final int expireSeconds) { - if (key == null) return; - CacheEntry entry = container.get(key); - if (entry == null) return; - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - entry.expireSeconds = expireSeconds; - } - - @Override - public CompletableFuture refreshAsync(final String key, final int expireSeconds) { - return CompletableFuture.runAsync(() -> refresh(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer); - } - - protected void set(CacheEntryType cacheType, String key, Object value) { - if (key == null) return; - CacheEntry entry = container.get(key); - if (entry == null) { - entry = new CacheEntry(cacheType, key, value, null, null, null); - container.putIfAbsent(key, entry); - } else { - entry.expireSeconds = 0; - entry.objectValue = value; - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - } - } - - protected void hset(CacheEntryType cacheType, String key, String field, Object value) { - if (key == null) return; - CacheEntry entry = container.get(key); - if (entry == null) { - entry = new CacheEntry(CacheEntryType.MAP, key, value, null, null, new ConcurrentHashMap<>()); - container.putIfAbsent(key, entry); - entry.mapValue.put(field, value); - } else { - entry.expireSeconds = 0; - entry.mapValue.put(field, value); - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - } - } - - @Override - public void set(String key, Convert convert, T value) { - set(CacheEntryType.OBJECT, key, value); - } - - @Override - public void set(String key, Type type, T value) { - set(CacheEntryType.OBJECT, key, value); - } - - @Override - public void set(String key, Convert convert, Type type, T value) { - set(CacheEntryType.OBJECT, key, value); - } - - @Override - public void setString(String key, String value) { - set(CacheEntryType.STRING, key, value); - } - - @Override - public void setLong(String key, long value) { - set(CacheEntryType.LONG, key, value); - } - - @Override - public CompletableFuture setAsync(String key, Convert convert, T value) { - return CompletableFuture.runAsync(() -> set(key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setAsync(String key, Type type, T value) { - return CompletableFuture.runAsync(() -> set(key, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setAsync(String key, Convert convert, Type type, T value) { - return CompletableFuture.runAsync(() -> set(key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setStringAsync(String key, String value) { - return CompletableFuture.runAsync(() -> setString(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setLongAsync(String key, long value) { - return CompletableFuture.runAsync(() -> setLong(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - protected void set(CacheEntryType cacheType, int expireSeconds, String key, Object value) { - if (key == null) return; - CacheEntry entry = container.get(key); - if (entry == null) { - entry = new CacheEntry(cacheType, expireSeconds, key, value, null, null, null); - container.putIfAbsent(key, entry); - } else { - if (expireSeconds > 0) entry.expireSeconds = expireSeconds; - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - entry.objectValue = value; - } - } - - @Override - public void set(final int expireSeconds, String key, Convert convert, T value) { - set(CacheEntryType.OBJECT, expireSeconds, key, value); - } - - @Override - public void set(final int expireSeconds, String key, Type type, T value) { - set(CacheEntryType.OBJECT, expireSeconds, key, value); - } - - @Override - public void set(final int expireSeconds, String key, Convert convert, Type type, T value) { - set(CacheEntryType.OBJECT, expireSeconds, key, value); - } - - @Override - public void setString(int expireSeconds, String key, String value) { - set(CacheEntryType.STRING, expireSeconds, key, value); - } - - @Override - public void setLong(int expireSeconds, String key, long value) { - set(CacheEntryType.LONG, expireSeconds, key, value); - } - - @Override - public CompletableFuture setAsync(int expireSeconds, String key, Convert convert, T value) { - return CompletableFuture.runAsync(() -> set(expireSeconds, key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setAsync(int expireSeconds, String key, Type type, T value) { - return CompletableFuture.runAsync(() -> set(expireSeconds, key, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setAsync(int expireSeconds, String key, Convert convert, Type type, T value) { - return CompletableFuture.runAsync(() -> set(expireSeconds, key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setStringAsync(int expireSeconds, String key, String value) { - return CompletableFuture.runAsync(() -> setString(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture setLongAsync(int expireSeconds, String key, long value) { - return CompletableFuture.runAsync(() -> setLong(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public void setExpireSeconds(String key, int expireSeconds) { - if (key == null) return; - CacheEntry entry = container.get(key); - if (entry == null) return; - entry.expireSeconds = expireSeconds; - } - - @Override - public CompletableFuture setExpireSecondsAsync(final String key, final int expireSeconds) { - return CompletableFuture.runAsync(() -> setExpireSeconds(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public int remove(String key) { - if (key == null) return 0; - return container.remove(key) == null ? 0 : 1; - } - - @Override - public long incr(final String key) { - return incr(key, 1); - } - - @Override - public CompletableFuture incrAsync(final String key) { - return CompletableFuture.supplyAsync(() -> incr(key), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public long incr(final String key, long num) { - CacheEntry entry = container.get(key); - if (entry == null) { - synchronized (container) { - entry = container.get(key); - if (entry == null) { - entry = new CacheEntry(CacheEntryType.ATOMIC, key, new AtomicLong(), null, null, null); - container.put(key, entry); - } - } - } - return ((AtomicLong) entry.objectValue).addAndGet(num); - } - - @Override - public CompletableFuture incrAsync(final String key, long num) { - return CompletableFuture.supplyAsync(() -> incr(key, num), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public long decr(final String key) { - return incr(key, -1); - } - - @Override - public CompletableFuture decrAsync(final String key) { - return CompletableFuture.supplyAsync(() -> decr(key), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public long decr(final String key, long num) { - return incr(key, -num); - } - - @Override - public CompletableFuture decrAsync(final String key, long num) { - return CompletableFuture.supplyAsync(() -> decr(key, num), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture removeAsync(final String key) { - return CompletableFuture.supplyAsync(() -> remove(key), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public Collection getCollection(final String key, final Type componentType) { - return (Collection) get(key, componentType); - } - - @Override - public Map> getCollectionMap(final boolean set, final Type componentType, final String... keys) { - Map> map = new HashMap<>(); - for (String key : keys) { - Collection s = (Collection) get(key, componentType); - if (s != null) map.put(key, s); - } - return map; - } - - @Override - public Collection getStringCollection(final String key) { - return (Collection) get(key, String.class); - } - - @Override - public Map> getStringCollectionMap(final boolean set, final String... keys) { - Map> map = new HashMap<>(); - for (String key : keys) { - Collection s = (Collection) get(key, String.class); - if (s != null) map.put(key, s); - } - return map; - } - - @Override - public Map getLongMap(final String... keys) { - Map map = new LinkedHashMap<>(); - for (String key : keys) { - Number n = (Number) get(key, long.class); - map.put(key, n == null ? null : n.longValue()); - } - return map; - } - - @Override - public Long[] getLongArray(final String... keys) { - Long[] rs = new Long[keys.length]; - int index = -1; - for (String key : keys) { - Number n = (Number) get(key, long.class); - rs[++index] = n == null ? null : n.longValue(); - } - return rs; - } - - @Override - public CompletableFuture> getLongMapAsync(final String... keys) { - return CompletableFuture.supplyAsync(() -> getLongMap(keys), getExecutor()); - } - - @Override - public CompletableFuture getLongArrayAsync(final String... keys) { - return CompletableFuture.supplyAsync(() -> getLongArray(keys), getExecutor()); - } - - @Override - public Map getStringMap(final String... keys) { - Map map = new LinkedHashMap<>(); - for (String key : keys) { - Object n = get(key, String.class); - map.put(key, n == null ? null : n.toString()); - } - return map; - } - - @Override - public String[] getStringArray(final String... keys) { - String[] rs = new String[keys.length]; - int index = -1; - for (String key : keys) { - Object n = get(key, String.class); - rs[++index] = n == null ? null : n.toString(); - } - return rs; - } - - @Override - public CompletableFuture> getStringMapAsync(final String... keys) { - return CompletableFuture.supplyAsync(() -> getStringMap(keys), getExecutor()); - } - - @Override - public CompletableFuture getStringArrayAsync(final String... keys) { - return CompletableFuture.supplyAsync(() -> getStringArray(keys), getExecutor()); - } - - @Override - public Map getMap(final Type componentType, final String... keys) { - Map map = new LinkedHashMap<>(); - for (String key : keys) { - map.put(key, (T) get(key, componentType)); - } - return map; - } - - @Override - public CompletableFuture> getMapAsync(final Type componentType, final String... keys) { - return CompletableFuture.supplyAsync(() -> getMap(componentType, keys), getExecutor()); - } - - @Override - public Collection getLongCollection(final String key) { - return (Collection) get(key, long.class); - } - - @Override - public Map> getLongCollectionMap(final boolean set, final String... keys) { - Map> map = new HashMap<>(); - for (String key : keys) { - Collection s = (Collection) get(key, long.class); - if (s != null) map.put(key, s); - } - return map; - } - - @Override - public CompletableFuture>> getCollectionMapAsync(boolean set, Type componentType, String... keys) { - return CompletableFuture.supplyAsync(() -> getCollectionMap(set, componentType, keys), getExecutor()); - } - - @Override - public CompletableFuture> getStringCollectionAsync(final String key) { - return CompletableFuture.supplyAsync(() -> getStringCollection(key), getExecutor()); - } - - @Override - public CompletableFuture>> getStringCollectionMapAsync(final boolean set, final String... keys) { - return CompletableFuture.supplyAsync(() -> getStringCollectionMap(set, keys), getExecutor()); - } - - @Override - public CompletableFuture> getLongCollectionAsync(final String key) { - return CompletableFuture.supplyAsync(() -> getLongCollection(key), getExecutor()); - } - - @Override - public CompletableFuture>> getLongCollectionMapAsync(final boolean set, final String... keys) { - return CompletableFuture.supplyAsync(() -> getLongCollectionMap(set, keys), getExecutor()); - } - - @Override - public CompletableFuture> getCollectionAsync(String key, Type componentType) { - return CompletableFuture.supplyAsync(() -> getCollection(key, componentType), getExecutor()); - } - - @Override - public int getCollectionSize(final String key) { - Collection collection = (Collection) get(key, Object.class); - return collection == null ? 0 : collection.size(); - } - - @Override - public CompletableFuture getCollectionSizeAsync(final String key) { - return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor()); - } - - @Override - public Collection getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType) { - return (Collection) getAndRefresh(key, expireSeconds, componentType); - } - - @Override - public Collection getStringCollectionAndRefresh(final String key, final int expireSeconds) { - return (Collection) getAndRefresh(key, expireSeconds, String.class); - } - - @Override - public boolean existsSetItem(final String key, final Type type, final T value) { - Collection list = getCollection(key, type); - return list != null && list.contains(value); - } - - @Override - public CompletableFuture existsSetItemAsync(final String key, final Type type, final T value) { - return CompletableFuture.supplyAsync(() -> existsSetItem(key, type, value), getExecutor()); - } - - @Override - public boolean existsStringSetItem(final String key, final String value) { - Collection list = getStringCollection(key); - return list != null && list.contains(value); - } - - @Override - public CompletableFuture existsStringSetItemAsync(final String key, final String value) { - return CompletableFuture.supplyAsync(() -> existsStringSetItem(key, value), getExecutor()); - } - - @Override - public boolean existsLongSetItem(final String key, final long value) { - Collection list = getLongCollection(key); - return list != null && list.contains(value); - } - - @Override - public CompletableFuture existsLongSetItemAsync(final String key, final long value) { - return CompletableFuture.supplyAsync(() -> existsLongSetItem(key, value), getExecutor()); - } - - @Override - public Collection getLongCollectionAndRefresh(String key, int expireSeconds) { - return (Collection) getAndRefresh(key, expireSeconds, long.class); - } - - @Override - public CompletableFuture> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType) { - return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds, componentType), getExecutor()); - } - - @Override - public CompletableFuture> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds) { - return CompletableFuture.supplyAsync(() -> getStringCollectionAndRefresh(key, expireSeconds), getExecutor()); - } - - @Override - public CompletableFuture> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds) { - return CompletableFuture.supplyAsync(() -> getLongCollectionAndRefresh(key, expireSeconds), getExecutor()); - } - - protected void appendListItem(CacheEntryType cacheType, String key, Object value) { - if (key == null) return; - CacheEntry entry = container.get(key); - if (entry == null || !entry.isListCacheType() || entry.listValue == null) { - ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); - entry = new CacheEntry(cacheType, key, null, null, list, null); - CacheEntry old = container.putIfAbsent(key, entry); - if (old != null) list = old.listValue; - if (list != null) list.add(value); - } else { - entry.listValue.add(value); - } - } - - @Override - public void appendListItem(String key, Type componentType, T value) { - appendListItem(CacheEntryType.OBJECT_LIST, key, value); - } - - @Override - public void appendStringListItem(String key, String value) { - appendListItem(CacheEntryType.STRING_LIST, key, value); - } - - @Override - public void appendLongListItem(String key, long value) { - appendListItem(CacheEntryType.LONG_LIST, key, value); - } - - @Override - public CompletableFuture appendListItemAsync(final String key, final Type componentType, final T value) { - return CompletableFuture.runAsync(() -> appendListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture appendStringListItemAsync(final String key, final String value) { - return CompletableFuture.runAsync(() -> appendStringListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture appendLongListItemAsync(final String key, final long value) { - return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public int removeListItem(String key, final Type componentType, T value) { - if (key == null) return 0; - CacheEntry entry = container.get(key); - if (entry == null || entry.listValue == null) return 0; - return entry.listValue.remove(value) ? 1 : 0; - } - - @Override - public int removeStringListItem(String key, String value) { - if (key == null) return 0; - CacheEntry entry = container.get(key); - if (entry == null || entry.listValue == null) return 0; - return entry.listValue.remove(value) ? 1 : 0; - } - - @Override - public int removeLongListItem(String key, long value) { - if (key == null) return 0; - CacheEntry entry = container.get(key); - if (entry == null || entry.listValue == null) return 0; - return entry.listValue.remove(value) ? 1 : 0; - } - - @Override - public CompletableFuture removeListItemAsync(final String key, final Type componentType, T value) { - return CompletableFuture.supplyAsync(() -> removeListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture removeStringListItemAsync(final String key, final String value) { - return CompletableFuture.supplyAsync(() -> removeStringListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture removeLongListItemAsync(final String key, final long value) { - return CompletableFuture.supplyAsync(() -> removeLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public String spopStringSetItem(final String key) { - return (String) spopSetItem(key, String.class); - } - - @Override - public List spopStringSetItem(final String key, int count) { - return (List) spopSetItem(key, count, String.class); - } - - @Override - public Long spopLongSetItem(final String key) { - return (Long) spopSetItem(key, long.class); - } - - @Override - public List spopLongSetItem(final String key, int count) { - return (List) spopSetItem(key, count, long.class); - } - - @Override - public T spopSetItem(final String key, final Type componentType) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) { - return null; - } - if (entry.csetValue.isEmpty()) return null; - Iterator it = entry.csetValue.iterator(); - if (it.hasNext()) { - Object obj = it.next(); - if (obj != null && componentType == long.class) obj = ((Number) obj).longValue(); - it.remove(); - return (T) obj; - } - return null; - } - - @Override - public List spopSetItem(final String key, final int count, final Type componentType) { - if (key == null) return new ArrayList<>(); - CacheEntry entry = container.get(key); - if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) { - return new ArrayList<>(); - } - if (entry.csetValue.isEmpty()) return new ArrayList<>(); - Iterator it = entry.csetValue.iterator(); - List list = new ArrayList<>(); - int index = 0; - while (it.hasNext()) { - Object obj = it.next(); - if (obj != null && componentType == long.class) obj = ((Number) obj).longValue(); - list.add((T) obj); - it.remove(); - if (++index >= count) break; - } - return list; - } - - protected void appendSetItem(CacheEntryType cacheType, String key, Object value) { - if (key == null) return; - CacheEntry entry = container.get(key); - if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) { - CopyOnWriteArraySet set = new CopyOnWriteArraySet(); - entry = new CacheEntry(cacheType, key, null, set, null, null); - CacheEntry old = container.putIfAbsent(key, entry); - if (old != null) set = old.csetValue; - if (set != null) set.add(value); - } else { - entry.csetValue.add(value); - } - } - - @Override - public void appendSetItem(String key, final Type componentType, T value) { - appendSetItem(CacheEntryType.OBJECT_SET, key, value); - } - - @Override - public void appendStringSetItem(String key, String value) { - appendSetItem(CacheEntryType.OBJECT_SET, key, value); - } - - @Override - public void appendLongSetItem(String key, long value) { - appendSetItem(CacheEntryType.OBJECT_SET, key, value); - } - - @Override - public CompletableFuture appendSetItemAsync(final String key, final Type componentType, T value) { - return CompletableFuture.runAsync(() -> appendSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture appendStringSetItemAsync(final String key, final String value) { - return CompletableFuture.runAsync(() -> appendStringSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture appendLongSetItemAsync(final String key, final long value) { - return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public int removeSetItem(String key, Type type, T value) { - if (key == null) return 0; - CacheEntry entry = container.get(key); - if (entry == null || entry.csetValue == null) return 0; - return entry.csetValue.remove(value) ? 1 : 0; - } - - @Override - public int removeStringSetItem(String key, String value) { - if (key == null) return 0; - CacheEntry entry = container.get(key); - if (entry == null || entry.csetValue == null) return 0; - return entry.csetValue.remove(value) ? 1 : 0; - } - - @Override - public int removeLongSetItem(String key, long value) { - if (key == null) return 0; - CacheEntry entry = container.get(key); - if (entry == null || entry.csetValue == null) return 0; - return entry.csetValue.remove(value) ? 1 : 0; - } - - @Override - public CompletableFuture removeSetItemAsync(final String key, final Type componentType, final T value) { - return CompletableFuture.supplyAsync(() -> removeSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture removeStringSetItemAsync(final String key, final String value) { - return CompletableFuture.supplyAsync(() -> removeStringSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture removeLongSetItemAsync(final String key, final long value) { - return CompletableFuture.supplyAsync(() -> removeLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public byte[] getBytes(final String key) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return null; - return (byte[]) entry.objectValue; - } - - @Override - public CompletableFuture getBytesAsync(final String key) { - return CompletableFuture.supplyAsync(() -> getBytes(key), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public byte[] getBytesAndRefresh(String key, final int expireSeconds) { - if (key == null) return null; - CacheEntry entry = container.get(key); - if (entry == null || entry.isExpired()) return null; - entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); - entry.expireSeconds = expireSeconds; - return (byte[]) entry.objectValue; - } - - @Override - public CompletableFuture getBytesAndRefreshAsync(final String key, final int expireSeconds) { - return CompletableFuture.supplyAsync(() -> getBytesAndRefresh(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public void setBytes(String key, byte[] value) { - set(CacheEntryType.BYTES, key, value); - } - - @Override - public CompletableFuture setBytesAsync(final String key, byte[] value) { - return CompletableFuture.runAsync(() -> setBytes(key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public void setBytes(final int expireSeconds, final String key, final byte[] value) { - set(CacheEntryType.BYTES, expireSeconds, key, value); - } - - @Override - public CompletableFuture setBytesAsync(final int expireSeconds, final String key, byte[] value) { - return CompletableFuture.runAsync(() -> setBytes(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public void setBytes(final String key, final Convert convert, final Type type, final T value) { - set(CacheEntryType.BYTES, key, convert.convertToBytes(type, value)); - } - - @Override - public CompletableFuture setBytesAsync(final String key, final Convert convert, final Type type, final T value) { - return CompletableFuture.runAsync(() -> setBytes(key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public void setBytes(final int expireSeconds, final String key, final Convert convert, final Type type, final T value) { - set(CacheEntryType.BYTES, expireSeconds, key, convert.convertToBytes(type, value)); - } - - @Override - public CompletableFuture setBytesAsync(final int expireSeconds, final String key, final Convert convert, final Type type, final T value) { - return CompletableFuture.runAsync(() -> setBytes(expireSeconds, key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public List queryKeys() { - return new ArrayList<>(container.keySet()); - } - - @Override - public List queryKeysStartsWith(String startsWith) { - if (startsWith == null) return queryKeys(); - List rs = new ArrayList<>(); - container.keySet().stream().filter(x -> x.startsWith(startsWith)).forEach(x -> rs.add(x)); - return rs; - } - - @Override - public List queryKeysEndsWith(String endsWith) { - if (endsWith == null) return queryKeys(); - List rs = new ArrayList<>(); - container.keySet().stream().filter(x -> x.endsWith(endsWith)).forEach(x -> rs.add(x)); - return rs; - } - - @Override - public int getKeySize() { - return container.size(); - } - - @Override - public CompletableFuture>> queryListAsync() { - return CompletableFuture.completedFuture(new ArrayList<>(container.values())); - } - - @Override - public List> queryList() { - return new ArrayList<>(container.values()); - } - - @Override - public CompletableFuture> queryKeysAsync() { - return CompletableFuture.completedFuture(new ArrayList<>(container.keySet())); - } - - @Override - public CompletableFuture> queryKeysStartsWithAsync(String startsWith) { - return CompletableFuture.completedFuture(queryKeysStartsWith(startsWith)); - } - - @Override - public CompletableFuture> queryKeysEndsWithAsync(String endsWith) { - return CompletableFuture.completedFuture(queryKeysEndsWith(endsWith)); - } - - @Override - public CompletableFuture getKeySizeAsync() { - return CompletableFuture.completedFuture(container.size()); - } - - @Override - public CompletableFuture spopSetItemAsync(String key, Type componentType) { - return CompletableFuture.supplyAsync(() -> spopSetItem(key, componentType), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture> spopSetItemAsync(String key, int count, Type componentType) { - return CompletableFuture.supplyAsync(() -> spopSetItem(key, count, componentType), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture spopStringSetItemAsync(String key) { - return CompletableFuture.supplyAsync(() -> spopStringSetItem(key), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture> spopStringSetItemAsync(String key, int count) { - return CompletableFuture.supplyAsync(() -> spopStringSetItem(key, count), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture spopLongSetItemAsync(String key) { - return CompletableFuture.supplyAsync(() -> spopLongSetItem(key), getExecutor()).whenComplete(futureCompleteConsumer); - } - - @Override - public CompletableFuture> spopLongSetItemAsync(String key, int count) { - return CompletableFuture.supplyAsync(() -> spopLongSetItem(key, count), getExecutor()).whenComplete(futureCompleteConsumer); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.*; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.*; +import java.util.logging.*; +import javax.annotation.Resource; +import org.redkale.convert.*; +import org.redkale.convert.json.*; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * CacheSource的默认实现--内存缓存 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Local +@AutoLoad(false) +@SuppressWarnings("unchecked") +@ResourceType(CacheSource.class) +public final class CacheMemorySource extends AbstractService implements CacheSource, Service, AutoCloseable, Resourcable { + + @Resource + private JsonConvert defaultConvert; + + @Resource(name = "$_convert") + private JsonConvert convert; + + private String name; + + private ScheduledThreadPoolExecutor scheduler; + + private Consumer expireHandler; + + private final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected final ConcurrentHashMap> container = new ConcurrentHashMap<>(); + + protected final BiConsumer futureCompleteConsumer = (r, t) -> { + if (t != null) logger.log(Level.SEVERE, "CompletableFuture complete error", (Throwable) t); + }; + + public CacheMemorySource(String resourceName) { + this.name = resourceName; + } + + @Override + public final String getType() { + return "memory"; + } + + @Override + public String toString() { + return "CacheMemorySource(type=memory, name='" + resourceName() + "')"; + } + + @Override //ServiceLoader时判断配置是否符合当前实现类 + public boolean acceptsConf(AnyValue config) { + return false; + } + + @Override + @SuppressWarnings("unchecked") + public void init(AnyValue conf) { + if (this.convert == null) this.convert = this.defaultConvert; + if (this.convert == null) this.convert = JsonConvert.root(); + final CacheMemorySource self = this; + AnyValue prop = conf == null ? null : conf.getAnyValue("properties"); + String expireHandlerClass = prop == null ? null : prop.getValue("expirehandler"); + if (expireHandlerClass != null) { + try { + Class clazz = Thread.currentThread().getContextClassLoader().loadClass(expireHandlerClass); + this.expireHandler = (Consumer) clazz.getDeclaredConstructor().newInstance(); + RedkaleClassLoader.putReflectionDeclaredConstructors(clazz, expireHandlerClass); + } catch (Throwable e) { + logger.log(Level.SEVERE, self.getClass().getSimpleName() + " new expirehandler class (" + expireHandlerClass + ") instance error", e); + } + } + if (scheduler == null) { + this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { + final Thread t = new Thread(r, "Redkale-" + self.getClass().getSimpleName() + "-Expirer-Thread"); + t.setDaemon(true); + return t; + }); + final List keys = new ArrayList<>(); + scheduler.scheduleWithFixedDelay(() -> { + try { + keys.clear(); + int now = (int) (System.currentTimeMillis() / 1000); + container.forEach((k, x) -> { + if (x.expireSeconds > 0 && (now > (x.lastAccessed + x.expireSeconds))) { + keys.add(x.key); + } + }); + for (String key : keys) { + CacheEntry entry = container.remove(key); + if (expireHandler != null && entry != null) expireHandler.accept(entry); + } + } catch (Throwable t) { + logger.log(Level.SEVERE, "CacheMemorySource schedule(interval=" + 10 + "s) error", t); + } + }, 10, 10, TimeUnit.SECONDS); + if (logger.isLoggable(Level.FINEST)) logger.finest(self.getClass().getSimpleName() + ":" + self.resourceName() + " start schedule expire executor"); + } + } + + @Override + public void close() throws Exception { //给Application 关闭时调用 + destroy(null); + } + + @Override + public String resourceName() { + return name; + } + + @Override + public void destroy(AnyValue conf) { + if (scheduler != null) scheduler.shutdownNow(); + } + + //----------- hxxx -------------- + @Override + public int hremove(final String key, String... fields) { + int count = 0; + CacheEntry entry = container.get(key); + if (entry == null || entry.mapValue == null) return 0; + for (String field : fields) { + if (entry.mapValue.remove(field) != null) count++; + } + return count; + } + + @Override + public List hkeys(final String key) { + List list = new ArrayList<>(); + CacheEntry entry = container.get(key); + if (entry == null || entry.mapValue == null) return list; + list.addAll(entry.mapValue.keySet()); + return list; + } + + @Override + public int hsize(final String key) { + CacheEntry entry = container.get(key); + if (entry == null || entry.mapValue == null) return 0; + return entry.mapValue.keySet().size(); + } + + @Override + public long hincr(final String key, String field) { + return hincr(key, field, 1); + } + + @Override + public long hincr(final String key, String field, long num) { + CacheEntry entry = container.get(key); + if (entry == null) { + synchronized (container) { + entry = container.get(key); + if (entry == null) { + ConcurrentHashMap map = new ConcurrentHashMap(); + map.put(field, new AtomicLong()); + entry = new CacheEntry(CacheEntryType.MAP, key, new AtomicLong(), null, null, map); + container.put(key, entry); + } + } + } + Serializable val = (Serializable) entry.mapValue.computeIfAbsent(field, f -> new AtomicLong()); + if (!(val instanceof AtomicLong)) { + synchronized (entry.mapValue) { + if (!(val instanceof AtomicLong)) { + if (val == null) { + val = new AtomicLong(); + } else { + val = new AtomicLong(((Number) val).longValue()); + } + entry.mapValue.put(field, val); + } + } + } + return ((AtomicLong) entry.mapValue.get(field)).addAndGet(num); + } + + @Override + public long hdecr(final String key, String field) { + return hincr(key, field, -1); + } + + @Override + public long hdecr(final String key, String field, long num) { + return hincr(key, field, -num); + } + + @Override + public boolean hexists(final String key, String field) { + if (key == null) return false; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired() || entry.mapValue == null) return false; + return entry.mapValue.contains(field); + } + + @Override + public void hset(final String key, final String field, final Convert convert, final T value) { + hset(CacheEntryType.MAP, key, field, value); + } + + @Override + public void hset(final String key, final String field, final Type type, final T value) { + hset(CacheEntryType.MAP, key, field, value); + } + + @Override + public void hset(final String key, final String field, final Convert convert, final Type type, final T value) { + hset(CacheEntryType.MAP, key, field, value); + } + + @Override + public void hsetString(final String key, final String field, final String value) { + hset(CacheEntryType.MAP, key, field, value); + } + + @Override + public void hsetLong(final String key, final String field, final long value) { + hset(CacheEntryType.MAP, key, field, value); + } + + @Override + public void hmset(final String key, final Serializable... values) { + for (int i = 0; i < values.length; i += 2) { + hset(CacheEntryType.MAP, key, (String) values[i], values[i + 1]); + } + } + + @Override + public List hmget(final String key, final Type type, final String... fields) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired() || entry.mapValue == null) return null; + List rs = new ArrayList<>(fields.length); + for (int i = 0; i < fields.length; i++) { + Serializable val = (Serializable) entry.mapValue.get(fields[i]); + if (type == String.class) { + rs.add(val == null ? null : (T) String.valueOf(val)); + } else { + rs.add((T) val); + } + } + return rs; + } + + @Override + public Map hmap(final String key, final Type type, int offset, int limit) { + return hmap(key, type, offset, limit, null); + } + + @Override + public Map hmap(final String key, final Type type, int offset, int limit, String pattern) { + if (key == null) return new HashMap(); + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired() || entry.mapValue == null) return new HashMap(); + return new HashMap(entry.mapValue); + } + + @Override + public T hget(final String key, final String field, final Type type) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired() || entry.mapValue == null) return null; + return (T) entry.mapValue.get(field); + } + + @Override + public String hgetString(final String key, final String field) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired() || entry.mapValue == null) return null; + return (String) entry.mapValue.get(field); + } + + @Override + public long hgetLong(final String key, final String field, long defValue) { + if (key == null) return defValue; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired() || entry.mapValue == null) return defValue; + return ((Number) entry.mapValue.getOrDefault(field, defValue)).longValue(); + } + //----------- hxxx -------------- + + @Override + public boolean exists(String key) { + if (key == null) return false; + CacheEntry entry = container.get(key); + if (entry == null) return false; + return !entry.isExpired(); + } + + @Override + public CompletableFuture existsAsync(final String key) { + return CompletableFuture.supplyAsync(() -> exists(key), getExecutor()); + } + + @Override + public T get(final String key, final Type type) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return null; + if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue)); + if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue)); + return (T) entry.objectValue; + } + + @Override + public String getString(String key) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return null; + return (String) entry.objectValue; + } + + @Override + public long getLong(String key, long defValue) { + if (key == null) return defValue; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return defValue; + return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue); + } + //----------- hxxx -------------- + + @Override + public CompletableFuture hremoveAsync(final String key, String... fields) { + return CompletableFuture.supplyAsync(() -> hremove(key, fields), getExecutor()); + } + + @Override + public CompletableFuture> hkeysAsync(final String key) { + return CompletableFuture.supplyAsync(() -> hkeys(key), getExecutor()); + } + + @Override + public CompletableFuture hsizeAsync(final String key) { + return CompletableFuture.supplyAsync(() -> hsize(key), getExecutor()); + } + + @Override + public CompletableFuture hincrAsync(final String key, String field) { + return CompletableFuture.supplyAsync(() -> hincr(key, field), getExecutor()); + } + + @Override + public CompletableFuture hincrAsync(final String key, String field, long num) { + return CompletableFuture.supplyAsync(() -> hincr(key, field, num), getExecutor()); + } + + @Override + public CompletableFuture hdecrAsync(final String key, String field) { + return CompletableFuture.supplyAsync(() -> hdecr(key, field), getExecutor()); + } + + @Override + public CompletableFuture hdecrAsync(final String key, String field, long num) { + return CompletableFuture.supplyAsync(() -> hdecr(key, field, num), getExecutor()); + } + + @Override + public CompletableFuture hexistsAsync(final String key, String field) { + return CompletableFuture.supplyAsync(() -> hexists(key, field), getExecutor()); + } + + @Override + public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final T value) { + return CompletableFuture.runAsync(() -> hset(key, field, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture hsetAsync(final String key, final String field, final Type type, final T value) { + return CompletableFuture.runAsync(() -> hset(key, field, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final Type type, final T value) { + return CompletableFuture.runAsync(() -> hset(key, field, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture hsetStringAsync(final String key, final String field, final String value) { + return CompletableFuture.runAsync(() -> hsetString(key, field, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture hsetLongAsync(final String key, final String field, final long value) { + return CompletableFuture.runAsync(() -> hsetLong(key, field, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture hmsetAsync(final String key, final Serializable... values) { + return CompletableFuture.runAsync(() -> hmset(key, values), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture> hmgetAsync(final String key, final Type type, final String... fields) { + return CompletableFuture.supplyAsync(() -> hmget(key, type, fields), getExecutor()); + } + + @Override + public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit) { + return CompletableFuture.supplyAsync(() -> hmap(key, type, offset, limit), getExecutor()); + } + + @Override + public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit, String pattern) { + return CompletableFuture.supplyAsync(() -> hmap(key, type, offset, limit, pattern), getExecutor()); + } + + @Override + public CompletableFuture hgetAsync(final String key, final String field, final Type type) { + return CompletableFuture.supplyAsync(() -> hget(key, field, type), getExecutor()); + } + + @Override + public CompletableFuture hgetStringAsync(final String key, final String field) { + return CompletableFuture.supplyAsync(() -> hgetString(key, field), getExecutor()); + } + + @Override + public CompletableFuture hgetLongAsync(final String key, final String field, long defValue) { + return CompletableFuture.supplyAsync(() -> hgetLong(key, field, defValue), getExecutor()); + } + + //----------- hxxx -------------- + @Override + public CompletableFuture getAsync(final String key, final Type type) { + return CompletableFuture.supplyAsync(() -> (T) get(key, type), getExecutor()); + } + + @Override + public CompletableFuture getStringAsync(final String key) { + return CompletableFuture.supplyAsync(() -> getString(key), getExecutor()); + } + + @Override + public CompletableFuture getLongAsync(final String key, long defValue) { + return CompletableFuture.supplyAsync(() -> getLong(key, defValue), getExecutor()); + } + + @Override + public T getAndRefresh(final String key, final int expireSeconds, final Type type) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return null; + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + entry.expireSeconds = expireSeconds; + if (entry.isListCacheType()) return (T) (entry.listValue == null ? null : new ArrayList(entry.listValue)); + if (entry.isSetCacheType()) return (T) (entry.csetValue == null ? null : new HashSet(entry.csetValue)); + return (T) entry.objectValue; + } + + @Override + @SuppressWarnings("unchecked") + public String getStringAndRefresh(String key, final int expireSeconds) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return null; + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + entry.expireSeconds = expireSeconds; + return (String) entry.objectValue; + } + + @Override + public long getLongAndRefresh(String key, final int expireSeconds, long defValue) { + if (key == null) return defValue; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return defValue; + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + entry.expireSeconds = expireSeconds; + return entry.objectValue == null ? defValue : (entry.objectValue instanceof AtomicLong ? ((AtomicLong) entry.objectValue).get() : (Long) entry.objectValue); + + } + + @Override + public CompletableFuture getAndRefreshAsync(final String key, final int expireSeconds, final Type type) { + return CompletableFuture.supplyAsync(() -> getAndRefresh(key, expireSeconds, type), getExecutor()); + } + + @Override + public CompletableFuture getStringAndRefreshAsync(final String key, final int expireSeconds) { + return CompletableFuture.supplyAsync(() -> getStringAndRefresh(key, expireSeconds), getExecutor()); + } + + @Override + public CompletableFuture getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue) { + return CompletableFuture.supplyAsync(() -> getLongAndRefresh(key, expireSeconds, defValue), getExecutor()); + } + + @Override + public void refresh(String key, final int expireSeconds) { + if (key == null) return; + CacheEntry entry = container.get(key); + if (entry == null) return; + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + entry.expireSeconds = expireSeconds; + } + + @Override + public CompletableFuture refreshAsync(final String key, final int expireSeconds) { + return CompletableFuture.runAsync(() -> refresh(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer); + } + + protected void set(CacheEntryType cacheType, String key, Object value) { + if (key == null) return; + CacheEntry entry = container.get(key); + if (entry == null) { + entry = new CacheEntry(cacheType, key, value, null, null, null); + container.putIfAbsent(key, entry); + } else { + entry.expireSeconds = 0; + entry.objectValue = value; + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + } + } + + protected void hset(CacheEntryType cacheType, String key, String field, Object value) { + if (key == null) return; + CacheEntry entry = container.get(key); + if (entry == null) { + entry = new CacheEntry(CacheEntryType.MAP, key, value, null, null, new ConcurrentHashMap<>()); + container.putIfAbsent(key, entry); + entry.mapValue.put(field, value); + } else { + entry.expireSeconds = 0; + entry.mapValue.put(field, value); + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + } + } + + @Override + public void set(String key, Convert convert, T value) { + set(CacheEntryType.OBJECT, key, value); + } + + @Override + public void set(String key, Type type, T value) { + set(CacheEntryType.OBJECT, key, value); + } + + @Override + public void set(String key, Convert convert, Type type, T value) { + set(CacheEntryType.OBJECT, key, value); + } + + @Override + public void setString(String key, String value) { + set(CacheEntryType.STRING, key, value); + } + + @Override + public void setLong(String key, long value) { + set(CacheEntryType.LONG, key, value); + } + + @Override + public CompletableFuture setAsync(String key, Convert convert, T value) { + return CompletableFuture.runAsync(() -> set(key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setAsync(String key, Type type, T value) { + return CompletableFuture.runAsync(() -> set(key, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setAsync(String key, Convert convert, Type type, T value) { + return CompletableFuture.runAsync(() -> set(key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setStringAsync(String key, String value) { + return CompletableFuture.runAsync(() -> setString(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setLongAsync(String key, long value) { + return CompletableFuture.runAsync(() -> setLong(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + protected void set(CacheEntryType cacheType, int expireSeconds, String key, Object value) { + if (key == null) return; + CacheEntry entry = container.get(key); + if (entry == null) { + entry = new CacheEntry(cacheType, expireSeconds, key, value, null, null, null); + container.putIfAbsent(key, entry); + } else { + if (expireSeconds > 0) entry.expireSeconds = expireSeconds; + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + entry.objectValue = value; + } + } + + @Override + public void set(final int expireSeconds, String key, Convert convert, T value) { + set(CacheEntryType.OBJECT, expireSeconds, key, value); + } + + @Override + public void set(final int expireSeconds, String key, Type type, T value) { + set(CacheEntryType.OBJECT, expireSeconds, key, value); + } + + @Override + public void set(final int expireSeconds, String key, Convert convert, Type type, T value) { + set(CacheEntryType.OBJECT, expireSeconds, key, value); + } + + @Override + public void setString(int expireSeconds, String key, String value) { + set(CacheEntryType.STRING, expireSeconds, key, value); + } + + @Override + public void setLong(int expireSeconds, String key, long value) { + set(CacheEntryType.LONG, expireSeconds, key, value); + } + + @Override + public CompletableFuture setAsync(int expireSeconds, String key, Convert convert, T value) { + return CompletableFuture.runAsync(() -> set(expireSeconds, key, convert, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setAsync(int expireSeconds, String key, Type type, T value) { + return CompletableFuture.runAsync(() -> set(expireSeconds, key, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setAsync(int expireSeconds, String key, Convert convert, Type type, T value) { + return CompletableFuture.runAsync(() -> set(expireSeconds, key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setStringAsync(int expireSeconds, String key, String value) { + return CompletableFuture.runAsync(() -> setString(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture setLongAsync(int expireSeconds, String key, long value) { + return CompletableFuture.runAsync(() -> setLong(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public void setExpireSeconds(String key, int expireSeconds) { + if (key == null) return; + CacheEntry entry = container.get(key); + if (entry == null) return; + entry.expireSeconds = expireSeconds; + } + + @Override + public CompletableFuture setExpireSecondsAsync(final String key, final int expireSeconds) { + return CompletableFuture.runAsync(() -> setExpireSeconds(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public int remove(String key) { + if (key == null) return 0; + return container.remove(key) == null ? 0 : 1; + } + + @Override + public long incr(final String key) { + return incr(key, 1); + } + + @Override + public CompletableFuture incrAsync(final String key) { + return CompletableFuture.supplyAsync(() -> incr(key), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public long incr(final String key, long num) { + CacheEntry entry = container.get(key); + if (entry == null) { + synchronized (container) { + entry = container.get(key); + if (entry == null) { + entry = new CacheEntry(CacheEntryType.ATOMIC, key, new AtomicLong(), null, null, null); + container.put(key, entry); + } + } + } + return ((AtomicLong) entry.objectValue).addAndGet(num); + } + + @Override + public CompletableFuture incrAsync(final String key, long num) { + return CompletableFuture.supplyAsync(() -> incr(key, num), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public long decr(final String key) { + return incr(key, -1); + } + + @Override + public CompletableFuture decrAsync(final String key) { + return CompletableFuture.supplyAsync(() -> decr(key), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public long decr(final String key, long num) { + return incr(key, -num); + } + + @Override + public CompletableFuture decrAsync(final String key, long num) { + return CompletableFuture.supplyAsync(() -> decr(key, num), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture removeAsync(final String key) { + return CompletableFuture.supplyAsync(() -> remove(key), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public Collection getCollection(final String key, final Type componentType) { + return (Collection) get(key, componentType); + } + + @Override + public Map> getCollectionMap(final boolean set, final Type componentType, final String... keys) { + Map> map = new HashMap<>(); + for (String key : keys) { + Collection s = (Collection) get(key, componentType); + if (s != null) map.put(key, s); + } + return map; + } + + @Override + public Collection getStringCollection(final String key) { + return (Collection) get(key, String.class); + } + + @Override + public Map> getStringCollectionMap(final boolean set, final String... keys) { + Map> map = new HashMap<>(); + for (String key : keys) { + Collection s = (Collection) get(key, String.class); + if (s != null) map.put(key, s); + } + return map; + } + + @Override + public Map getLongMap(final String... keys) { + Map map = new LinkedHashMap<>(); + for (String key : keys) { + Number n = (Number) get(key, long.class); + map.put(key, n == null ? null : n.longValue()); + } + return map; + } + + @Override + public Long[] getLongArray(final String... keys) { + Long[] rs = new Long[keys.length]; + int index = -1; + for (String key : keys) { + Number n = (Number) get(key, long.class); + rs[++index] = n == null ? null : n.longValue(); + } + return rs; + } + + @Override + public CompletableFuture> getLongMapAsync(final String... keys) { + return CompletableFuture.supplyAsync(() -> getLongMap(keys), getExecutor()); + } + + @Override + public CompletableFuture getLongArrayAsync(final String... keys) { + return CompletableFuture.supplyAsync(() -> getLongArray(keys), getExecutor()); + } + + @Override + public Map getStringMap(final String... keys) { + Map map = new LinkedHashMap<>(); + for (String key : keys) { + Object n = get(key, String.class); + map.put(key, n == null ? null : n.toString()); + } + return map; + } + + @Override + public String[] getStringArray(final String... keys) { + String[] rs = new String[keys.length]; + int index = -1; + for (String key : keys) { + Object n = get(key, String.class); + rs[++index] = n == null ? null : n.toString(); + } + return rs; + } + + @Override + public CompletableFuture> getStringMapAsync(final String... keys) { + return CompletableFuture.supplyAsync(() -> getStringMap(keys), getExecutor()); + } + + @Override + public CompletableFuture getStringArrayAsync(final String... keys) { + return CompletableFuture.supplyAsync(() -> getStringArray(keys), getExecutor()); + } + + @Override + public Map getMap(final Type componentType, final String... keys) { + Map map = new LinkedHashMap<>(); + for (String key : keys) { + map.put(key, (T) get(key, componentType)); + } + return map; + } + + @Override + public CompletableFuture> getMapAsync(final Type componentType, final String... keys) { + return CompletableFuture.supplyAsync(() -> getMap(componentType, keys), getExecutor()); + } + + @Override + public Collection getLongCollection(final String key) { + return (Collection) get(key, long.class); + } + + @Override + public Map> getLongCollectionMap(final boolean set, final String... keys) { + Map> map = new HashMap<>(); + for (String key : keys) { + Collection s = (Collection) get(key, long.class); + if (s != null) map.put(key, s); + } + return map; + } + + @Override + public CompletableFuture>> getCollectionMapAsync(boolean set, Type componentType, String... keys) { + return CompletableFuture.supplyAsync(() -> getCollectionMap(set, componentType, keys), getExecutor()); + } + + @Override + public CompletableFuture> getStringCollectionAsync(final String key) { + return CompletableFuture.supplyAsync(() -> getStringCollection(key), getExecutor()); + } + + @Override + public CompletableFuture>> getStringCollectionMapAsync(final boolean set, final String... keys) { + return CompletableFuture.supplyAsync(() -> getStringCollectionMap(set, keys), getExecutor()); + } + + @Override + public CompletableFuture> getLongCollectionAsync(final String key) { + return CompletableFuture.supplyAsync(() -> getLongCollection(key), getExecutor()); + } + + @Override + public CompletableFuture>> getLongCollectionMapAsync(final boolean set, final String... keys) { + return CompletableFuture.supplyAsync(() -> getLongCollectionMap(set, keys), getExecutor()); + } + + @Override + public CompletableFuture> getCollectionAsync(String key, Type componentType) { + return CompletableFuture.supplyAsync(() -> getCollection(key, componentType), getExecutor()); + } + + @Override + public int getCollectionSize(final String key) { + Collection collection = (Collection) get(key, Object.class); + return collection == null ? 0 : collection.size(); + } + + @Override + public CompletableFuture getCollectionSizeAsync(final String key) { + return CompletableFuture.supplyAsync(() -> getCollectionSize(key), getExecutor()); + } + + @Override + public Collection getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType) { + return (Collection) getAndRefresh(key, expireSeconds, componentType); + } + + @Override + public Collection getStringCollectionAndRefresh(final String key, final int expireSeconds) { + return (Collection) getAndRefresh(key, expireSeconds, String.class); + } + + @Override + public boolean existsSetItem(final String key, final Type type, final T value) { + Collection list = getCollection(key, type); + return list != null && list.contains(value); + } + + @Override + public CompletableFuture existsSetItemAsync(final String key, final Type type, final T value) { + return CompletableFuture.supplyAsync(() -> existsSetItem(key, type, value), getExecutor()); + } + + @Override + public boolean existsStringSetItem(final String key, final String value) { + Collection list = getStringCollection(key); + return list != null && list.contains(value); + } + + @Override + public CompletableFuture existsStringSetItemAsync(final String key, final String value) { + return CompletableFuture.supplyAsync(() -> existsStringSetItem(key, value), getExecutor()); + } + + @Override + public boolean existsLongSetItem(final String key, final long value) { + Collection list = getLongCollection(key); + return list != null && list.contains(value); + } + + @Override + public CompletableFuture existsLongSetItemAsync(final String key, final long value) { + return CompletableFuture.supplyAsync(() -> existsLongSetItem(key, value), getExecutor()); + } + + @Override + public Collection getLongCollectionAndRefresh(String key, int expireSeconds) { + return (Collection) getAndRefresh(key, expireSeconds, long.class); + } + + @Override + public CompletableFuture> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType) { + return CompletableFuture.supplyAsync(() -> getCollectionAndRefresh(key, expireSeconds, componentType), getExecutor()); + } + + @Override + public CompletableFuture> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds) { + return CompletableFuture.supplyAsync(() -> getStringCollectionAndRefresh(key, expireSeconds), getExecutor()); + } + + @Override + public CompletableFuture> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds) { + return CompletableFuture.supplyAsync(() -> getLongCollectionAndRefresh(key, expireSeconds), getExecutor()); + } + + protected void appendListItem(CacheEntryType cacheType, String key, Object value) { + if (key == null) return; + CacheEntry entry = container.get(key); + if (entry == null || !entry.isListCacheType() || entry.listValue == null) { + ConcurrentLinkedQueue list = new ConcurrentLinkedQueue(); + entry = new CacheEntry(cacheType, key, null, null, list, null); + CacheEntry old = container.putIfAbsent(key, entry); + if (old != null) list = old.listValue; + if (list != null) list.add(value); + } else { + entry.listValue.add(value); + } + } + + @Override + public void appendListItem(String key, Type componentType, T value) { + appendListItem(CacheEntryType.OBJECT_LIST, key, value); + } + + @Override + public void appendStringListItem(String key, String value) { + appendListItem(CacheEntryType.STRING_LIST, key, value); + } + + @Override + public void appendLongListItem(String key, long value) { + appendListItem(CacheEntryType.LONG_LIST, key, value); + } + + @Override + public CompletableFuture appendListItemAsync(final String key, final Type componentType, final T value) { + return CompletableFuture.runAsync(() -> appendListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture appendStringListItemAsync(final String key, final String value) { + return CompletableFuture.runAsync(() -> appendStringListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture appendLongListItemAsync(final String key, final long value) { + return CompletableFuture.runAsync(() -> appendLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public int removeListItem(String key, final Type componentType, T value) { + if (key == null) return 0; + CacheEntry entry = container.get(key); + if (entry == null || entry.listValue == null) return 0; + return entry.listValue.remove(value) ? 1 : 0; + } + + @Override + public int removeStringListItem(String key, String value) { + if (key == null) return 0; + CacheEntry entry = container.get(key); + if (entry == null || entry.listValue == null) return 0; + return entry.listValue.remove(value) ? 1 : 0; + } + + @Override + public int removeLongListItem(String key, long value) { + if (key == null) return 0; + CacheEntry entry = container.get(key); + if (entry == null || entry.listValue == null) return 0; + return entry.listValue.remove(value) ? 1 : 0; + } + + @Override + public CompletableFuture removeListItemAsync(final String key, final Type componentType, T value) { + return CompletableFuture.supplyAsync(() -> removeListItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture removeStringListItemAsync(final String key, final String value) { + return CompletableFuture.supplyAsync(() -> removeStringListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture removeLongListItemAsync(final String key, final long value) { + return CompletableFuture.supplyAsync(() -> removeLongListItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public String spopStringSetItem(final String key) { + return (String) spopSetItem(key, String.class); + } + + @Override + public Set spopStringSetItem(final String key, int count) { + return spopSetItem(key, count, String.class); + } + + @Override + public Long spopLongSetItem(final String key) { + return (Long) spopSetItem(key, long.class); + } + + @Override + public Set spopLongSetItem(final String key, int count) { + return spopSetItem(key, count, long.class); + } + + @Override + public T spopSetItem(final String key, final Type componentType) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) { + return null; + } + if (entry.csetValue.isEmpty()) return null; + Iterator it = entry.csetValue.iterator(); + if (it.hasNext()) { + Object obj = it.next(); + if (obj != null && componentType == long.class) obj = ((Number) obj).longValue(); + it.remove(); + return (T) obj; + } + return null; + } + + @Override + public Set spopSetItem(final String key, final int count, final Type componentType) { + if (key == null) return new LinkedHashSet<>(); + CacheEntry entry = container.get(key); + if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) { + return new LinkedHashSet<>(); + } + if (entry.csetValue.isEmpty()) return new LinkedHashSet<>(); + Iterator it = entry.csetValue.iterator(); + Set list = new LinkedHashSet<>(); + int index = 0; + while (it.hasNext()) { + Object obj = it.next(); + if (obj != null && componentType == long.class) obj = ((Number) obj).longValue(); + list.add((T) obj); + it.remove(); + if (++index >= count) break; + } + return list; + } + + protected void appendSetItem(CacheEntryType cacheType, String key, Object value) { + if (key == null) return; + CacheEntry entry = container.get(key); + if (entry == null || !entry.isSetCacheType() || entry.csetValue == null) { + CopyOnWriteArraySet set = new CopyOnWriteArraySet(); + entry = new CacheEntry(cacheType, key, null, set, null, null); + CacheEntry old = container.putIfAbsent(key, entry); + if (old != null) set = old.csetValue; + if (set != null) set.add(value); + } else { + entry.csetValue.add(value); + } + } + + @Override + public void appendSetItem(String key, final Type componentType, T value) { + appendSetItem(CacheEntryType.OBJECT_SET, key, value); + } + + @Override + public void appendStringSetItem(String key, String value) { + appendSetItem(CacheEntryType.OBJECT_SET, key, value); + } + + @Override + public void appendLongSetItem(String key, long value) { + appendSetItem(CacheEntryType.OBJECT_SET, key, value); + } + + @Override + public CompletableFuture appendSetItemAsync(final String key, final Type componentType, T value) { + return CompletableFuture.runAsync(() -> appendSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture appendStringSetItemAsync(final String key, final String value) { + return CompletableFuture.runAsync(() -> appendStringSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture appendLongSetItemAsync(final String key, final long value) { + return CompletableFuture.runAsync(() -> appendLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public int removeSetItem(String key, Type type, T value) { + if (key == null) return 0; + CacheEntry entry = container.get(key); + if (entry == null || entry.csetValue == null) return 0; + return entry.csetValue.remove(value) ? 1 : 0; + } + + @Override + public int removeStringSetItem(String key, String value) { + if (key == null) return 0; + CacheEntry entry = container.get(key); + if (entry == null || entry.csetValue == null) return 0; + return entry.csetValue.remove(value) ? 1 : 0; + } + + @Override + public int removeLongSetItem(String key, long value) { + if (key == null) return 0; + CacheEntry entry = container.get(key); + if (entry == null || entry.csetValue == null) return 0; + return entry.csetValue.remove(value) ? 1 : 0; + } + + @Override + public CompletableFuture removeSetItemAsync(final String key, final Type componentType, final T value) { + return CompletableFuture.supplyAsync(() -> removeSetItem(key, componentType, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture removeStringSetItemAsync(final String key, final String value) { + return CompletableFuture.supplyAsync(() -> removeStringSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture removeLongSetItemAsync(final String key, final long value) { + return CompletableFuture.supplyAsync(() -> removeLongSetItem(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public byte[] getBytes(final String key) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return null; + return (byte[]) entry.objectValue; + } + + @Override + public CompletableFuture getBytesAsync(final String key) { + return CompletableFuture.supplyAsync(() -> getBytes(key), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public byte[] getBytesAndRefresh(String key, final int expireSeconds) { + if (key == null) return null; + CacheEntry entry = container.get(key); + if (entry == null || entry.isExpired()) return null; + entry.lastAccessed = (int) (System.currentTimeMillis() / 1000); + entry.expireSeconds = expireSeconds; + return (byte[]) entry.objectValue; + } + + @Override + public CompletableFuture getBytesAndRefreshAsync(final String key, final int expireSeconds) { + return CompletableFuture.supplyAsync(() -> getBytesAndRefresh(key, expireSeconds), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public void setBytes(String key, byte[] value) { + set(CacheEntryType.BYTES, key, value); + } + + @Override + public CompletableFuture setBytesAsync(final String key, byte[] value) { + return CompletableFuture.runAsync(() -> setBytes(key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public void setBytes(final int expireSeconds, final String key, final byte[] value) { + set(CacheEntryType.BYTES, expireSeconds, key, value); + } + + @Override + public CompletableFuture setBytesAsync(final int expireSeconds, final String key, byte[] value) { + return CompletableFuture.runAsync(() -> setBytes(expireSeconds, key, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public void setBytes(final String key, final Convert convert, final Type type, final T value) { + set(CacheEntryType.BYTES, key, convert.convertToBytes(type, value)); + } + + @Override + public CompletableFuture setBytesAsync(final String key, final Convert convert, final Type type, final T value) { + return CompletableFuture.runAsync(() -> setBytes(key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public void setBytes(final int expireSeconds, final String key, final Convert convert, final Type type, final T value) { + set(CacheEntryType.BYTES, expireSeconds, key, convert.convertToBytes(type, value)); + } + + @Override + public CompletableFuture setBytesAsync(final int expireSeconds, final String key, final Convert convert, final Type type, final T value) { + return CompletableFuture.runAsync(() -> setBytes(expireSeconds, key, convert, type, value), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public List queryKeys() { + return new ArrayList<>(container.keySet()); + } + + @Override + public List queryKeysStartsWith(String startsWith) { + if (startsWith == null) return queryKeys(); + List rs = new ArrayList<>(); + container.keySet().stream().filter(x -> x.startsWith(startsWith)).forEach(x -> rs.add(x)); + return rs; + } + + @Override + public List queryKeysEndsWith(String endsWith) { + if (endsWith == null) return queryKeys(); + List rs = new ArrayList<>(); + container.keySet().stream().filter(x -> x.endsWith(endsWith)).forEach(x -> rs.add(x)); + return rs; + } + + @Override + public int getKeySize() { + return container.size(); + } + + @Override + public CompletableFuture> queryKeysAsync() { + return CompletableFuture.completedFuture(new ArrayList<>(container.keySet())); + } + + @Override + public CompletableFuture> queryKeysStartsWithAsync(String startsWith) { + return CompletableFuture.completedFuture(queryKeysStartsWith(startsWith)); + } + + @Override + public CompletableFuture> queryKeysEndsWithAsync(String endsWith) { + return CompletableFuture.completedFuture(queryKeysEndsWith(endsWith)); + } + + @Override + public CompletableFuture getKeySizeAsync() { + return CompletableFuture.completedFuture(container.size()); + } + + @Override + public CompletableFuture spopSetItemAsync(String key, Type componentType) { + return CompletableFuture.supplyAsync(() -> spopSetItem(key, componentType), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture> spopSetItemAsync(String key, int count, Type componentType) { + return CompletableFuture.supplyAsync(() -> spopSetItem(key, count, componentType), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture spopStringSetItemAsync(String key) { + return CompletableFuture.supplyAsync(() -> spopStringSetItem(key), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture> spopStringSetItemAsync(String key, int count) { + return CompletableFuture.supplyAsync(() -> spopStringSetItem(key, count), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture spopLongSetItemAsync(String key) { + return CompletableFuture.supplyAsync(() -> spopLongSetItem(key), getExecutor()).whenComplete(futureCompleteConsumer); + } + + @Override + public CompletableFuture> spopLongSetItemAsync(String key, int count) { + return CompletableFuture.supplyAsync(() -> spopLongSetItem(key, count), getExecutor()).whenComplete(futureCompleteConsumer); + } + + public static enum CacheEntryType { + LONG, STRING, OBJECT, BYTES, ATOMIC, MAP, + LONG_SET, STRING_SET, OBJECT_SET, + LONG_LIST, STRING_LIST, OBJECT_LIST; + } + + public static final class CacheEntry { + + final CacheEntryType cacheType; + + final String key; + + //<=0表示永久保存 + int expireSeconds; + + volatile int lastAccessed; //最后刷新时间 + + T objectValue; + + ConcurrentHashMap mapValue; + + CopyOnWriteArraySet csetValue; + + ConcurrentLinkedQueue listValue; + + public CacheEntry(CacheEntryType cacheType, String key, T objectValue, CopyOnWriteArraySet csetValue, ConcurrentLinkedQueue listValue, ConcurrentHashMap mapValue) { + this(cacheType, 0, key, objectValue, csetValue, listValue, mapValue); + } + + public CacheEntry(CacheEntryType cacheType, int expireSeconds, String key, T objectValue, CopyOnWriteArraySet csetValue, ConcurrentLinkedQueue listValue, ConcurrentHashMap mapValue) { + this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, objectValue, csetValue, listValue, mapValue); + } + + @ConstructorParameters({"cacheType", "expireSeconds", "lastAccessed", "key", "objectValue", "csetValue", "listValue", "mapValue"}) + public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, String key, T objectValue, CopyOnWriteArraySet csetValue, ConcurrentLinkedQueue listValue, ConcurrentHashMap mapValue) { + this.cacheType = cacheType; + this.expireSeconds = expireSeconds; + this.lastAccessed = lastAccessed; + this.key = key; + this.objectValue = objectValue; + this.csetValue = csetValue; + this.listValue = listValue; + this.mapValue = mapValue; + } + + @Override + public String toString() { + return JsonFactory.root().getConvert().convertTo(this); + } + + @ConvertColumn(ignore = true) + public boolean isListCacheType() { + return cacheType == CacheEntryType.LONG_LIST || cacheType == CacheEntryType.STRING_LIST || cacheType == CacheEntryType.OBJECT_LIST; + } + + @ConvertColumn(ignore = true) + public boolean isSetCacheType() { + return cacheType == CacheEntryType.LONG_SET || cacheType == CacheEntryType.STRING_SET || cacheType == CacheEntryType.OBJECT_SET; + } + + @ConvertColumn(ignore = true) + public boolean isMapCacheType() { + return cacheType == CacheEntryType.MAP; + } + + @ConvertColumn(ignore = true) + public boolean isExpired() { + return (expireSeconds > 0 && lastAccessed + expireSeconds < (System.currentTimeMillis() / 1000)); + } + + public CacheEntryType getCacheType() { + return cacheType; + } + + public int getExpireSeconds() { + return expireSeconds; + } + + public int getLastAccessed() { + return lastAccessed; + } + + public String getKey() { + return key; + } + + public T getObjectValue() { + return objectValue; + } + + public CopyOnWriteArraySet getCsetValue() { + return csetValue; + } + + public ConcurrentLinkedQueue getListValue() { + return listValue; + } + + public ConcurrentHashMap getMapValue() { + return mapValue; + } + } +} diff --git a/src/org/redkale/source/CacheSource.java b/src/main/java/org/redkale/source/CacheSource.java similarity index 77% rename from src/org/redkale/source/CacheSource.java rename to src/main/java/org/redkale/source/CacheSource.java index 981441957..ad0377e9e 100644 --- a/src/org/redkale/source/CacheSource.java +++ b/src/main/java/org/redkale/source/CacheSource.java @@ -1,515 +1,409 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.lang.reflect.Type; -import java.util.*; -import java.util.concurrent.*; -import org.redkale.convert.*; -import org.redkale.convert.json.JsonFactory; -import org.redkale.util.*; - -/** - * Redkale中缓存数据源的核心类。 主要供业务开发者使用, 技术开发者提供CacheSource的实现。
    - * CacheSource提供三种数据类型操作: String、Long和泛型指定的数据类型。
    - * String统一用setString、getString等系列方法。
    - * Long统一用setLong、getLong、incr等系列方法。
    - * 其他则供自定义数据类型使用。 - * - * param V value的类型 移除 @2.4.0 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface CacheSource { - - public String getType(); - - //ServiceLoader时判断配置是否符合当前实现类 - public boolean match(AnyValue config); - - default boolean isOpen() { - return true; - } - - public boolean exists(final String key); - - public T get(final String key, final Type type); - - public T getAndRefresh(final String key, final int expireSeconds, final Type type); - - //----------- hxxx -------------- - public int hremove(final String key, String... fields); - - public List hkeys(final String key); - - public int hsize(final String key); - - public long hincr(final String key, String field); - - public long hincr(final String key, String field, long num); - - public long hdecr(final String key, String field); - - public long hdecr(final String key, String field, long num); - - public boolean hexists(final String key, String field); - - public void hset(final String key, final String field, final Convert convert, final T value); - - public void hset(final String key, final String field, final Type type, final T value); - - public void hset(final String key, final String field, final Convert convert, final Type type, final T value); - - public void hsetString(final String key, final String field, final String value); - - public void hsetLong(final String key, final String field, final long value); - - public void hmset(final String key, final Serializable... values); - - public List hmget(final String key, final Type type, final String... fields); - - public Map hmap(final String key, final Type type, int offset, int limit); - - public Map hmap(final String key, final Type type, int offset, int limit, String pattern); - - public T hget(final String key, final String field, final Type type); - - public String hgetString(final String key, final String field); - - public long hgetLong(final String key, final String field, long defValue); - //----------- hxxx -------------- - - public void refresh(final String key, final int expireSeconds); - - public void set(final String key, final Convert convert, final T value); - - public void set(final String key, final Type type, final T value); - - public void set(final String key, final Convert convert, final Type type, final T value); - - public void set(final int expireSeconds, final String key, final Convert convert, final T value); - - public void set(final int expireSeconds, final String key, final Type type, final T value); - - public void set(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); - - public void setExpireSeconds(final String key, final int expireSeconds); - - public int remove(final String key); - - public long incr(final String key); - - public long incr(final String key, long num); - - public long decr(final String key); - - public long decr(final String key, long num); - - public Map getMap(final Type componentType, final String... keys); - - public Collection getCollection(final String key, final Type componentType); - - public Map> getCollectionMap(final boolean set, final Type componentType, final String... keys); - - public int getCollectionSize(final String key); - - public Collection getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType); - - public void appendListItem(final String key, final Type componentType, final T value); - - public int removeListItem(final String key, final Type componentType, final T value); - - public boolean existsSetItem(final String key, final Type componentType, final T value); - - public void appendSetItem(final String key, final Type componentType, final T value); - - public int removeSetItem(final String key, final Type componentType, final T value); - - public T spopSetItem(final String key, final Type componentType); - - public List spopSetItem(final String key, final int count, final Type componentType); - - public byte[] getBytes(final String key); - - public byte[] getBytesAndRefresh(final String key, final int expireSeconds); - - public void setBytes(final String key, final byte[] value); - - public void setBytes(final int expireSeconds, final String key, final byte[] value); - - public void setBytes(final String key, final Convert convert, final Type type, final T value); - - public void setBytes(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); - - public List queryKeys(); - - public List queryKeysStartsWith(String startsWith); - - public List queryKeysEndsWith(String endsWith); - - public int getKeySize(); - - public List> queryList(); - - public String getString(final String key); - - public String getStringAndRefresh(final String key, final int expireSeconds); - - public void setString(final String key, final String value); - - public void setString(final int expireSeconds, final String key, final String value); - - public Map getStringMap(final String... keys); - - public String[] getStringArray(final String... keys); - - public Collection getStringCollection(final String key); - - public Map> getStringCollectionMap(final boolean set, final String... keys); - - public Collection getStringCollectionAndRefresh(final String key, final int expireSeconds); - - public void appendStringListItem(final String key, final String value); - - public String spopStringSetItem(final String key); - - public List spopStringSetItem(final String key, final int count); - - public int removeStringListItem(final String key, final String value); - - public boolean existsStringSetItem(final String key, final String value); - - public void appendStringSetItem(final String key, final String value); - - public int removeStringSetItem(final String key, final String value); - - public long getLong(final String key, long defValue); - - public long getLongAndRefresh(final String key, final int expireSeconds, long defValue); - - public void setLong(final String key, final long value); - - public void setLong(final int expireSeconds, final String key, final long value); - - public Map getLongMap(final String... keys); - - public Long[] getLongArray(final String... keys); - - public Collection getLongCollection(final String key); - - public Map> getLongCollectionMap(final boolean set, final String... keys); - - public Collection getLongCollectionAndRefresh(final String key, final int expireSeconds); - - public void appendLongListItem(final String key, final long value); - - public Long spopLongSetItem(final String key); - - public List spopLongSetItem(final String key, final int count); - - public int removeLongListItem(final String key, final long value); - - public boolean existsLongSetItem(final String key, final long value); - - public void appendLongSetItem(final String key, final long value); - - public int removeLongSetItem(final String key, final long value); - - //---------------------- CompletableFuture 异步版 --------------------------------- - public CompletableFuture existsAsync(final String key); - - public CompletableFuture getAsync(final String key, final Type type); - - public CompletableFuture getAndRefreshAsync(final String key, final int expireSeconds, final Type type); - - public CompletableFuture refreshAsync(final String key, final int expireSeconds); - - public CompletableFuture setAsync(final String key, final Convert convert, final T value); - - public CompletableFuture setAsync(final String key, final Type type, final T value); - - public CompletableFuture setAsync(final String key, final Convert convert, final Type type, final T value); - - public CompletableFuture setAsync(final int expireSeconds, final String key, final Convert convert, final T value); - - public CompletableFuture setAsync(final int expireSeconds, final String key, final Type type, final T value); - - public CompletableFuture setAsync(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); - - public CompletableFuture setExpireSecondsAsync(final String key, final int expireSeconds); - - public CompletableFuture removeAsync(final String key); - - public CompletableFuture incrAsync(final String key); - - public CompletableFuture incrAsync(final String key, long num); - - public CompletableFuture decrAsync(final String key); - - public CompletableFuture decrAsync(final String key, long num); - - //----------- hxxx -------------- - public CompletableFuture hremoveAsync(final String key, String... fields); - - public CompletableFuture> hkeysAsync(final String key); - - public CompletableFuture hsizeAsync(final String key); - - public CompletableFuture hincrAsync(final String key, String field); - - public CompletableFuture hincrAsync(final String key, String field, long num); - - public CompletableFuture hdecrAsync(final String key, String field); - - public CompletableFuture hdecrAsync(final String key, String field, long num); - - public CompletableFuture hexistsAsync(final String key, String field); - - public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final T value); - - public CompletableFuture hsetAsync(final String key, final String field, final Type type, final T value); - - public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final Type type, final T value); - - public CompletableFuture hsetStringAsync(final String key, final String field, final String value); - - public CompletableFuture hsetLongAsync(final String key, final String field, final long value); - - public CompletableFuture hmsetAsync(final String key, final Serializable... values); - - public CompletableFuture> hmgetAsync(final String key, final Type type, final String... fields); - - public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit); - - public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit, String pattern); - - public CompletableFuture hgetAsync(final String key, final String field, final Type type); - - public CompletableFuture hgetStringAsync(final String key, final String field); - - public CompletableFuture hgetLongAsync(final String key, final String field, long defValue); - //----------- hxxx -------------- - - public CompletableFuture> getMapAsync(final Type componentType, final String... keys); - - public CompletableFuture> getCollectionAsync(final String key, final Type componentType); - - public CompletableFuture>> getCollectionMapAsync(final boolean set, final Type componentType, final String... keys); - - public CompletableFuture getCollectionSizeAsync(final String key); - - public CompletableFuture> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType); - - public CompletableFuture spopSetItemAsync(final String key, final Type componentType); - - public CompletableFuture> spopSetItemAsync(final String key, final int count, final Type componentType); - - public CompletableFuture appendListItemAsync(final String key, final Type componentType, final T value); - - public CompletableFuture removeListItemAsync(final String key, final Type componentType, final T value); - - public CompletableFuture existsSetItemAsync(final String key, final Type componentType, final T value); - - public CompletableFuture appendSetItemAsync(final String key, final Type componentType, final T value); - - public CompletableFuture removeSetItemAsync(final String key, final Type componentType, final T value); - - public CompletableFuture getBytesAsync(final String key); - - public CompletableFuture getBytesAndRefreshAsync(final String key, final int expireSeconds); - - public CompletableFuture setBytesAsync(final String key, final byte[] value); - - public CompletableFuture setBytesAsync(final int expireSeconds, final String key, final byte[] value); - - public CompletableFuture setBytesAsync(final String key, final Convert convert, final Type type, final T value); - - public CompletableFuture setBytesAsync(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); - - public CompletableFuture> queryKeysAsync(); - - public CompletableFuture> queryKeysStartsWithAsync(String startsWith); - - public CompletableFuture> queryKeysEndsWithAsync(String endsWith); - - public CompletableFuture getKeySizeAsync(); - - public CompletableFuture>> queryListAsync(); - - public CompletableFuture getStringAsync(final String key); - - public CompletableFuture getStringAndRefreshAsync(final String key, final int expireSeconds); - - public CompletableFuture setStringAsync(final String key, final String value); - - public CompletableFuture setStringAsync(final int expireSeconds, final String key, final String value); - - public CompletableFuture> getStringMapAsync(final String... keys); - - public CompletableFuture getStringArrayAsync(final String... keys); - - public CompletableFuture> getStringCollectionAsync(final String key); - - public CompletableFuture>> getStringCollectionMapAsync(final boolean set, final String... keys); - - public CompletableFuture> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds); - - public CompletableFuture appendStringListItemAsync(final String key, final String value); - - public CompletableFuture spopStringSetItemAsync(final String key); - - public CompletableFuture> spopStringSetItemAsync(final String key, final int count); - - public CompletableFuture removeStringListItemAsync(final String key, final String value); - - public CompletableFuture existsStringSetItemAsync(final String key, final String value); - - public CompletableFuture appendStringSetItemAsync(final String key, final String value); - - public CompletableFuture removeStringSetItemAsync(final String key, final String value); - - public CompletableFuture getLongAsync(final String key, long defValue); - - public CompletableFuture getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue); - - public CompletableFuture setLongAsync(final String key, long value); - - public CompletableFuture setLongAsync(final int expireSeconds, final String key, final long value); - - public CompletableFuture> getLongMapAsync(final String... keys); - - public CompletableFuture getLongArrayAsync(final String... keys); - - public CompletableFuture> getLongCollectionAsync(final String key); - - public CompletableFuture>> getLongCollectionMapAsync(final boolean set, final String... keys); - - public CompletableFuture> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds); - - public CompletableFuture appendLongListItemAsync(final String key, final long value); - - public CompletableFuture spopLongSetItemAsync(final String key); - - public CompletableFuture> spopLongSetItemAsync(final String key, final int count); - - public CompletableFuture removeLongListItemAsync(final String key, final long value); - - public CompletableFuture existsLongSetItemAsync(final String key, final long value); - - public CompletableFuture appendLongSetItemAsync(final String key, final long value); - - public CompletableFuture removeLongSetItemAsync(final String key, final long value); - - default CompletableFuture isOpenAsync() { - return CompletableFuture.completedFuture(isOpen()); - } - - public static enum CacheEntryType { - LONG, STRING, OBJECT, BYTES, ATOMIC, MAP, - LONG_SET, STRING_SET, OBJECT_SET, - LONG_LIST, STRING_LIST, OBJECT_LIST; - } - - public static final class CacheEntry { - - final CacheEntryType cacheType; - - final String key; - - //<=0表示永久保存 - int expireSeconds; - - volatile int lastAccessed; //最后刷新时间 - - T objectValue; - - ConcurrentHashMap mapValue; - - CopyOnWriteArraySet csetValue; - - ConcurrentLinkedQueue listValue; - - public CacheEntry(CacheEntryType cacheType, String key, T objectValue, CopyOnWriteArraySet csetValue, ConcurrentLinkedQueue listValue, ConcurrentHashMap mapValue) { - this(cacheType, 0, key, objectValue, csetValue, listValue, mapValue); - } - - public CacheEntry(CacheEntryType cacheType, int expireSeconds, String key, T objectValue, CopyOnWriteArraySet csetValue, ConcurrentLinkedQueue listValue, ConcurrentHashMap mapValue) { - this(cacheType, expireSeconds, (int) (System.currentTimeMillis() / 1000), key, objectValue, csetValue, listValue, mapValue); - } - - @ConstructorParameters({"cacheType", "expireSeconds", "lastAccessed", "key", "objectValue", "csetValue", "listValue", "mapValue"}) - public CacheEntry(CacheEntryType cacheType, int expireSeconds, int lastAccessed, String key, T objectValue, CopyOnWriteArraySet csetValue, ConcurrentLinkedQueue listValue, ConcurrentHashMap mapValue) { - this.cacheType = cacheType; - this.expireSeconds = expireSeconds; - this.lastAccessed = lastAccessed; - this.key = key; - this.objectValue = objectValue; - this.csetValue = csetValue; - this.listValue = listValue; - this.mapValue = mapValue; - } - - @Override - public String toString() { - return JsonFactory.root().getConvert().convertTo(this); - } - - @ConvertColumn(ignore = true) - public boolean isListCacheType() { - return cacheType == CacheEntryType.LONG_LIST || cacheType == CacheEntryType.STRING_LIST || cacheType == CacheEntryType.OBJECT_LIST; - } - - @ConvertColumn(ignore = true) - public boolean isSetCacheType() { - return cacheType == CacheEntryType.LONG_SET || cacheType == CacheEntryType.STRING_SET || cacheType == CacheEntryType.OBJECT_SET; - } - - @ConvertColumn(ignore = true) - public boolean isMapCacheType() { - return cacheType == CacheEntryType.MAP; - } - - @ConvertColumn(ignore = true) - public boolean isExpired() { - return (expireSeconds > 0 && lastAccessed + expireSeconds < (System.currentTimeMillis() / 1000)); - } - - public CacheEntryType getCacheType() { - return cacheType; - } - - public int getExpireSeconds() { - return expireSeconds; - } - - public int getLastAccessed() { - return lastAccessed; - } - - public String getKey() { - return key; - } - - public T getObjectValue() { - return objectValue; - } - - public CopyOnWriteArraySet getCsetValue() { - return csetValue; - } - - public ConcurrentLinkedQueue getListValue() { - return listValue; - } - - public ConcurrentHashMap getMapValue() { - return mapValue; - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.lang.reflect.Type; +import java.util.*; +import java.util.concurrent.*; +import org.redkale.convert.*; +import org.redkale.util.*; + +/** + * Redkale中缓存数据源的核心类。 主要供业务开发者使用, 技术开发者提供CacheSource的实现。
    + * CacheSource提供三种数据类型操作: String、Long、byte[]和泛型指定的数据类型。
    + * String统一用setString、getString等系列方法。
    + * Long统一用setLong、getLong、incr等系列方法。
    + * byte[]统一用setBytes、getBytes等系列方法。
    + * 其他则供自定义数据类型使用。 + * + * param V value的类型 移除 @2.4.0 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface CacheSource { + + public String getType(); + + //ServiceLoader时判断配置是否符合当前实现类 + public boolean acceptsConf(AnyValue config); + + default boolean isOpen() { + return true; + } + + public boolean exists(final String key); + + public T get(final String key, final Type type); + + public T getAndRefresh(final String key, final int expireSeconds, final Type type); + + //----------- hxxx -------------- + public int hremove(final String key, String... fields); + + public List hkeys(final String key); + + public int hsize(final String key); + + public long hincr(final String key, String field); + + public long hincr(final String key, String field, long num); + + public long hdecr(final String key, String field); + + public long hdecr(final String key, String field, long num); + + public boolean hexists(final String key, String field); + + public void hset(final String key, final String field, final Convert convert, final T value); + + public void hset(final String key, final String field, final Type type, final T value); + + public void hset(final String key, final String field, final Convert convert, final Type type, final T value); + + public void hsetString(final String key, final String field, final String value); + + public void hsetLong(final String key, final String field, final long value); + + public void hmset(final String key, final Serializable... values); + + public List hmget(final String key, final Type type, final String... fields); + + public Map hmap(final String key, final Type type, int offset, int limit); + + public Map hmap(final String key, final Type type, int offset, int limit, String pattern); + + public T hget(final String key, final String field, final Type type); + + public String hgetString(final String key, final String field); + + public long hgetLong(final String key, final String field, long defValue); + //----------- hxxx -------------- + + public void refresh(final String key, final int expireSeconds); + + public void set(final String key, final Convert convert, final T value); + + public void set(final String key, final Type type, final T value); + + public void set(final String key, final Convert convert, final Type type, final T value); + + public void set(final int expireSeconds, final String key, final Convert convert, final T value); + + public void set(final int expireSeconds, final String key, final Type type, final T value); + + public void set(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); + + public void setExpireSeconds(final String key, final int expireSeconds); + + public int remove(final String key); + + public long incr(final String key); + + public long incr(final String key, long num); + + public long decr(final String key); + + public long decr(final String key, long num); + + public Map getMap(final Type componentType, final String... keys); + + public Collection getCollection(final String key, final Type componentType); + + public Map> getCollectionMap(final boolean set, final Type componentType, final String... keys); + + public int getCollectionSize(final String key); + + public Collection getCollectionAndRefresh(final String key, final int expireSeconds, final Type componentType); + + public void appendListItem(final String key, final Type componentType, final T value); + + public int removeListItem(final String key, final Type componentType, final T value); + + public boolean existsSetItem(final String key, final Type componentType, final T value); + + public void appendSetItem(final String key, final Type componentType, final T value); + + public int removeSetItem(final String key, final Type componentType, final T value); + + public T spopSetItem(final String key, final Type componentType); + + public Set spopSetItem(final String key, final int count, final Type componentType); + + public byte[] getBytes(final String key); + + public byte[] getBytesAndRefresh(final String key, final int expireSeconds); + + public void setBytes(final String key, final byte[] value); + + public void setBytes(final int expireSeconds, final String key, final byte[] value); + + public void setBytes(final String key, final Convert convert, final Type type, final T value); + + public void setBytes(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); + + public List queryKeys(); + + public List queryKeysStartsWith(String startsWith); + + public List queryKeysEndsWith(String endsWith); + + public int getKeySize(); + + public String getString(final String key); + + public String getStringAndRefresh(final String key, final int expireSeconds); + + public void setString(final String key, final String value); + + public void setString(final int expireSeconds, final String key, final String value); + + public Map getStringMap(final String... keys); + + public String[] getStringArray(final String... keys); + + public Collection getStringCollection(final String key); + + public Map> getStringCollectionMap(final boolean set, final String... keys); + + public Collection getStringCollectionAndRefresh(final String key, final int expireSeconds); + + public void appendStringListItem(final String key, final String value); + + public String spopStringSetItem(final String key); + + public Set spopStringSetItem(final String key, final int count); + + public int removeStringListItem(final String key, final String value); + + public boolean existsStringSetItem(final String key, final String value); + + public void appendStringSetItem(final String key, final String value); + + public int removeStringSetItem(final String key, final String value); + + public long getLong(final String key, long defValue); + + public long getLongAndRefresh(final String key, final int expireSeconds, long defValue); + + public void setLong(final String key, final long value); + + public void setLong(final int expireSeconds, final String key, final long value); + + public Map getLongMap(final String... keys); + + public Long[] getLongArray(final String... keys); + + public Collection getLongCollection(final String key); + + public Map> getLongCollectionMap(final boolean set, final String... keys); + + public Collection getLongCollectionAndRefresh(final String key, final int expireSeconds); + + public void appendLongListItem(final String key, final long value); + + public Long spopLongSetItem(final String key); + + public Set spopLongSetItem(final String key, final int count); + + public int removeLongListItem(final String key, final long value); + + public boolean existsLongSetItem(final String key, final long value); + + public void appendLongSetItem(final String key, final long value); + + public int removeLongSetItem(final String key, final long value); + + //---------------------- CompletableFuture 异步版 --------------------------------- + public CompletableFuture existsAsync(final String key); + + public CompletableFuture getAsync(final String key, final Type type); + + public CompletableFuture getAndRefreshAsync(final String key, final int expireSeconds, final Type type); + + public CompletableFuture refreshAsync(final String key, final int expireSeconds); + + public CompletableFuture setAsync(final String key, final Convert convert, final T value); + + public CompletableFuture setAsync(final String key, final Type type, final T value); + + public CompletableFuture setAsync(final String key, final Convert convert, final Type type, final T value); + + public CompletableFuture setAsync(final int expireSeconds, final String key, final Convert convert, final T value); + + public CompletableFuture setAsync(final int expireSeconds, final String key, final Type type, final T value); + + public CompletableFuture setAsync(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); + + public CompletableFuture setExpireSecondsAsync(final String key, final int expireSeconds); + + public CompletableFuture removeAsync(final String key); + + public CompletableFuture incrAsync(final String key); + + public CompletableFuture incrAsync(final String key, long num); + + public CompletableFuture decrAsync(final String key); + + public CompletableFuture decrAsync(final String key, long num); + + //----------- hxxx -------------- + public CompletableFuture hremoveAsync(final String key, String... fields); + + public CompletableFuture> hkeysAsync(final String key); + + public CompletableFuture hsizeAsync(final String key); + + public CompletableFuture hincrAsync(final String key, String field); + + public CompletableFuture hincrAsync(final String key, String field, long num); + + public CompletableFuture hdecrAsync(final String key, String field); + + public CompletableFuture hdecrAsync(final String key, String field, long num); + + public CompletableFuture hexistsAsync(final String key, String field); + + public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final T value); + + public CompletableFuture hsetAsync(final String key, final String field, final Type type, final T value); + + public CompletableFuture hsetAsync(final String key, final String field, final Convert convert, final Type type, final T value); + + public CompletableFuture hsetStringAsync(final String key, final String field, final String value); + + public CompletableFuture hsetLongAsync(final String key, final String field, final long value); + + public CompletableFuture hmsetAsync(final String key, final Serializable... values); + + public CompletableFuture> hmgetAsync(final String key, final Type type, final String... fields); + + public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit); + + public CompletableFuture> hmapAsync(final String key, final Type type, int offset, int limit, String pattern); + + public CompletableFuture hgetAsync(final String key, final String field, final Type type); + + public CompletableFuture hgetStringAsync(final String key, final String field); + + public CompletableFuture hgetLongAsync(final String key, final String field, long defValue); + //----------- hxxx -------------- + + public CompletableFuture> getMapAsync(final Type componentType, final String... keys); + + public CompletableFuture> getCollectionAsync(final String key, final Type componentType); + + public CompletableFuture>> getCollectionMapAsync(final boolean set, final Type componentType, final String... keys); + + public CompletableFuture getCollectionSizeAsync(final String key); + + public CompletableFuture> getCollectionAndRefreshAsync(final String key, final int expireSeconds, final Type componentType); + + public CompletableFuture spopSetItemAsync(final String key, final Type componentType); + + public CompletableFuture> spopSetItemAsync(final String key, final int count, final Type componentType); + + public CompletableFuture appendListItemAsync(final String key, final Type componentType, final T value); + + public CompletableFuture removeListItemAsync(final String key, final Type componentType, final T value); + + public CompletableFuture existsSetItemAsync(final String key, final Type componentType, final T value); + + public CompletableFuture appendSetItemAsync(final String key, final Type componentType, final T value); + + public CompletableFuture removeSetItemAsync(final String key, final Type componentType, final T value); + + public CompletableFuture getBytesAsync(final String key); + + public CompletableFuture getBytesAndRefreshAsync(final String key, final int expireSeconds); + + public CompletableFuture setBytesAsync(final String key, final byte[] value); + + public CompletableFuture setBytesAsync(final int expireSeconds, final String key, final byte[] value); + + public CompletableFuture setBytesAsync(final String key, final Convert convert, final Type type, final T value); + + public CompletableFuture setBytesAsync(final int expireSeconds, final String key, final Convert convert, final Type type, final T value); + + public CompletableFuture> queryKeysAsync(); + + public CompletableFuture> queryKeysStartsWithAsync(String startsWith); + + public CompletableFuture> queryKeysEndsWithAsync(String endsWith); + + public CompletableFuture getKeySizeAsync(); + + public CompletableFuture getStringAsync(final String key); + + public CompletableFuture getStringAndRefreshAsync(final String key, final int expireSeconds); + + public CompletableFuture setStringAsync(final String key, final String value); + + public CompletableFuture setStringAsync(final int expireSeconds, final String key, final String value); + + public CompletableFuture> getStringMapAsync(final String... keys); + + public CompletableFuture getStringArrayAsync(final String... keys); + + public CompletableFuture> getStringCollectionAsync(final String key); + + public CompletableFuture>> getStringCollectionMapAsync(final boolean set, final String... keys); + + public CompletableFuture> getStringCollectionAndRefreshAsync(final String key, final int expireSeconds); + + public CompletableFuture appendStringListItemAsync(final String key, final String value); + + public CompletableFuture spopStringSetItemAsync(final String key); + + public CompletableFuture> spopStringSetItemAsync(final String key, final int count); + + public CompletableFuture removeStringListItemAsync(final String key, final String value); + + public CompletableFuture existsStringSetItemAsync(final String key, final String value); + + public CompletableFuture appendStringSetItemAsync(final String key, final String value); + + public CompletableFuture removeStringSetItemAsync(final String key, final String value); + + public CompletableFuture getLongAsync(final String key, long defValue); + + public CompletableFuture getLongAndRefreshAsync(final String key, final int expireSeconds, long defValue); + + public CompletableFuture setLongAsync(final String key, long value); + + public CompletableFuture setLongAsync(final int expireSeconds, final String key, final long value); + + public CompletableFuture> getLongMapAsync(final String... keys); + + public CompletableFuture getLongArrayAsync(final String... keys); + + public CompletableFuture> getLongCollectionAsync(final String key); + + public CompletableFuture>> getLongCollectionMapAsync(final boolean set, final String... keys); + + public CompletableFuture> getLongCollectionAndRefreshAsync(final String key, final int expireSeconds); + + public CompletableFuture appendLongListItemAsync(final String key, final long value); + + public CompletableFuture spopLongSetItemAsync(final String key); + + public CompletableFuture> spopLongSetItemAsync(final String key, final int count); + + public CompletableFuture removeLongListItemAsync(final String key, final long value); + + public CompletableFuture existsLongSetItemAsync(final String key, final long value); + + public CompletableFuture appendLongSetItemAsync(final String key, final long value); + + public CompletableFuture removeLongSetItemAsync(final String key, final long value); + + default CompletableFuture isOpenAsync() { + return CompletableFuture.completedFuture(isOpen()); + } + +} diff --git a/src/org/redkale/source/CacheSourceLoader.java b/src/main/java/org/redkale/source/CacheSourceProvider.java similarity index 60% rename from src/org/redkale/source/CacheSourceLoader.java rename to src/main/java/org/redkale/source/CacheSourceProvider.java index 0df17e6dd..2c220d2b8 100644 --- a/src/org/redkale/source/CacheSourceLoader.java +++ b/src/main/java/org/redkale/source/CacheSourceProvider.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import org.redkale.util.AnyValue; - -/** - * 自定义的CacheSource加载器 - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.4.0 - */ -public interface CacheSourceLoader { - - public boolean match(AnyValue config); - - public Class sourceClass(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import org.redkale.util.AnyValue; + +/** + * + * 自定义的CacheSource加载器, 如果标记@Priority加载器的优先级需要大于1000, 1000以下预留给官方加载器 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +public interface CacheSourceProvider { + + public boolean acceptsConf(AnyValue config); + + public Class sourceClass(); +} diff --git a/src/org/redkale/source/ColumnExpress.java b/src/main/java/org/redkale/source/ColumnExpress.java similarity index 94% rename from src/org/redkale/source/ColumnExpress.java rename to src/main/java/org/redkale/source/ColumnExpress.java index dcfd6c807..b07c5a9a9 100644 --- a/src/org/redkale/source/ColumnExpress.java +++ b/src/main/java/org/redkale/source/ColumnExpress.java @@ -1,49 +1,49 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -/** - * 函数表达式, 均与SQL定义中的表达式相同 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public enum ColumnExpress { - /** - * 赋值 col = val - */ - MOV, - /** - * 加值 col = col + val - */ - INC, - /** - * 加值 col = col - val - */ - DEC, - /** - * 乘值 col = col * val - */ - MUL, - /** - * 除值 col = col / val - */ - DIV, - /** - * 取模 col = col % val - */ - MOD, - /** - * 与值 col = col & val - */ - AND, //与值 col = col & val - /** - * 或值 col = col | val - */ - ORR; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +/** + * 函数表达式, 均与SQL定义中的表达式相同 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public enum ColumnExpress { + /** + * 赋值 col = val + */ + MOV, + /** + * 加值 col = col + val + */ + INC, + /** + * 加值 col = col - val + */ + DEC, + /** + * 乘值 col = col * val + */ + MUL, + /** + * 除值 col = col / val + */ + DIV, + /** + * 取模 col = col % val + */ + MOD, + /** + * 与值 col = col & val + */ + AND, //与值 col = col & val + /** + * 或值 col = col | val + */ + ORR; +} diff --git a/src/org/redkale/source/ColumnFuncNode.java b/src/main/java/org/redkale/source/ColumnFuncNode.java similarity index 96% rename from src/org/redkale/source/ColumnFuncNode.java rename to src/main/java/org/redkale/source/ColumnFuncNode.java index c0ba981ec..7edcfa2cd 100644 --- a/src/org/redkale/source/ColumnFuncNode.java +++ b/src/main/java/org/redkale/source/ColumnFuncNode.java @@ -1,83 +1,83 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; - -/** - * 与ColumnNodeValue 组合,用于复杂的字段表达式 。 - * String 视为 字段名 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.0.0 - */ -public class ColumnFuncNode implements ColumnNode { - - protected FilterFunc func; - - protected Serializable value;//类型只能是String、ColumnNodeValue - - public ColumnFuncNode() { - } - - public ColumnFuncNode(FilterFunc func, Serializable node) { - if (!(node instanceof String) && !(node instanceof ColumnNodeValue)) throw new IllegalArgumentException("value must be String or ColumnNodeValue"); - this.func = func; - this.value = node; - } - - public static ColumnFuncNode create(FilterFunc func, Serializable node) { - return new ColumnFuncNode(func, node); - } - - public static ColumnFuncNode avg(Serializable node) { - return new ColumnFuncNode(FilterFunc.AVG, node); - } - - public static ColumnFuncNode count(Serializable node) { - return new ColumnFuncNode(FilterFunc.COUNT, node); - } - - public static ColumnFuncNode distinctCount(Serializable node) { - return new ColumnFuncNode(FilterFunc.DISTINCTCOUNT, node); - } - - public static ColumnFuncNode max(Serializable node) { - return new ColumnFuncNode(FilterFunc.MAX, node); - } - - public static ColumnFuncNode min(Serializable node) { - return new ColumnFuncNode(FilterFunc.MIN, node); - } - - public static ColumnFuncNode sum(Serializable node) { - return new ColumnFuncNode(FilterFunc.SUM, node); - } - - public FilterFunc getFunc() { - return func; - } - - public void setFunc(FilterFunc func) { - this.func = func; - } - - public Serializable getValue() { - return value; - } - - public void setValue(Serializable value) { - this.value = value; - } - - @Override - public String toString() { - return "{\"func\":\"" + func + "\", \"value\":" + ((value instanceof CharSequence) ? ("\"" + value + "\"") : value) + "}"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; + +/** + * 与ColumnNodeValue 组合,用于复杂的字段表达式 。 + * String 视为 字段名 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.0.0 + */ +public class ColumnFuncNode implements ColumnNode { + + protected FilterFunc func; + + protected Serializable value;//类型只能是String、ColumnNodeValue + + public ColumnFuncNode() { + } + + public ColumnFuncNode(FilterFunc func, Serializable node) { + if (!(node instanceof String) && !(node instanceof ColumnNodeValue)) throw new IllegalArgumentException("value must be String or ColumnNodeValue"); + this.func = func; + this.value = node; + } + + public static ColumnFuncNode create(FilterFunc func, Serializable node) { + return new ColumnFuncNode(func, node); + } + + public static ColumnFuncNode avg(Serializable node) { + return new ColumnFuncNode(FilterFunc.AVG, node); + } + + public static ColumnFuncNode count(Serializable node) { + return new ColumnFuncNode(FilterFunc.COUNT, node); + } + + public static ColumnFuncNode distinctCount(Serializable node) { + return new ColumnFuncNode(FilterFunc.DISTINCTCOUNT, node); + } + + public static ColumnFuncNode max(Serializable node) { + return new ColumnFuncNode(FilterFunc.MAX, node); + } + + public static ColumnFuncNode min(Serializable node) { + return new ColumnFuncNode(FilterFunc.MIN, node); + } + + public static ColumnFuncNode sum(Serializable node) { + return new ColumnFuncNode(FilterFunc.SUM, node); + } + + public FilterFunc getFunc() { + return func; + } + + public void setFunc(FilterFunc func) { + this.func = func; + } + + public Serializable getValue() { + return value; + } + + public void setValue(Serializable value) { + this.value = value; + } + + @Override + public String toString() { + return "{\"func\":\"" + func + "\", \"value\":" + ((value instanceof CharSequence) ? ("\"" + value + "\"") : value) + "}"; + } +} diff --git a/src/org/redkale/source/ColumnNode.java b/src/main/java/org/redkale/source/ColumnNode.java similarity index 95% rename from src/org/redkale/source/ColumnNode.java rename to src/main/java/org/redkale/source/ColumnNode.java index e73e69015..b62b4a62c 100644 --- a/src/org/redkale/source/ColumnNode.java +++ b/src/main/java/org/redkale/source/ColumnNode.java @@ -1,21 +1,21 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; - -/** - * ColumnFuncNode与ColumnNodeValue 的接口 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.0.0 - */ -public interface ColumnNode extends Serializable { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; + +/** + * ColumnFuncNode与ColumnNodeValue 的接口 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.0.0 + */ +public interface ColumnNode extends Serializable { + +} diff --git a/src/org/redkale/source/ColumnNodeValue.java b/src/main/java/org/redkale/source/ColumnNodeValue.java similarity index 74% rename from src/org/redkale/source/ColumnNodeValue.java rename to src/main/java/org/redkale/source/ColumnNodeValue.java index 2deb9950e..0627fa8af 100644 --- a/src/org/redkale/source/ColumnNodeValue.java +++ b/src/main/java/org/redkale/source/ColumnNodeValue.java @@ -1,144 +1,154 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import static org.redkale.source.ColumnExpress.*; - -/** - * 作为ColumnValue的value字段值,用于复杂的字段表达式 。 - * String 视为 字段名 - * Number 视为 数值 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.0.0 - */ -public class ColumnNodeValue implements ColumnNode { - - protected Serializable left;//类型只能是String、Number、ColumnNodeValue - - protected ColumnExpress express; //不能是MOV - - protected Serializable right;//类型只能是String、Number、ColumnNodeValue - - public ColumnNodeValue() { - } - - public ColumnNodeValue(Serializable left, ColumnExpress express, Serializable right) { - if (express == null || express == ColumnExpress.MOV) { - throw new IllegalArgumentException("express cannot be null or MOV"); - } - if (!(left instanceof String) && !(left instanceof Number) && !(left instanceof ColumnNodeValue) && !(left instanceof ColumnFuncNode)) { - throw new IllegalArgumentException("left value must be String, Number, ColumnFuncNode or ColumnNodeValue"); - } - if (!(right instanceof String) && !(right instanceof Number) && !(right instanceof ColumnNodeValue) && !(right instanceof ColumnFuncNode)) { - throw new IllegalArgumentException("right value must be String, Number, ColumnFuncNode or ColumnNodeValue"); - } - this.left = left; - this.express = express; - this.right = right; - } - - public static ColumnNodeValue create(Serializable left, ColumnExpress express, Serializable right) { - return new ColumnNodeValue(left, express, right); - } - - public static ColumnNodeValue inc(Serializable left, Serializable right) { - return new ColumnNodeValue(left, INC, right); - } - - public static ColumnNodeValue dec(Serializable left, Serializable right) { - return new ColumnNodeValue(left, DEC, right); - } - - public static ColumnNodeValue mul(Serializable left, Serializable right) { - return new ColumnNodeValue(left, MUL, right); - } - - public static ColumnNodeValue div(Serializable left, Serializable right) { - return new ColumnNodeValue(left, DIV, right); - } - - public static ColumnNodeValue mod(Serializable left, Serializable right) { - return new ColumnNodeValue(left, MOD, right); - } - - public static ColumnNodeValue and(Serializable left, Serializable right) { - return new ColumnNodeValue(left, AND, right); - } - - public static ColumnNodeValue orr(Serializable left, Serializable right) { - return new ColumnNodeValue(left, ORR, right); - } - - public ColumnNodeValue inc(Serializable right) { - return any(INC, right); - } - - public ColumnNodeValue dec(Serializable right) { - return any(DEC, right); - } - - public ColumnNodeValue mul(Serializable right) { - return any(MUL, right); - } - - public ColumnNodeValue div(Serializable right) { - return any(DIV, right); - } - - public ColumnNodeValue mod(Serializable right) { - return any(MOD, right); - } - - public ColumnNodeValue and(Serializable right) { - return any(AND, right); - } - - public ColumnNodeValue orr(Serializable right) { - return any(ORR, right); - } - - protected ColumnNodeValue any(ColumnExpress express, Serializable right) { - ColumnNodeValue one = new ColumnNodeValue(this.left, this.express, this.right); - this.left = one; - this.express = express; - this.right = right; - return this; - } - - public Serializable getLeft() { - return left; - } - - public void setLeft(Serializable left) { - this.left = left; - } - - public ColumnExpress getExpress() { - return express; - } - - public void setExpress(ColumnExpress express) { - this.express = express; - } - - public Serializable getRight() { - return right; - } - - public void setRight(Serializable right) { - this.right = right; - } - - @Override - public String toString() { - return "{\"column\":" + ((left instanceof CharSequence) ? ("\"" + left + "\"") : left) + ", \"express\":" + express + ", \"value\":" + ((right instanceof CharSequence) ? ("\"" + right + "\"") : right) + "}"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import static org.redkale.source.ColumnExpress.*; + +/** + * 作为ColumnValue的value字段值,用于复杂的字段表达式 。
    + * String 视为 字段名
    + * Number 视为 数值
    + * 例如: UPDATE Reord SET updateTime = createTime + 10 WHERE id = 1
    + * source.updateColumn(Record.class, 1, ColumnValue.mov("updateTime", ColumnNodeValue.inc("createTime", 10))); + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.0.0 + */ +public class ColumnNodeValue implements ColumnNode { + + protected Serializable left;//类型只能是String、Number、ColumnNodeValue + + protected ColumnExpress express; //MOV时,left必须是String, right必须是null + + protected Serializable right;//类型只能是String、Number、ColumnNodeValue + + public ColumnNodeValue() { + } + + public ColumnNodeValue(Serializable left, ColumnExpress express, Serializable right) { + if (express == null) { + throw new IllegalArgumentException("express cannot be null"); + } + if (express == MOV) { + if (!(left instanceof String) || right != null) throw new IllegalArgumentException("left value must be String, right value must be null on ColumnExpress.MOV"); + } else { + if (!(left instanceof String) && !(left instanceof Number) && !(left instanceof ColumnNodeValue) && !(left instanceof ColumnFuncNode)) { + throw new IllegalArgumentException("left value must be String, Number, ColumnFuncNode or ColumnNodeValue"); + } + if (!(right instanceof String) && !(right instanceof Number) && !(right instanceof ColumnNodeValue) && !(right instanceof ColumnFuncNode)) { + throw new IllegalArgumentException("right value must be String, Number, ColumnFuncNode or ColumnNodeValue"); + } + } + this.left = left; + this.express = express; + this.right = right; + } + + public static ColumnNodeValue create(Serializable left, ColumnExpress express, Serializable right) { + return new ColumnNodeValue(left, express, right); + } + + public static ColumnNodeValue mov(String left) { + return new ColumnNodeValue(left, MOV, null); + } + + public static ColumnNodeValue inc(Serializable left, Serializable right) { + return new ColumnNodeValue(left, INC, right); + } + + public static ColumnNodeValue dec(Serializable left, Serializable right) { + return new ColumnNodeValue(left, DEC, right); + } + + public static ColumnNodeValue mul(Serializable left, Serializable right) { + return new ColumnNodeValue(left, MUL, right); + } + + public static ColumnNodeValue div(Serializable left, Serializable right) { + return new ColumnNodeValue(left, DIV, right); + } + + public static ColumnNodeValue mod(Serializable left, Serializable right) { + return new ColumnNodeValue(left, MOD, right); + } + + public static ColumnNodeValue and(Serializable left, Serializable right) { + return new ColumnNodeValue(left, AND, right); + } + + public static ColumnNodeValue orr(Serializable left, Serializable right) { + return new ColumnNodeValue(left, ORR, right); + } + + public ColumnNodeValue inc(Serializable right) { + return any(INC, right); + } + + public ColumnNodeValue dec(Serializable right) { + return any(DEC, right); + } + + public ColumnNodeValue mul(Serializable right) { + return any(MUL, right); + } + + public ColumnNodeValue div(Serializable right) { + return any(DIV, right); + } + + public ColumnNodeValue mod(Serializable right) { + return any(MOD, right); + } + + public ColumnNodeValue and(Serializable right) { + return any(AND, right); + } + + public ColumnNodeValue orr(Serializable right) { + return any(ORR, right); + } + + protected ColumnNodeValue any(ColumnExpress express, Serializable right) { + ColumnNodeValue one = new ColumnNodeValue(this.left, this.express, this.right); + this.left = one; + this.express = express; + this.right = right; + return this; + } + + public Serializable getLeft() { + return left; + } + + public void setLeft(Serializable left) { + this.left = left; + } + + public ColumnExpress getExpress() { + return express; + } + + public void setExpress(ColumnExpress express) { + this.express = express; + } + + public Serializable getRight() { + return right; + } + + public void setRight(Serializable right) { + this.right = right; + } + + @Override + public String toString() { + return "{\"column\":" + ((left instanceof CharSequence) ? ("\"" + left + "\"") : left) + ", \"express\":" + express + ", \"value\":" + ((right instanceof CharSequence) ? ("\"" + right + "\"") : right) + "}"; + } +} diff --git a/src/org/redkale/source/ColumnValue.java b/src/main/java/org/redkale/source/ColumnValue.java similarity index 95% rename from src/org/redkale/source/ColumnValue.java rename to src/main/java/org/redkale/source/ColumnValue.java index 7b8162f1a..e71b05548 100644 --- a/src/org/redkale/source/ColumnValue.java +++ b/src/main/java/org/redkale/source/ColumnValue.java @@ -1,203 +1,204 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import static org.redkale.source.ColumnExpress.*; - -/** - * ColumnValue主要用于多个字段更新的表达式。 - * 用于 DataSource.updateColumn 方法 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class ColumnValue { - - private String column; - - private ColumnExpress express; - - private Serializable value; - - public ColumnValue() { - } - - public ColumnValue(String column, Serializable value) { - this(column, ColumnExpress.MOV, value); - } - - public ColumnValue(String column, ColumnExpress express, Serializable value) { - this.column = column; - this.express = express == null ? ColumnExpress.MOV : express; - this.value = value; - } - - /** - * 同 mov 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue create(String column, Serializable value) { - return new ColumnValue(column, value); - } - - /** - * 返回 {column} = {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue mov(String column, Serializable value) { - return new ColumnValue(column, MOV, value); - } - - /** - * 返回 {column} = {column} + {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue inc(String column, Serializable value) { - return new ColumnValue(column, INC, value); - } - - /** - * 返回 {column} = {column} + 1 操作 - * - * @param column 字段名 - * - * @return ColumnValue - * - * @since 2.4.0 - */ - public static ColumnValue inc(String column) { - return new ColumnValue(column, INC, 1); - } - - /** - * 返回 {column} = {column} - {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue dec(String column, Serializable value) { - return new ColumnValue(column, DEC, value); - } - - /** - * 返回 {column} = {column} - 1 操作 - * - * @param column 字段名 - * - * @return ColumnValue - * - * @since 2.4.0 - */ - public static ColumnValue dec(String column) { - return new ColumnValue(column, DEC, 1); - } - - /** - * 返回 {column} = {column} * {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue mul(String column, Serializable value) { - return new ColumnValue(column, MUL, value); - } - - /** - * 返回 {column} = {column} / {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue div(String column, Serializable value) { - return new ColumnValue(column, DIV, value); - } - - /** - * 返回 {column} = {column} % {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - //不常用,防止开发者容易在mov时误输入mod -// public static ColumnValue mod(String column, Serializable value) { -// return new ColumnValue(column, MOD, value); -// } - /** - * 返回 {column} = {column} & {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue and(String column, Serializable value) { - return new ColumnValue(column, AND, value); - } - - /** - * 返回 {column} = {column} | {value} 操作 - * - * @param column 字段名 - * @param value 字段值 - * - * @return ColumnValue - */ - public static ColumnValue orr(String column, Serializable value) { - return new ColumnValue(column, ORR, value); - } - - public String getColumn() { - return column; - } - - public void setColumn(String column) { - this.column = column; - } - - public ColumnExpress getExpress() { - return express; - } - - public void setExpress(ColumnExpress express) { - this.express = express; - } - - public Serializable getValue() { - return value; - } - - public void setValue(Serializable value) { - this.value = value; - } - - @Override - public String toString() { - return "{\"column\":\"" + column + "\", \"express\":" + express + ", \"value\":" + ((value instanceof CharSequence) ? ("\"" + value + "\"") : value) + "}"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import static org.redkale.source.ColumnExpress.*; + +/** + * ColumnValue主要用于多个字段更新的表达式。 + * value值一般为: ColumnNodeValue、ColumnFuncNode、Number、String等
    + * 用于 DataSource.updateColumn 方法
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class ColumnValue { + + private String column; + + private ColumnExpress express; + + private Serializable value; + + public ColumnValue() { + } + + public ColumnValue(String column, Serializable value) { + this(column, ColumnExpress.MOV, value); + } + + public ColumnValue(String column, ColumnExpress express, Serializable value) { + this.column = column; + this.express = express == null ? ColumnExpress.MOV : express; + this.value = value; + } + + /** + * 同 mov 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue create(String column, Serializable value) { + return new ColumnValue(column, value); + } + + /** + * 返回 {column} = {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue mov(String column, Serializable value) { + return new ColumnValue(column, MOV, value); + } + + /** + * 返回 {column} = {column} + {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue inc(String column, Serializable value) { + return new ColumnValue(column, INC, value); + } + + /** + * 返回 {column} = {column} + 1 操作 + * + * @param column 字段名 + * + * @return ColumnValue + * + * @since 2.4.0 + */ + public static ColumnValue inc(String column) { + return new ColumnValue(column, INC, 1); + } + + /** + * 返回 {column} = {column} - {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue dec(String column, Serializable value) { + return new ColumnValue(column, DEC, value); + } + + /** + * 返回 {column} = {column} - 1 操作 + * + * @param column 字段名 + * + * @return ColumnValue + * + * @since 2.4.0 + */ + public static ColumnValue dec(String column) { + return new ColumnValue(column, DEC, 1); + } + + /** + * 返回 {column} = {column} * {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue mul(String column, Serializable value) { + return new ColumnValue(column, MUL, value); + } + + /** + * 返回 {column} = {column} / {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue div(String column, Serializable value) { + return new ColumnValue(column, DIV, value); + } + + /** + * 返回 {column} = {column} % {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + //不常用,防止开发者容易在mov时误输入mod +// public static ColumnValue mod(String column, Serializable value) { +// return new ColumnValue(column, MOD, value); +// } + /** + * 返回 {column} = {column} & {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue and(String column, Serializable value) { + return new ColumnValue(column, AND, value); + } + + /** + * 返回 {column} = {column} | {value} 操作 + * + * @param column 字段名 + * @param value 字段值 + * + * @return ColumnValue + */ + public static ColumnValue orr(String column, Serializable value) { + return new ColumnValue(column, ORR, value); + } + + public String getColumn() { + return column; + } + + public void setColumn(String column) { + this.column = column; + } + + public ColumnExpress getExpress() { + return express; + } + + public void setExpress(ColumnExpress express) { + this.express = express; + } + + public Serializable getValue() { + return value; + } + + public void setValue(Serializable value) { + this.value = value; + } + + @Override + public String toString() { + return "{\"column\":\"" + column + "\", \"express\":" + express + ", \"value\":" + ((value instanceof CharSequence) ? ("\"" + value + "\"") : value) + "}"; + } +} diff --git a/src/org/redkale/source/CryptColumn.java b/src/main/java/org/redkale/source/CryptColumn.java similarity index 96% rename from src/org/redkale/source/CryptColumn.java rename to src/main/java/org/redkale/source/CryptColumn.java index 35a5c39c9..aa5add93f 100644 --- a/src/org/redkale/source/CryptColumn.java +++ b/src/main/java/org/redkale/source/CryptColumn.java @@ -1,30 +1,30 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - -/** - * 加密字段标记
    - * 注意: 加密字段不能用于 LIKE 等过滤查询
    - * 如果有对加密字段进行过滤查询的需求,就要保证加密算法也能兼容LIKE,如:"abc"的加密字符串也是"abcde"的加密字符串的一部分 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.0.0 - */ -@Inherited -@Documented -@Target({FIELD}) -@Retention(RUNTIME) -public @interface CryptColumn { - - Class handler(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; + +/** + * 加密字段标记
    + * 注意: 加密字段不能用于 LIKE 等过滤查询
    + * 如果有对加密字段进行过滤查询的需求,就要保证加密算法也能兼容LIKE,如:"abc"的加密字符串也是"abcde"的加密字符串的一部分 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.0.0 + */ +@Inherited +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +public @interface CryptColumn { + + Class handler(); +} diff --git a/src/org/redkale/source/CryptHandler.java b/src/main/java/org/redkale/source/CryptHandler.java similarity index 95% rename from src/org/redkale/source/CryptHandler.java rename to src/main/java/org/redkale/source/CryptHandler.java index cf08dc0fd..0fece30af 100644 --- a/src/org/redkale/source/CryptHandler.java +++ b/src/main/java/org/redkale/source/CryptHandler.java @@ -1,38 +1,38 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -/** - * 字段加密解密接口 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.0.0 - * @param 加密的字段类型 - * @param 加密后的数据类型 - */ -public interface CryptHandler { - - /** - * 加密 - * - * @param value 加密前的字段值 - * - * @return 加密后的字段值 - */ - public D encrypt(S value); - - /** - * 解密 - * - * @param value 加密的字段值 - * - * @return 解密后的字段值 - */ - public S decrypt(D value); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +/** + * 字段加密解密接口 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.0.0 + * @param 加密的字段类型 + * @param 加密后的数据类型 + */ +public interface CryptHandler { + + /** + * 加密 + * + * @param value 加密前的字段值 + * + * @return 加密后的字段值 + */ + public D encrypt(S value); + + /** + * 解密 + * + * @param value 加密的字段值 + * + * @return 解密后的字段值 + */ + public S decrypt(D value); +} diff --git a/src/main/java/org/redkale/source/DataJdbcSource.java b/src/main/java/org/redkale/source/DataJdbcSource.java new file mode 100644 index 000000000..5ac537bfd --- /dev/null +++ b/src/main/java/org/redkale/source/DataJdbcSource.java @@ -0,0 +1,1096 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.*; +import java.net.URL; +import java.sql.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.*; +import org.redkale.service.Local; +import static org.redkale.source.DataSources.*; +import org.redkale.util.*; + +/** + * DataSource的JDBC实现类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Local +@AutoLoad(false) +@SuppressWarnings("unchecked") +@ResourceType(DataSource.class) +public class DataJdbcSource extends DataSqlSource { + + protected ConnectionPool readPool; + + protected ConnectionPool writePool; + + public DataJdbcSource(String unitName, URL persistFile, String dbtype, Properties readprop, Properties writeprop) { + super(unitName, persistFile, dbtype, readprop, writeprop); + } + + @Override + public void init(AnyValue conf) { + super.init(conf); + this.readPool = new ConnectionPool(readConfProps); + if (readConfProps == writeConfProps) { + this.writePool = readPool; + } else { + this.writePool = new ConnectionPool(writeConfProps); + } + } + + @Override + public void destroy(AnyValue config) { + if (readPool != null) readPool.close(); + if (writePool != null) writePool.close(); + } + + @Local + @Override + public void close() throws Exception { + super.close(); + if (readPool != null) readPool.close(); + if (writePool != null) writePool.close(); + } + + public static boolean acceptsConf(Properties property) { + try { + final Class driverClass = DriverManager.getDriver(property.getProperty(JDBC_URL)).getClass(); + RedkaleClassLoader.putReflectionDeclaredConstructors(driverClass, driverClass.getName()); + RedkaleClassLoader.putServiceLoader(java.sql.Driver.class); + } catch (Exception e) { + return false; + } + return true; + } + + @Local + protected ConnectionPool readPool() { + return readPool; + } + + @Local + protected ConnectionPool writePool() { + return writePool; + } + + @Override + protected final String prepareParamSign(int index) { + return "?"; + } + + @Override + protected final boolean isAsync() { + return false; + } + + @Override + protected CompletableFuture insertDB(EntityInfo info, T... entitys) { + Connection conn = null; + try { + int c = 0; + conn = writePool.pollConnection(); + final String sql = info.getInsertQuestionPrepareSQL(entitys[0]); + final Class primaryType = info.getPrimary().type(); + final Attribute primary = info.getPrimary(); + Attribute[] attrs = info.insertAttributes; + conn.setReadOnly(false); + conn.setAutoCommit(true); + PreparedStatement prestmt = createInsertPreparedStatement(conn, sql, info, entitys); + try { + int[] cs = prestmt.executeBatch(); + int c1 = 0; + for (int cc : cs) { + c1 += cc; + } + c = c1; + } catch (SQLException se) { + if (!isTableNotExist(info, se.getSQLState())) throw se; + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql == null) throw se; + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } else { + synchronized (info.disTableLock()) { + final String catalog = conn.getCatalog(); + final String newTable = info.getTable(entitys[0]); + final String tablekey = newTable.indexOf('.') > 0 ? newTable : (catalog + '.' + newTable); + if (!info.containsDisTable(tablekey)) { + try { + //执行一遍复制表操作 + Statement st = conn.createStatement(); + st.execute(getTableCopySQL(info, newTable)); + st.close(); + info.addDisTable(tablekey); + } catch (SQLException sqle) { //多进程并发时可能会出现重复建表 + if (isTableNotExist(info, sqle.getSQLState())) { + if (newTable.indexOf('.') < 0) { + String tablesql = createTableSql(info); + if (tablesql != null) { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + //再执行一遍复制表操作 + st = conn.createStatement(); + st.execute(getTableCopySQL(info, newTable)); + st.close(); + info.addDisTable(tablekey); + } + } else { //需要先建库 + Statement st; + try { + st = conn.createStatement(); + st.execute(("postgresql".equals(dbtype()) ? "CREATE SCHEMA IF NOT EXISTS " : "CREATE DATABASE IF NOT EXISTS ") + newTable.substring(0, newTable.indexOf('.'))); + st.close(); + } catch (SQLException sqle1) { + logger.log(Level.SEVERE, "create database(" + newTable.substring(0, newTable.indexOf('.')) + ") error", sqle1); + } + try { + //再执行一遍复制表操作 + st = conn.createStatement(); + st.execute(getTableCopySQL(info, newTable)); + st.close(); + info.addDisTable(tablekey); + } catch (SQLException sqle2) { + if (isTableNotExist(info, sqle2.getSQLState())) { + String tablesql = createTableSql(info); + if (tablesql != null) { + st = conn.createStatement(); + st.execute(tablesql); + st.close(); + //再执行一遍复制表操作 + st = conn.createStatement(); + st.execute(getTableCopySQL(info, newTable)); + st.close(); + info.addDisTable(tablekey); + } + } else { + logger.log(Level.SEVERE, "create table2(" + getTableCopySQL(info, newTable) + ") error", sqle2); + } + } + } + } + } + } + } + } + prestmt.close(); + prestmt = createInsertPreparedStatement(conn, sql, info, entitys); + int[] cs = prestmt.executeBatch(); + int c1 = 0; + for (int cc : cs) { + c1 += cc; + } + c = c1; + } + prestmt.close(); + //------------------------------------------------------------ + if (info.isLoggable(logger, Level.FINEST)) { //打印调试信息 + char[] sqlchars = sql.toCharArray(); + for (final T value : entitys) { + //----------------------------- + StringBuilder sb = new StringBuilder(128); + int i = 0; + for (char ch : sqlchars) { + if (ch == '?') { + Object obj = info.getSQLValue(attrs[i++], value); + if (obj != null && obj.getClass().isArray()) { + sb.append("'[length=").append(java.lang.reflect.Array.getLength(obj)).append("]'"); + } else { + sb.append(info.formatSQLValue(obj, sqlFormatter)); + } + } else { + sb.append(ch); + } + } + String debugsql = sb.toString(); + if (info.isLoggable(logger, Level.FINEST, debugsql)) logger.finest(info.getType().getSimpleName() + " insert sql=" + debugsql.replaceAll("(\r|\n)", "\\n")); + } + } //打印结束 + return CompletableFuture.completedFuture(c); + } catch (SQLException e) { + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) writePool.offerConnection(conn); + } + } + + protected PreparedStatement createInsertPreparedStatement(final Connection conn, final String sql, + final EntityInfo info, T... entitys) throws SQLException { + Attribute[] attrs = info.insertAttributes; + final PreparedStatement prestmt = conn.prepareStatement(sql); + + for (final T value : entitys) { + batchStatementParameters(conn, prestmt, info, attrs, value); + prestmt.addBatch(); + } + return prestmt; + } + + protected int batchStatementParameters(Connection conn, PreparedStatement prestmt, EntityInfo info, Attribute[] attrs, T entity) throws SQLException { + int i = 0; + for (Attribute attr : attrs) { + Object val = info.getSQLValue(attr, entity); + if (val instanceof byte[]) { + Blob blob = conn.createBlob(); + blob.setBytes(1, (byte[]) val); + prestmt.setObject(++i, blob); + } else if (val instanceof Boolean) { + prestmt.setObject(++i, ((Boolean) val) ? (byte) 1 : (byte) 0); + } else if (val instanceof AtomicInteger) { + prestmt.setObject(++i, ((AtomicInteger) val).get()); + } else if (val instanceof AtomicLong) { + prestmt.setObject(++i, ((AtomicLong) val).get()); + } else if (val != null && !(val instanceof Number) && !(val instanceof CharSequence) && !(val instanceof java.util.Date) + && !val.getClass().getName().startsWith("java.sql.") && !val.getClass().getName().startsWith("java.time.")) { + prestmt.setObject(++i, info.jsonConvert.convertTo(attr.genericType(), val)); + } else if (val == null && info.isNotNullJson(attr)) { + prestmt.setObject(++i, ""); + } else { + prestmt.setObject(++i, val); + } + } + return i; + } + + @Override + protected CompletableFuture deleteDB(EntityInfo info, Flipper flipper, String sql) { + Connection conn = null; + try { + conn = writePool.pollConnection(); + conn.setReadOnly(false); + conn.setAutoCommit(true); + sql += ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit())); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql); + final Statement stmt = conn.createStatement(); + int c = stmt.executeUpdate(sql); + stmt.close(); + return CompletableFuture.completedFuture(c); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + return CompletableFuture.completedFuture(0); + } catch (SQLException e2) { + return CompletableFuture.failedFuture(e2); + } + } + } + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) writePool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture clearTableDB(EntityInfo info, final String table, String sql) { + Connection conn = null; + try { + conn = writePool.pollConnection(); + conn.setReadOnly(false); + conn.setAutoCommit(true); + final Statement stmt = conn.createStatement(); + int c = stmt.executeUpdate(sql); + stmt.close(); + return CompletableFuture.completedFuture(c); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) return CompletableFuture.completedFuture(-1); + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) writePool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture dropTableDB(EntityInfo info, final String table, String sql) { + Connection conn = null; + try { + conn = writePool.pollConnection(); + conn.setReadOnly(false); + conn.setAutoCommit(true); + final Statement stmt = conn.createStatement(); + int c = stmt.executeUpdate(sql); + stmt.close(); + if (info.getTableStrategy() != null) { + String tablekey = table.indexOf('.') > 0 ? table : (conn.getCatalog() + '.' + table); + info.removeDisTable(tablekey); + } + return CompletableFuture.completedFuture(c); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) return CompletableFuture.completedFuture(-1); + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) writePool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture updateEntityDB(EntityInfo info, T... entitys) { + Connection conn = null; + try { + conn = writePool.pollConnection(); + conn.setReadOnly(false); + conn.setAutoCommit(true); + final String updateSQL = info.getUpdateQuestionPrepareSQL(entitys[0]); + final PreparedStatement prestmt = conn.prepareStatement(updateSQL); + Attribute[] attrs = info.updateAttributes; + final boolean debugfinest = info.isLoggable(logger, Level.FINEST); + char[] sqlchars = debugfinest ? updateSQL.toCharArray() : null; + final Attribute primary = info.getPrimary(); + for (final T value : entitys) { + int k = batchStatementParameters(conn, prestmt, info, attrs, value); + prestmt.setObject(++k, primary.get(value)); + prestmt.addBatch();//------------------------------------------------------------ + if (debugfinest) { //打印调试信息 + //----------------------------- + int i = 0; + StringBuilder sb = new StringBuilder(128); + for (char ch : sqlchars) { + if (ch == '?') { + Object obj = i == attrs.length ? info.getSQLValue(primary, value) : info.getSQLValue(attrs[i++], value); + if (obj != null && obj.getClass().isArray()) { + sb.append("'[length=").append(java.lang.reflect.Array.getLength(obj)).append("]'"); + } else { + sb.append(info.formatSQLValue(obj, sqlFormatter)); + } + } else { + sb.append(ch); + } + } + String debugsql = sb.toString(); + if (info.isLoggable(logger, Level.FINEST, debugsql)) logger.finest(info.getType().getSimpleName() + " updates sql=" + debugsql.replaceAll("(\r|\n)", "\\n")); + } //打印结束 + } + int[] pc = prestmt.executeBatch(); + int c = 0; + for (int p : pc) { + if (p >= 0) c += p; + } + prestmt.close(); + return CompletableFuture.completedFuture(c); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(0); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) writePool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture updateColumnDB(EntityInfo info, Flipper flipper, String sql, boolean prepared, Object... params) { + Connection conn = null; + try { + conn = writePool.pollConnection(); + conn.setReadOnly(false); + conn.setAutoCommit(true); + if (prepared) { + final PreparedStatement prestmt = conn.prepareStatement(sql); + int index = 0; + for (Object param : params) { + Blob blob = conn.createBlob(); + blob.setBytes(1, (byte[]) param); + prestmt.setBlob(++index, blob); + } + int c = prestmt.executeUpdate(); + prestmt.close(); + return CompletableFuture.completedFuture(c); + } else { + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql); + final Statement stmt = conn.createStatement(); + int c = stmt.executeUpdate(sql); + stmt.close(); + return CompletableFuture.completedFuture(c); + } + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(0); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) writePool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture> getNumberMapDB(EntityInfo info, String sql, FilterFuncColumn... columns) { + Connection conn = null; + final Map map = new HashMap<>(); + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final Statement stmt = conn.createStatement(); + ResultSet set = stmt.executeQuery(sql); + if (set.next()) { + int index = 0; + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + Object o = set.getObject(++index); + Number rs = ffc.getDefvalue(); + if (o != null) rs = (Number) o; + map.put(ffc.col(col), rs); + } + } + } + set.close(); + stmt.close(); + return CompletableFuture.completedFuture(map); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(map); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture getNumberResultDB(EntityInfo info, String sql, Number defVal, String column) { + Connection conn = null; + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final Statement stmt = conn.createStatement(); + Number rs = defVal; + ResultSet set = stmt.executeQuery(sql); + if (set.next()) { + Object o = set.getObject(1); + if (o != null) rs = (Number) o; + } + set.close(); + stmt.close(); + return CompletableFuture.completedFuture(rs); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(defVal); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture> queryColumnMapDB(EntityInfo info, String sql, String keyColumn) { + Connection conn = null; + Map rs = new LinkedHashMap<>(); + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final Statement stmt = conn.createStatement(); + ResultSet set = stmt.executeQuery(sql); + ResultSetMetaData rsd = set.getMetaData(); + boolean smallint = rsd == null ? false : rsd.getColumnType(1) == Types.SMALLINT; + while (set.next()) { + rs.put((K) (smallint ? set.getShort(1) : set.getObject(1)), (N) set.getObject(2)); + } + set.close(); + stmt.close(); + return CompletableFuture.completedFuture(rs); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(rs); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture> queryColumnMapDB(EntityInfo info, String sql, final ColumnNode[] funcNodes, final String[] groupByColumns) { + Connection conn = null; + Map rs = new LinkedHashMap<>(); + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final Statement stmt = conn.createStatement(); + ResultSet set = stmt.executeQuery(sql); + ResultSetMetaData rsd = set.getMetaData(); + boolean[] smallints = null; + while (set.next()) { + int index = 0; + Serializable[] keys = new Serializable[groupByColumns.length]; + if (smallints == null) { + smallints = new boolean[keys.length]; + for (int i = 0; i < keys.length; i++) { + smallints[i] = rsd == null ? false : rsd.getColumnType(i + 1) == Types.SMALLINT; + } + } + for (int i = 0; i < keys.length; i++) { + keys[i] = (Serializable) ((smallints[i] && index == 0) ? set.getShort(++index) : set.getObject(++index)); + } + Number[] vals = new Number[funcNodes.length]; + for (int i = 0; i < vals.length; i++) { + vals[i] = (Number) set.getObject(++index); + } + rs.put(keys, vals); + } + set.close(); + stmt.close(); + return CompletableFuture.completedFuture(rs); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(rs); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture findDB(EntityInfo info, String sql, boolean onlypk, SelectColumn selects) { + Connection conn = null; + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + ps.setFetchSize(1); + final DataResultSet set = createDataResultSet(ps.executeQuery()); + T rs = set.next() ? selects == null ? info.getFullEntityValue(set) : info.getEntityValue(selects, set) : null; + set.close(); + ps.close(); + return CompletableFuture.completedFuture(rs); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(null); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture findColumnDB(EntityInfo info, String sql, boolean onlypk, String column, Serializable defValue) { + Connection conn = null; + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final Attribute attr = info.getAttribute(column); + final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + ps.setFetchSize(1); + final DataResultSet set = createDataResultSet(ps.executeQuery()); + Serializable val = defValue; + if (set.next()) { + val = info.getFieldValue(attr, set, 1); + } + set.close(); + ps.close(); + return CompletableFuture.completedFuture(val == null ? defValue : val); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(null); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture existsDB(EntityInfo info, String sql, boolean onlypk) { + Connection conn = null; + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + final ResultSet set = ps.executeQuery(); + boolean rs = set.next() ? (set.getInt(1) > 0) : false; + set.close(); + ps.close(); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists (" + rs + ") sql=" + sql); + return CompletableFuture.completedFuture(rs); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(false); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + @Override + protected CompletableFuture> querySheetDB(EntityInfo info, final boolean readcache, boolean needtotal, final boolean distinct, SelectColumn selects, Flipper flipper, FilterNode node) { + Connection conn = null; + try { + conn = readPool.pollConnection(); + //conn.setReadOnly(true); + final SelectColumn sels = selects; + final List list = new ArrayList(); + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + if ("mysql".equals(dbtype()) || "postgresql".equals(dbtype())) { //sql可以带limit、offset + final String listsql = "SELECT " + (distinct ? "DISTINCT " : "") + info.getFullQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + createSQLOrderby(info, flipper) + + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getLimit() + " OFFSET " + flipper.getOffset())); + if (readcache && info.isLoggable(logger, Level.FINEST, listsql)) { + logger.finest(info.getType().getSimpleName() + " query sql=" + listsql); + } + PreparedStatement ps = conn.prepareStatement(listsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + ResultSet set = ps.executeQuery(); + final DataResultSet rr = createDataResultSet(set); + while (set.next()) { + list.add(getEntityValue(info, sels, rr)); + } + set.close(); + ps.close(); + long total = list.size(); + if (needtotal) { + final String countsql = "SELECT " + (distinct ? "DISTINCT COUNT(" + info.getQueryColumns("a", selects) + ")" : "COUNT(*)") + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (readcache && info.isLoggable(logger, Level.FINEST, countsql)) { + logger.finest(info.getType().getSimpleName() + " query countsql=" + countsql); + } + ps = conn.prepareStatement(countsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + set = ps.executeQuery(); + if (set.next()) total = set.getLong(1); + set.close(); + ps.close(); + } + return CompletableFuture.completedFuture(new Sheet<>(total, list)); + } + final String listsql = "SELECT " + (distinct ? "DISTINCT " : "") + info.getFullQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + info.createSQLOrderby(flipper); + if (readcache && info.isLoggable(logger, Level.FINEST, listsql)) { + logger.finest(info.getType().getSimpleName() + " query sql=" + listsql + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getLimit() + " OFFSET " + flipper.getOffset()))); + } + //conn.setReadOnly(true); + final PreparedStatement ps = conn.prepareStatement(listsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + if (flipper != null && flipper.getLimit() > 0) ps.setFetchSize(flipper.getLimit()); + final ResultSet set = ps.executeQuery(); + if (flipper != null && flipper.getOffset() > 0) set.absolute(flipper.getOffset()); + final int limit = flipper == null || flipper.getLimit() < 1 ? Integer.MAX_VALUE : flipper.getLimit(); + int i = 0; + final DataResultSet rr = createDataResultSet(set); + if (sels == null) { + while (set.next()) { + i++; + list.add(info.getFullEntityValue(rr)); + if (limit <= i) break; + } + } else { + while (set.next()) { + i++; + list.add(info.getEntityValue(sels, rr)); + if (limit <= i) break; + } + } + long total = list.size(); + if (needtotal && flipper != null) { + set.last(); + total = set.getRow(); + } + set.close(); + ps.close(); + return CompletableFuture.completedFuture(new Sheet<>(total, list)); + } catch (SQLException e) { + if (isTableNotExist(info, e.getSQLState())) { + if (info.getTableStrategy() == null) { + String tablesql = createTableSql(info); + if (tablesql != null) { + try { + Statement st = conn.createStatement(); + st.execute(tablesql); + st.close(); + } catch (SQLException e2) { + } + } + } + return CompletableFuture.completedFuture(new Sheet<>(0, new ArrayList())); + } + return CompletableFuture.failedFuture(e); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + /** + * 直接本地执行SQL语句进行增删改操作,远程模式不可用
    + * 通常用于复杂的更新操作
    + * + * @param sql SQL语句 + * + * @return 结果数组 + */ + @Local + @Override + public int directExecute(String sql) { + return directExecute(new String[]{sql})[0]; + } + + /** + * 直接本地执行SQL语句进行增删改操作,远程模式不可用
    + * 通常用于复杂的更新操作
    + * + * @param sqls SQL语句 + * + * @return 结果数组 + */ + @Local + @Override + public int[] directExecute(String... sqls) { + if (sqls.length == 0) return new int[0]; + Connection conn = writePool.pollConnection(); + try { + conn.setReadOnly(false); + final Statement stmt = conn.createStatement(); + final int[] rs = new int[sqls.length]; + int i = -1; + for (String sql : sqls) { + rs[++i] = stmt.execute(sql) ? 1 : 0; + } + stmt.close(); + return rs; + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + if (conn != null) writePool.offerConnection(conn); + } + } + + /** + * 直接本地执行SQL语句进行查询,远程模式不可用
    + * 通常用于复杂的关联查询
    + * + * @param 泛型 + * @param sql SQL语句 + * @param handler 回调函数 + * + * @return 结果 + */ + @Local + @Override + public V directQuery(String sql, Function handler) { + final Connection conn = readPool.pollConnection(); + try { + if (logger.isLoggable(Level.FINEST)) logger.finest("direct query sql=" + sql); + //conn.setReadOnly(true); + final Statement statement = conn.createStatement(); + //final PreparedStatement statement = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + final ResultSet set = statement.executeQuery(sql);// ps.executeQuery(); + V rs = handler.apply(createDataResultSet(set)); + set.close(); + statement.close(); + return rs; + } catch (Exception ex) { + throw new RuntimeException(ex); + } finally { + if (conn != null) readPool.offerConnection(conn); + } + } + + public static DataResultSet createDataResultSet(ResultSet set) { + + final ResultSet rr = set; + + return new DataResultSet() { + + @Override + public Serializable getObject(Attribute attr, int index, String column) { + Class t = attr.type(); + if (t == java.util.Date.class) { + Object val = index > 0 ? getObject(index) : getObject(column); + if (val == null) return null; + return new java.util.Date(((java.sql.Date) val).getTime()); + } else if (t == java.time.LocalDate.class) { + Object val = index > 0 ? getObject(index) : getObject(column); + if (val == null) return null; + return ((java.sql.Date) val).toLocalDate(); + } else if (t == java.time.LocalTime.class) { + Object val = index > 0 ? getObject(index) : getObject(column); + if (val == null) return null; + return ((java.sql.Time) val).toLocalTime(); + } else if (t == java.time.LocalDateTime.class) { + Object val = index > 0 ? getObject(index) : getObject(column); + if (val == null) return null; + return ((java.sql.Timestamp) val).toLocalDateTime(); + } else if (t.getName().startsWith("java.sql.")) { + return index > 0 ? (Serializable) getObject(index) : (Serializable) getObject(column); + } + return DataResultSet.getRowColumnValue(this, attr, index, column); + } + + @Override + public boolean next() { + try { + return rr.next(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public boolean wasNull() { + try { + return rr.wasNull(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public void close() { + try { + rr.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object getObject(int index) { + try { + return rr.getObject(index); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + @Override + public Object getObject(String column) { + try { + return rr.getObject(column); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + }; + } + + protected class ConnectionPool implements AutoCloseable { + + protected final LongAdder closeCounter = new LongAdder(); //已关闭连接数 + + protected final LongAdder usingCounter = new LongAdder(); //使用中连接数 + + protected final LongAdder creatCounter = new LongAdder(); //已创建连接数 + + protected final LongAdder cycleCounter = new LongAdder(); //已复用连接数 + + protected final LongAdder waitingCounter = new LongAdder(); //可用中连接数 + + protected final java.sql.Driver driver; + + protected final Properties connectAttrs; + + protected final ArrayBlockingQueue queue; + + protected int connectTimeoutSeconds; + + protected int maxconns; + + protected String url; + + public ConnectionPool(Properties prop) { + this.connectTimeoutSeconds = Integer.decode(prop.getProperty(JDBC_CONNECTTIMEOUT_SECONDS, "6")); + this.maxconns = Math.max(1, Integer.decode(prop.getProperty(JDBC_CONNECTIONS_LIMIT, "" + Utility.cpus() * 4))); + this.queue = new ArrayBlockingQueue<>(maxconns); + this.url = prop.getProperty(JDBC_URL); + String username = prop.getProperty(JDBC_USER, ""); + String password = prop.getProperty(JDBC_PWD, ""); + this.connectAttrs = new Properties(); + if (username != null) this.connectAttrs.put("user", username); + if (password != null) this.connectAttrs.put("password", password); + try { + this.driver = DriverManager.getDriver(this.url); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public synchronized Connection pollConnection() { + Connection conn = queue.poll(); + if (conn == null) { + if (usingCounter.intValue() >= maxconns) { + try { + conn = queue.poll(connectTimeoutSeconds, TimeUnit.SECONDS); + } catch (InterruptedException t) { + logger.log(Level.WARNING, "take pooled connection error", t); + } + if (conn == null) throw new RuntimeException("create pooled connection timeout"); + } + } + if (conn != null) { + usingCounter.increment(); + waitingCounter.decrement(); + if (checkValid(conn)) { + cycleCounter.increment(); + return conn; + } else { + offerConnection(conn); + conn = null; + } + } + try { + conn = driver.connect(url, connectAttrs); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + usingCounter.increment(); + creatCounter.increment(); + return conn; + } + + public void offerConnection(final C connection) { + Connection conn = (Connection) connection; + if (conn == null) return; + try { + if (checkValid(conn) && queue.offer(conn)) { + usingCounter.decrement(); + waitingCounter.increment(); + } else { + usingCounter.decrement(); + closeCounter.increment(); + conn.close(); + } + } catch (Exception e) { + logger.log(Level.WARNING, "closeSQLConnection abort", e); + } + } + + protected boolean checkValid(Connection conn) { + try { + return !conn.isClosed() && conn.isValid(1); + } catch (SQLException ex) { + if (!"08S01".equals(ex.getSQLState())) {//MySQL特性, 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException + logger.log(Level.FINER, "result.getConnection from pooled connection abort [" + ex.getSQLState() + "]", ex); + } + return false; + } + } + + @Override + public void close() { + queue.stream().forEach(x -> { + try { + x.close(); + } catch (Exception e) { + } + }); + } + } +} diff --git a/src/org/redkale/source/DataMemorySource.java b/src/main/java/org/redkale/source/DataMemorySource.java similarity index 79% rename from src/org/redkale/source/DataMemorySource.java rename to src/main/java/org/redkale/source/DataMemorySource.java index 77d57b7fd..b9a80fd44 100644 --- a/src/org/redkale/source/DataMemorySource.java +++ b/src/main/java/org/redkale/source/DataMemorySource.java @@ -1,157 +1,167 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.net.URL; -import java.sql.ResultSet; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import org.redkale.net.*; -import org.redkale.service.Local; -import org.redkale.util.*; - -/** - * - * - * @author zhangjx - */ -/** - * DataSource的Memory实现类
    - * 注意: javax.persistence.jdbc.url 需要指定为 memory:source - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Local -@AutoLoad(false) -@SuppressWarnings("unchecked") -@ResourceType(DataSource.class) -public class DataMemorySource extends DataSqlSource { - - public DataMemorySource(String unitName, URL persistxml, Properties readprop, Properties writeprop) { - super(unitName, persistxml, readprop, writeprop); - this.cacheForbidden = false; - } - - @Local - @Override - public String getType() { - return "memory"; - } - - @Override - protected boolean isOnlyCache(EntityInfo info) { - return true; - } - - @Local - @Override - public int directExecute(String sql) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Local - @Override - public int[] directExecute(String... sqls) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Local - @Override - public V directQuery(String sql, Function handler) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - protected boolean isAsync() { - return true; - } - - @Override - protected String prepareParamSign(int index) { - throw new UnsupportedOperationException("Not supported yet."); - } - - @Override - protected PoolSource createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) { - return null; - } - - @Override - protected CompletableFuture insertDB(EntityInfo info, T... entitys) { - return CompletableFuture.completedFuture(0); - } - - @Override - protected CompletableFuture deleteDB(EntityInfo info, Flipper flipper, String sql) { - return CompletableFuture.completedFuture(0); - } - - @Override - protected CompletableFuture clearTableDB(EntityInfo info, final String table, String sql) { - return CompletableFuture.completedFuture(0); - } - - @Override - protected CompletableFuture dropTableDB(EntityInfo info, final String table, String sql) { - return CompletableFuture.completedFuture(0); - } - - @Override - protected CompletableFuture updateDB(EntityInfo info, ChannelContext context, T... entitys) { - return CompletableFuture.completedFuture(0); - } - - @Override - protected CompletableFuture updateDB(EntityInfo info, Flipper flipper, String sql, boolean prepared, Object... params) { - return CompletableFuture.completedFuture(0); - } - - @Override - protected CompletableFuture> getNumberMapDB(EntityInfo info, String sql, FilterFuncColumn... columns) { - return CompletableFuture.completedFuture(null); - } - - @Override - protected CompletableFuture getNumberResultDB(EntityInfo info, String sql, Number defVal, String column) { - return CompletableFuture.completedFuture(defVal); - } - - @Override - protected CompletableFuture> queryColumnMapDB(EntityInfo info, String sql, String keyColumn) { - return CompletableFuture.completedFuture(null); - } - - @Override - protected CompletableFuture> queryColumnMapDB(final EntityInfo info, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns) { - return CompletableFuture.completedFuture(null); - } - - @Override - protected CompletableFuture findDB(EntityInfo info, ChannelContext context, String sql, boolean onlypk, SelectColumn selects) { - return CompletableFuture.completedFuture(null); - } - - @Override - protected CompletableFuture findColumnDB(EntityInfo info, String sql, boolean onlypk, String column, Serializable defValue) { - return CompletableFuture.completedFuture(null); - } - - @Override - protected CompletableFuture existsDB(EntityInfo info, String sql, boolean onlypk) { - return CompletableFuture.completedFuture(false); - } - - @Override - protected CompletableFuture> querySheetDB(EntityInfo info, final boolean readcache, boolean needtotal, final boolean distinct, SelectColumn selects, Flipper flipper, FilterNode node) { - return CompletableFuture.completedFuture(new Sheet<>(0, new ArrayList())); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.net.URL; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import org.redkale.service.Local; +import org.redkale.util.*; + +/** + * + * + * @author zhangjx + */ +/** + * DataSource的Memory实现类
    + * 注意: javax.persistence.jdbc.url 需要指定为 memory:source + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Local +@AutoLoad(false) +@SuppressWarnings("unchecked") +@ResourceType(DataSource.class) +public class DataMemorySource extends DataSqlSource implements SearchSource { + + public DataMemorySource(String unitName, URL persistFile, Properties readprop, Properties writeprop) { + super(unitName, persistFile, "memory", readprop, writeprop); + this.cacheForbidden = false; + } + + @Local + @Override + public String getType() { + return "memory"; + } + + @Override + @Local + public void compile(Class clazz) { + EntityInfo entityInfo = EntityInfo.compile(clazz, this); + if (entityInfo.getCache() == null) new EntityCache<>(entityInfo, null).clear(); + } + + @Override + public int updateMapping(final Class clazz) { + return 0; + } + + @Override + public CompletableFuture updateMappingAsync(final Class clazz) { + return CompletableFuture.completedFuture(0); + } + + @Override + protected boolean isOnlyCache(EntityInfo info) { + return true; + } + + @Local + @Override + public int directExecute(String sql) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Local + @Override + public int[] directExecute(String... sqls) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Local + @Override + public V directQuery(String sql, Function handler) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected boolean isAsync() { + return true; + } + + @Override + protected String prepareParamSign(int index) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + protected CompletableFuture insertDB(EntityInfo info, T... entitys) { + return CompletableFuture.completedFuture(0); + } + + @Override + protected CompletableFuture deleteDB(EntityInfo info, Flipper flipper, String sql) { + return CompletableFuture.completedFuture(0); + } + + @Override + protected CompletableFuture clearTableDB(EntityInfo info, final String table, String sql) { + return CompletableFuture.completedFuture(0); + } + + @Override + protected CompletableFuture dropTableDB(EntityInfo info, final String table, String sql) { + return CompletableFuture.completedFuture(0); + } + + @Override + protected CompletableFuture updateEntityDB(EntityInfo info, T... entitys) { + return CompletableFuture.completedFuture(0); + } + + @Override + protected CompletableFuture updateColumnDB(EntityInfo info, Flipper flipper, String sql, boolean prepared, Object... params) { + return CompletableFuture.completedFuture(0); + } + + @Override + protected CompletableFuture> getNumberMapDB(EntityInfo info, String sql, FilterFuncColumn... columns) { + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture getNumberResultDB(EntityInfo info, String sql, Number defVal, String column) { + return CompletableFuture.completedFuture(defVal); + } + + @Override + protected CompletableFuture> queryColumnMapDB(EntityInfo info, String sql, String keyColumn) { + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture> queryColumnMapDB(final EntityInfo info, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns) { + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture findDB(EntityInfo info, String sql, boolean onlypk, SelectColumn selects) { + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture findColumnDB(EntityInfo info, String sql, boolean onlypk, String column, Serializable defValue) { + return CompletableFuture.completedFuture(null); + } + + @Override + protected CompletableFuture existsDB(EntityInfo info, String sql, boolean onlypk) { + return CompletableFuture.completedFuture(false); + } + + @Override + protected CompletableFuture> querySheetDB(EntityInfo info, final boolean readcache, boolean needtotal, final boolean distinct, SelectColumn selects, Flipper flipper, FilterNode node) { + return CompletableFuture.completedFuture(new Sheet<>(0, new ArrayList())); + } + +} diff --git a/src/main/java/org/redkale/source/DataResultSet.java b/src/main/java/org/redkale/source/DataResultSet.java new file mode 100644 index 000000000..b0cbbdf41 --- /dev/null +++ b/src/main/java/org/redkale/source/DataResultSet.java @@ -0,0 +1,151 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.math.*; +import java.util.concurrent.atomic.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.Attribute; + +/** + * java.sql.ResultSet的简化版。
    + *
    + * 字段类型支持:
    + * 1、boolean/Boolean
    + * 2、byte/Byte
    + * 3、short/Short
    + * 4、char/Character
    + * 5、int/Integer/AtomicInteger
    + * 6、long/Long/AtomicLong/LongAdder
    + * 7、float/Float
    + * 8、double/Double
    + * 9、java.math.BigInteger
    + * 10、java.math.BigDecimal
    + * 11、String
    + * 12、byte[]
    + * 13、java.time.LocalDate/java.sql.Date/java.util.Date
    + * 14、java.time.LocalTime/java.sql.Time
    + * 15、java.time.LocalDateTime/java.sql.Timestamp
    + * 16、JavaBean/其他可JSON化类型
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +public interface DataResultSet extends EntityInfo.DataResultSetRow { + + public boolean next(); + + public void close(); + + public static Serializable getRowColumnValue(final EntityInfo.DataResultSetRow row, Attribute attr, int index, String column) { + final Class t = attr.type(); + Serializable o; + if (t == byte[].class) { + Object blob = index > 0 ? row.getObject(index) : row.getObject(column); + if (blob == null) { + o = null; + } else { //不支持超过2G的数据 +// if (blob instanceof java.sql.Blob) { +// java.sql.Blob b = (java.sql.Blob) blob; +// try { +// o = b.getBytes(1, (int) b.length()); +// } catch (Exception e) { //一般不会发生 +// o = null; +// } +// } else { + o = (byte[]) blob; + //} + CryptHandler cryptHandler = attr.attach(); + if (cryptHandler != null) o = (Serializable) cryptHandler.decrypt(o); + } + } else { + o = (Serializable) (index > 0 ? row.getObject(index) : row.getObject(column)); + CryptHandler cryptHandler = attr.attach(); + if (cryptHandler != null) o = (Serializable) cryptHandler.decrypt(o); + if (t.isPrimitive()) { + if (o != null) { + if (t == int.class) { + o = ((Number) o).intValue(); + } else if (t == long.class) { + o = ((Number) o).longValue(); + } else if (t == short.class) { + o = ((Number) o).shortValue(); + } else if (t == float.class) { + o = ((Number) o).floatValue(); + } else if (t == double.class) { + o = ((Number) o).doubleValue(); + } else if (t == byte.class) { + o = ((Number) o).byteValue(); + } else if (t == char.class) { + o = (char) ((Number) o).intValue(); + } else if (t == boolean.class) { + o = (Boolean) o; + } + } else if (t == int.class) { + o = 0; + } else if (t == long.class) { + o = 0L; + } else if (t == short.class) { + o = (short) 0; + } else if (t == float.class) { + o = 0.0f; + } else if (t == double.class) { + o = 0.0d; + } else if (t == byte.class) { + o = (byte) 0; + } else if (t == boolean.class) { + o = false; + } else if (t == char.class) { + o = (char) 0; + } + } else if (t == AtomicInteger.class) { + if (o != null) { + o = new AtomicInteger(((Number) o).intValue()); + } else { + o = new AtomicInteger(); + } + } else if (t == AtomicLong.class) { + if (o != null) { + o = new AtomicLong(((Number) o).longValue()); + } else { + o = new AtomicLong(); + } + } else if (t == LongAdder.class) { + if (o != null) { + LongAdder v = new LongAdder(); + v.add(((Number) o).longValue()); + o = v; + } else { + o = new LongAdder(); + } + } else if (t == BigInteger.class) { + if (o != null && !(o instanceof BigInteger)) { + if (o instanceof byte[]) { + o = new BigInteger((byte[]) o); + } else { + o = new BigInteger(o.toString(), 10); + } + } + } else if (t == BigDecimal.class) { + if (o != null && !(o instanceof BigDecimal)) { + if (o instanceof byte[]) { + o = new BigDecimal(new String((byte[]) o)); + } else { + o = new BigInteger(o.toString()); + } + } + } else if (o != null && !t.isAssignableFrom(o.getClass()) && o instanceof CharSequence) { + o = ((CharSequence) o).length() == 0 ? null : JsonConvert.root().convertFrom(attr.genericType(), o.toString()); + } + } + return o; + } + +} diff --git a/src/org/redkale/source/DataSource.java b/src/main/java/org/redkale/source/DataSource.java similarity index 95% rename from src/org/redkale/source/DataSource.java rename to src/main/java/org/redkale/source/DataSource.java index 887696f57..0f9b294d7 100644 --- a/src/org/redkale/source/DataSource.java +++ b/src/main/java/org/redkale/source/DataSource.java @@ -1,3204 +1,3326 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Stream; -import org.redkale.service.Local; -import org.redkale.util.*; -import org.redkale.net.ChannelContext; - -/** - * - * DataSource 为数据库或内存数据库的数据源,提供类似JPA、Hibernate的接口与功能。
    - * 返回类型为CompletableFuture的接口为异步接口 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public interface DataSource { - - /** - * 获取数据源类型 - * - * @return String - */ - public String getType(); - - //----------------------insertAsync----------------------------- - //insert 暂时不支持Collection、Stream, 因为存在@RpcCall问题 - /** - * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - public int insert(final T... entitys); - - /** - * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - default int insert(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return 0; - return insert(entitys.toArray()); - } - - /** - * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - default int insert(final Stream entitys) { - if (entitys == null) return 0; - return insert(entitys.toArray()); - } - - /** - * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return CompletableFuture - */ - public CompletableFuture insertAsync(final T... entitys); - - /** - * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return CompletableFuture - */ - default CompletableFuture insertAsync(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); - return insertAsync(entitys.toArray()); - } - - /** - * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return CompletableFuture - */ - default CompletableFuture insertAsync(final Stream entitys) { - if (entitys == null) return CompletableFuture.completedFuture(0); - return insertAsync(entitys.toArray()); - } - - //-------------------------deleteAsync-------------------------- - /** - * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - public int delete(final T... entitys); - - /** - * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - default int delete(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return 0; - return delete(entitys.toArray()); - } - - /** - * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - default int delete(final Stream entitys) { - if (entitys == null) return 0; - return delete(entitys.toArray()); - } - - /** - * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture deleteAsync(final T... entitys); - - /** - * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture deleteAsync(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); - return deleteAsync(entitys.toArray()); - } - - /** - * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture deleteAsync(final Stream entitys) { - if (entitys == null) return CompletableFuture.completedFuture(0); - return deleteAsync(entitys.toArray()); - } - - /** - * 删除指定主键值的记录,多主键值必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pks 主键值 - * - * @return 影响的记录条数 - */ - public int delete(final Class clazz, final Serializable... pks); - - /** - * 删除指定主键值的记录,多主键值必须在同一张表中
    - * 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pks 主键值 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture deleteAsync(final Class clazz, final Serializable... pks); - - /** - * 删除符合过滤条件的记录
    - * 等价SQL: DELETE FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 影响的记录条数 - */ - default int delete(final Class clazz, final FilterNode node) { - return delete(clazz, (Flipper) null, node); - } - - /** - * 删除符合过滤条件的记录
    - * 等价SQL: DELETE FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture deleteAsync(final Class clazz, final FilterNode node) { - return deleteAsync(clazz, (Flipper) null, node); - } - - /** - * 删除符合过滤条件且指定最大影响条数的记录
    - * Flipper中offset字段将被忽略
    - * 等价SQL: DELETE FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 影响的记录条数 - */ - public int delete(final Class clazz, final Flipper flipper, final FilterNode node); - - /** - * 删除符合过滤条件且指定最大影响条数的记录
    - * Flipper中offset字段将被忽略
    - * 等价SQL: DELETE FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture deleteAsync(final Class clazz, final Flipper flipper, final FilterNode node); - - //------------------------clearAsync--------------------------- - /** - * 清空表
    - * 等价SQL: TRUNCATE TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return 影响的记录条数 -1表示表不存在 - */ - default int clearTable(final Class clazz) { - return clearTable(clazz, (FilterNode) null); - } - - /** - * 清空表
    - * 等价SQL: TRUNCATE TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return 影响的记录条数CompletableFuture -1表示表不存在 - */ - default CompletableFuture clearTableAsync(final Class clazz) { - return clearTableAsync(clazz, (FilterNode) null); - } - - /** - * 清空表
    - * 等价SQL: TRUNCATE TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 影响的记录条数 -1表示表不存在 - */ - public int clearTable(final Class clazz, final FilterNode node); - - /** - * 清空表
    - * 等价SQL: TRUNCATE TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 影响的记录条数CompletableFuture -1表示表不存在 - */ - public CompletableFuture clearTableAsync(final Class clazz, final FilterNode node); - - //------------------------dropAsync--------------------------- - /** - * 删除表
    - * 等价SQL: DROP TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return 影响的记录条数 -1表示表不存在 - */ - default int dropTable(final Class clazz) { - return dropTable(clazz, (FilterNode) null); - } - - /** - * 删除表
    - * 等价SQL: DROP TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return 影响的记录条数CompletableFuture -1表示表不存在 - */ - default CompletableFuture dropTableAsync(final Class clazz) { - return dropTableAsync(clazz, (FilterNode) null); - } - - /** - * 删除表
    - * 等价SQL: DROP TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 影响的记录条数 -1表示表不存在 - */ - public int dropTable(final Class clazz, final FilterNode node); - - /** - * 删除表
    - * 等价SQL: DROP TABLE {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 影响的记录条数CompletableFuture -1表示表不存在 - */ - public CompletableFuture dropTableAsync(final Class clazz, final FilterNode node); - - //------------------------updateAsync--------------------------- - /** - * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL:
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    - * ···
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - public int update(final T... entitys); - - /** - * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL:
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    - * ···
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - default int update(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return 0; - return update(entitys.toArray()); - } - - /** - * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL:
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    - * ···
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - default int update(final Stream entitys) { - if (entitys == null) return 0; - return update(entitys.toArray()); - } - - /** - * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL:
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    - * ···
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateAsync(final T... entitys); - - /** - * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL:
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    - * ···
    - * - * @param context ContextChannel - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateAsync(final ChannelContext context, final T... entitys); - - /** - * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL:
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    - * ···
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture updateAsync(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); - return updateAsync(entitys.toArray()); - } - - /** - * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    - * 等价SQL:
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    - * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    - * ···
    - * - * @param 泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture updateAsync(final Stream entitys) { - if (entitys == null) return CompletableFuture.completedFuture(0); - return updateAsync(entitys.toArray()); - } - - /** - * 更新单个记录的单个字段
    - * 注意:即使字段标记为@Column(updatable=false)也会被更新
    - * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键 - * @param column 待更新的字段名 - * @param value 更新值 - * - * @return 影响的记录条数 - */ - public int updateColumn(final Class clazz, final Serializable pk, final String column, final Serializable value); - - /** - * 更新单个记录的单个字段
    - * 注意:即使字段标记为@Column(updatable=false)也会被更新
    - * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键 - * @param column 待更新的字段名 - * @param value 更新值 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final String column, final Serializable value); - - /** - * 更新符合过滤条件记录的单个字段
    - * 注意:即使字段标记为@Column(updatable=false)也会被更新
    - * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 待更新的字段名 - * @param value 更新值 - * @param node 过滤条件 - * - * @return 影响的记录条数 - */ - public int updateColumn(final Class clazz, final String column, final Serializable value, final FilterNode node); - - /** - * 更新符合过滤条件记录的单个字段
    - * 注意:即使字段标记为@Column(updatable=false)也会被更新
    - * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 待更新的字段名 - * @param value 更新值 - * @param node 过滤条件 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateColumnAsync(final Class clazz, final String column, final Serializable value, final FilterNode node); - - /** - * 更新指定主键值记录的部分字段
    - * 字段赋值操作选项见 ColumnExpress
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键 - * @param values 更新字段 - * - * @return 影响的记录条数 - */ - public int updateColumn(final Class clazz, final Serializable pk, final ColumnValue... values); - - /** - * 更新指定主键值记录的部分字段
    - * 字段赋值操作选项见 ColumnExpress
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键 - * @param values 更新字段 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final ColumnValue... values); - - /** - * 更新符合过滤条件记录的部分字段
    - * 字段赋值操作选项见 ColumnExpress
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * @param values 更新字段 - * - * @return 影响的记录条数 - */ - default int updateColumn(final Class clazz, final FilterNode node, final ColumnValue... values) { - return updateColumn(clazz, node, (Flipper) null, values); - } - - /** - * 更新符合过滤条件记录的部分字段
    - * 字段赋值操作选项见 ColumnExpress
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * @param values 更新字段 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final ColumnValue... values) { - return updateColumnAsync(clazz, node, (Flipper) null, values); - } - - /** - * 更新符合过滤条件的记录的指定字段
    - * Flipper中offset字段将被忽略
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node} ORDER BY {flipper.sort}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * @param flipper 翻页对象 - * @param values 更新字段 - * - * @return 影响的记录条数 - */ - public int updateColumn(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values); - - /** - * 更新符合过滤条件的记录的指定字段
    - * Flipper中offset字段将被忽略
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node} ORDER BY {flipper.sort}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * @param flipper 翻页对象 - * @param values 更新字段 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values); - - /** - * 更新单个记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param columns 需更新的字段名 - * - * @return 影响的记录条数 - */ - default int updateColumn(final T entity, final String... columns) { - return updateColumn(entity, (FilterNode) null, columns); - } - - /** - * 更新单个记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param columns 需更新的字段名 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture updateColumnAsync(final T entity, final String... columns) { - return updateColumnAsync(entity, (FilterNode) null, columns); - } - - /** - * 更新符合过滤条件记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param node 过滤条件 - * @param columns 需更新的字段名 - * - * @return 影响的记录条数 - */ - public int updateColumn(final T entity, final FilterNode node, final String... columns); - - /** - * 更新符合过滤条件记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param node 过滤条件 - * @param columns 需更新的字段名 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final String... columns); - - /** - * 更新单个记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param selects 指定字段 - * - * @return 影响的记录条数 - */ - default int updateColumn(final T entity, final SelectColumn selects) { - return updateColumn(entity, (FilterNode) null, selects); - } - - /** - * 更新单个记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param selects 指定字段 - * - * @return 影响的记录条数CompletableFuture - */ - default CompletableFuture updateColumnAsync(final T entity, final SelectColumn selects) { - return updateColumnAsync(entity, (FilterNode) null, selects); - } - - /** - * 更新符合过滤条件记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param node 过滤条件 - * @param selects 指定字段 - * - * @return 影响的记录条数 - */ - public int updateColumn(final T entity, final FilterNode node, final SelectColumn selects); - - /** - * 更新符合过滤条件记录的指定字段
    - * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    - * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    - * - * @param Entity泛型 - * @param entity 待更新的Entity对象 - * @param node 过滤条件 - * @param selects 指定字段 - * - * @return 影响的记录条数CompletableFuture - */ - public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final SelectColumn selects); - - //############################################# 查询接口 ############################################# - //-----------------------getXXXXResult----------------------------- - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回null
    - * 等价SQL: SELECT FUNC{column} FROM {table}
    - * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null) 等价于: SELECT COUNT(*) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param column 指定字段 - * - * @return 聚合结果 - */ - default Number getNumberResult(final Class entityClass, final FilterFunc func, final String column) { - return getNumberResult(entityClass, func, null, column, (FilterNode) null); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回null
    - * 等价SQL: SELECT FUNC{column} FROM {table}
    - * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null) 等价于: SELECT COUNT(*) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param column 指定字段 - * - * @return 聚合结果CompletableFuture - */ - default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column) { - return getNumberResultAsync(entityClass, func, null, column, (FilterNode) null); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回null
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    - * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null, (FilterBean)null) 等价于: SELECT COUNT(*) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param column 指定字段 - * @param bean 过滤条件 - * - * @return 聚合结果 - */ - default Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterBean bean) { - return getNumberResult(entityClass, func, column, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回null
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    - * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null, (FilterBean)null) 等价于: SELECT COUNT(*) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param column 指定字段 - * @param bean 过滤条件 - * - * @return 聚合结果CompletableFuture - */ - default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterBean bean) { - return getNumberResultAsync(entityClass, func, column, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回null
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param column 指定字段 - * @param node 过滤条件 - * - * @return 聚合结果 - */ - default Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { - return getNumberResult(entityClass, func, null, column, node); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回null
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param column 指定字段 - * @param node 过滤条件 - * - * @return 聚合结果 - */ - default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { - return getNumberResultAsync(entityClass, func, null, column, node); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    - * 等价SQL: SELECT FUNC{column} FROM {table}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime") 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param defVal 默认值 - * @param column 指定字段 - * - * @return 聚合结果 - */ - default Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { - return getNumberResult(entityClass, func, defVal, column, (FilterNode) null); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    - * 等价SQL: SELECT FUNC{column} FROM {table}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime") 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param defVal 默认值 - * @param column 指定字段 - * - * @return 聚合结果CompletableFuture - */ - default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { - return getNumberResultAsync(entityClass, func, defVal, column, (FilterNode) null); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param defVal 默认值 - * @param column 指定字段 - * @param bean 过滤条件 - * - * @return 聚合结果 - */ - default Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterBean bean) { - return getNumberResult(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param defVal 默认值 - * @param column 指定字段 - * @param bean 过滤条件 - * - * @return 聚合结果CompletableFuture - */ - default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterBean bean) { - return getNumberResultAsync(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param defVal 默认值 - * @param column 指定字段 - * @param node 过滤条件 - * - * @return 聚合结果 - */ - public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node); - - /** - * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    - * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    - * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param entityClass Entity类 - * @param func 聚合函数 - * @param defVal 默认值 - * @param column 指定字段 - * @param node 过滤条件 - * - * @return 聚合结果CompletableFuture - */ - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node); - - /** - * 获取符合过滤条件记录的聚合结果Map
    - * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table}
    - * 如 getNumberMapAsync(User.class, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param Number - * @param entityClass Entity类 - * @param columns 聚合字段 - * - * @return 聚合结果Map - */ - default Map getNumberMap(final Class entityClass, final FilterFuncColumn... columns) { - return getNumberMap(entityClass, (FilterNode) null, columns); - } - - /** - * 获取符合过滤条件记录的聚合结果Map
    - * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table}
    - * 如 getNumberMapAsync(User.class, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param Number - * @param entityClass Entity类 - * @param columns 聚合字段 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterFuncColumn... columns) { - return getNumberMapAsync(entityClass, (FilterNode) null, columns); - } - - /** - * 获取符合过滤条件记录的聚合结果Map
    - * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter bean}
    - * 如 getNumberMapAsync(User.class, (FilterBean)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param Number - * @param entityClass Entity类 - * @param bean 过滤条件 - * @param columns 聚合字段 - * - * @return 聚合结果Map - */ - default Map getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { - return getNumberMap(entityClass, FilterNodeBean.createFilterNode(bean), columns); - } - - /** - * 获取符合过滤条件记录的聚合结果Map
    - * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter bean}
    - * 如 getNumberMapAsync(User.class, (FilterBean)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param Number - * @param entityClass Entity类 - * @param bean 过滤条件 - * @param columns 聚合字段 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { - return getNumberMapAsync(entityClass, FilterNodeBean.createFilterNode(bean), columns); - } - - /** - * 获取符合过滤条件记录的聚合结果Map
    - * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter node}
    - * 如 getNumberMapAsync(User.class, (FilterNode)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param Number - * @param entityClass Entity类 - * @param node 过滤条件 - * @param columns 聚合字段 - * - * @return 聚合结果Map - */ - public Map getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns); - - /** - * 获取符合过滤条件记录的聚合结果Map
    - * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter node}
    - * 如 getNumberMapAsync(User.class, (FilterNode)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    - * - * @param Number - * @param entityClass Entity类 - * @param node 过滤条件 - * @param columns 聚合字段 - * - * @return 聚合结果Map - */ - public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns); - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} GROUP BY {keyColumn}
    - * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime") 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param keyColumn Key字段 - * @param func 聚合函数 - * @param funcColumn 聚合字段 - * - * @return 聚合结果Map - */ - default Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { - return queryColumnMap(entityClass, keyColumn, func, funcColumn, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} GROUP BY {keyColumn}
    - * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime") 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param keyColumn Key字段 - * @param func 聚合函数 - * @param funcColumn 聚合字段 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { - return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter bean} GROUP BY {keyColumn}
    - * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterBean)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param keyColumn Key字段 - * @param func 聚合函数 - * @param funcColumn 聚合字段 - * @param bean 过滤条件 - * - * @return 聚合结果Map - */ - default Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { - return queryColumnMap(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter bean} GROUP BY {keyColumn}
    - * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterBean)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param keyColumn Key字段 - * @param func 聚合函数 - * @param funcColumn 聚合字段 - * @param bean 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { - return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter node} GROUP BY {keyColumn}
    - * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param keyColumn Key字段 - * @param func 聚合函数 - * @param funcColumn 聚合字段 - * @param node 过滤条件 - * - * @return 聚合结果Map - */ - public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterNode node); - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter node} GROUP BY {keyColumn}
    - * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param keyColumn Key字段 - * @param func 聚合函数 - * @param funcColumn 聚合字段 - * @param node 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterNode node); - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE GROUP BY {col1}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid") - * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumn GROUP BY字段 - * - * @return 聚合结果Map CompletableFuture - */ - default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { - return queryColumnMap(entityClass, funcNodes, groupByColumn, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} GROUP BY {col1}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid") - * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumn GROUP BY字段 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterBean)null) - * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumn GROUP BY字段 - * @param bean 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { - return queryColumnMap(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterBean)null) - * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumn GROUP BY字段 - * @param bean 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterNode)null) - * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumn GROUP BY字段 - * @param node 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node); - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterNode)null) - * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumn GROUP BY字段 - * @param node 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node); - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} GROUP BY {col1}, {col2}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid")) - * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumns GROUP BY字段 - * - * @return 聚合结果Map CompletableFuture - */ - default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { - return queryColumnMap(entityClass, funcNodes, groupByColumns, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} GROUP BY {col1}, {col2}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid")) - * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumns GROUP BY字段 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}, {col2}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterBean)null) - * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumns GROUP BY字段 - * @param bean 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { - return queryColumnMap(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}, {col2}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterBean)null) - * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumns GROUP BY字段 - * @param bean 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}, {col2}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterNode)null) - * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumns GROUP BY字段 - * @param node 过滤条件 - * - * @return 聚合结果Map - */ - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node); - - /** - * 查询符合过滤条件记录的GROUP BY聚合结果Map
    - * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}, {col2}
    - * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterNode)null) - * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    - * - * @param Entity泛型 - * @param Key字段的数据类型 - * @param Number - * @param entityClass Entity类 - * @param funcNodes ColumnNode[] - * @param groupByColumns GROUP BY字段 - * @param node 过滤条件 - * - * @return 聚合结果Map CompletableFuture - */ - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node); - - //-----------------------findAsync---------------------------- - /** - * 获取指定主键值的单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键值 - * - * @return Entity对象 - */ - default T find(final Class clazz, final Serializable pk) { - return find(clazz, (SelectColumn) null, pk); - } - - /** - * 获取指定主键值的单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键值 - * - * @return Entity对象 CompletableFuture - */ - default CompletableFuture findAsync(final Class clazz, final Serializable pk) { - return findAsync(clazz, (SelectColumn) null, pk); - } - - /** - * 获取指定主键值的单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id}
    - * - * @param context ContextChannel - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键值 - * - * @return Entity对象 CompletableFuture - */ - @Local - public CompletableFuture findAsync(final Class clazz, final ChannelContext context, final Serializable pk); - - /** - * 获取指定主键值的单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param pk 主键值 - * - * @return Entity对象 - */ - public T find(final Class clazz, final SelectColumn selects, final Serializable pk); - - /** - * 获取指定主键值的单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param pk 主键值 - * - * @return Entity对象CompletableFuture - */ - public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final Serializable pk); - - /** - * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    - * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pks 主键值集合 - * - * @return Entity对象 - */ - //public T[] finds(final Class clazz, final Serializable... pks); - /** - * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    - * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pks 主键值集合 - * - * @return Entity对象 CompletableFuture - */ - //public CompletableFuture findsAsync(final Class clazz, final Serializable... pks); - /** - * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param pks 主键值集合 - * - * @return Entity对象 - */ - //public T[] finds(final Class clazz, final SelectColumn selects, final Serializable... pks); - /** - * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param pks 主键值集合 - * - * @return Entity对象CompletableFuture - */ - //public CompletableFuture findsAsync(final Class clazz, final SelectColumn selects, final Serializable... pks); - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity对象 - */ - public T find(final Class clazz, final String column, final Serializable colval); - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity对象CompletableFuture - */ - public CompletableFuture findAsync(final Class clazz, final String column, final Serializable colval); - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return Entity对象 - */ - default T find(final Class clazz, final FilterBean bean) { - return find(clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return Entity对象CompletableFuture - */ - default CompletableFuture findAsync(final Class clazz, final FilterBean bean) { - return findAsync(clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return Entity对象 - */ - default T find(final Class clazz, final FilterNode node) { - return find(clazz, (SelectColumn) null, node); - } - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return Entity对象CompletableFuture - */ - default CompletableFuture findAsync(final Class clazz, final FilterNode node) { - return findAsync(clazz, (SelectColumn) null, node); - } - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean 过滤条件 - * - * @return Entity对象 - */ - default T find(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return find(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean 过滤条件 - * - * @return Entity对象 CompletableFuture - */ - default CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return findAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node 过滤条件 - * - * @return Entity对象 - */ - public T find(final Class clazz, final SelectColumn selects, final FilterNode node); - - /** - * 获取符合过滤条件单个记录, 返回null表示不存在值
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node 过滤条件 - * - * @return Entity对象 CompletableFuture - */ - public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterNode node); - - /** - * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param pk 主键值 - * - * @return Entity对象 - */ - public Serializable findColumn(final Class clazz, final String column, final Serializable pk); - - /** - * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param pk 主键值 - * - * @return Entity对象 CompletableFuture - */ - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable pk); - - /** - * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param bean 过滤条件 - * - * @return 字段值 - */ - default Serializable findColumn(final Class clazz, final String column, final FilterBean bean) { - return findColumn(clazz, column, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param bean 过滤条件 - * - * @return 字段值 CompletableFuture - */ - default CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterBean bean) { - return findColumnAsync(clazz, column, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param node 过滤条件 - * - * @return 字段值 - */ - default Serializable findColumn(final Class clazz, final String column, final FilterNode node) { - return findColumn(clazz, column, null, node); - } - - /** - * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param node 过滤条件 - * - * @return 字段值 CompletableFuture - */ - default CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterNode node) { - return findColumnAsync(clazz, column, null, node); - } - - /** - * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param defValue 默认值 - * @param pk 主键值 - * - * @return 字段值 - */ - public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final Serializable pk); - - /** - * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param defValue 默认值 - * @param pk 主键值 - * - * @return 字段值 CompletableFuture - */ - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final Serializable pk); - - /** - * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param defValue 默认值 - * @param bean 过滤条件 - * - * @return 字段值 - */ - default Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { - return findColumn(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param defValue 默认值 - * @param bean 过滤条件 - * - * @return 字段值 CompletableFuture - */ - default CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { - return findColumnAsync(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param defValue 默认值 - * @param node 过滤条件 - * - * @return 字段值 - */ - public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterNode node); - - /** - * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    - * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 字段名 - * @param defValue 默认值 - * @param node 过滤条件 - * - * @return 字段值 CompletableFuture - */ - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterNode node); - - /** - * 判断是否存在主键值的记录
    - * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键值 - * - * @return 是否存在 - */ - public boolean exists(final Class clazz, final Serializable pk); - - /** - * 判断是否存在主键值的记录
    - * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {primary} = {id}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param pk 主键值 - * - * @return 是否存在CompletableFuture - */ - public CompletableFuture existsAsync(final Class clazz, final Serializable pk); - - /** - * 判断是否存在符合过滤条件的记录
    - * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return 是否存在 - */ - default boolean exists(final Class clazz, final FilterBean bean) { - return exists(clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 判断是否存在符合过滤条件的记录
    - * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return 是否存在CompletableFuture - */ - default CompletableFuture existsAsync(final Class clazz, final FilterBean bean) { - return existsAsync(clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 判断是否存在符合过滤条件的记录
    - * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 是否存在 - */ - public boolean exists(final Class clazz, final FilterNode node); - - /** - * 判断是否存在符合过滤条件的记录
    - * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 是否存在CompletableFuture - */ - public CompletableFuture existsAsync(final Class clazz, final FilterNode node); - - //-----------------------list set---------------------------- - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {column} = {key}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return 字段值的集合 - */ - public Set queryColumnSet(final String selectedColumn, final Class clazz, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {column} = {key}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return 字段值的集合CompletableFuture - */ - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return 字段值的集合 - */ - default Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnSet(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - default CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnSetAsync(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 字段值的集合 - */ - default Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnSet(selectedColumn, clazz, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - default CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnSetAsync(selectedColumn, clazz, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return 字段值的集合 - */ - default Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - default CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 字段值的集合 - */ - public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的某个字段Set集合
    - * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {column} = {key}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return 字段值的集合 - */ - public List queryColumnList(final String selectedColumn, final Class clazz, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {column} = {key}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return 字段值的集合CompletableFuture - */ - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return 字段值的集合 - */ - default List queryColumnList(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnList(selectedColumn, clazz, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - default CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnListAsync(selectedColumn, clazz, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 字段值的集合 - */ - default List queryColumnList(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnList(selectedColumn, clazz, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - default CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnListAsync(selectedColumn, clazz, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return 字段值的集合 - */ - default List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnList(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - default CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnListAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 字段值的集合 - */ - public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的某个字段List集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的某个字段Sheet集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return 字段值的集合 - */ - default Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSheet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段Sheet集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - default CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSheetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的某个字段Sheet集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 字段值的集合 - */ - public Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的某个字段Sheet集合
    - * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param 字段类型 - * @param selectedColumn 指定字段 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return 字段值的集合CompletableFuture - */ - public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param keyStream 主键Stream - * - * @return Entity的集合 - */ - default Map queryMap(final Class clazz, final Stream keyStream) { - return queryMap(clazz, (SelectColumn) null, keyStream); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param keyStream 主键Stream - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryMapAsync(final Class clazz, final Stream keyStream) { - return queryMapAsync(clazz, (SelectColumn) null, keyStream); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param bean FilterBean - * - * @return Entity的集合 - */ - default Map queryMap(final Class clazz, final FilterBean bean) { - return queryMap(clazz, (SelectColumn) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param bean FilterBean - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryMapAsync(final Class clazz, final FilterBean bean) { - return queryMapAsync(clazz, (SelectColumn) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param node FilterNode - * - * @return Entity的集合 - */ - default Map queryMap(final Class clazz, final FilterNode node) { - return queryMap(clazz, (SelectColumn) null, node); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param node FilterNode - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryMapAsync(final Class clazz, final FilterNode node) { - return queryMapAsync(clazz, (SelectColumn) null, node); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param keyStream 主键Stream - * - * @return Entity的集合 - */ - public Map queryMap(final Class clazz, final SelectColumn selects, final Stream keyStream); - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param keyStream 主键Stream - * - * @return Entity的集合CompletableFuture - */ - public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final Stream keyStream); - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean FilterBean - * - * @return Entity的集合 - */ - default Map queryMap(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return queryMap(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean FilterBean - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, FilterBean bean) { - return queryMapAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node FilterNode - * - * @return Entity的集合 - */ - public Map queryMap(final Class clazz, final SelectColumn selects, final FilterNode node); - - /** - * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node FilterNode - * - * @return Entity的集合CompletableFuture - */ - public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterNode node); - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz, final String column, final Serializable colval) { - return querySet(clazz, (Flipper) null, column, colval); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz, final String column, final Serializable colval) { - return querySetAsync(clazz, (Flipper) null, column, colval); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz, final FilterBean bean) { - return querySet(clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz, final FilterBean bean) { - return querySetAsync(clazz, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz) { - return querySet(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz, final FilterNode node) { - return querySet(clazz, (SelectColumn) null, (Flipper) null, node); - } - - /** - * 查询记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz) { - return querySetAsync(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz, final FilterNode node) { - return querySetAsync(clazz, (SelectColumn) null, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return querySet(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return querySetAsync(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz, final SelectColumn selects, final FilterNode node) { - return querySet(clazz, selects, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { - return querySetAsync(clazz, selects, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合 - */ - public Set querySet(final Class clazz, final Flipper flipper, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合CompletableFuture - */ - public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySet(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySetAsync(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - * - */ - default Set querySet(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySet(clazz, (SelectColumn) null, flipper, node); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - * - */ - default CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySetAsync(clazz, (SelectColumn) null, flipper, node); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的Set集合
    - * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz, final String column, final Serializable colval) { - return queryList(clazz, (Flipper) null, column, colval); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz, final String column, final Serializable colval) { - return queryListAsync(clazz, (Flipper) null, column, colval); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz, final FilterBean bean) { - return queryList(clazz, (SelectColumn) null, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz, final FilterBean bean) { - return queryListAsync(clazz, (SelectColumn) null, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询记录的List集合
    - * 等价SQL: SELECT * FROM {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz) { - return queryList(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz, final FilterNode node) { - return queryList(clazz, (SelectColumn) null, (Flipper) null, node); - } - - /** - * 查询记录的List集合
    - * 等价SQL: SELECT * FROM {table}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz) { - return queryListAsync(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz, final FilterNode node) { - return queryListAsync(clazz, (SelectColumn) null, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return queryList(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return queryListAsync(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz, final SelectColumn selects, final FilterNode node) { - return queryList(clazz, selects, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { - return queryListAsync(clazz, selects, (Flipper) null, node); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合 - */ - public List queryList(final Class clazz, final Flipper flipper, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity的集合CompletableFuture - */ - public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval); - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryList(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryListAsync(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - * - */ - default List queryList(final Class clazz, final Flipper flipper, final FilterNode node) { - return queryList(clazz, (SelectColumn) null, flipper, node); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - * - */ - default CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterNode node) { - return queryListAsync(clazz, (SelectColumn) null, flipper, node); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return queryList(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return queryListAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的List集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); - - //-----------------------sheet---------------------------- - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default Sheet querySheet(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySheet(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySheetAsync(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - default Sheet querySheet(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySheet(clazz, (SelectColumn) null, flipper, node); - } - - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySheetAsync(clazz, (SelectColumn) null, flipper, node); - } - - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合 - */ - default Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySheet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param bean 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - default CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySheetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合 - */ - public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); - - /** - * 查询符合过滤条件记录的Sheet集合
    - * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param flipper 翻页对象 - * @param node 过滤条件 - * - * @return Entity的集合CompletableFuture - */ - public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; +import org.redkale.util.*; + +/** + * + * DataSource 为数据库或内存数据库的数据源,提供类似JPA、Hibernate的接口与功能。
    + * 返回类型为CompletableFuture的接口为异步接口
    + *
    + * 字段类型支持:
    + * 1、boolean/Boolean
    + * 2、byte/Byte
    + * 3、short/Short
    + * 4、char/Character
    + * 5、int/Integer/AtomicInteger
    + * 6、long/Long/AtomicLong/LongAdder
    + * 7、float/Float
    + * 8、double/Double
    + * 9、java.math.BigInteger
    + * 10、java.math.BigDecimal
    + * 11、String
    + * 12、byte[]
    + * 13、java.time.LocalDate/java.sql.Date/java.util.Date
    + * 14、java.time.LocalTime/java.sql.Time
    + * 15、java.time.LocalDateTime/java.sql.Timestamp
    + * 16、JavaBean/其他可JSON化类型
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public interface DataSource { + + /** + * 获取数据源类型 + * + * @return String + */ + public String getType(); + + /** + * + * 提取预编译Entity类,主要用于native-image使用 + * + * @param 泛型 + * @param clazz Entity实体类 + */ + public void compile(Class clazz); + + //----------------------insertAsync----------------------------- + //insert 暂时不支持Collection、Stream, 因为存在@RpcCall问题 + /** + * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + public int insert(final T... entitys); + + /** + * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + default int insert(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return 0; + return insert(entitys.toArray()); + } + + /** + * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + default int insert(final Stream entitys) { + if (entitys == null) return 0; + return insert(entitys.toArray()); + } + + /** + * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return CompletableFuture + */ + public CompletableFuture insertAsync(final T... entitys); + + /** + * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return CompletableFuture + */ + default CompletableFuture insertAsync(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); + return insertAsync(entitys.toArray()); + } + + /** + * 新增记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return CompletableFuture + */ + default CompletableFuture insertAsync(final Stream entitys) { + if (entitys == null) return CompletableFuture.completedFuture(0); + return insertAsync(entitys.toArray()); + } + + //-------------------------deleteAsync-------------------------- + /** + * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + public int delete(final T... entitys); + + /** + * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + default int delete(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return 0; + return delete(entitys.toArray()); + } + + /** + * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + default int delete(final Stream entitys) { + if (entitys == null) return 0; + return delete(entitys.toArray()); + } + + /** + * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture deleteAsync(final T... entitys); + + /** + * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture deleteAsync(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); + return deleteAsync(entitys.toArray()); + } + + /** + * 删除指定主键值的记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {values.id}
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture deleteAsync(final Stream entitys) { + if (entitys == null) return CompletableFuture.completedFuture(0); + return deleteAsync(entitys.toArray()); + } + + /** + * 删除指定主键值的记录,多主键值必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pks 主键值 + * + * @return 影响的记录条数 + */ + public int delete(final Class clazz, final Serializable... pks); + + /** + * 删除指定主键值的记录,多主键值必须在同一张表中
    + * 等价SQL: DELETE FROM {table} WHERE {primary} IN {ids}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pks 主键值 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture deleteAsync(final Class clazz, final Serializable... pks); + + /** + * 删除符合过滤条件的记录
    + * 等价SQL: DELETE FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 影响的记录条数 + */ + default int delete(final Class clazz, final FilterNode node) { + return delete(clazz, (Flipper) null, node); + } + + /** + * 删除符合过滤条件的记录
    + * 等价SQL: DELETE FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture deleteAsync(final Class clazz, final FilterNode node) { + return deleteAsync(clazz, (Flipper) null, node); + } + + /** + * 删除符合过滤条件且指定最大影响条数的记录
    + * Flipper中offset字段将被忽略
    + * 等价SQL: DELETE FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 影响的记录条数 + */ + public int delete(final Class clazz, final Flipper flipper, final FilterNode node); + + /** + * 删除符合过滤条件且指定最大影响条数的记录
    + * Flipper中offset字段将被忽略
    + * 等价SQL: DELETE FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture deleteAsync(final Class clazz, final Flipper flipper, final FilterNode node); + + //------------------------clearAsync--------------------------- + /** + * 清空表
    + * 等价SQL: TRUNCATE TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return 影响的记录条数 -1表示表不存在 + */ + default int clearTable(final Class clazz) { + return clearTable(clazz, (FilterNode) null); + } + + /** + * 清空表
    + * 等价SQL: TRUNCATE TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return 影响的记录条数CompletableFuture -1表示表不存在 + */ + default CompletableFuture clearTableAsync(final Class clazz) { + return clearTableAsync(clazz, (FilterNode) null); + } + + /** + * 清空表
    + * 等价SQL: TRUNCATE TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 影响的记录条数 -1表示表不存在 + */ + public int clearTable(final Class clazz, final FilterNode node); + + /** + * 清空表
    + * 等价SQL: TRUNCATE TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 影响的记录条数CompletableFuture -1表示表不存在 + */ + public CompletableFuture clearTableAsync(final Class clazz, final FilterNode node); + + //------------------------dropAsync--------------------------- + /** + * 删除表
    + * 等价SQL: DROP TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return 影响的记录条数 -1表示表不存在 + */ + default int dropTable(final Class clazz) { + return dropTable(clazz, (FilterNode) null); + } + + /** + * 删除表
    + * 等价SQL: DROP TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return 影响的记录条数CompletableFuture -1表示表不存在 + */ + default CompletableFuture dropTableAsync(final Class clazz) { + return dropTableAsync(clazz, (FilterNode) null); + } + + /** + * 删除表
    + * 等价SQL: DROP TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 影响的记录条数 -1表示表不存在 + */ + public int dropTable(final Class clazz, final FilterNode node); + + /** + * 删除表
    + * 等价SQL: DROP TABLE {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 影响的记录条数CompletableFuture -1表示表不存在 + */ + public CompletableFuture dropTableAsync(final Class clazz, final FilterNode node); + + //------------------------updateAsync--------------------------- + /** + * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL:
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    + * ···
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + public int update(final T... entitys); + + /** + * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL:
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    + * ···
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + default int update(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return 0; + return update(entitys.toArray()); + } + + /** + * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL:
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    + * ···
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + default int update(final Stream entitys) { + if (entitys == null) return 0; + return update(entitys.toArray()); + } + + /** + * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL:
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    + * ···
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture updateAsync(final T... entitys); + + /** + * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL:
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    + * ···
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture updateAsync(final Collection entitys) { + if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); + return updateAsync(entitys.toArray()); + } + + /** + * 更新记录, 多对象必须是同一个Entity类且必须在同一张表中
    + * 等价SQL:
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id1}
    + * UPDATE {table} SET column1 = value1, column2 = value2, ··· WHERE {primary} = {id2}
    + * ···
    + * + * @param 泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture updateAsync(final Stream entitys) { + if (entitys == null) return CompletableFuture.completedFuture(0); + return updateAsync(entitys.toArray()); + } + + /** + * 更新单个记录的单个字段
    + * 注意:即使字段标记为@Column(updatable=false)也会被更新
    + * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键 + * @param column 待更新的字段名 + * @param value 更新值 + * + * @return 影响的记录条数 + */ + public int updateColumn(final Class clazz, final Serializable pk, final String column, final Serializable value); + + /** + * 更新单个记录的单个字段
    + * 注意:即使字段标记为@Column(updatable=false)也会被更新
    + * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键 + * @param column 待更新的字段名 + * @param value 更新值 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final String column, final Serializable value); + + /** + * 更新符合过滤条件记录的单个字段
    + * 注意:即使字段标记为@Column(updatable=false)也会被更新
    + * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 待更新的字段名 + * @param value 更新值 + * @param node 过滤条件 + * + * @return 影响的记录条数 + */ + public int updateColumn(final Class clazz, final String column, final Serializable value, final FilterNode node); + + /** + * 更新符合过滤条件记录的单个字段
    + * 注意:即使字段标记为@Column(updatable=false)也会被更新
    + * 等价SQL: UPDATE {table} SET {column} = {value} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 待更新的字段名 + * @param value 更新值 + * @param node 过滤条件 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture updateColumnAsync(final Class clazz, final String column, final Serializable value, final FilterNode node); + + /** + * 更新指定主键值记录的部分字段
    + * 字段赋值操作选项见 ColumnExpress
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键 + * @param values 更新字段 + * + * @return 影响的记录条数 + */ + public int updateColumn(final Class clazz, final Serializable pk, final ColumnValue... values); + + /** + * 更新指定主键值记录的部分字段
    + * 字段赋值操作选项见 ColumnExpress
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键 + * @param values 更新字段 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final ColumnValue... values); + + /** + * 更新符合过滤条件记录的部分字段
    + * 字段赋值操作选项见 ColumnExpress
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * @param values 更新字段 + * + * @return 影响的记录条数 + */ + default int updateColumn(final Class clazz, final FilterNode node, final ColumnValue... values) { + return updateColumn(clazz, node, (Flipper) null, values); + } + + /** + * 更新符合过滤条件记录的部分字段
    + * 字段赋值操作选项见 ColumnExpress
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * @param values 更新字段 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final ColumnValue... values) { + return updateColumnAsync(clazz, node, (Flipper) null, values); + } + + /** + * 更新符合过滤条件的记录的指定字段
    + * Flipper中offset字段将被忽略
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node} ORDER BY {flipper.sort}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * @param flipper 翻页对象 + * @param values 更新字段 + * + * @return 影响的记录条数 + */ + public int updateColumn(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values); + + /** + * 更新符合过滤条件的记录的指定字段
    + * Flipper中offset字段将被忽略
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} += {value2}, {column3} *= {value3}, ··· WHERE {filter node} ORDER BY {flipper.sort}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * @param flipper 翻页对象 + * @param values 更新字段 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values); + + /** + * 更新单个记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param columns 需更新的字段名 + * + * @return 影响的记录条数 + */ + default int updateColumn(final T entity, final String... columns) { + return updateColumn(entity, (FilterNode) null, columns); + } + + /** + * 更新单个记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param columns 需更新的字段名 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture updateColumnAsync(final T entity, final String... columns) { + return updateColumnAsync(entity, (FilterNode) null, columns); + } + + /** + * 更新符合过滤条件记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param node 过滤条件 + * @param columns 需更新的字段名 + * + * @return 影响的记录条数 + */ + public int updateColumn(final T entity, final FilterNode node, final String... columns); + + /** + * 更新符合过滤条件记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param node 过滤条件 + * @param columns 需更新的字段名 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final String... columns); + + /** + * 更新单个记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param selects 指定字段 + * + * @return 影响的记录条数 + */ + default int updateColumn(final T entity, final SelectColumn selects) { + return updateColumn(entity, (FilterNode) null, selects); + } + + /** + * 更新单个记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {primary} = {bean.id}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param selects 指定字段 + * + * @return 影响的记录条数CompletableFuture + */ + default CompletableFuture updateColumnAsync(final T entity, final SelectColumn selects) { + return updateColumnAsync(entity, (FilterNode) null, selects); + } + + /** + * 更新符合过滤条件记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param node 过滤条件 + * @param selects 指定字段 + * + * @return 影响的记录条数 + */ + public int updateColumn(final T entity, final FilterNode node, final SelectColumn selects); + + /** + * 更新符合过滤条件记录的指定字段
    + * 注意:Entity类中标记为@Column(updatable=false)不会被更新
    + * 等价SQL: UPDATE {table} SET {column1} = {value1}, {column2} = {value2}, {column3} = {value3}, ··· WHERE {filter node}
    + * + * @param Entity泛型 + * @param entity 待更新的Entity对象 + * @param node 过滤条件 + * @param selects 指定字段 + * + * @return 影响的记录条数CompletableFuture + */ + public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final SelectColumn selects); + + //############################################# 查询接口 ############################################# + //-----------------------getXXXXResult----------------------------- + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回null
    + * 等价SQL: SELECT FUNC{column} FROM {table}
    + * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null) 等价于: SELECT COUNT(*) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param column 指定字段 + * + * @return 聚合结果 + */ + default Number getNumberResult(final Class entityClass, final FilterFunc func, final String column) { + return getNumberResult(entityClass, func, null, column, (FilterNode) null); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回null
    + * 等价SQL: SELECT FUNC{column} FROM {table}
    + * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null) 等价于: SELECT COUNT(*) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param column 指定字段 + * + * @return 聚合结果CompletableFuture + */ + default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column) { + return getNumberResultAsync(entityClass, func, null, column, (FilterNode) null); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回null
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    + * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null, (FilterBean)null) 等价于: SELECT COUNT(*) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param column 指定字段 + * @param bean 过滤条件 + * + * @return 聚合结果 + */ + default Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterBean bean) { + return getNumberResult(entityClass, func, column, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回null
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    + * 如 getNumberResultAsync(User.class, FilterFunc.COUNT, null, (FilterBean)null) 等价于: SELECT COUNT(*) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param column 指定字段 + * @param bean 过滤条件 + * + * @return 聚合结果CompletableFuture + */ + default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterBean bean) { + return getNumberResultAsync(entityClass, func, column, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回null
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param column 指定字段 + * @param node 过滤条件 + * + * @return 聚合结果 + */ + default Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { + return getNumberResult(entityClass, func, null, column, node); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回null
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param column 指定字段 + * @param node 过滤条件 + * + * @return 聚合结果 + */ + default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { + return getNumberResultAsync(entityClass, func, null, column, node); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    + * 等价SQL: SELECT FUNC{column} FROM {table}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime") 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param defVal 默认值 + * @param column 指定字段 + * + * @return 聚合结果 + */ + default Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { + return getNumberResult(entityClass, func, defVal, column, (FilterNode) null); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    + * 等价SQL: SELECT FUNC{column} FROM {table}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime") 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param defVal 默认值 + * @param column 指定字段 + * + * @return 聚合结果CompletableFuture + */ + default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { + return getNumberResultAsync(entityClass, func, defVal, column, (FilterNode) null); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param defVal 默认值 + * @param column 指定字段 + * @param bean 过滤条件 + * + * @return 聚合结果 + */ + default Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterBean bean) { + return getNumberResult(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter bean}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param defVal 默认值 + * @param column 指定字段 + * @param bean 过滤条件 + * + * @return 聚合结果CompletableFuture + */ + default CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterBean bean) { + return getNumberResultAsync(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param defVal 默认值 + * @param column 指定字段 + * @param node 过滤条件 + * + * @return 聚合结果 + */ + public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node); + + /** + * 获取符合过滤条件记录的聚合结果, 无结果返回默认值
    + * 等价SQL: SELECT FUNC{column} FROM {table} WHERE {filter node}
    + * 如 getNumberResultAsync(User.class, FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param entityClass Entity类 + * @param func 聚合函数 + * @param defVal 默认值 + * @param column 指定字段 + * @param node 过滤条件 + * + * @return 聚合结果CompletableFuture + */ + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node); + + /** + * 获取符合过滤条件记录的聚合结果Map
    + * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table}
    + * 如 getNumberMapAsync(User.class, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param Number + * @param entityClass Entity类 + * @param columns 聚合字段 + * + * @return 聚合结果Map + */ + default Map getNumberMap(final Class entityClass, final FilterFuncColumn... columns) { + return getNumberMap(entityClass, (FilterNode) null, columns); + } + + /** + * 获取符合过滤条件记录的聚合结果Map
    + * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table}
    + * 如 getNumberMapAsync(User.class, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param Number + * @param entityClass Entity类 + * @param columns 聚合字段 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterFuncColumn... columns) { + return getNumberMapAsync(entityClass, (FilterNode) null, columns); + } + + /** + * 获取符合过滤条件记录的聚合结果Map
    + * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter bean}
    + * 如 getNumberMapAsync(User.class, (FilterBean)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param Number + * @param entityClass Entity类 + * @param bean 过滤条件 + * @param columns 聚合字段 + * + * @return 聚合结果Map + */ + default Map getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { + return getNumberMap(entityClass, FilterNodeBean.createFilterNode(bean), columns); + } + + /** + * 获取符合过滤条件记录的聚合结果Map
    + * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter bean}
    + * 如 getNumberMapAsync(User.class, (FilterBean)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param Number + * @param entityClass Entity类 + * @param bean 过滤条件 + * @param columns 聚合字段 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { + return getNumberMapAsync(entityClass, FilterNodeBean.createFilterNode(bean), columns); + } + + /** + * 获取符合过滤条件记录的聚合结果Map
    + * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter node}
    + * 如 getNumberMapAsync(User.class, (FilterNode)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param Number + * @param entityClass Entity类 + * @param node 过滤条件 + * @param columns 聚合字段 + * + * @return 聚合结果Map + */ + public Map getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns); + + /** + * 获取符合过滤条件记录的聚合结果Map
    + * 等价SQL: SELECT FUNC1{column1}, FUNC2{column2}, ··· FROM {table} WHERE {filter node}
    + * 如 getNumberMapAsync(User.class, (FilterNode)null, new FilterFuncColumn(FilterFunc.MAX, "createtime")) 等价于: SELECT MAX(createtime) FROM {table}
    + * + * @param Number + * @param entityClass Entity类 + * @param node 过滤条件 + * @param columns 聚合字段 + * + * @return 聚合结果Map + */ + public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns); + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} GROUP BY {keyColumn}
    + * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime") 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param keyColumn Key字段 + * @param func 聚合函数 + * @param funcColumn 聚合字段 + * + * @return 聚合结果Map + */ + default Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { + return queryColumnMap(entityClass, keyColumn, func, funcColumn, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} GROUP BY {keyColumn}
    + * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime") 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param keyColumn Key字段 + * @param func 聚合函数 + * @param funcColumn 聚合字段 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { + return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter bean} GROUP BY {keyColumn}
    + * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterBean)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param keyColumn Key字段 + * @param func 聚合函数 + * @param funcColumn 聚合字段 + * @param bean 过滤条件 + * + * @return 聚合结果Map + */ + default Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { + return queryColumnMap(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter bean} GROUP BY {keyColumn}
    + * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterBean)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param keyColumn Key字段 + * @param func 聚合函数 + * @param funcColumn 聚合字段 + * @param bean 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { + return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter node} GROUP BY {keyColumn}
    + * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param keyColumn Key字段 + * @param func 聚合函数 + * @param funcColumn 聚合字段 + * @param node 过滤条件 + * + * @return 聚合结果Map + */ + public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterNode node); + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT keyColumn, FUNC{funcColumn} FROM {table} WHERE {filter node} GROUP BY {keyColumn}
    + * 如 queryColumnMapAsync(User.class, "name", FilterFunc.MAX, "createtime", (FilterNode)null) 等价于: SELECT name, MAX(createtime) FROM user GROUP BY name
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param keyColumn Key字段 + * @param func 聚合函数 + * @param funcColumn 聚合字段 + * @param node 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterNode node); + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE GROUP BY {col1}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid") + * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumn GROUP BY字段 + * + * @return 聚合结果Map CompletableFuture + */ + default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { + return queryColumnMap(entityClass, funcNodes, groupByColumn, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} GROUP BY {col1}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid") + * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumn GROUP BY字段 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterBean)null) + * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumn GROUP BY字段 + * @param bean 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { + return queryColumnMap(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterBean)null) + * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumn GROUP BY字段 + * @param bean 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterNode)null) + * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumn GROUP BY字段 + * @param node 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node); + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), "targetid", (FilterNode)null) + * 等价于: SELECT targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumn GROUP BY字段 + * @param node 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node); + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} GROUP BY {col1}, {col2}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid")) + * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumns GROUP BY字段 + * + * @return 聚合结果Map CompletableFuture + */ + default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { + return queryColumnMap(entityClass, funcNodes, groupByColumns, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} GROUP BY {col1}, {col2}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid")) + * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumns GROUP BY字段 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}, {col2}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterBean)null) + * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumns GROUP BY字段 + * @param bean 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + default Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { + return queryColumnMap(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter bean} GROUP BY {col1}, {col2}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterBean)null) + * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumns GROUP BY字段 + * @param bean 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + default CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { + return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}, {col2}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterNode)null) + * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumns GROUP BY字段 + * @param node 过滤条件 + * + * @return 聚合结果Map + */ + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node); + + /** + * 查询符合过滤条件记录的GROUP BY聚合结果Map
    + * 等价SQL: SELECT col1, col2, FUNC{funcColumn1}, FUNC{funcColumn2} FROM {table} WHERE {filter node} GROUP BY {col1}, {col2}
    + * 如 queryColumnMapAsync(OrderRecord.class, Utility.ofArray(ColumnNodeValue.div(ColumnFuncNode.sum("money"), 100), ColumnFuncNode.avg(ColumnNodeValue.dec("money", 20)))), Utility.ofArray("fromid", "targetid"), (FilterNode)null) + * 等价于: SELECT fromid, targetid, SUM(money) / 100, AVG(money - 20) FROM orderrecord GROUP BY fromid, targetid
    + * + * @param Entity泛型 + * @param Key字段的数据类型 + * @param Number + * @param entityClass Entity类 + * @param funcNodes ColumnNode[] + * @param groupByColumns GROUP BY字段 + * @param node 过滤条件 + * + * @return 聚合结果Map CompletableFuture + */ + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node); + + //-----------------------findAsync---------------------------- + /** + * 获取指定主键值的单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键值 + * + * @return Entity对象 + */ + default T find(final Class clazz, final Serializable pk) { + return find(clazz, (SelectColumn) null, pk); + } + + /** + * 获取指定主键值的单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键值 + * + * @return Entity对象 CompletableFuture + */ + public CompletableFuture findAsync(final Class clazz, final Serializable pk); + + /** + * 获取指定主键值的单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param pk 主键值 + * + * @return Entity对象 + */ + public T find(final Class clazz, final SelectColumn selects, final Serializable pk); + + /** + * 获取指定主键值的单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param pk 主键值 + * + * @return Entity对象CompletableFuture + */ + public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final Serializable pk); + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pks 主键值集合 + * + * @return Entity对象 + */ + default T[] finds(final Class clazz, final Serializable... pks) { + return finds(clazz, (SelectColumn) null, pks); + } + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param pks 主键值集合 + * + * @return Entity对象 + */ + default T[] finds(final Class clazz, final Stream pks) { + return finds(clazz, (SelectColumn) null, pks.toArray(v -> new Serializable[v])); + } + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pks 主键值集合 + * + * @return Entity对象 CompletableFuture + */ + default CompletableFuture findsAsync(final Class clazz, final Serializable... pks) { + return findsAsync(clazz, (SelectColumn) null, pks); + } + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT * FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param pks 主键值集合 + * + * @return Entity对象 CompletableFuture + */ + default CompletableFuture findsAsync(final Class clazz, final Stream pks) { + return findsAsync(clazz, (SelectColumn) null, pks.toArray(v -> new Serializable[v])); + } + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param pks 主键值集合 + * + * @return Entity对象 + */ + public T[] finds(final Class clazz, final SelectColumn selects, final Serializable... pks); + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param pks 主键值集合 + * + * @return Entity对象 + */ + default T[] finds(final Class clazz, final SelectColumn selects, final Stream pks) { + return finds(clazz, selects, pks.toArray(v -> new Serializable[v])); + } + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param pks 主键值集合 + * + * @return Entity对象CompletableFuture + */ + public CompletableFuture findsAsync(final Class clazz, final SelectColumn selects, final Serializable... pks); + + /** + * 获取指定主键值的多个记录, 返回数组,数组长度与pks一样
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {primary} = {id1,id2, ···}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param pks 主键值集合 + * + * @return Entity对象 + */ + default CompletableFuture findsAsync(final Class clazz, final SelectColumn selects, final Stream pks) { + return findsAsync(clazz, selects, pks.toArray(v -> new Serializable[v])); + } + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity对象 + */ + public T find(final Class clazz, final String column, final Serializable colval); + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity对象CompletableFuture + */ + public CompletableFuture findAsync(final Class clazz, final String column, final Serializable colval); + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return Entity对象 + */ + default T find(final Class clazz, final FilterBean bean) { + return find(clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return Entity对象CompletableFuture + */ + default CompletableFuture findAsync(final Class clazz, final FilterBean bean) { + return findAsync(clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return Entity对象 + */ + default T find(final Class clazz, final FilterNode node) { + return find(clazz, (SelectColumn) null, node); + } + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return Entity对象CompletableFuture + */ + default CompletableFuture findAsync(final Class clazz, final FilterNode node) { + return findAsync(clazz, (SelectColumn) null, node); + } + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean 过滤条件 + * + * @return Entity对象 + */ + default T find(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return find(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean 过滤条件 + * + * @return Entity对象 CompletableFuture + */ + default CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return findAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node 过滤条件 + * + * @return Entity对象 + */ + public T find(final Class clazz, final SelectColumn selects, final FilterNode node); + + /** + * 获取符合过滤条件单个记录, 返回null表示不存在值
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node 过滤条件 + * + * @return Entity对象 CompletableFuture + */ + public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterNode node); + + /** + * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param pk 主键值 + * + * @return Entity对象 + */ + public Serializable findColumn(final Class clazz, final String column, final Serializable pk); + + /** + * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param pk 主键值 + * + * @return Entity对象 CompletableFuture + */ + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable pk); + + /** + * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param bean 过滤条件 + * + * @return 字段值 + */ + default Serializable findColumn(final Class clazz, final String column, final FilterBean bean) { + return findColumn(clazz, column, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param bean 过滤条件 + * + * @return 字段值 CompletableFuture + */ + default CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterBean bean) { + return findColumnAsync(clazz, column, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param node 过滤条件 + * + * @return 字段值 + */ + default Serializable findColumn(final Class clazz, final String column, final FilterNode node) { + return findColumn(clazz, column, null, node); + } + + /** + * 获取符合过滤条件单个记录的单个字段值, 返回null表示不存在值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param node 过滤条件 + * + * @return 字段值 CompletableFuture + */ + default CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterNode node) { + return findColumnAsync(clazz, column, null, node); + } + + /** + * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param defValue 默认值 + * @param pk 主键值 + * + * @return 字段值 + */ + public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final Serializable pk); + + /** + * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param defValue 默认值 + * @param pk 主键值 + * + * @return 字段值 CompletableFuture + */ + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final Serializable pk); + + /** + * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param defValue 默认值 + * @param bean 过滤条件 + * + * @return 字段值 + */ + default Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { + return findColumn(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param defValue 默认值 + * @param bean 过滤条件 + * + * @return 字段值 CompletableFuture + */ + default CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { + return findColumnAsync(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param defValue 默认值 + * @param node 过滤条件 + * + * @return 字段值 + */ + public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterNode node); + + /** + * 获取符合过滤条件单个记录的单个字段值, 不存在值则返回默认值
    + * 等价SQL: SELECT {column} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 字段名 + * @param defValue 默认值 + * @param node 过滤条件 + * + * @return 字段值 CompletableFuture + */ + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterNode node); + + /** + * 判断是否存在主键值的记录
    + * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键值 + * + * @return 是否存在 + */ + public boolean exists(final Class clazz, final Serializable pk); + + /** + * 判断是否存在主键值的记录
    + * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {primary} = {id}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param pk 主键值 + * + * @return 是否存在CompletableFuture + */ + public CompletableFuture existsAsync(final Class clazz, final Serializable pk); + + /** + * 判断是否存在符合过滤条件的记录
    + * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return 是否存在 + */ + default boolean exists(final Class clazz, final FilterBean bean) { + return exists(clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 判断是否存在符合过滤条件的记录
    + * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return 是否存在CompletableFuture + */ + default CompletableFuture existsAsync(final Class clazz, final FilterBean bean) { + return existsAsync(clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 判断是否存在符合过滤条件的记录
    + * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 是否存在 + */ + public boolean exists(final Class clazz, final FilterNode node); + + /** + * 判断是否存在符合过滤条件的记录
    + * 等价SQL: SELECT COUNT(*) FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 是否存在CompletableFuture + */ + public CompletableFuture existsAsync(final Class clazz, final FilterNode node); + + //-----------------------list set---------------------------- + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {column} = {key}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return 字段值的集合 + */ + public Set queryColumnSet(final String selectedColumn, final Class clazz, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {column} = {key}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return 字段值的集合CompletableFuture + */ + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return 字段值的集合 + */ + default Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnSet(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + default CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnSetAsync(selectedColumn, clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 字段值的集合 + */ + default Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnSet(selectedColumn, clazz, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + default CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnSetAsync(selectedColumn, clazz, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return 字段值的集合 + */ + default Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + default CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 字段值的集合 + */ + public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的某个字段Set集合
    + * 等价SQL: SELECT DISTINCT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {column} = {key}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return 字段值的集合 + */ + public List queryColumnList(final String selectedColumn, final Class clazz, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {column} = {key}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return 字段值的集合CompletableFuture + */ + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return 字段值的集合 + */ + default List queryColumnList(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnList(selectedColumn, clazz, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + default CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { + return queryColumnListAsync(selectedColumn, clazz, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 字段值的集合 + */ + default List queryColumnList(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnList(selectedColumn, clazz, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + default CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterNode node) { + return queryColumnListAsync(selectedColumn, clazz, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return 字段值的集合 + */ + default List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnList(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + default CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnListAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 字段值的集合 + */ + public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的某个字段List集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的某个字段Sheet集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return 字段值的集合 + */ + default Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSheet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段Sheet集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + default CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryColumnSheetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的某个字段Sheet集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 字段值的集合 + */ + public Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的某个字段Sheet集合
    + * 等价SQL: SELECT {selectedColumn} FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param 字段类型 + * @param selectedColumn 指定字段 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return 字段值的集合CompletableFuture + */ + public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param keyStream 主键Stream + * + * @return Entity的集合 + */ + default Map queryMap(final Class clazz, final Stream keyStream) { + return queryMap(clazz, (SelectColumn) null, keyStream); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param keyStream 主键Stream + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryMapAsync(final Class clazz, final Stream keyStream) { + return queryMapAsync(clazz, (SelectColumn) null, keyStream); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param bean FilterBean + * + * @return Entity的集合 + */ + default Map queryMap(final Class clazz, final FilterBean bean) { + return queryMap(clazz, (SelectColumn) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param bean FilterBean + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryMapAsync(final Class clazz, final FilterBean bean) { + return queryMapAsync(clazz, (SelectColumn) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param node FilterNode + * + * @return Entity的集合 + */ + default Map queryMap(final Class clazz, final FilterNode node) { + return queryMap(clazz, (SelectColumn) null, node); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param node FilterNode + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryMapAsync(final Class clazz, final FilterNode node) { + return queryMapAsync(clazz, (SelectColumn) null, node); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param keyStream 主键Stream + * + * @return Entity的集合 + */ + public Map queryMap(final Class clazz, final SelectColumn selects, final Stream keyStream); + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE id IN {ids}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param keyStream 主键Stream + * + * @return Entity的集合CompletableFuture + */ + public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final Stream keyStream); + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean FilterBean + * + * @return Entity的集合 + */ + default Map queryMap(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return queryMap(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean FilterBean + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, FilterBean bean) { + return queryMapAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node FilterNode + * + * @return Entity的集合 + */ + public Map queryMap(final Class clazz, final SelectColumn selects, final FilterNode node); + + /** + * 查询符合过滤条件记录的List转Map集合, key=主键值, value=Entity对象
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node FilterNode + * + * @return Entity的集合CompletableFuture + */ + public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterNode node); + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz, final String column, final Serializable colval) { + return querySet(clazz, (Flipper) null, column, colval); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz, final String column, final Serializable colval) { + return querySetAsync(clazz, (Flipper) null, column, colval); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz, final FilterBean bean) { + return querySet(clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz, final FilterBean bean) { + return querySetAsync(clazz, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz) { + return querySet(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz, final FilterNode node) { + return querySet(clazz, (SelectColumn) null, (Flipper) null, node); + } + + /** + * 查询记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz) { + return querySetAsync(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz, final FilterNode node) { + return querySetAsync(clazz, (SelectColumn) null, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return querySet(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return querySetAsync(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz, final SelectColumn selects, final FilterNode node) { + return querySet(clazz, selects, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { + return querySetAsync(clazz, selects, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合 + */ + public Set querySet(final Class clazz, final Flipper flipper, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合CompletableFuture + */ + public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySet(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySetAsync(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + * + */ + default Set querySet(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySet(clazz, (SelectColumn) null, flipper, node); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + * + */ + default CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySetAsync(clazz, (SelectColumn) null, flipper, node); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的Set集合
    + * 等价SQL: SELECT DISTINCT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final String column, final Serializable colval) { + return queryList(clazz, (Flipper) null, column, colval); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final String column, final Serializable colval) { + return queryListAsync(clazz, (Flipper) null, column, colval); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final FilterBean bean) { + return queryList(clazz, (SelectColumn) null, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final FilterBean bean) { + return queryListAsync(clazz, (SelectColumn) null, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询记录的List集合
    + * 等价SQL: SELECT * FROM {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz) { + return queryList(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final FilterNode node) { + return queryList(clazz, (SelectColumn) null, (Flipper) null, node); + } + + /** + * 查询记录的List集合
    + * 等价SQL: SELECT * FROM {table}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz) { + return queryListAsync(clazz, (SelectColumn) null, (Flipper) null, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final FilterNode node) { + return queryListAsync(clazz, (SelectColumn) null, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return queryList(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { + return queryListAsync(clazz, selects, (Flipper) null, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final SelectColumn selects, final FilterNode node) { + return queryList(clazz, selects, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { + return queryListAsync(clazz, selects, (Flipper) null, node); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合 + */ + public List queryList(final Class clazz, final Flipper flipper, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return Entity的集合CompletableFuture + */ + public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval); + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final Flipper flipper) { + return queryList(clazz, (SelectColumn) null, flipper, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper) { + return queryListAsync(clazz, (SelectColumn) null, flipper, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryList(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { + return queryListAsync(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + * + */ + default List queryList(final Class clazz, final Flipper flipper, final FilterNode node) { + return queryList(clazz, (SelectColumn) null, flipper, node); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + * + */ + default CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterNode node) { + return queryListAsync(clazz, (SelectColumn) null, flipper, node); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper) { + return queryList(clazz, selects, flipper, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper) { + return queryListAsync(clazz, selects, flipper, (FilterNode) null); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return queryList(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return queryListAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的List集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); + + //-----------------------sheet---------------------------- + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default Sheet querySheet(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySheet(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { + return querySheetAsync(clazz, (SelectColumn) null, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + default Sheet querySheet(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySheet(clazz, (SelectColumn) null, flipper, node); + } + + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT * FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { + return querySheetAsync(clazz, (SelectColumn) null, flipper, node); + } + + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合 + */ + default Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySheet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter bean} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param bean 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + default CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { + return querySheetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); + } + + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合 + */ + public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); + + /** + * 查询符合过滤条件记录的Sheet集合
    + * 等价SQL: SELECT {column1},{column2}, ··· FROM {table} WHERE {filter node} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param flipper 翻页对象 + * @param node 过滤条件 + * + * @return Entity的集合CompletableFuture + */ + public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node); + +} diff --git a/src/org/redkale/source/DataSourceLoader.java b/src/main/java/org/redkale/source/DataSourceProvider.java similarity index 60% rename from src/org/redkale/source/DataSourceLoader.java rename to src/main/java/org/redkale/source/DataSourceProvider.java index 43acb9efd..c07b45fa9 100644 --- a/src/org/redkale/source/DataSourceLoader.java +++ b/src/main/java/org/redkale/source/DataSourceProvider.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import org.redkale.util.AnyValue; - -/** - * 自定义的DataSource加载器 - * - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.4.0 - */ -public interface DataSourceLoader { - - public boolean match(AnyValue config); - - public Class sourceClass(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import org.redkale.util.AnyValue; + +/** + * + * 自定义的DataSource加载器, 如果标记@Priority加载器的优先级需要大于1000, 1000以下预留给官方加载器 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +public interface DataSourceProvider { + + public boolean acceptsConf(AnyValue config); + + public Class sourceClass(); +} diff --git a/src/org/redkale/source/DataSources.java b/src/main/java/org/redkale/source/DataSources.java similarity index 53% rename from src/org/redkale/source/DataSources.java rename to src/main/java/org/redkale/source/DataSources.java index 79b8cc901..b12cf2ccb 100644 --- a/src/org/redkale/source/DataSources.java +++ b/src/main/java/org/redkale/source/DataSources.java @@ -1,222 +1,315 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.*; -import java.lang.reflect.Constructor; -import java.net.*; -import java.util.*; -import javax.xml.stream.*; -import org.redkale.util.AnyValue; - -/** - * - * @author zhangjx - */ -public final class DataSources { - - public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH"; - - public static final String JDBC_DATASOURCE_CLASS = "javax.persistence.datasource"; - - public static final String JDBC_CACHE_MODE = "javax.persistence.cachemode"; - - public static final String JDBC_CONNECTIONS_LIMIT = "javax.persistence.connections.limit"; - - public static final String JDBC_CONNECTIONSCAPACITY = "javax.persistence.connections.bufcapacity"; - - public static final String JDBC_CONTAIN_SQLTEMPLATE = "javax.persistence.contain.sqltemplate"; - - public static final String JDBC_NOTCONTAIN_SQLTEMPLATE = "javax.persistence.notcontain.sqltemplate"; - - public static final String JDBC_TABLENOTEXIST_SQLSTATES = "javax.persistence.tablenotexist.sqlstates"; - - public static final String JDBC_TABLECOPY_SQLTEMPLATE = "javax.persistence.tablecopy.sqltemplate"; - - public static final String JDBC_CONNECTTIMEOUT_SECONDS = "javax.persistence.connecttimeout"; - - public static final String JDBC_READTIMEOUT_SECONDS = "javax.persistence.readtimeout"; - - public static final String JDBC_WRITETIMEOUT_SECONDS = "javax.persistence.writetimeout"; - - public static final String JDBC_URL = "javax.persistence.jdbc.url"; - - public static final String JDBC_USER = "javax.persistence.jdbc.user"; - - public static final String JDBC_PWD = "javax.persistence.jdbc.password"; - - public static final String JDBC_ENCODING = "javax.persistence.jdbc.encoding"; - - public static final String JDBC_DRIVER = "javax.persistence.jdbc.driver"; - - public static final String JDBC_SOURCE = "javax.persistence.jdbc.source"; - - //@since 2.4.0 for SearchSource default value: true - public static final String JDBC_AUTO_MAPPING = "javax.persistence.jdbc.auto-mapping"; - - private DataSources() { - } - - public static DataSource createDataSource(final String unitName, final AnyValue conf) throws IOException { - Properties prop = new Properties(); - AnyValue[] confs = conf.getAnyValues("property"); - if (confs != null) { - for (AnyValue itemConf : confs) { - String name = itemConf.getValue("name"); - String value = itemConf.getValue("value"); - if (name != null && value != null) prop.put(name, value); - } - } - return createDataSource(unitName, prop, prop); - } - - public static DataSource createDataSource(final String unitName, Properties prop) throws IOException { - return createDataSource(unitName, prop, prop); - } - - public static DataSource createDataSource(final String unitName, Properties readprop, Properties writeprop) throws IOException { - return createDataSource(unitName, null, readprop, writeprop); - } - - public static DataSource createDataSource(final String unitName, URL persistxml, Properties readprop, Properties writeprop) throws IOException { - String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName()); - if (DataJdbcSource.class.getName().equals(impl)) { - if (PoolJdbcSource.checkSource(readprop)) { - return new DataJdbcSource(unitName, persistxml, readprop, writeprop); - } - - final String url = readprop.getProperty(JDBC_URL); - String dbtype = null; - { - if (url.startsWith("http://") || url.startsWith("https://")) { //elasticsearch or opensearch - dbtype = "search"; - } else { - /* jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 */ - int pos = url.indexOf("://"); - if (pos > 0) { - String url0 = url.substring(0, pos); - pos = url0.lastIndexOf(':'); - if (pos > 0) dbtype = url0.substring(pos + 1); - } else { //jdbc:oracle:thin:@localhost:1521 - String url0 = url.substring(url.indexOf(":") + 1); - pos = url0.indexOf(':'); - if (pos > 0) dbtype = url0.substring(0, pos); - } - } - } - if (dbtype == null) throw new RuntimeException("not found datasource implements class, url=" + url); - - Iterator it = ServiceLoader.load(DataSourceLoader.class).iterator(); - Class dsClass = null; - final AnyValue.DefaultAnyValue lc = new AnyValue.DefaultAnyValue(); - readprop.forEach((k, v) -> lc.addValue(k.toString(), v.toString())); - lc.setValue("dbtype", dbtype); - while (it.hasNext()) { - DataSourceLoader loader = it.next(); - if (loader != null && loader.match(lc)) { - dsClass = loader.sourceClass(); - if (dsClass != null) break; - } - } - if (dsClass == null) throw new RuntimeException("not found datasource implements ServiceLoader, url=" + url); - impl = dsClass.getName(); - } - try { - Class ds = Thread.currentThread().getContextClassLoader().loadClass(impl); - for (Constructor d : ds.getConstructors()) { - Class[] paramtypes = d.getParameterTypes(); - if (paramtypes.length == 1 && paramtypes[0] == Properties.class) { - return (DataSource) d.newInstance(readprop); - } else if (paramtypes.length == 2 && paramtypes[0] == String.class && paramtypes[1] == Properties.class) { - return (DataSource) d.newInstance(unitName, readprop); - } else if (paramtypes.length == 3 && paramtypes[0] == String.class && paramtypes[1] == Properties.class && paramtypes[2] == Properties.class) { - return (DataSource) d.newInstance(unitName, readprop, writeprop); - } else if (paramtypes.length == 4 && paramtypes[0] == String.class && paramtypes[1] == URL.class && paramtypes[2] == Properties.class && paramtypes[3] == Properties.class) { - return (DataSource) d.newInstance(unitName, persistxml, readprop, writeprop); - } - } - throw new IOException("DataSource impl class (" + impl + ") have no Constructor by (Properties prop) or (String name, Properties prop) or (String name, Properties readprop, Propertieswriteprop) or (String name, URL persistxml, Properties readprop, Propertieswriteprop)"); - } catch (IOException ex) { - throw ex; - } catch (Exception e) { - throw new IOException(e); - } - } - - public static DataSource createDataSource(final String unitName) throws IOException { - return createDataSource(unitName, System.getProperty(DATASOURCE_CONFPATH) == null - ? DataJdbcSource.class.getResource("/META-INF/persistence.xml") - : (System.getProperty(DATASOURCE_CONFPATH, "").contains("://") ? URI.create(System.getProperty(DATASOURCE_CONFPATH)).toURL() : new File(System.getProperty(DATASOURCE_CONFPATH)).toURI().toURL())); - } - - public static DataSource createDataSource(final String unitName, URL persistxml) throws IOException { - if (persistxml == null) persistxml = DataSources.class.getResource("/persistence.xml"); - InputStream in = persistxml == null ? null : persistxml.openStream(); - if (in == null) return null; - Map map = loadPersistenceXml(in); - Properties readprop = null; - Properties writeprop = null; - if (unitName != null) { - readprop = map.get(unitName); - writeprop = readprop; - if (readprop == null) { - readprop = map.get(unitName + ".read"); - writeprop = map.get(unitName + ".write"); - } - } - if (unitName == null || unitName.isEmpty()) { - String key = null; - for (Map.Entry en : map.entrySet()) { - key = en.getKey(); - readprop = en.getValue(); - writeprop = readprop; - break; - } - if (key != null && (key.endsWith(".read") || key.endsWith(".write"))) { - if (key.endsWith(".read")) { - writeprop = map.get(key.substring(0, key.lastIndexOf('.')) + ".write"); - } else { - readprop = map.get(key.substring(0, key.lastIndexOf('.')) + ".read"); - } - } - } - if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource"); - if (writeprop == null) writeprop = readprop; - if (readprop.getProperty(JDBC_URL, "").startsWith("memory:source")) return new DataMemorySource(unitName, persistxml, readprop, writeprop); - - return createDataSource(unitName, readprop, writeprop); - } - - public static Map loadPersistenceXml(final InputStream in0) { - final Map map = new LinkedHashMap(); - Properties result = new Properties(); - boolean flag = false; - try (final InputStream in = in0) { - XMLStreamReader reader = XMLInputFactory.newFactory().createXMLStreamReader(in); - while (reader.hasNext()) { - int event = reader.next(); - if (event == XMLStreamConstants.START_ELEMENT) { - if ("persistence-unit".equalsIgnoreCase(reader.getLocalName())) { - if (!result.isEmpty()) result = new Properties(); - map.put(reader.getAttributeValue(null, "name"), result); - flag = true; - } else if (flag && "property".equalsIgnoreCase(reader.getLocalName())) { - String name = reader.getAttributeValue(null, "name"); - String value = reader.getAttributeValue(null, "value"); - if (name == null) continue; - result.put(name, value); - } else if (flag && "shared-cache-mode".equalsIgnoreCase(reader.getLocalName())) { //兼容shared-cache-mode属性 - result.put(JDBC_CACHE_MODE, reader.getElementText()); - } - } - } - in.close(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - return map; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.*; +import java.lang.reflect.Constructor; +import java.net.*; +import java.util.*; +import javax.annotation.Priority; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.AnyValue; +import org.redkale.util.RedkaleClassLoader; + +/** + * + * @author zhangjx + */ +public final class DataSources { + + public static final String DATASOURCE_CONFPATH = "DATASOURCE_CONFPATH"; + + public static final String JDBC_DATASOURCE_CLASS = "javax.persistence.datasource"; + + public static final String JDBC_TABLE_AUTODDL = "javax.persistence.table.autoddl"; + + public static final String JDBC_CACHE_MODE = "javax.persistence.cachemode"; + + public static final String JDBC_CONNECTIONS_LIMIT = "javax.persistence.connections.limit"; + + public static final String JDBC_CONNECTIONSCAPACITY = "javax.persistence.connections.bufcapacity"; + + public static final String JDBC_CONTAIN_SQLTEMPLATE = "javax.persistence.contain.sqltemplate"; + + public static final String JDBC_NOTCONTAIN_SQLTEMPLATE = "javax.persistence.notcontain.sqltemplate"; + + public static final String JDBC_TABLENOTEXIST_SQLSTATES = "javax.persistence.tablenotexist.sqlstates"; + + public static final String JDBC_TABLECOPY_SQLTEMPLATE = "javax.persistence.tablecopy.sqltemplate"; + + public static final String JDBC_CONNECTTIMEOUT_SECONDS = "javax.persistence.connecttimeout"; + + public static final String JDBC_READTIMEOUT_SECONDS = "javax.persistence.readtimeout"; + + public static final String JDBC_WRITETIMEOUT_SECONDS = "javax.persistence.writetimeout"; + + public static final String JDBC_URL = "javax.persistence.jdbc.url"; + + public static final String JDBC_USER = "javax.persistence.jdbc.user"; + + public static final String JDBC_PWD = "javax.persistence.jdbc.password"; + + public static final String JDBC_ENCODING = "javax.persistence.jdbc.encoding"; + + @Deprecated //@deprecated @since 2.5.0 + public static final String JDBC_DRIVER = "javax.persistence.jdbc.driver"; + + @Deprecated //@deprecated @since 2.5.0 + public static final String JDBC_SOURCE = "javax.persistence.jdbc.source"; + + //@since 2.4.0 for SearchSource default value: true + public static final String JDBC_AUTO_MAPPING = "javax.persistence.jdbc.auto-mapping"; + + private DataSources() { + } + + public static DataSource createDataSource(final String unitName, final AnyValue conf) throws IOException { + Properties prop = new Properties(); + AnyValue[] confs = conf.getAnyValues("property"); + if (confs != null) { + for (AnyValue itemConf : confs) { + String name = itemConf.getValue("name"); + String value = itemConf.getValue("value"); + if (name != null && value != null) prop.put(name, value); + } + } + return createDataSource(unitName, prop, prop); + } + + public static DataSource createDataSource(final String unitName, Properties prop) throws IOException { + return createDataSource(unitName, prop, prop); + } + + public static DataSource createDataSource(final String unitName, Properties readprop, Properties writeprop) throws IOException { + return createDataSource(unitName, null, readprop, writeprop); + } + + public static DataSource createDataSource(final String unitName, URL persistxml, Properties readprop, Properties writeprop) throws IOException { + String impl = readprop.getProperty(JDBC_DATASOURCE_CLASS, DataJdbcSource.class.getName()); + String dbtype = null; + if (DataJdbcSource.class.getName().equals(impl)) { + if (DataJdbcSource.acceptsConf(readprop)) { + return new DataJdbcSource(unitName, persistxml, parseDbtype(readprop.getProperty(JDBC_URL)), readprop, writeprop); + } + String url = readprop.getProperty(JDBC_URL); + dbtype = parseDbtype(url); + if (dbtype == null) throw new RuntimeException("not found datasource implements class, url=" + url); + + RedkaleClassLoader.putServiceLoader(DataSourceProvider.class); + Class dsClass = null; + final AnyValue.DefaultAnyValue lc = new AnyValue.DefaultAnyValue(); + readprop.forEach((k, v) -> lc.addValue(k.toString(), v.toString())); + lc.setValue("dbtype", dbtype); + List providers = new ArrayList<>(); + Iterator it = ServiceLoader.load(DataSourceProvider.class).iterator(); + while (it.hasNext()) { + DataSourceProvider provider = it.next(); + if (provider != null) { + RedkaleClassLoader.putReflectionPublicConstructors(provider.getClass(), provider.getClass().getName()); + } + if (provider != null && provider.acceptsConf(lc)) { + providers.add(provider); + } + } + Collections.sort(providers, (a, b) -> { + Priority p1 = a == null ? null : a.getClass().getAnnotation(Priority.class); + Priority p2 = b == null ? null : b.getClass().getAnnotation(Priority.class); + return (p2 == null ? 0 : p2.value()) - (p1 == null ? 0 : p1.value()); + }); + for (DataSourceProvider provider : providers) { + dsClass = provider.sourceClass(); + if (dsClass != null) break; + } + if (dsClass == null) throw new RuntimeException("not found datasource implements ServiceLoader, url=" + url); + impl = dsClass.getName(); + } + try { + Class ds = Thread.currentThread().getContextClassLoader().loadClass(impl); + RedkaleClassLoader.putReflectionPublicConstructors(ds, impl); + for (Constructor d : ds.getConstructors()) { + Class[] paramtypes = d.getParameterTypes(); + if (paramtypes.length == 1 && paramtypes[0] == Properties.class) { + RedkaleClassLoader.putReflectionDeclaredConstructors(ds, impl, Properties.class); + return (DataSource) d.newInstance(readprop); + } else if (paramtypes.length == 2 && paramtypes[0] == String.class && paramtypes[1] == Properties.class) { + RedkaleClassLoader.putReflectionDeclaredConstructors(ds, impl, String.class, Properties.class); + return (DataSource) d.newInstance(unitName, readprop); + } else if (paramtypes.length == 3 && paramtypes[0] == String.class && paramtypes[1] == Properties.class && paramtypes[2] == Properties.class) { + RedkaleClassLoader.putReflectionDeclaredConstructors(ds, impl, String.class, Properties.class, Properties.class); + return (DataSource) d.newInstance(unitName, readprop, writeprop); + } else if (paramtypes.length == 4 && paramtypes[0] == String.class && paramtypes[1] == String.class && paramtypes[2] == Properties.class && paramtypes[3] == Properties.class) { + RedkaleClassLoader.putReflectionDeclaredConstructors(ds, impl, String.class, String.class, Properties.class, Properties.class); + return (DataSource) d.newInstance(unitName, dbtype, readprop, writeprop); + } else if (paramtypes.length == 4 && paramtypes[0] == String.class && paramtypes[1] == URL.class && paramtypes[2] == Properties.class && paramtypes[3] == Properties.class) { + RedkaleClassLoader.putReflectionDeclaredConstructors(ds, impl, String.class, URL.class, Properties.class, Properties.class); + return (DataSource) d.newInstance(unitName, persistxml, readprop, writeprop); + } else if (paramtypes.length == 5 && paramtypes[0] == String.class && paramtypes[1] == URL.class && paramtypes[2] == String.class && paramtypes[3] == Properties.class && paramtypes[4] == Properties.class) { + RedkaleClassLoader.putReflectionDeclaredConstructors(ds, impl, String.class, URL.class, Properties.class, Properties.class); + if (dbtype == null) dbtype = parseDbtype(readprop.getProperty(JDBC_URL)); + return (DataSource) d.newInstance(unitName, persistxml, dbtype, readprop, writeprop); + } + } + throw new IOException("DataSource impl class (" + impl + ") have no Constructor by (Properties prop) or (String name, Properties prop) or (String name, Properties readprop, Propertieswriteprop) or (String name, URL persistxml, Properties readprop, Propertieswriteprop)"); + } catch (IOException ex) { + throw ex; + } catch (Exception e) { + throw new IOException(e); + } + } + + public static String parseDbtype(String url) { + String dbtype = null; + if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("search://") || url.startsWith("searchs://")) { //elasticsearch or opensearch + dbtype = "search"; + } else { + /* jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 */ + int pos = url.indexOf("://"); + if (pos > 0) { + String url0 = url.substring(0, pos); + pos = url0.lastIndexOf(':'); + if (pos > 0) { + dbtype = url0.substring(pos + 1); + } else { //mongodb://127.0.01:27017 + dbtype = url0; + } + } else { //jdbc:oracle:thin:@localhost:1521 + String url0 = url.substring(url.indexOf(":") + 1); + pos = url0.indexOf(':'); + if (pos > 0) dbtype = url0.substring(0, pos); + } + } + if ("mariadb".equals(dbtype)) return "mysql"; + return dbtype; + } + + public static DataSource createDataSource(final String confURI, final String unitName) throws IOException { + return createDataSource(unitName, System.getProperty(DATASOURCE_CONFPATH, "").isEmpty() + ? RedkaleClassLoader.getConfResourceAsURI(confURI, "persistence.xml").toURL() + : (System.getProperty(DATASOURCE_CONFPATH, "").contains("://") ? URI.create(System.getProperty(DATASOURCE_CONFPATH)).toURL() : new File(System.getProperty(DATASOURCE_CONFPATH)).toURI().toURL())); + } + + public static DataSource createDataSource(final String unitName, URL persistxml) throws IOException { + if (persistxml == null) persistxml = DataSources.class.getResource("/persistence.xml"); + InputStream in = persistxml == null ? null : persistxml.openStream(); + if (in == null) return null; + Map map = loadPersistenceXml(in); + Properties readprop = null; + Properties writeprop = null; + if (unitName != null) { + readprop = map.get(unitName); + writeprop = readprop; + if (readprop == null) { + readprop = map.get(unitName + ".read"); + writeprop = map.get(unitName + ".write"); + } + } + if (unitName == null || unitName.isEmpty()) { + String key = null; + for (Map.Entry en : map.entrySet()) { + key = en.getKey(); + readprop = en.getValue(); + writeprop = readprop; + break; + } + if (key != null && (key.endsWith(".read") || key.endsWith(".write"))) { + if (key.endsWith(".read")) { + writeprop = map.get(key.substring(0, key.lastIndexOf('.')) + ".write"); + } else { + readprop = map.get(key.substring(0, key.lastIndexOf('.')) + ".read"); + } + } + } + if (readprop == null) throw new IOException("Cannot find (resource.name = '" + unitName + "') DataSource"); + if (writeprop == null) writeprop = readprop; + if (readprop.getProperty(JDBC_URL, "").startsWith("memory:source")) return new DataMemorySource(unitName, persistxml, readprop, writeprop); + + return createDataSource(unitName, readprop, writeprop); + } + + public static Map loadPersistenceXml(final InputStream in0) { + final Map map = new LinkedHashMap(); + + boolean flag = false; + try (final InputStream in = in0) { + + AnyValue config = AnyValue.loadFromXml(in).getAnyValue("persistence"); + + for (AnyValue conf : config.getAnyValues("persistence-unit")) { + Properties result = new Properties(); + conf.forEach(null, (n, c) -> { + if ("properties".equals(n)) { + for (AnyValue sub : c.getAnyValues("property")) { + result.put(sub.getValue("name"), sub.getValue("value")); + } + } else { + String v = c.getValue(AnyValue.XML_TEXT_NODE_NAME); + if (v != null) { + if ("shared-cache-mode".equalsIgnoreCase(n)) { + result.put(JDBC_CACHE_MODE, v); + } else { + result.put(n, v); + } + } + } + }); + map.put(conf.getValue("name"), result); + } + in.close(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + return map; + } + + public static UrlInfo parseUrl(final String url) { + final UrlInfo info = new UrlInfo(); + info.url = url; + if (url.startsWith("jdbc:h2:")) return info; + String url0 = url.substring(url.indexOf("://") + 3); + int pos = url0.indexOf('?'); //127.0.0.1:5432/db?charset=utr8&xxx=yy + if (pos > 0) { + String params = url0.substring(pos + 1).replace("&", "&"); + for (String param : params.split("&")) { + int p = param.indexOf('='); + if (p < 1) continue; + info.attributes.put(param.substring(0, p), param.substring(p + 1)); + } + url0 = url0.substring(0, pos); + } + pos = url0.indexOf('/'); //127.0.0.1:5432/db + if (pos > 0) { + info.database = url0.substring(pos + 1); + url0 = url0.substring(0, pos); + } + pos = url0.indexOf(':'); + if (pos > 0) { + info.servaddr = new InetSocketAddress(url0.substring(0, pos), Integer.parseInt(url0.substring(pos + 1))); + } else if (url.startsWith("http://")) { + info.servaddr = new InetSocketAddress(url0, 80); + } else if (url.startsWith("https://")) { + info.servaddr = new InetSocketAddress(url0, 443); + } else { + throw new RuntimeException(url + " parse port error"); + } + return info; + } + + public static class UrlInfo { + + public Properties attributes = new Properties(); + + public String url; + + public String database; + + public InetSocketAddress servaddr; + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/org/redkale/source/DataSqlSource.java b/src/main/java/org/redkale/source/DataSqlSource.java similarity index 62% rename from src/org/redkale/source/DataSqlSource.java rename to src/main/java/org/redkale/source/DataSqlSource.java index 3057731e3..bb96626d5 100644 --- a/src/org/redkale/source/DataSqlSource.java +++ b/src/main/java/org/redkale/source/DataSqlSource.java @@ -1,2619 +1,2130 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.net.URL; -import java.sql.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.logging.*; -import java.util.stream.Stream; -import javax.annotation.Resource; -import org.redkale.net.AsyncGroup; -import org.redkale.service.*; -import static org.redkale.source.DataSources.*; -import org.redkale.util.*; -import static org.redkale.boot.Application.RESNAME_APP_GROUP; -import org.redkale.net.*; - -/** - * DataSource的SQL抽象实现类
    - * 注意: 所有的操作只能作用在一张表上,不能同时变更多张表 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Local -@AutoLoad(false) -@SuppressWarnings("unchecked") -@ResourceType(DataSource.class) -public abstract class DataSqlSource extends AbstractService implements DataSource, Function, AutoCloseable, Resourcable { - - protected static final Flipper FLIPPER_ONE = new Flipper(1); - - protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); - - protected String name; - - protected URL persistxml; - - protected Properties readprop; - - protected Properties writeprop; - - protected boolean cacheForbidden; - - protected PoolSource readPool; - - protected PoolSource writePool; - - @Resource(name = RESNAME_APP_GROUP) - protected AsyncGroup asyncGroup; - - protected final BiFunction sqlFormatter; - - protected final BiConsumer futureCompleteConsumer = (r, t) -> { - if (t != null) logger.log(Level.INFO, "CompletableFuture complete error", (Throwable) t); - }; - - protected final BiFunction> fullloader = (s, i) - -> ((CompletableFuture) querySheetDB(i, false, false, false, null, null, (FilterNode) null)).thenApply(e -> e == null ? new ArrayList() : e.list(true)); - - @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"}) - public DataSqlSource(String unitName, URL persistxml, Properties readprop, Properties writeprop) { - if (readprop == null) readprop = new Properties(); - if (writeprop == null) writeprop = readprop; - this.name = unitName; - this.persistxml = persistxml; - this.readprop = readprop; - this.writeprop = writeprop; - this.sqlFormatter = (info, val) -> formatValueToString(info, val); - } - - @Override - public void init(AnyValue conf) { - int maxconns = Math.max(8, Integer.decode(readprop.getProperty(JDBC_CONNECTIONS_LIMIT, "" + Runtime.getRuntime().availableProcessors() * 32))); - if (readprop != writeprop) maxconns = 0; - this.cacheForbidden = "NONE".equalsIgnoreCase(readprop.getProperty(JDBC_CACHE_MODE)); - ArrayBlockingQueue queue = maxconns > 0 ? new ArrayBlockingQueue(maxconns) : null; - Semaphore semaphore = maxconns > 0 ? new Semaphore(maxconns) : null; - this.readPool = createPoolSource(this, this.asyncGroup, "read", queue, semaphore, readprop); - this.writePool = createPoolSource(this, this.asyncGroup, "write", queue, semaphore, writeprop); - } - - public void updateMaxconns(int maxconns) { - if (readprop == writeprop) { - Semaphore semaphore = new Semaphore(maxconns); - this.readPool.setSemaphore(semaphore); - this.writePool.setSemaphore(semaphore); - if (readPool instanceof PoolTcpSource) { - ArrayBlockingQueue connQueue = new ArrayBlockingQueue(maxconns); - ((PoolTcpSource) readPool).updateConnQueue(connQueue); - ((PoolTcpSource) writePool).updateConnQueue(connQueue); - } - } else { - int onemax = maxconns / 2; - this.readPool.setSemaphore(new Semaphore(onemax)); - this.writePool.setSemaphore(new Semaphore(onemax)); - if (readPool instanceof PoolTcpSource) { - ((PoolTcpSource) readPool).updateConnQueue(new ArrayBlockingQueue(onemax)); - ((PoolTcpSource) writePool).updateConnQueue(new ArrayBlockingQueue(onemax)); - } - } - } - - @Override - public void destroy(AnyValue config) { - if (readPool != null) readPool.close(); - if (writePool != null) writePool.close(); - } - - @Local - public abstract int directExecute(String sql); - - @Local - public abstract int[] directExecute(String... sqls); - - @Local - public abstract V directQuery(String sql, Function handler); - - //是否异步, 为true则只能调用pollAsync方法,为false则只能调用poll方法 - protected abstract boolean isAsync(); - - //index从1开始 - protected abstract String prepareParamSign(int index); - - //创建连接池 - protected abstract PoolSource createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop); - - //插入纪录 - protected abstract CompletableFuture insertDB(final EntityInfo info, T... entitys); - - //删除记录 - protected abstract CompletableFuture deleteDB(final EntityInfo info, Flipper flipper, final String sql); - - //清空表 - protected abstract CompletableFuture clearTableDB(final EntityInfo info, final String table, final String sql); - - //删除表 - protected abstract CompletableFuture dropTableDB(final EntityInfo info, final String table, final String sql); - - //更新纪录 - protected abstract CompletableFuture updateDB(final EntityInfo info, final ChannelContext context, T... entitys); - - //更新纪录 - protected abstract CompletableFuture updateDB(final EntityInfo info, Flipper flipper, final String sql, final boolean prepared, Object... params); - - //查询Number Map数据 - protected abstract CompletableFuture> getNumberMapDB(final EntityInfo info, final String sql, final FilterFuncColumn... columns); - - //查询Number数据 - protected abstract CompletableFuture getNumberResultDB(final EntityInfo info, final String sql, final Number defVal, final String column); - - //查询Map数据 - protected abstract CompletableFuture> queryColumnMapDB(final EntityInfo info, final String sql, final String keyColumn); - - //查询Map数据 - protected abstract CompletableFuture> queryColumnMapDB(final EntityInfo info, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns); - - //查询单条记录 - protected abstract CompletableFuture findDB(final EntityInfo info, final ChannelContext context, final String sql, final boolean onlypk, final SelectColumn selects); - - //查询单条记录的单个字段 - protected abstract CompletableFuture findColumnDB(final EntityInfo info, final String sql, final boolean onlypk, final String column, final Serializable defValue); - - //判断记录是否存在 - protected abstract CompletableFuture existsDB(final EntityInfo info, final String sql, final boolean onlypk); - - //查询一页数据 - protected abstract CompletableFuture> querySheetDB(final EntityInfo info, final boolean readcache, final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, final FilterNode node); - - protected T getEntityValue(EntityInfo info, final SelectColumn sels, final ResultSet set) throws SQLException { - return info.getEntityValue(sels, set); - } - - protected Serializable getFieldValue(EntityInfo info, Attribute attr, final ResultSet set, int index) throws SQLException { - return info.getFieldValue(attr, set, index); - } - - protected Serializable getFieldValue(EntityInfo info, Attribute attr, final ResultSet set) throws SQLException { - return info.getFieldValue(attr, set); - } - - protected String createSQLOrderby(EntityInfo info, Flipper flipper) { - return info.createSQLOrderby(flipper); - } - - protected Map getJoinTabalis(FilterNode node) { - return node == null ? null : node.getJoinTabalis(); - } - - protected CharSequence createSQLJoin(FilterNode node, final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { - return node == null ? null : node.createSQLJoin(func, update, joinTabalis, haset, info); - } - - protected CharSequence createSQLExpress(FilterNode node, final EntityInfo info, final Map joinTabalis) { - return node == null ? null : node.createSQLExpress(info, joinTabalis); - } - - @Local - @Override - public String getType() { - return "sql"; - } - - @Override - public final String resourceName() { - return name; - } - - @Local - @Override - public void close() throws Exception { - if (readPool != null) readPool.close(); - if (writePool != null) writePool.close(); - } - - @Local - public PoolSource getReadPoolSource() { - return readPool; - } - - @Local - public PoolSource getWritePoolSource() { - return writePool; - } - - @Local - @Override - public EntityInfo apply(Class t) { - return loadEntityInfo(t); - } - - protected EntityInfo loadEntityInfo(Class clazz) { - return EntityInfo.load(clazz, this.cacheForbidden, this.readPool == null ? null : this.readPool.props, this, fullloader); - } - - protected boolean isOnlyCache(EntityInfo info) { - return info.isVirtualEntity(); - } - - public EntityCache loadCache(Class clazz) { - EntityInfo info = loadEntityInfo(clazz); - return info.getCache(); - } - - /** - * 将entity的对象全部加载到Cache中去,如果clazz没有被@javax.persistence.Cacheable注解则不做任何事 - * - * @param Entity类泛型 - * @param clazz Entity类 - */ - public void refreshCache(Class clazz) { - EntityInfo info = loadEntityInfo(clazz); - EntityCache cache = info.getCache(); - if (cache == null) return; - cache.fullLoadAsync(); - } - ////检查对象是否都是同一个Entity类 - - protected CompletableFuture checkEntity(String action, boolean async, T... entitys) { - if (entitys.length < 1) return null; - Class clazz = null; - for (T val : entitys) { - if (clazz == null) { - clazz = val.getClass(); - continue; - } - if (clazz != val.getClass()) { - if (async) { - CompletableFuture future = new CompletableFuture<>(); - future.completeExceptionally(new SQLException("DataSource." + action + " must the same Class Entity, but diff is " + clazz + " and " + val.getClass())); - return future; - } - throw new RuntimeException("DataSource." + action + " must the same Class Entity, but diff is " + clazz + " and " + val.getClass()); - } - } - return null; - } - - protected CharSequence formatValueToString(final EntityInfo info, Object value) { - final String dbtype = this.readPool.getDbtype(); - if ("mysql".equals(dbtype)) { - if (value == null) return null; - if (value instanceof CharSequence) { - return new StringBuilder().append('\'').append(value.toString().replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); - } else if (!(value instanceof Number) && !(value instanceof java.util.Date) - && !value.getClass().getName().startsWith("java.sql.") && !value.getClass().getName().startsWith("java.time.")) { - return new StringBuilder().append('\'').append(info.getJsonConvert().convertTo(value).replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); - } - return String.valueOf(value); - } - return info.formatSQLValue(value, null); - } - - //----------------------------- insert ----------------------------- - /** - * 新增对象, 必须是Entity对象 - * - * @param Entity类泛型 - * @param entitys Entity对象 - * - * @return 影响的记录条数 - */ - @Override - public int insert(T... entitys) { - if (entitys.length == 0) return 0; - checkEntity("insert", false, entitys); - final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); - if (isOnlyCache(info)) return insertCache(info, entitys); - return insertDB(info, entitys).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - insertCache(info, entitys); - } - }).join(); - } - - @Override - public final int insert(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return 0; - return insert(entitys.toArray()); - } - - @Override - public final int insert(final Stream entitys) { - if (entitys == null) return 0; - return insert(entitys.toArray()); - } - - @Override - public CompletableFuture insertAsync(T... entitys) { - if (entitys.length == 0) return CompletableFuture.completedFuture(0); - CompletableFuture future = checkEntity("insert", true, entitys); - if (future != null) return future; - final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(insertCache(info, entitys)); - } - if (isAsync()) return insertDB(info, entitys).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - insertCache(info, entitys); - } - }); - return CompletableFuture.supplyAsync(() -> insertDB(info, entitys).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - insertCache(info, entitys); - } - }); - } - - @Override - public final CompletableFuture insertAsync(final Collection entitys) { - if (entitys == null || entitys.isEmpty()) return CompletableFuture.completedFuture(0); - return insertAsync(entitys.toArray()); - } - - @Override - public final CompletableFuture insertAsync(final Stream entitys) { - if (entitys == null) return CompletableFuture.completedFuture(0); - return insertAsync(entitys.toArray()); - } - - protected int insertCache(final EntityInfo info, T... entitys) { - final EntityCache cache = info.getCache(); - if (cache == null) return 0; - int c = 0; - for (final T value : entitys) { - c += cache.insert(value); - } - return c; - } - - //----------------------------- deleteCompose ----------------------------- - /** - * 删除对象, 必须是Entity对象 - * - * @param Entity类泛型 - * @param entitys Entity对象 - * - * @return 删除的数据条数 - */ - @Override - public int delete(T... entitys) { - if (entitys.length == 0) return -1; - checkEntity("delete", false, entitys); - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.getPrimary(); - Serializable[] ids = new Serializable[entitys.length]; - int i = 0; - for (final T value : entitys) { - ids[i++] = (Serializable) primary.get(value); - } - return delete(clazz, ids); - } - - @Override - public CompletableFuture deleteAsync(final T... entitys) { - if (entitys.length == 0) return CompletableFuture.completedFuture(-1); - CompletableFuture future = checkEntity("delete", true, entitys); - if (future != null) return future; - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.getPrimary(); - Serializable[] ids = new Serializable[entitys.length]; - int i = 0; - for (final T value : entitys) { - ids[i++] = (Serializable) primary.get(value); - } - return deleteAsync(clazz, ids); - } - - @Override - public int delete(Class clazz, Serializable... pks) { - if (pks.length == 0) return -1; - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return deleteCache(info, -1, pks); - return deleteCompose(info, pks).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, pks); - } - }).join(); - } - - @Override - public CompletableFuture deleteAsync(final Class clazz, final Serializable... pks) { - if (pks.length == 0) return CompletableFuture.completedFuture(-1); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(deleteCache(info, -1, pks)); - } - if (isAsync()) return deleteCompose(info, pks).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, pks); - } - }); - return CompletableFuture.supplyAsync(() -> deleteCompose(info, pks).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, pks); - } - }); - } - - @Override - public int delete(Class clazz, FilterNode node) { - return delete(clazz, (Flipper) null, node); - } - - @Override - public CompletableFuture deleteAsync(final Class clazz, final FilterNode node) { - return deleteAsync(clazz, (Flipper) null, node); - } - - @Override - public int delete(Class clazz, final Flipper flipper, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return deleteCache(info, -1, flipper, node); - return this.deleteCompose(info, flipper, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, flipper, node); - } - }).join(); - } - - @Override - public CompletableFuture deleteAsync(final Class clazz, final Flipper flipper, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(deleteCache(info, -1, flipper, node)); - } - if (isAsync()) return this.deleteCompose(info, flipper, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, flipper, node); - } - }); - return CompletableFuture.supplyAsync(() -> this.deleteCompose(info, flipper, node).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - deleteCache(info, rs, flipper, node); - } - }); - } - - protected CompletableFuture deleteCompose(final EntityInfo info, final Serializable... pks) { - if (pks.length == 1) { - String sql = "DELETE FROM " + info.getTable(pks[0]) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pks[0], sqlFormatter); - return deleteDB(info, null, sql); - } - String sql = "DELETE FROM " + info.getTable(pks[0]) + " WHERE " + info.getPrimarySQLColumn() + " IN ("; - for (int i = 0; i < pks.length; i++) { - if (i > 0) sql += ','; - sql += info.formatSQLValue(info.getPrimarySQLColumn(), pks[i], sqlFormatter); - } - sql += ")"; - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql); - return deleteDB(info, null, sql); - } - - protected CompletableFuture deleteCompose(final EntityInfo info, final Flipper flipper, final FilterNode node) { - Map joinTabalis = node.getJoinTabalis(); - CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - CharSequence where = node.createSQLExpress(info, joinTabalis); - - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - String sql = "DELETE " + ("mysql".equals(this.readPool.getDbtype()) ? "a" : "") + " FROM " + info.getTable(node) + " a" + (join1 == null ? "" : (", " + join1)) - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + info.createSQLOrderby(flipper); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" - + (sql + ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit())))); - return deleteDB(info, flipper, sql); - } - - //----------------------------- clearTableCompose ----------------------------- - @Override - public int clearTable(Class clazz) { - return clearTable(clazz, (FilterNode) null); - } - - @Override - public int clearTable(Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return clearTableCache(info, node); - return this.clearTableCompose(info, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - clearTableCache(info, node); - } - }).join(); - } - - @Override - public CompletableFuture clearTableAsync(final Class clazz) { - return clearTableAsync(clazz, (FilterNode) null); - } - - @Override - public CompletableFuture clearTableAsync(final Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(clearTableCache(info, node)); - } - if (isAsync()) return this.clearTableCompose(info, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - clearTableCache(info, node); - } - }); - return CompletableFuture.supplyAsync(() -> this.clearTableCompose(info, node).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - clearTableCache(info, node); - } - }); - } - - protected CompletableFuture clearTableCompose(final EntityInfo info, final FilterNode node) { - final String table = info.getTable(node); - String sql = "TRUNCATE TABLE " + table; - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " clearTable sql=" + sql); - return clearTableDB(info, table, sql); - } - - //----------------------------- dropTableCompose ----------------------------- - @Override - public int dropTable(Class clazz) { - return dropTable(clazz, (FilterNode) null); - } - - @Override - public int dropTable(Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return dropTableCache(info, node); - return this.dropTableCompose(info, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - dropTableCache(info, node); - } - }).join(); - } - - @Override - public CompletableFuture dropTableAsync(final Class clazz) { - return dropTableAsync(clazz, (FilterNode) null); - } - - @Override - public CompletableFuture dropTableAsync(final Class clazz, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(dropTableCache(info, node)); - } - if (isAsync()) return this.dropTableCompose(info, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - dropTableCache(info, node); - } - }); - return CompletableFuture.supplyAsync(() -> this.dropTableCompose(info, node).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - dropTableCache(info, node); - } - }); - } - - protected CompletableFuture dropTableCompose(final EntityInfo info, final FilterNode node) { - final String table = info.getTable(node); - String sql = "DROP TABLE " + table; - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " dropTable sql=" + sql); - return dropTableDB(info, table, sql); - } - - protected int clearTableCache(final EntityInfo info, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) return -1; - return cache.clear(); - } - - protected int dropTableCache(final EntityInfo info, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) return -1; - return cache.drop(); - } - - protected int deleteCache(final EntityInfo info, int count, Flipper flipper, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) return -1; - Serializable[] ids = cache.delete(flipper, node); - return count >= 0 ? count : (ids == null ? 0 : ids.length); - } - - protected int deleteCache(final EntityInfo info, int count, Serializable... pks) { - final EntityCache cache = info.getCache(); - if (cache == null) return -1; - int c = 0; - for (Serializable key : pks) { - c += cache.delete(key); - } - return count >= 0 ? count : c; - } - - protected static StringBuilder multisplit(char ch1, char ch2, String split, StringBuilder sb, String str, int from) { - if (str == null) return sb; - int pos1 = str.indexOf(ch1, from); - if (pos1 < 0) return sb; - int pos2 = str.indexOf(ch2, from); - if (pos2 < 0) return sb; - if (sb.length() > 0) sb.append(split); - sb.append(str.substring(pos1 + 1, pos2)); - return multisplit(ch1, ch2, split, sb, str, pos2 + 1); - } - - //---------------------------- update ---------------------------- - /** - * 更新对象, 必须是Entity对象 - * - * @param Entity类泛型 - * @param entitys Entity对象 - * - * @return 更新的数据条数 - */ - @Override - public int update(T... entitys) { - if (entitys.length == 0) return -1; - checkEntity("update", false, entitys); - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return updateCache(info, -1, entitys); - return updateDB(info, null, entitys).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, entitys); - } - }).join(); - } - - @Override - public CompletableFuture updateAsync(final T... entitys) { - return updateAsync((ChannelContext) null, entitys); - } - - @Override - public CompletableFuture updateAsync(final ChannelContext context, final T... entitys) { - if (entitys.length == 0) return CompletableFuture.completedFuture(-1); - CompletableFuture future = checkEntity("update", true, entitys); - if (future != null) return future; - final Class clazz = (Class) entitys[0].getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, entitys)); - } - if (isAsync()) return updateDB(info, context, entitys).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, entitys); - } - }); - return CompletableFuture.supplyAsync(() -> updateDB(info, context, entitys).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, entitys); - } - }); - } - - /** - * 根据主键值更新对象的column对应的值, 必须是Entity Class - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param pk 主键值 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return 更新的数据条数 - */ - @Override - public int updateColumn(Class clazz, Serializable pk, String column, Serializable colval) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return updateCache(info, -1, pk, column, colval); - return updateColumnCompose(info, pk, column, colval).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, column, colval); - } - }).join(); - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final String column, final Serializable colval) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, pk, column, colval)); - } - if (isAsync()) return updateColumnCompose(info, pk, column, colval).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, column, colval); - } - }); - return CompletableFuture.supplyAsync(() -> updateColumnCompose(info, pk, column, colval).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, column, colval); - } - }); - } - - protected CompletableFuture updateColumnCompose(final EntityInfo info, Serializable pk, String column, final Serializable colval) { - if (colval instanceof byte[]) { - String sql = "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" + prepareParamSign(1) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - return updateDB(info, null, sql, true, colval); - } else { - String sql = "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" - + info.formatSQLValue(column, colval, sqlFormatter) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - return updateDB(info, null, sql, false); - } - } - - /** - * 根据主键值更新对象的column对应的值, 必须是Entity Class - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * @param node 过滤node 不能为null - * - * @return 更新的数据条数 - */ - @Override - public int updateColumn(Class clazz, String column, Serializable colval, FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return updateCache(info, -1, column, colval, node); - return this.updateColumnCompose(info, column, colval, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, column, colval, node); - } - }).join(); - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final String column, final Serializable colval, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, column, colval, node)); - } - if (isAsync()) return this.updateColumnCompose(info, column, colval, node).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, column, colval, node); - } - }); - return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, column, colval, node).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, column, colval, node); - } - }); - } - - protected CompletableFuture updateColumnCompose(final EntityInfo info, final String column, final Serializable colval, final FilterNode node) { - Map joinTabalis = node.getJoinTabalis(); - CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - CharSequence where = node.createSQLExpress(info, joinTabalis); - - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - String alias = "postgresql".equals(writePool.dbtype) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 - if (colval instanceof byte[]) { - String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) - + " SET " + info.getSQLColumn(alias, column) + "=" + prepareParamSign(1) - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - return updateDB(info, null, sql, true, colval); - } else { - String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) - + " SET " + info.getSQLColumn(alias, column) + "=" + info.formatSQLValue(colval, sqlFormatter) - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - return updateDB(info, null, sql, false); - } - } - - /** - * 根据主键值更新对象的多个column对应的值, 必须是Entity Class - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param pk 主键值 - * @param values 字段值 - * - * @return 更新的数据条数 - */ - @Override - public int updateColumn(final Class clazz, final Serializable pk, final ColumnValue... values) { - if (values == null || values.length < 1) return -1; - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return updateCache(info, -1, pk, values); - return this.updateColumnCompose(info, pk, values).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, values); - } - }).join(); - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final ColumnValue... values) { - if (values == null || values.length < 1) return CompletableFuture.completedFuture(-1); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, pk, values)); - } - if (isAsync()) return this.updateColumnCompose(info, pk, values).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, values); - } - }); - return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, pk, values).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, pk, values); - } - }); - } - - protected CompletableFuture updateColumnCompose(final EntityInfo info, final Serializable pk, final ColumnValue... values) { - StringBuilder setsql = new StringBuilder(); - List blobs = null; - int index = 0; - for (ColumnValue col : values) { - if (col == null) continue; - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) throw new RuntimeException(info.getType() + " cannot found column " + col.getColumn()); - if (setsql.length() > 0) setsql.append(", "); - String sqlColumn = info.getSQLColumn(null, col.getColumn()); - if (col.getValue() instanceof byte[]) { - if (blobs == null) blobs = new ArrayList<>(); - blobs.add((byte[]) col.getValue()); - setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); - } else { - setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); - } - } - if (setsql.length() < 1) return CompletableFuture.completedFuture(0); - String sql = "UPDATE " + info.getTable(pk) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - if (blobs == null) return updateDB(info, null, sql, false); - return updateDB(info, null, sql, true, blobs.toArray()); - } - - /** - * 根据主键值更新对象的多个column对应的值, 必须是Entity Class - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param node 过滤条件 - * @param values 字段值 - * - * @return 更新的数据条数 - */ - @Override - public int updateColumn(final Class clazz, final FilterNode node, final ColumnValue... values) { - return updateColumn(clazz, node, null, values); - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final ColumnValue... values) { - return updateColumnAsync(clazz, node, null, values); - } - - @Override - public int updateColumn(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - if (values == null || values.length < 1) return -1; - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return updateCache(info, -1, node, flipper, values); - return this.updateColumnCompose(info, node, flipper, values).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, node, flipper, values); - } - }).join(); - } - - @Override - public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - if (values == null || values.length < 1) return CompletableFuture.completedFuture(-1); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, node, flipper, values)); - } - if (isAsync()) return this.updateColumnCompose(info, node, flipper, values).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, node, flipper, values); - } - }); - return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, node, flipper, values).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, node, flipper, values); - } - }); - } - - protected CompletableFuture updateColumnCompose(final EntityInfo info, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - StringBuilder setsql = new StringBuilder(); - List blobs = null; - int index = 0; - String alias = "postgresql".equals(writePool.dbtype) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 - for (ColumnValue col : values) { - if (col == null) continue; - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) continue; - if (setsql.length() > 0) setsql.append(", "); - String sqlColumn = info.getSQLColumn(alias, col.getColumn()); - if (col.getValue() instanceof byte[]) { - if (blobs == null) blobs = new ArrayList<>(); - blobs.add((byte[]) col.getValue()); - setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); - } else { - setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); - } - } - if (setsql.length() < 1) return CompletableFuture.completedFuture(0); - Map joinTabalis = node == null ? null : node.getJoinTabalis(); - CharSequence join = node == null ? null : node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) - + info.createSQLOrderby(flipper); - if (blobs == null) return updateDB(info, null, sql, false); - return updateDB(info, flipper, sql, true, blobs.toArray()); - } - - @Override - public int updateColumn(final T entity, final String... columns) { - return updateColumn(entity, SelectColumn.includes(columns)); - } - - @Override - public CompletableFuture updateColumnAsync(final T entity, final String... columns) { - return updateColumnAsync(entity, SelectColumn.includes(columns)); - } - - @Override - public int updateColumn(final T entity, final FilterNode node, final String... columns) { - return updateColumn(entity, node, SelectColumn.includes(columns)); - } - - @Override - public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final String... columns) { - return updateColumnAsync(entity, node, SelectColumn.includes(columns)); - } - - @Override - public int updateColumn(final T entity, final SelectColumn selects) { - if (entity == null || selects == null) return -1; - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return updateCache(info, -1, false, entity, null, selects); - return this.updateColumnCompose(info, false, entity, null, selects).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, false, entity, null, selects); - } - }).join(); - } - - @Override - public CompletableFuture updateColumnAsync(final T entity, final SelectColumn selects) { - if (entity == null || selects == null) return CompletableFuture.completedFuture(-1); - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, false, entity, null, selects)); - } - if (isAsync()) return this.updateColumnCompose(info, false, entity, null, selects).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, false, entity, null, selects); - } - }); - return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, false, entity, null, selects).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, false, entity, null, selects); - } - }); - } - - @Override - public int updateColumn(final T entity, final FilterNode node, final SelectColumn selects) { - if (entity == null || node == null || selects == null) return -1; - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) return updateCache(info, -1, true, entity, node, selects); - return this.updateColumnCompose(info, true, entity, node, selects).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, true, entity, node, selects); - } - }).join(); - } - - @Override - public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final SelectColumn selects) { - if (entity == null || node == null || selects == null) return CompletableFuture.completedFuture(-1); - Class clazz = (Class) entity.getClass(); - final EntityInfo info = loadEntityInfo(clazz); - if (isOnlyCache(info)) { - return CompletableFuture.completedFuture(updateCache(info, -1, true, entity, node, selects)); - } - if (isAsync()) return this.updateColumnCompose(info, true, entity, node, selects).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, true, entity, node, selects); - } - }); - return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, true, entity, node, selects).join(), getExecutor()).whenComplete((rs, t) -> { - if (t != null) { - futureCompleteConsumer.accept(rs, t); - } else { - updateCache(info, rs, true, entity, node, selects); - } - }); - } - - protected CompletableFuture updateColumnCompose(final EntityInfo info, final boolean neednode, final T entity, final FilterNode node, final SelectColumn selects) { - StringBuilder setsql = new StringBuilder(); - List blobs = null; - int index = 0; - String alias = "postgresql".equals(writePool.dbtype) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 - for (Attribute attr : info.updateAttributes) { - if (!selects.test(attr.field())) continue; - if (setsql.length() > 0) setsql.append(", "); - setsql.append(info.getSQLColumn(alias, attr.field())); - Serializable val = info.getFieldValue(attr, entity); - if (val instanceof byte[]) { - if (blobs == null) blobs = new ArrayList<>(); - blobs.add((byte[]) val); - setsql.append("=").append(prepareParamSign(++index)); - } else { - CharSequence sqlval = info.formatSQLValue(val, sqlFormatter); - if (sqlval == null && info.isNotNullJson(attr)) sqlval = "''"; - setsql.append("=").append(sqlval); - } - } - if (neednode) { - Map joinTabalis = node.getJoinTabalis(); - CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); - CharSequence where = node.createSQLExpress(info, joinTabalis); - StringBuilder join1 = null; - StringBuilder join2 = null; - if (join != null) { - String joinstr = join.toString(); - join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); - join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); - } - String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql - + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) - : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); - if (blobs == null) return updateDB(info, null, sql, false); - return updateDB(info, null, sql, true, blobs.toArray()); - } else { - final Serializable id = (Serializable) info.getSQLValue(info.getPrimary(), entity); - String sql = "UPDATE " + info.getTable(id) + " a SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(id, sqlFormatter); - if (blobs == null) return updateDB(info, null, sql, false); - return updateDB(info, null, sql, true, blobs.toArray()); - } - } - - protected int updateCache(final EntityInfo info, int count, final boolean neednode, final T entity, final FilterNode node, final SelectColumn selects) { - final EntityCache cache = info.getCache(); - if (cache == null) return count; - final List> attrs = new ArrayList<>(); - for (Attribute attr : info.updateAttributes) { - if (!selects.test(attr.field())) continue; - attrs.add(attr); - } - if (neednode) { - T[] rs = cache.update(entity, attrs, node); - return count >= 0 ? count : (rs == null ? 0 : rs.length); - } else { - T rs = cache.update(entity, attrs); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - } - - protected int updateCache(final EntityInfo info, int count, final FilterNode node, final Flipper flipper, final ColumnValue... values) { - final EntityCache cache = info.getCache(); - if (cache == null) return count; - final List> attrs = new ArrayList<>(); - final List cols = new ArrayList<>(); - for (ColumnValue col : values) { - if (col == null) continue; - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) continue; - attrs.add(attr); - cols.add(col); - } - T[] rs = cache.updateColumn(node, flipper, attrs, cols); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, final Serializable pk, final ColumnValue... values) { - final EntityCache cache = info.getCache(); - if (cache == null) return count; - final List> attrs = new ArrayList<>(); - final List cols = new ArrayList<>(); - for (ColumnValue col : values) { - if (col == null) continue; - Attribute attr = info.getUpdateAttribute(col.getColumn()); - if (attr == null) continue; - attrs.add(attr); - cols.add(col); - } - T rs = cache.updateColumn(pk, attrs, cols); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, String column, final Serializable colval, FilterNode node) { - final EntityCache cache = info.getCache(); - if (cache == null) return count; - T[] rs = cache.update(info.getAttribute(column), colval, node); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, final Serializable pk, final String column, final Serializable colval) { - final EntityCache cache = info.getCache(); - if (cache == null) return count; - T rs = cache.update(pk, info.getAttribute(column), colval); - return count >= 0 ? count : (rs == null ? 0 : 1); - } - - protected int updateCache(final EntityInfo info, int count, T... entitys) { - final EntityCache cache = info.getCache(); - if (cache == null) return -1; - int c2 = 0; - for (final T value : entitys) { - c2 += cache.update(value); - } - return count >= 0 ? count : c2; - } - - public int reloadCache(Class clazz, Serializable... pks) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache == null) return -1; - String column = info.getPrimary().field(); - int c = 0; - for (Serializable id : pks) { - Sheet sheet = querySheetCompose(false, true, false, clazz, null, FLIPPER_ONE, FilterNode.create(column, id)).join(); - T value = sheet.isEmpty() ? null : sheet.list().get(0); - if (value != null) c += cache.update(value); - } - return c; - } - - //------------------------- getNumberMapCompose ------------------------- - @Override - public Map getNumberMap(final Class entityClass, final FilterFuncColumn... columns) { - return getNumberMap(entityClass, (FilterNode) null, columns); - } - - @Override - public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterFuncColumn... columns) { - return getNumberMapAsync(entityClass, (FilterNode) null, columns); - } - - @Override - public Map getNumberMap(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { - return getNumberMap(entityClass, FilterNodeBean.createFilterNode(bean), columns); - } - - @Override - public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterBean bean, final FilterFuncColumn... columns) { - return getNumberMapAsync(entityClass, FilterNodeBean.createFilterNode(bean), columns); - } - - @Override - public Map getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - final Map map = new HashMap<>(); - if (node == null || node.isCacheUseable(this)) { - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - map.put(ffc.col(col), cache.getNumberResult(ffc.func, ffc.defvalue, col, node)); - } - } - return map; - } - } - return (Map) getNumberMapCompose(info, node, columns).join(); - } - - @Override - public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - final Map map = new HashMap<>(); - if (node == null || node.isCacheUseable(this)) { - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - map.put(ffc.col(col), cache.getNumberResult(ffc.func, ffc.defvalue, col, node)); - } - } - return CompletableFuture.completedFuture(map); - } - } - if (isAsync()) return getNumberMapCompose(info, node, columns); - return CompletableFuture.supplyAsync(() -> (Map) getNumberMapCompose(info, node, columns).join(), getExecutor()); - } - - protected CompletableFuture> getNumberMapCompose(final EntityInfo info, final FilterNode node, final FilterFuncColumn... columns) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - StringBuilder sb = new StringBuilder(); - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - if (sb.length() > 0) sb.append(", "); - sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : info.getSQLColumn("a", col)))); - } - } - final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a" - + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " getnumbermap sql=" + sql); - return getNumberMapDB(info, sql, columns); - } - - //------------------------ getNumberResultCompose ----------------------- - @Override - public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column) { - return getNumberResult(entityClass, func, null, column, (FilterNode) null); - } - - @Override - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column) { - return getNumberResultAsync(entityClass, func, null, column, (FilterNode) null); - } - - @Override - public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, FilterBean bean) { - return getNumberResult(entityClass, func, null, column, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterBean bean) { - return getNumberResultAsync(entityClass, func, null, column, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Number getNumberResult(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { - return getNumberResult(entityClass, func, null, column, node); - } - - @Override - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final String column, final FilterNode node) { - return getNumberResultAsync(entityClass, func, null, column, node); - } - - @Override - public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { - return getNumberResult(entityClass, func, defVal, column, (FilterNode) null); - } - - @Override - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column) { - return getNumberResultAsync(entityClass, func, defVal, column, (FilterNode) null); - } - - @Override - public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, FilterBean bean) { - return getNumberResult(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, FilterBean bean) { - return getNumberResultAsync(entityClass, func, defVal, column, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || node.isCacheUseable(this)) { - return cache.getNumberResult(func, defVal, column, node); - } - } - return getNumberResultCompose(info, entityClass, func, defVal, column, node).join(); - } - - @Override - public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || node.isCacheUseable(this)) { - return CompletableFuture.completedFuture(cache.getNumberResult(func, defVal, column, node)); - } - } - if (isAsync()) return getNumberResultCompose(info, entityClass, func, defVal, column, node); - return CompletableFuture.supplyAsync(() -> getNumberResultCompose(info, entityClass, func, defVal, column, node).join(), getExecutor()); - } - - protected CompletableFuture getNumberResultCompose(final EntityInfo info, final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : info.getSQLColumn("a", column))) + " FROM " + info.getTable(node) + " a" - + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(entityClass.getSimpleName() + " getnumberresult sql=" + sql); - return getNumberResultDB(info, sql, defVal, column); - } - - //------------------------ queryColumnMapCompose ------------------------ - @Override - public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { - return queryColumnMap(entityClass, keyColumn, func, funcColumn, (FilterNode) null); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn) { - return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, (FilterNode) null); - } - - @Override - public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { - return queryColumnMap(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, final FilterBean bean) { - return queryColumnMapAsync(entityClass, keyColumn, func, funcColumn, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || node.isCacheUseable(this)) { - return cache.queryColumnMap(keyColumn, func, funcColumn, node); - } - } - return (Map) queryColumnMapCompose(info, keyColumn, func, funcColumn, node).join(); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || node.isCacheUseable(this)) { - return CompletableFuture.completedFuture(cache.queryColumnMap(keyColumn, func, funcColumn, node)); - } - } - if (isAsync()) return queryColumnMapCompose(info, keyColumn, func, funcColumn, node); - return CompletableFuture.supplyAsync(() -> (Map) queryColumnMapCompose(info, keyColumn, func, funcColumn, node).join(), getExecutor()); - } - - protected CompletableFuture> queryColumnMapCompose(final EntityInfo info, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - final String keySqlColumn = info.getSQLColumn(null, keyColumn); - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - final String funcSqlColumn = func == null ? info.getSQLColumn("a", funcColumn) : func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn))); - final String sql = "SELECT a." + keySqlColumn + ", " + funcSqlColumn - + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + keySqlColumn; - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " querycolumnmap sql=" + sql); - return queryColumnMapDB(info, sql, keyColumn); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { - return queryColumnMap(entityClass, funcNodes, groupByColumn, (FilterNode) null); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, (FilterNode) null); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { - return queryColumnMap(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterBean bean) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumn, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { - Map map = queryColumnMap(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); - final Map rs = new LinkedHashMap<>(); - map.forEach((keys, values) -> rs.put(keys[0], values)); - return rs; - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { - CompletableFuture> future = queryColumnMapAsync(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); - return future.thenApply(map -> { - final Map rs = new LinkedHashMap<>(); - map.forEach((keys, values) -> rs.put(keys[0], values)); - return rs; - }); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { - return queryColumnMap(entityClass, funcNodes, groupByColumns, (FilterNode) null); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, (FilterNode) null); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { - return queryColumnMap(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterBean bean) { - return queryColumnMapAsync(entityClass, funcNodes, groupByColumns, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || node.isCacheUseable(this)) { - return cache.queryColumnMap(funcNodes, groupByColumns, node); - } - } - return (Map) queryColumnMapCompose(info, funcNodes, groupByColumns, node).join(); - } - - @Override - public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { - final EntityInfo info = loadEntityInfo(entityClass); - final EntityCache cache = info.getCache(); - if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { - if (node == null || node.isCacheUseable(this)) { - return CompletableFuture.completedFuture(cache.queryColumnMap(funcNodes, groupByColumns, node)); - } - } - if (isAsync()) return queryColumnMapCompose(info, funcNodes, groupByColumns, node); - return CompletableFuture.supplyAsync(() -> (Map) queryColumnMapCompose(info, funcNodes, groupByColumns, node).join(), getExecutor()); - } - - protected CompletableFuture> queryColumnMapCompose(final EntityInfo info, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { - final StringBuilder groupBySqlColumns = new StringBuilder(); - if (groupByColumns != null && groupByColumns.length > 0) { - for (int i = 0; i < groupByColumns.length; i++) { - if (groupBySqlColumns.length() > 0) groupBySqlColumns.append(", "); - groupBySqlColumns.append(info.getSQLColumn("a", groupByColumns[i])); - } - } - final StringBuilder funcSqlColumns = new StringBuilder(); - for (int i = 0; i < funcNodes.length; i++) { - if (funcSqlColumns.length() > 0) funcSqlColumns.append(", "); - if (funcNodes[i] instanceof ColumnFuncNode) { - funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnFuncNode) funcNodes[i], sqlFormatter)); - } else { - funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnNodeValue) funcNodes[i], sqlFormatter)); - } - } - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final Set haset = new HashSet<>(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - String sql = "SELECT "; - if (groupBySqlColumns.length() > 0) sql += groupBySqlColumns + ", "; - sql += funcSqlColumns + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - if (groupBySqlColumns.length() > 0) sql += " GROUP BY " + groupBySqlColumns; - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " querycolumnmap sql=" + sql); - return queryColumnMapDB(info, sql, funcNodes, groupByColumns); - } - - //----------------------------- findCompose ----------------------------- - /** - * 根据主键获取对象 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param pk 主键值 - * - * @return Entity对象 - */ - @Override - public T find(Class clazz, Serializable pk) { - return find(clazz, (SelectColumn) null, pk); - } - - @Override - public CompletableFuture findAsync(final Class clazz, final Serializable pk) { - return findAsync(clazz, (SelectColumn) null, pk); - } - - @Override - public CompletableFuture findAsync(final Class clazz, ChannelContext context, final Serializable pk) { - return findAsync(clazz, context, (SelectColumn) null, pk); - } - - @Override - public T find(Class clazz, final SelectColumn selects, Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); - if (cache.isFullLoaded() || rs != null) return rs; - } - return findCompose(info, null, selects, pk).join(); - } - - @Override - public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final Serializable pk) { - return findAsync(clazz, null, selects, pk); - } - - protected CompletableFuture findAsync(final Class clazz, final ChannelContext context, final SelectColumn selects, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); - if (cache.isFullLoaded() || rs != null) return CompletableFuture.completedFuture(rs); - } - if (isAsync()) return findCompose(info, context, selects, pk); - return CompletableFuture.supplyAsync(() -> findCompose(info, context, selects, pk).join(), getExecutor()); - } - - protected CompletableFuture findCompose(final EntityInfo info, final ChannelContext context, final SelectColumn selects, Serializable pk) { - String column = info.getPrimarySQLColumn(); - final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + column + "=" + info.formatSQLValue(column, pk, sqlFormatter); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - return findDB(info, context, sql, true, selects); - } - - @Override - public T find(final Class clazz, final String column, final Serializable colval) { - return find(clazz, null, FilterNode.create(column, colval)); - } - - @Override - public CompletableFuture findAsync(final Class clazz, final String column, final Serializable colval) { - return findAsync(clazz, null, FilterNode.create(column, colval)); - } - - @Override - public T find(final Class clazz, final FilterBean bean) { - return find(clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture findAsync(final Class clazz, final FilterBean bean) { - return findAsync(clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public T find(final Class clazz, final FilterNode node) { - return find(clazz, null, node); - } - - @Override - public CompletableFuture findAsync(final Class clazz, final FilterNode node) { - return findAsync(clazz, null, node); - } - - @Override - public T find(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return find(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return findAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public T find(final Class clazz, final SelectColumn selects, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null && cache.isFullLoaded() && (node == null || node.isCacheUseable(this))) return cache.find(selects, node); - return this.findCompose(info, selects, node).join(); - } - - @Override - public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null && cache.isFullLoaded() && (node == null || node.isCacheUseable(this))) { - return CompletableFuture.completedFuture(cache.find(selects, node)); - } - if (isAsync()) return this.findCompose(info, selects, node); - return CompletableFuture.supplyAsync(() -> this.findCompose(info, selects, node).join(), getExecutor()); - } - - protected CompletableFuture findCompose(final EntityInfo info, final SelectColumn selects, final FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - return findDB(info, null, sql, false, selects); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final Serializable pk) { - return findColumn(clazz, column, null, pk); - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable pk) { - return findColumnAsync(clazz, column, null, pk); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final FilterBean bean) { - return findColumn(clazz, column, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterBean bean) { - return findColumnAsync(clazz, column, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final FilterNode node) { - return findColumn(clazz, column, null, node); - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final FilterNode node) { - return findColumnAsync(clazz, column, null, node); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { - return findColumn(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterBean bean) { - return findColumnAsync(clazz, column, defValue, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, pk); - if (cache.isFullLoaded() || val != null) return val; - } - return findColumnCompose(info, column, defValue, pk).join(); - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, pk); - if (cache.isFullLoaded() || val != null) return CompletableFuture.completedFuture(val); - } - if (isAsync()) return findColumnCompose(info, column, defValue, pk); - return CompletableFuture.supplyAsync(() -> findColumnCompose(info, column, defValue, pk).join(), getExecutor()); - } - - protected CompletableFuture findColumnCompose(final EntityInfo info, String column, final Serializable defValue, final Serializable pk) { - final String sql = "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - return findColumnDB(info, sql, true, column, defValue); - } - - @Override - public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, node); - if (cache.isFullLoaded() || val != null) return val; - } - return this.findColumnCompose(info, column, defValue, node).join(); - } - - @Override - public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - Serializable val = cache.findColumn(column, defValue, node); - if (cache.isFullLoaded() || val != null) return CompletableFuture.completedFuture(val); - } - if (isAsync()) return this.findColumnCompose(info, column, defValue, node); - return CompletableFuture.supplyAsync(() -> this.findColumnCompose(info, column, defValue, node).join(), getExecutor()); - } - - protected CompletableFuture findColumnCompose(final EntityInfo info, String column, final Serializable defValue, final FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - final String sql = "SELECT " + info.getSQLColumn("a", column) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); - return findColumnDB(info, sql, false, column, defValue); - } - - //---------------------------- existsCompose ---------------------------- - @Override - public boolean exists(Class clazz, Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(pk); - if (rs || cache.isFullLoaded()) return rs; - } - return existsCompose(info, pk).join(); - } - - @Override - public CompletableFuture existsAsync(final Class clazz, final Serializable pk) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(pk); - if (rs || cache.isFullLoaded()) return CompletableFuture.completedFuture(rs); - } - if (isAsync()) return existsCompose(info, pk); - return CompletableFuture.supplyAsync(() -> existsCompose(info, pk).join(), getExecutor()); - } - - protected CompletableFuture existsCompose(final EntityInfo info, Serializable pk) { - final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); - return existsDB(info, sql, true); - } - - @Override - public boolean exists(final Class clazz, final FilterBean bean) { - return exists(clazz, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture existsAsync(final Class clazz, final FilterBean bean) { - return existsAsync(clazz, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public boolean exists(final Class clazz, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(node); - if (rs || cache.isFullLoaded()) return rs; - } - return this.existsCompose(info, node).join(); - } - - @Override - public CompletableFuture existsAsync(final Class clazz, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (cache != null) { - boolean rs = cache.exists(node); - if (rs || cache.isFullLoaded()) return CompletableFuture.completedFuture(rs); - } - if (isAsync()) return this.existsCompose(info, node); - return CompletableFuture.supplyAsync(() -> this.existsCompose(info, node).join(), getExecutor()); - } - - protected CompletableFuture existsCompose(final EntityInfo info, FilterNode node) { - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - final String sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); - return existsDB(info, sql, false); - } - - //-----------------------list set---------------------------- - @Override - public Set queryColumnSet(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { - return queryColumnSet(selectedColumn, clazz, null, FilterNode.create(column, colval)); - } - - @Override - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { - return queryColumnSetAsync(selectedColumn, clazz, null, FilterNode.create(column, colval)); - } - - @Override - public Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnSet(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnSetAsync(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Set queryColumnSet(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnSet(selectedColumn, clazz, null, node); - } - - @Override - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnSetAsync(selectedColumn, clazz, null, node); - } - - @Override - public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - final Set list = querySet(clazz, SelectColumn.includes(selectedColumn), flipper, node); - final Set rs = new LinkedHashSet<>(); - if (list.isEmpty()) return rs; - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - } - - @Override - public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - return querySetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Set list) -> { - final Set rs = new LinkedHashSet<>(); - if (list.isEmpty()) return rs; - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - }); - } - - @Override - public List queryColumnList(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { - return queryColumnList(selectedColumn, clazz, null, FilterNode.create(column, colval)); - } - - @Override - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final String column, final Serializable colval) { - return queryColumnListAsync(selectedColumn, clazz, null, FilterNode.create(column, colval)); - } - - @Override - public List queryColumnList(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnList(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterBean bean) { - return queryColumnListAsync(selectedColumn, clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public List queryColumnList(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnList(selectedColumn, clazz, null, node); - } - - @Override - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final FilterNode node) { - return queryColumnListAsync(selectedColumn, clazz, null, node); - } - - @Override - public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnList(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnListAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - final List list = queryList(clazz, SelectColumn.includes(selectedColumn), flipper, node); - final List rs = new ArrayList<>(); - if (list.isEmpty()) return rs; - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - } - - @Override - public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - return queryListAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((List list) -> { - final List rs = new ArrayList<>(); - if (list.isEmpty()) return rs; - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - for (T t : list) { - rs.add(selected.get(t)); - } - return rs; - }); - } - - /** - * 根据指定参数查询对象某个字段的集合 - *

    - * @param Entity类的泛型 - * @param 字段值的类型 - * @param selectedColumn 字段名 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤Bean - * - * @return 字段集合 - */ - @Override - public Sheet queryColumnSheet(final String selectedColumn, Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSheet(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryColumnSheetAsync(selectedColumn, clazz, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - Sheet sheet = querySheet(clazz, SelectColumn.includes(selectedColumn), flipper, node); - final Sheet rs = new Sheet<>(); - if (sheet.isEmpty()) return rs; - rs.setTotal(sheet.getTotal()); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - final List list = new ArrayList<>(); - for (T t : sheet.getRows()) { - list.add(selected.get(t)); - } - rs.setRows(list); - return rs; - } - - @Override - public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { - return querySheetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Sheet sheet) -> { - final Sheet rs = new Sheet<>(); - if (sheet.isEmpty()) return rs; - rs.setTotal(sheet.getTotal()); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute selected = (Attribute) info.getAttribute(selectedColumn); - final List list = new ArrayList<>(); - for (T t : sheet.getRows()) { - list.add(selected.get(t)); - } - rs.setRows(list); - return rs; - }); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param keyStream 主键Stream - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final Stream keyStream) { - return queryMap(clazz, null, keyStream); - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final Stream keyStream) { - return queryMapAsync(clazz, null, keyStream); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param bean FilterBean - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final FilterBean bean) { - return queryMap(clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final FilterBean bean) { - return queryMapAsync(clazz, null, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param node FilterNode - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final FilterNode node) { - return queryMap(clazz, null, node); - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final FilterNode node) { - return queryMapAsync(clazz, null, node); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param keyStream 主键Stream - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final SelectColumn selects, final Stream keyStream) { - if (keyStream == null) return new LinkedHashMap<>(); - final EntityInfo info = loadEntityInfo(clazz); - final ArrayList ids = new ArrayList<>(); - keyStream.forEach(k -> ids.add(k)); - final Attribute primary = info.primary; - List rs = queryList(clazz, FilterNode.create(primary.field(), ids)); - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) return new LinkedHashMap<>(); - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final Stream keyStream) { - if (keyStream == null) return CompletableFuture.completedFuture(new LinkedHashMap<>()); - final EntityInfo info = loadEntityInfo(clazz); - final ArrayList pks = new ArrayList<>(); - keyStream.forEach(k -> pks.add(k)); - final Attribute primary = info.primary; - return queryListAsync(clazz, FilterNode.create(primary.field(), pks)).thenApply((List rs) -> { - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) return new LinkedHashMap<>(); - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - }); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param bean FilterBean - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return queryMap(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return queryMapAsync(clazz, selects, FilterNodeBean.createFilterNode(bean)); - } - - /** - * 查询符合过滤条件记录的Map集合, 主键值为key
    - * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    - * - * @param 主键泛型 - * @param Entity泛型 - * @param clazz Entity类 - * @param selects 指定字段 - * @param node FilterNode - * - * @return Entity的集合 - */ - @Override - public Map queryMap(final Class clazz, final SelectColumn selects, final FilterNode node) { - List rs = queryList(clazz, selects, node); - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.primary; - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) return new LinkedHashMap<>(); - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - } - - @Override - public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { - return queryListAsync(clazz, selects, node).thenApply((List rs) -> { - final EntityInfo info = loadEntityInfo(clazz); - final Attribute primary = info.primary; - Map map = new LinkedHashMap<>(); - if (rs.isEmpty()) return new LinkedHashMap<>(); - for (T item : rs) { - map.put((K) primary.get(item), item); - } - return map; - }); - } - - /** - * 根据指定字段值查询对象集合 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity对象的集合 - */ - @Override - public Set querySet(final Class clazz, final String column, final Serializable colval) { - return querySet(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final String column, final Serializable colval) { - return querySetAsync(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); - } - - @Override - public Set querySet(final Class clazz) { - return querySet(clazz, (SelectColumn) null, null, (FilterNode) null); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz) { - return querySetAsync(clazz, (SelectColumn) null, null, (FilterNode) null); - } - - /** - * 根据过滤对象FilterBean查询对象集合 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param bean 过滤Bean - * - * @return Entity对象集合 - */ - @Override - public Set querySet(final Class clazz, final FilterBean bean) { - return querySet(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final FilterBean bean) { - return querySetAsync(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Set querySet(final Class clazz, final FilterNode node) { - return querySet(clazz, (SelectColumn) null, null, node); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final FilterNode node) { - return querySetAsync(clazz, (SelectColumn) null, null, node); - } - - /** - * 根据过滤对象FilterBean查询对象集合, 对象只填充或排除SelectField指定的字段 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param selects 收集的字段 - * @param bean 过滤Bean - * - * @return Entity对象的集合 - */ - @Override - public Set querySet(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return querySet(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, SelectColumn selects, final FilterBean bean) { - return querySetAsync(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Set querySet(final Class clazz, final SelectColumn selects, final FilterNode node) { - return querySet(clazz, selects, null, node); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, SelectColumn selects, final FilterNode node) { - return querySetAsync(clazz, selects, null, node); - } - - @Override - public Set querySet(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { - return querySet(clazz, null, flipper, FilterNode.create(column, colval)); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { - return querySetAsync(clazz, null, flipper, FilterNode.create(column, colval)); - } - - @Override - public Set querySet(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySet(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySetAsync(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Set querySet(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySet(clazz, null, flipper, node); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySetAsync(clazz, null, flipper, node); - } - - @Override - public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - return new LinkedHashSet<>(querySheetCompose(true, false, true, clazz, selects, flipper, node).join().list(true)); - } - - @Override - public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - return querySheetCompose(true, false, true, clazz, selects, flipper, node).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))); - } - - /** - * 根据指定字段值查询对象集合 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param column 过滤字段名 - * @param colval 过滤字段值 - * - * @return Entity对象的集合 - */ - @Override - public List queryList(final Class clazz, final String column, final Serializable colval) { - return queryList(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final String column, final Serializable colval) { - return queryListAsync(clazz, (SelectColumn) null, null, FilterNode.create(column, colval)); - } - - @Override - public List queryList(final Class clazz) { - return queryList(clazz, (SelectColumn) null, null, (FilterNode) null); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz) { - return queryListAsync(clazz, (SelectColumn) null, null, (FilterNode) null); - } - - /** - * 根据过滤对象FilterBean查询对象集合 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param bean 过滤Bean - * - * @return Entity对象集合 - */ - @Override - public List queryList(final Class clazz, final FilterBean bean) { - return queryList(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final FilterBean bean) { - return queryListAsync(clazz, (SelectColumn) null, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public List queryList(final Class clazz, final FilterNode node) { - return queryList(clazz, (SelectColumn) null, null, node); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final FilterNode node) { - return queryListAsync(clazz, (SelectColumn) null, null, node); - } - - /** - * 根据过滤对象FilterBean查询对象集合, 对象只填充或排除SelectField指定的字段 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param selects 收集的字段 - * @param bean 过滤Bean - * - * @return Entity对象的集合 - */ - @Override - public List queryList(final Class clazz, final SelectColumn selects, final FilterBean bean) { - return queryList(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, SelectColumn selects, final FilterBean bean) { - return queryListAsync(clazz, selects, null, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public List queryList(final Class clazz, final SelectColumn selects, final FilterNode node) { - return queryList(clazz, selects, null, node); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, SelectColumn selects, final FilterNode node) { - return queryListAsync(clazz, selects, null, node); - } - - @Override - public List queryList(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { - return queryList(clazz, null, flipper, FilterNode.create(column, colval)); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final String column, final Serializable colval) { - return queryListAsync(clazz, null, flipper, FilterNode.create(column, colval)); - } - - @Override - public List queryList(final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryList(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { - return queryListAsync(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public List queryList(final Class clazz, final Flipper flipper, final FilterNode node) { - return queryList(clazz, null, flipper, node); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final Flipper flipper, final FilterNode node) { - return queryListAsync(clazz, null, flipper, node); - } - - @Override - public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return queryList(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return queryListAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - return querySheetCompose(true, false, false, clazz, selects, flipper, node).join().list(true); - } - - @Override - public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - return querySheetCompose(true, false, false, clazz, selects, flipper, node).thenApply((rs) -> rs.list(true)); - } - - //-----------------------sheet---------------------------- - /** - * 根据过滤对象FilterBean和翻页对象Flipper查询一页的数据 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param flipper 翻页对象 - * @param bean 过滤Bean - * - * @return Entity对象的集合 - */ - @Override - public Sheet querySheet(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySheet(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterBean bean) { - return querySheetAsync(clazz, null, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Sheet querySheet(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySheet(clazz, null, flipper, node); - } - - @Override - public CompletableFuture> querySheetAsync(final Class clazz, final Flipper flipper, final FilterNode node) { - return querySheetAsync(clazz, null, flipper, node); - } - - /** - * 根据过滤对象FilterBean和翻页对象Flipper查询一页的数据, 对象只填充或排除SelectField指定的字段 - * - * @param Entity类的泛型 - * @param clazz Entity类 - * @param selects 收集的字段集合 - * @param flipper 翻页对象 - * @param bean 过滤Bean - * - * @return Entity对象的集合 - */ - @Override - public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySheet(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterBean bean) { - return querySheetAsync(clazz, selects, flipper, FilterNodeBean.createFilterNode(bean)); - } - - @Override - public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - return querySheetCompose(true, true, false, clazz, selects, flipper, node).join(); - } - - @Override - public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - if (isAsync()) return querySheetCompose(true, true, false, clazz, selects, flipper, node); - return CompletableFuture.supplyAsync(() -> querySheetCompose(true, true, false, clazz, selects, flipper, node).join(), getExecutor()); - } - - protected CompletableFuture> querySheetCompose(final boolean readcache, final boolean needtotal, final boolean distinct, final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { - final EntityInfo info = loadEntityInfo(clazz); - final EntityCache cache = info.getCache(); - if (readcache && cache != null && cache.isFullLoaded()) { - if (node == null || node.isCacheUseable(this)) { - if (info.isLoggable(logger, Level.FINEST, " cache query predicate = ")) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : node.createPredicate(cache))); - return CompletableFuture.completedFuture(cache.querySheet(needtotal, distinct, selects, flipper, node)); - } - } - return querySheetDB(info, readcache, needtotal, distinct, selects, flipper, node); - } - - protected static enum UpdateMode { - INSERT, DELETE, UPDATE, CLEAR, DROP, ALTER, OTHER; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.math.*; +import java.net.URL; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.*; +import java.util.stream.Stream; +import javax.annotation.Resource; +import static org.redkale.boot.Application.*; +import org.redkale.net.AsyncGroup; +import org.redkale.service.*; +import static org.redkale.source.DataSources.*; +import org.redkale.source.EntityInfo.EntityColumn; +import org.redkale.util.*; + +/** + * DataSource的SQL抽象实现类
    + * 注意: 所有的操作只能作用在一张表上,不能同时变更多张表 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Local +@AutoLoad(false) +@SuppressWarnings("unchecked") +@ResourceType(DataSource.class) +public abstract class DataSqlSource extends AbstractDataSource implements Function { + + protected static final Flipper FLIPPER_ONE = new Flipper(1); + + protected final Logger logger = Logger.getLogger(this.getClass().getSimpleName()); + + protected String name; + + protected URL persistFile; + + protected boolean cacheForbidden; + + private final String dbtype; + + private final boolean autoddl; + + protected Properties readConfProps; + + protected Properties writeConfProps; + + @Resource(name = RESNAME_APP_ASYNCGROUP) + protected AsyncGroup asyncGroup; + + @Resource(name = RESNAME_APP_EXECUTOR) + protected ExecutorService workExecutor; + + protected final BiFunction sqlFormatter; + + protected BiConsumer futureCompleteConsumer = (r, t) -> { + if (t != null) logger.log(Level.INFO, "CompletableFuture complete error", (Throwable) t); + }; + + protected final BiFunction> fullloader = (s, i) + -> ((CompletableFuture) querySheetDB(i, false, false, false, null, null, (FilterNode) null)).thenApply(e -> e == null ? new ArrayList() : e.list(true)); + + //用于反向LIKE使用 + protected final String containSQL; + + //用于反向LIKE使用 + protected final String notcontainSQL; + + //用于判断表不存在的使用, 多个SQLState用;隔开 + protected final String tablenotexistSqlstates; + + //用于复制表结构使用 + protected final String tablecopySQL; + + @SuppressWarnings({"OverridableMethodCallInConstructor", "LeakingThisInConstructor"}) + public DataSqlSource(String unitName, URL persistFile, String dbtype, Properties readConf, Properties writeConf) { + if (readConf == null) readConf = new Properties(); + if (writeConf == null) writeConf = readConf; + this.dbtype = dbtype; + initProperties(readConf); + if (writeConf != readConf) initProperties(writeConf); + this.name = unitName; + this.persistFile = persistFile; + this.readConfProps = readConf; + this.writeConfProps = writeConf; + this.sqlFormatter = (info, val) -> formatValueToString(info, val); + this.autoddl = "true".equals(readConf.getProperty(DataSources.JDBC_TABLE_AUTODDL, "false").trim()); + + this.containSQL = readConf.getProperty(DataSources.JDBC_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0"); + this.notcontainSQL = readConf.getProperty(DataSources.JDBC_NOTCONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) = 0"); + + this.tablenotexistSqlstates = ";" + readConf.getProperty(DataSources.JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02") + ";"; + this.tablecopySQL = readConf.getProperty(DataSources.JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} LIKE ${oldtable}"); + } + + protected void initProperties(Properties props) { + if ("oracle".equals(this.dbtype)) { + props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0"); + props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) = 0"); + if (!props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) { + props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02"); + } + if (!props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) { + //注意:此语句复制表结构会导致默认值和主键信息的丢失 + props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} AS SELECT * FROM ${oldtable} WHERE 1=2"); + } + } else if ("sqlserver".equals(this.dbtype)) { + props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0"); + props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0"); + } else if ("postgresql".equals(this.dbtype)) { + if (!props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) { //注意:此语句复制表结构会导致默认值和主键信息的丢失 + //注意:postgresql不支持跨库复制表结构 + //props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS (SELECT * FROM ${oldtable} LIMIT 0)"); + props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE IF NOT EXISTS ${newtable} (LIKE ${oldtable} INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING INDEXES)"); + } + if (!props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) { + props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42P01;3F000"); + } + } + } + + //生成创建表的SQL + protected String createTableSql(EntityInfo info) { + if (info == null || !autoddl) return null; + javax.persistence.Table table = info.getType().getAnnotation(javax.persistence.Table.class); + if ("mysql".equals(dbtype())) { //mysql + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TABLE IF NOT EXISTS `").append(info.getOriginTable()).append("`(\n"); + EntityColumn primary = null; + T one = info.constructorAttributes == null ? info.getCreator().create() : null; + for (EntityColumn column : info.getDDLColumns()) { + if (column.primary) primary = column; + String sqltype = "VARCHAR(" + column.length + ")"; + String sqlnull = column.primary ? "NOT NULL" : "NULL"; + if (column.type == boolean.class || column.type == Boolean.class) { + sqltype = "TINYINT(1)"; + Boolean val = one == null ? null : (Boolean) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val != null && val ? 1 : 0); + } else if (column.type == byte.class || column.type == Byte.class) { + sqltype = "TINYINT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.byteValue()); + } else if (column.type == short.class || column.type == Short.class) { + sqltype = "SMALLINT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == char.class || column.type == Character.class) { + sqltype = "SMALLINT UNSIGNED"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.intValue()); + } else if (column.type == int.class || column.type == Integer.class || column.type == AtomicInteger.class) { + sqltype = "INT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == long.class || column.type == Long.class || column.type == AtomicLong.class || column.type == LongAdder.class) { + sqltype = "BIGINT"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == float.class || column.type == Float.class) { + sqltype = "FLOAT"; + if (column.precision > 0) sqltype += "(" + column.precision + "," + column.scale + ")"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == double.class || column.type == Double.class) { + sqltype = "DOUBLE"; + if (column.precision > 0) sqltype += "(" + column.precision + "," + column.scale + ")"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigInteger.class) { + sqltype = "DECIMAL"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } else { + sqltype += "(19,2)"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigDecimal.class) { + sqltype = "DECIMAL"; + if (column.precision > 0) sqltype += "(" + column.precision + "," + column.scale + ")"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == String.class) { + if (column.length < 65535) { + String val = one == null ? null : (String) info.getAttribute(column.field).get(one); + if (val != null) { + sqlnull = "NOT NULL DEFAULT '" + val.replace('\'', '"') + "'"; + } else if (column.primary) { + sqlnull = "NOT NULL DEFAULT ''"; + } + } else if (column.length == 65535) { + sqltype = "TEXT"; + if (!column.nullable) sqlnull = "NOT NULL"; + } else if (column.length <= 16777215) { + sqltype = "MEDIUMTEXT"; + if (!column.nullable) sqlnull = "NOT NULL"; + } else { + sqltype = "LONGTEXT"; + if (!column.nullable) sqlnull = "NOT NULL"; + } + } else if (column.type == byte[].class) { + if (column.length <= 65535) { + sqltype = "BLOB"; + if (!column.nullable) sqlnull = "NOT NULL"; + } else if (column.length <= 16777215) { + sqltype = "MEDIUMBLOB"; + if (!column.nullable) sqlnull = "NOT NULL"; + } else { + sqltype = "LONGBLOB"; + if (!column.nullable) sqlnull = "NOT NULL"; + } + } else if (column.type == java.time.LocalDate.class || column.type == java.util.Date.class || "java.sql.Date".equals(column.type.getName())) { + sqltype = "DATE"; + } else if (column.type == java.time.LocalTime.class || "java.sql.Time".equals(column.type.getName())) { + sqltype = "TIME"; + } else if (column.type == java.time.LocalDateTime.class || "java.sql.Timestamp".equals(column.type.getName())) { + sqltype = "DATETIME"; + } else { //JavaBean + sqltype = column.length >= 65535 ? "TEXT" : ("VARCHAR(" + column.length + ")"); + sqlnull = !column.nullable ? "NOT NULL DEFAULT ''" : "NULL"; + } + sb.append(" `").append(column.column).append("` ").append(sqltype).append(" ").append(sqlnull); + if (column.comment != null && !column.comment.isEmpty()) { + sb.append(" COMMENT '").append(column.comment.replace('\'', '"')).append("'"); + } + sb.append(",\n"); + } + sb.append(" PRIMARY KEY (`").append(primary.column).append("`)\n"); + sb.append(")ENGINE=InnoDB DEFAULT CHARSET=UTF8MB4"); + if (table != null && !table.comment().isEmpty()) { + sb.append(" COMMENT '").append(table.comment().replace('\'', '"')).append("'"); + } + return sb.toString(); + } else if ("postgresql".equals(dbtype())) { //postgresql + StringBuilder sb = new StringBuilder(); + sb.append("CREATE TABLE IF NOT EXISTS ").append(info.getOriginTable()).append("(\n"); + EntityColumn primary = null; + T one = info.constructorAttributes == null ? info.getCreator().create() : null; + for (EntityColumn column : info.getDDLColumns()) { + if (column.primary) primary = column; + String sqltype = "VARCHAR(" + column.length + ")"; + String sqlnull = column.primary ? "NOT NULL" : "NULL"; + if (column.type == boolean.class || column.type == Boolean.class) { + sqltype = "BOOL"; + Boolean val = one == null ? null : (Boolean) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val != null && val ? 1 : 0); + } else if (column.type == byte.class || column.type == Byte.class) { + sqltype = "INT2"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.byteValue()); + } else if (column.type == short.class || column.type == Short.class) { + sqltype = "INT2"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == char.class || column.type == Character.class) { + sqltype = "INT2 UNSIGNED"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val.intValue()); + } else if (column.type == int.class || column.type == Integer.class || column.type == AtomicInteger.class) { + sqltype = "INT4"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == long.class || column.type == Long.class || column.type == AtomicLong.class || column.type == LongAdder.class) { + sqltype = "INT8"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == float.class || column.type == Float.class) { + sqltype = "FLOAT4"; + if (column.precision > 0) sqltype += "(" + column.precision + "," + column.scale + ")"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == double.class || column.type == Double.class) { + sqltype = "FLOAT8"; + if (column.precision > 0) sqltype += "(" + column.precision + "," + column.scale + ")"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigInteger.class) { + sqltype = "NUMERIC"; + if (column.precision > 0) { + sqltype += "(" + column.precision + "," + column.scale + ")"; + } else { + sqltype += "(19,2)"; + } + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == BigDecimal.class) { + sqltype = "NUMERIC"; + if (column.precision > 0) sqltype += "(" + column.precision + "," + column.scale + ")"; + Number val = one == null ? null : (Number) info.getAttribute(column.field).get(one); + sqlnull = "NOT NULL DEFAULT " + (val == null ? 0 : val); + } else if (column.type == String.class) { + if (column.length < 65535) { + String val = one == null ? null : (String) info.getAttribute(column.field).get(one); + if (val != null) { + sqlnull = "NOT NULL DEFAULT '" + val.replace('\'', '"') + "'"; + } else if (column.primary) { + sqlnull = "NOT NULL DEFAULT ''"; + } + } else { + sqltype = "TEXT"; + if (!column.nullable) sqlnull = "NOT NULL"; + } + } else if (column.type == byte[].class) { + sqltype = "OID"; + if (!column.nullable) sqlnull = "NOT NULL"; + } else if (column.type == java.time.LocalDate.class || column.type == java.util.Date.class || "java.sql.Date".equals(column.type.getName())) { + sqltype = "DATE"; + } else if (column.type == java.time.LocalTime.class || "java.sql.Time".equals(column.type.getName())) { + sqltype = "TIME"; + } else if (column.type == java.time.LocalDateTime.class || "java.sql.Timestamp".equals(column.type.getName())) { + sqltype = "TIMESTAMP"; + } else { //JavaBean + sqltype = column.length >= 65535 ? "TEXT" : ("VARCHAR(" + column.length + ")"); + sqlnull = !column.nullable ? "NOT NULL DEFAULT ''" : "NULL"; + } + sb.append(" ").append(column.column).append(" ").append(sqltype).append(" ").append(sqlnull); + if (column.comment != null && !column.comment.isEmpty()) { + //postgresql不支持DDL中带comment + } + sb.append(",\n"); + } + sb.append(" PRIMARY KEY (").append(primary.column).append(")\n"); + sb.append(")"); + return sb.toString(); + } + return null; + } + + @Local + protected boolean isTableNotExist(EntityInfo info, String code) { + if (code == null || code.isEmpty()) return false; + return tablenotexistSqlstates.contains(';' + code + ';'); + } + + @Local + protected String getTableCopySQL(EntityInfo info, String newTable) { + return tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", info.table); + } + + @Override + public void init(AnyValue conf) { + this.cacheForbidden = "NONE".equalsIgnoreCase(readConfProps.getProperty(JDBC_CACHE_MODE)); + } + + @Override + public void destroy(AnyValue config) { + super.destroy(config); + } + + @Override + @Local + public void compile(Class clazz) { + EntityInfo.compile(clazz, this); + } + + @Local + public final String dbtype() { + if (dbtype == null) throw new NullPointerException("dbtype is null"); + return dbtype; + } + + @Local + public final boolean autoddl() { + return autoddl; + } + + @Local + public abstract int directExecute(String sql); + + @Local + public abstract int[] directExecute(String... sqls); + + @Local + public abstract V directQuery(String sql, Function handler); + + //是否异步 + protected abstract boolean isAsync(); + + //index从1开始 + protected abstract String prepareParamSign(int index); + + //插入纪录 + protected abstract CompletableFuture insertDB(final EntityInfo info, T... entitys); + + //删除记录 + protected abstract CompletableFuture deleteDB(final EntityInfo info, Flipper flipper, final String sql); + + //清空表 + protected abstract CompletableFuture clearTableDB(final EntityInfo info, final String table, final String sql); + + //删除表 + protected abstract CompletableFuture dropTableDB(final EntityInfo info, final String table, final String sql); + + //更新纪录 + protected abstract CompletableFuture updateEntityDB(final EntityInfo info, T... entitys); + + //更新纪录 + protected abstract CompletableFuture updateColumnDB(final EntityInfo info, Flipper flipper, final String sql, final boolean prepared, Object... params); + + //查询Number Map数据 + protected abstract CompletableFuture> getNumberMapDB(final EntityInfo info, final String sql, final FilterFuncColumn... columns); + + //查询Number数据 + protected abstract CompletableFuture getNumberResultDB(final EntityInfo info, final String sql, final Number defVal, final String column); + + //查询Map数据 + protected abstract CompletableFuture> queryColumnMapDB(final EntityInfo info, final String sql, final String keyColumn); + + //查询Map数据 + protected abstract CompletableFuture> queryColumnMapDB(final EntityInfo info, final String sql, final ColumnNode[] funcNodes, final String[] groupByColumns); + + //查询单条记录 + protected abstract CompletableFuture findDB(final EntityInfo info, final String sql, final boolean onlypk, final SelectColumn selects); + + //查询单条记录的单个字段 + protected abstract CompletableFuture findColumnDB(final EntityInfo info, final String sql, final boolean onlypk, final String column, final Serializable defValue); + + //判断记录是否存在 + protected abstract CompletableFuture existsDB(final EntityInfo info, final String sql, final boolean onlypk); + + //查询一页数据 + protected abstract CompletableFuture> querySheetDB(final EntityInfo info, final boolean readcache, final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, final FilterNode node); + + protected CharSequence createSQLJoin(FilterNode node, final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { + return node == null ? null : node.createSQLJoin(func, update, joinTabalis, haset, info); + } + + protected CharSequence createSQLExpress(FilterNode node, final EntityInfo info, final Map joinTabalis) { + return node == null ? null : node.createSQLExpress(this, info, joinTabalis); + } + + @Local + @Override + public String getType() { + return "sql"; + } + + @Override + public final String resourceName() { + return name; + } + + @Local + @Override + public EntityInfo apply(Class t) { + return loadEntityInfo(t); + } + + @Local + @Override + public void close() throws Exception { + } + + protected EntityInfo loadEntityInfo(Class clazz) { + return loadEntityInfo(clazz, this.cacheForbidden, readConfProps, fullloader); + } + + public EntityCache loadCache(Class clazz) { + EntityInfo info = loadEntityInfo(clazz); + return info.getCache(); + } + + /** + * 将entity的对象全部加载到Cache中去,如果clazz没有被@javax.persistence.Cacheable注解则不做任何事 + * + * @param Entity类泛型 + * @param clazz Entity类 + */ + public void refreshCache(Class clazz) { + EntityInfo info = loadEntityInfo(clazz); + EntityCache cache = info.getCache(); + if (cache == null) return; + cache.fullLoadAsync(); + } + + protected CharSequence formatValueToString(final EntityInfo info, Object value) { + final String dbtype = dbtype(); + if ("mysql".equals(dbtype)) { + if (value == null) return null; + if (value instanceof CharSequence) { + return new StringBuilder().append('\'').append(value.toString().replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); + } else if (!(value instanceof Number) && !(value instanceof java.util.Date) + && !value.getClass().getName().startsWith("java.sql.") && !value.getClass().getName().startsWith("java.time.")) { + return new StringBuilder().append('\'').append(info.getJsonConvert().convertTo(value).replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); + } + return String.valueOf(value); + } else if (value != null && value instanceof CharSequence && "postgresql".equals(dbtype)) { + String s = String.valueOf(value); + int pos = s.indexOf('\''); + if (pos >= 0) return new StringBuilder().append("E'").append(value.toString().replace("\\", "\\\\").replace("'", "\\'")).append('\'').toString(); + } + return info.formatSQLValue(value, null); + } + + //----------------------------- insert ----------------------------- + /** + * 新增对象, 必须是Entity对象 + * + * @param Entity类泛型 + * @param entitys Entity对象 + * + * @return 影响的记录条数 + */ + @Override + public int insert(T... entitys) { + if (entitys.length == 0) return 0; + checkEntity("insert", false, entitys); + final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); + if (isOnlyCache(info)) return insertCache(info, entitys); + return insertDB(info, entitys).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + insertCache(info, entitys); + } + }).join(); + } + + @Override + public CompletableFuture insertAsync(T... entitys) { + if (entitys.length == 0) return CompletableFuture.completedFuture(0); + CompletableFuture future = checkEntity("insert", true, entitys); + if (future != null) return future; + final EntityInfo info = loadEntityInfo((Class) entitys[0].getClass()); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(insertCache(info, entitys)); + } + if (isAsync()) return insertDB(info, entitys).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + insertCache(info, entitys); + } + }); + return CompletableFuture.supplyAsync(() -> insertDB(info, entitys).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + insertCache(info, entitys); + } + }); + } + + protected int insertCache(final EntityInfo info, T... entitys) { + final EntityCache cache = info.getCache(); + if (cache == null) return 0; + int c = 0; + for (final T value : entitys) { + c += cache.insert(value); + } + return c; + } + + //----------------------------- deleteCompose ----------------------------- + /** + * 删除对象, 必须是Entity对象 + * + * @param Entity类泛型 + * @param entitys Entity对象 + * + * @return 删除的数据条数 + */ + @Override + public int delete(T... entitys) { + if (entitys.length == 0) return -1; + checkEntity("delete", false, entitys); + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Serializable[] ids = new Serializable[entitys.length]; + int i = 0; + for (final T value : entitys) { + ids[i++] = (Serializable) primary.get(value); + } + return delete(clazz, ids); + } + + @Override + public CompletableFuture deleteAsync(final T... entitys) { + if (entitys.length == 0) return CompletableFuture.completedFuture(-1); + CompletableFuture future = checkEntity("delete", true, entitys); + if (future != null) return future; + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Serializable[] ids = new Serializable[entitys.length]; + int i = 0; + for (final T value : entitys) { + ids[i++] = (Serializable) primary.get(value); + } + return deleteAsync(clazz, ids); + } + + @Override + public int delete(Class clazz, Serializable... pks) { + if (pks.length == 0) return -1; + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return deleteCache(info, -1, pks); + return deleteCompose(info, pks).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, pks); + } + }).join(); + } + + @Override + public CompletableFuture deleteAsync(final Class clazz, final Serializable... pks) { + if (pks.length == 0) return CompletableFuture.completedFuture(-1); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(deleteCache(info, -1, pks)); + } + if (isAsync()) return deleteCompose(info, pks).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, pks); + } + }); + return CompletableFuture.supplyAsync(() -> deleteCompose(info, pks).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, pks); + } + }); + } + + @Override + public int delete(Class clazz, FilterNode node) { + return delete(clazz, (Flipper) null, node); + } + + @Override + public CompletableFuture deleteAsync(final Class clazz, final FilterNode node) { + return deleteAsync(clazz, (Flipper) null, node); + } + + @Override + public int delete(Class clazz, final Flipper flipper, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return deleteCache(info, -1, flipper, node); + return this.deleteCompose(info, flipper, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, flipper, node); + } + }).join(); + } + + @Override + public CompletableFuture deleteAsync(final Class clazz, final Flipper flipper, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(deleteCache(info, -1, flipper, node)); + } + if (isAsync()) return this.deleteCompose(info, flipper, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, flipper, node); + } + }); + return CompletableFuture.supplyAsync(() -> this.deleteCompose(info, flipper, node).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + deleteCache(info, rs, flipper, node); + } + }); + } + + protected CompletableFuture deleteCompose(final EntityInfo info, final Serializable... pks) { + if (pks.length == 1) { + String sql = "DELETE FROM " + info.getTable(pks[0]) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pks[0], sqlFormatter); + return deleteDB(info, null, sql); + } + String sql = "DELETE FROM " + info.getTable(pks[0]) + " WHERE " + info.getPrimarySQLColumn() + " IN ("; + for (int i = 0; i < pks.length; i++) { + if (i > 0) sql += ','; + sql += info.formatSQLValue(info.getPrimarySQLColumn(), pks[i], sqlFormatter); + } + sql += ")"; + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql); + return deleteDB(info, null, sql); + } + + protected CompletableFuture deleteCompose(final EntityInfo info, final Flipper flipper, final FilterNode node) { + Map joinTabalis = null; + CharSequence join = null; + CharSequence where = null; + if (node != null) { + joinTabalis = node.getJoinTabalis(); + join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + where = node.createSQLExpress(this, info, joinTabalis); + } + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + String sql = "DELETE " + ("mysql".equals(dbtype()) ? "a" : "") + " FROM " + info.getTable(node) + " a" + (join1 == null ? "" : (", " + join1)) + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + info.createSQLOrderby(flipper); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" + + (sql + ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit())))); + return deleteDB(info, flipper, sql); + } + + //----------------------------- clearTableCompose ----------------------------- + @Override + public int clearTable(Class clazz) { + return clearTable(clazz, (FilterNode) null); + } + + @Override + public int clearTable(Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return clearTableCache(info, node); + return this.clearTableCompose(info, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + clearTableCache(info, node); + } + }).join(); + } + + @Override + public CompletableFuture clearTableAsync(final Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(clearTableCache(info, node)); + } + if (isAsync()) return this.clearTableCompose(info, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + clearTableCache(info, node); + } + }); + return CompletableFuture.supplyAsync(() -> this.clearTableCompose(info, node).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + clearTableCache(info, node); + } + }); + } + + protected CompletableFuture clearTableCompose(final EntityInfo info, final FilterNode node) { + final String table = info.getTable(node); + String sql = "TRUNCATE TABLE " + table; + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " clearTable sql=" + sql); + return clearTableDB(info, table, sql); + } + + //----------------------------- dropTableCompose ----------------------------- + @Override + public int dropTable(Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return dropTableCache(info, node); + return this.dropTableCompose(info, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + dropTableCache(info, node); + } + }).join(); + } + + @Override + public CompletableFuture dropTableAsync(final Class clazz, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(dropTableCache(info, node)); + } + if (isAsync()) return this.dropTableCompose(info, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + dropTableCache(info, node); + } + }); + return CompletableFuture.supplyAsync(() -> this.dropTableCompose(info, node).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + dropTableCache(info, node); + } + }); + } + + protected CompletableFuture dropTableCompose(final EntityInfo info, final FilterNode node) { + final String table = info.getTable(node); + String sql = "DROP TABLE " + table; + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " dropTable sql=" + sql); + return dropTableDB(info, table, sql); + } + + protected int clearTableCache(final EntityInfo info, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) return -1; + return cache.clear(); + } + + protected int dropTableCache(final EntityInfo info, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) return -1; + return cache.drop(); + } + + protected int deleteCache(final EntityInfo info, int count, Flipper flipper, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) return -1; + Serializable[] ids = cache.delete(flipper, node); + return count >= 0 ? count : (ids == null ? 0 : ids.length); + } + + protected int deleteCache(final EntityInfo info, int count, Serializable... pks) { + final EntityCache cache = info.getCache(); + if (cache == null) return -1; + int c = 0; + for (Serializable key : pks) { + c += cache.delete(key); + } + return count >= 0 ? count : c; + } + + protected static StringBuilder multisplit(char ch1, char ch2, String split, StringBuilder sb, String str, int from) { + if (str == null) return sb; + int pos1 = str.indexOf(ch1, from); + if (pos1 < 0) return sb; + int pos2 = str.indexOf(ch2, from); + if (pos2 < 0) return sb; + if (sb.length() > 0) sb.append(split); + sb.append(str.substring(pos1 + 1, pos2)); + return multisplit(ch1, ch2, split, sb, str, pos2 + 1); + } + + //---------------------------- update ---------------------------- + /** + * 更新对象, 必须是Entity对象 + * + * @param Entity类泛型 + * @param entitys Entity对象 + * + * @return 更新的数据条数 + */ + @Override + public int update(T... entitys) { + if (entitys.length == 0) return -1; + checkEntity("update", false, entitys); + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return updateCache(info, -1, entitys); + return updateEntityDB(info, entitys).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, entitys); + } + }).join(); + } + + @Override + public CompletableFuture updateAsync(final T... entitys) { + if (entitys.length == 0) return CompletableFuture.completedFuture(-1); + CompletableFuture future = checkEntity("update", true, entitys); + if (future != null) return future; + final Class clazz = (Class) entitys[0].getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, entitys)); + } + if (isAsync()) return updateEntityDB(info, entitys).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, entitys); + } + }); + return CompletableFuture.supplyAsync(() -> updateEntityDB(info, entitys).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, entitys); + } + }); + } + + /** + * 根据主键值更新对象的column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param pk 主键值 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * + * @return 更新的数据条数 + */ + @Override + public int updateColumn(Class clazz, Serializable pk, String column, Serializable colval) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return updateCache(info, -1, pk, column, colval); + return updateColumnCompose(info, pk, column, colval).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, column, colval); + } + }).join(); + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final String column, final Serializable colval) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, pk, column, colval)); + } + if (isAsync()) return updateColumnCompose(info, pk, column, colval).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, column, colval); + } + }); + return CompletableFuture.supplyAsync(() -> updateColumnCompose(info, pk, column, colval).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, column, colval); + } + }); + } + + protected CompletableFuture updateColumnCompose(final EntityInfo info, Serializable pk, String column, final Serializable colval) { + if (colval instanceof byte[]) { + String sql = "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" + prepareParamSign(1) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + return updateColumnDB(info, null, sql, true, colval); + } else { + String sql = "UPDATE " + info.getTable(pk) + " SET " + info.getSQLColumn(null, column) + "=" + + info.formatSQLValue(column, colval, sqlFormatter) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + return updateColumnDB(info, null, sql, false); + } + } + + /** + * 根据主键值更新对象的column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param column 过滤字段名 + * @param colval 过滤字段值 + * @param node 过滤node 不能为null + * + * @return 更新的数据条数 + */ + @Override + public int updateColumn(Class clazz, String column, Serializable colval, FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return updateCache(info, -1, column, colval, node); + return this.updateColumnCompose(info, column, colval, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, column, colval, node); + } + }).join(); + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final String column, final Serializable colval, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, column, colval, node)); + } + if (isAsync()) return this.updateColumnCompose(info, column, colval, node).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, column, colval, node); + } + }); + return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, column, colval, node).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, column, colval, node); + } + }); + } + + protected CompletableFuture updateColumnCompose(final EntityInfo info, final String column, final Serializable colval, final FilterNode node) { + Map joinTabalis = node.getJoinTabalis(); + CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + CharSequence where = node.createSQLExpress(this, info, joinTabalis); + + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + String alias = "postgresql".equals(dbtype()) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 + if (colval instanceof byte[]) { + String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + + " SET " + info.getSQLColumn(alias, column) + "=" + prepareParamSign(1) + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + return updateColumnDB(info, null, sql, true, colval); + } else { + String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + + " SET " + info.getSQLColumn(alias, column) + "=" + info.formatSQLValue(colval, sqlFormatter) + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + return updateColumnDB(info, null, sql, false); + } + } + + /** + * 根据主键值更新对象的多个column对应的值, 必须是Entity Class + * + * @param Entity类的泛型 + * @param clazz Entity类 + * @param pk 主键值 + * @param values 字段值 + * + * @return 更新的数据条数 + */ + @Override + public int updateColumn(final Class clazz, final Serializable pk, final ColumnValue... values) { + if (values == null || values.length < 1) return -1; + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return updateCache(info, -1, pk, values); + return this.updateColumnCompose(info, pk, values).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, values); + } + }).join(); + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final Serializable pk, final ColumnValue... values) { + if (values == null || values.length < 1) return CompletableFuture.completedFuture(-1); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, pk, values)); + } + if (isAsync()) return this.updateColumnCompose(info, pk, values).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, values); + } + }); + return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, pk, values).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, pk, values); + } + }); + } + + protected CompletableFuture updateColumnCompose(final EntityInfo info, final Serializable pk, final ColumnValue... values) { + StringBuilder setsql = new StringBuilder(); + List blobs = null; + int index = 0; + for (ColumnValue col : values) { + if (col == null) continue; + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) throw new RuntimeException(info.getType() + " cannot found column " + col.getColumn()); + if (setsql.length() > 0) setsql.append(", "); + String sqlColumn = info.getSQLColumn(null, col.getColumn()); + if (col.getValue() instanceof byte[]) { + if (blobs == null) blobs = new ArrayList<>(); + blobs.add((byte[]) col.getValue()); + setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); + } else { + setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); + } + } + if (setsql.length() < 1) return CompletableFuture.completedFuture(0); + String sql = "UPDATE " + info.getTable(pk) + " SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + if (blobs == null) return updateColumnDB(info, null, sql, false); + return updateColumnDB(info, null, sql, true, blobs.toArray()); + } + + @Override + public int updateColumn(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + if (values == null || values.length < 1) return -1; + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return updateCache(info, -1, node, flipper, values); + return this.updateColumnCompose(info, node, flipper, values).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, node, flipper, values); + } + }).join(); + } + + @Override + public CompletableFuture updateColumnAsync(final Class clazz, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + if (values == null || values.length < 1) return CompletableFuture.completedFuture(-1); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, node, flipper, values)); + } + if (isAsync()) return this.updateColumnCompose(info, node, flipper, values).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, node, flipper, values); + } + }); + return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, node, flipper, values).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, node, flipper, values); + } + }); + } + + protected CompletableFuture updateColumnCompose(final EntityInfo info, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + StringBuilder setsql = new StringBuilder(); + List blobs = null; + int index = 0; + String alias = "postgresql".equals(dbtype()) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 + for (ColumnValue col : values) { + if (col == null) continue; + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) continue; + if (setsql.length() > 0) setsql.append(", "); + String sqlColumn = info.getSQLColumn(alias, col.getColumn()); + if (col.getValue() instanceof byte[]) { + if (blobs == null) blobs = new ArrayList<>(); + blobs.add((byte[]) col.getValue()); + setsql.append(sqlColumn).append("=").append(prepareParamSign(++index)); + } else { + setsql.append(sqlColumn).append("=").append(info.formatSQLValue(sqlColumn, attr, col, sqlFormatter)); + } + } + if (setsql.length() < 1) return CompletableFuture.completedFuture(0); + Map joinTabalis = node == null ? null : node.getJoinTabalis(); + CharSequence join = node == null ? null : node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))) + + info.createSQLOrderby(flipper); + if (blobs == null) return updateColumnDB(info, null, sql, false); + return updateColumnDB(info, flipper, sql, true, blobs.toArray()); + } + + @Override + public int updateColumn(final T entity, final SelectColumn selects) { + if (entity == null || selects == null) return -1; + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return updateCache(info, -1, false, entity, null, selects); + return this.updateColumnCompose(info, false, entity, null, selects).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, false, entity, null, selects); + } + }).join(); + } + + @Override + public CompletableFuture updateColumnAsync(final T entity, final SelectColumn selects) { + if (entity == null || selects == null) return CompletableFuture.completedFuture(-1); + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, false, entity, null, selects)); + } + if (isAsync()) return this.updateColumnCompose(info, false, entity, null, selects).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, false, entity, null, selects); + } + }); + return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, false, entity, null, selects).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, false, entity, null, selects); + } + }); + } + + @Override + public int updateColumn(final T entity, final FilterNode node, final SelectColumn selects) { + if (entity == null || node == null || selects == null) return -1; + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) return updateCache(info, -1, true, entity, node, selects); + return this.updateColumnCompose(info, true, entity, node, selects).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, true, entity, node, selects); + } + }).join(); + } + + @Override + public CompletableFuture updateColumnAsync(final T entity, final FilterNode node, final SelectColumn selects) { + if (entity == null || node == null || selects == null) return CompletableFuture.completedFuture(-1); + Class clazz = (Class) entity.getClass(); + final EntityInfo info = loadEntityInfo(clazz); + if (isOnlyCache(info)) { + return CompletableFuture.completedFuture(updateCache(info, -1, true, entity, node, selects)); + } + if (isAsync()) return this.updateColumnCompose(info, true, entity, node, selects).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, true, entity, node, selects); + } + }); + return CompletableFuture.supplyAsync(() -> this.updateColumnCompose(info, true, entity, node, selects).join(), getExecutor()).whenComplete((rs, t) -> { + if (t != null) { + futureCompleteConsumer.accept(rs, t); + } else { + updateCache(info, rs, true, entity, node, selects); + } + }); + } + + protected CompletableFuture updateColumnCompose(final EntityInfo info, final boolean neednode, final T entity, final FilterNode node, final SelectColumn selects) { + StringBuilder setsql = new StringBuilder(); + List blobs = null; + int index = 0; + String alias = "postgresql".equals(dbtype()) ? null : "a"; //postgresql的BUG, UPDATE的SET中不能含别名 + for (Attribute attr : info.updateAttributes) { + if (!selects.test(attr.field())) continue; + if (setsql.length() > 0) setsql.append(", "); + setsql.append(info.getSQLColumn(alias, attr.field())); + Serializable val = info.getFieldValue(attr, entity); + if (val instanceof byte[]) { + if (blobs == null) blobs = new ArrayList<>(); + blobs.add((byte[]) val); + setsql.append("=").append(prepareParamSign(++index)); + } else { + CharSequence sqlval = info.formatSQLValue(val, sqlFormatter); + if (sqlval == null && info.isNotNullJson(attr)) sqlval = "''"; + setsql.append("=").append(sqlval); + } + } + if (neednode) { + Map joinTabalis = node.getJoinTabalis(); + CharSequence join = node.createSQLJoin(this, true, joinTabalis, new HashSet<>(), info); + CharSequence where = node.createSQLExpress(this, info, joinTabalis); + StringBuilder join1 = null; + StringBuilder join2 = null; + if (join != null) { + String joinstr = join.toString(); + join1 = multisplit('[', ']', ",", new StringBuilder(), joinstr, 0); + join2 = multisplit('{', '}', " AND ", new StringBuilder(), joinstr, 0); + } + String sql = "UPDATE " + info.getTable(node) + " a " + (join1 == null ? "" : (", " + join1)) + " SET " + setsql + + ((where == null || where.length() == 0) ? (join2 == null ? "" : (" WHERE " + join2)) + : (" WHERE " + where + (join2 == null ? "" : (" AND " + join2)))); + if (blobs == null) return updateColumnDB(info, null, sql, false); + return updateColumnDB(info, null, sql, true, blobs.toArray()); + } else { + final Serializable id = (Serializable) info.getSQLValue(info.getPrimary(), entity); + String sql = "UPDATE " + info.getTable(id) + " a SET " + setsql + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(id, sqlFormatter); + if (blobs == null) return updateColumnDB(info, null, sql, false); + return updateColumnDB(info, null, sql, true, blobs.toArray()); + } + } + + protected int updateCache(final EntityInfo info, int count, final boolean neednode, final T entity, final FilterNode node, final SelectColumn selects) { + final EntityCache cache = info.getCache(); + if (cache == null) return count; + final List> attrs = new ArrayList<>(); + for (Attribute attr : info.updateAttributes) { + if (!selects.test(attr.field())) continue; + attrs.add(attr); + } + if (neednode) { + T[] rs = cache.update(entity, attrs, node); + return count >= 0 ? count : (rs == null ? 0 : rs.length); + } else { + T rs = cache.update(entity, attrs); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + } + + protected int updateCache(final EntityInfo info, int count, final FilterNode node, final Flipper flipper, final ColumnValue... values) { + final EntityCache cache = info.getCache(); + if (cache == null) return count; + final List> attrs = new ArrayList<>(); + final List cols = new ArrayList<>(); + for (ColumnValue col : values) { + if (col == null) continue; + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) continue; + attrs.add(attr); + cols.add(col); + } + T[] rs = cache.updateColumn(node, flipper, attrs, cols); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, final Serializable pk, final ColumnValue... values) { + final EntityCache cache = info.getCache(); + if (cache == null) return count; + final List> attrs = new ArrayList<>(); + final List cols = new ArrayList<>(); + for (ColumnValue col : values) { + if (col == null) continue; + Attribute attr = info.getUpdateAttribute(col.getColumn()); + if (attr == null) continue; + attrs.add(attr); + cols.add(col); + } + T rs = cache.updateColumn(pk, attrs, cols); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, String column, final Serializable colval, FilterNode node) { + final EntityCache cache = info.getCache(); + if (cache == null) return count; + T[] rs = cache.update(info.getAttribute(column), colval, node); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, final Serializable pk, final String column, final Serializable colval) { + final EntityCache cache = info.getCache(); + if (cache == null) return count; + T rs = cache.update(pk, info.getAttribute(column), colval); + return count >= 0 ? count : (rs == null ? 0 : 1); + } + + protected int updateCache(final EntityInfo info, int count, T... entitys) { + final EntityCache cache = info.getCache(); + if (cache == null) return -1; + int c2 = 0; + for (final T value : entitys) { + c2 += cache.update(value); + } + return count >= 0 ? count : c2; + } + + public int reloadCache(Class clazz, Serializable... pks) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache == null) return -1; + String column = info.getPrimary().field(); + int c = 0; + for (Serializable id : pks) { + Sheet sheet = querySheetCompose(false, true, false, clazz, null, FLIPPER_ONE, FilterNode.create(column, id)).join(); + T value = sheet.isEmpty() ? null : sheet.list().get(0); + if (value != null) c += cache.update(value); + } + return c; + } + + //------------------------- getNumberMapCompose ------------------------- + @Override + public Map getNumberMap(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + final Map map = new HashMap<>(); + if (node == null || isCacheUseable(node, this)) { + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + map.put(ffc.col(col), cache.getNumberResult(ffc.func, ffc.defvalue, col, node)); + } + } + return map; + } + } + return (Map) getNumberMapCompose(info, node, columns).join(); + } + + @Override + public CompletableFuture> getNumberMapAsync(final Class entityClass, final FilterNode node, final FilterFuncColumn... columns) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + final Map map = new HashMap<>(); + if (node == null || isCacheUseable(node, this)) { + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + map.put(ffc.col(col), cache.getNumberResult(ffc.getFunc(), ffc.getDefvalue(), col, node)); + } + } + return CompletableFuture.completedFuture(map); + } + } + if (isAsync()) return getNumberMapCompose(info, node, columns); + return CompletableFuture.supplyAsync(() -> (Map) getNumberMapCompose(info, node, columns).join(), getExecutor()); + } + + protected CompletableFuture> getNumberMapCompose(final EntityInfo info, final FilterNode node, final FilterFuncColumn... columns) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + StringBuilder sb = new StringBuilder(); + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + if (sb.length() > 0) sb.append(", "); + sb.append(ffc.func.getColumn((col == null || col.isEmpty() ? "*" : info.getSQLColumn("a", col)))); + } + } + final String sql = "SELECT " + sb + " FROM " + info.getTable(node) + " a" + + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " getnumbermap sql=" + sql); + return getNumberMapDB(info, sql, columns); + } + + @Local + protected CompletableFuture> getNumberMapDBApply(EntityInfo info, CompletableFuture future, FilterFuncColumn... columns) { + return future.thenApply((DataResultSet dataset) -> { + final Map map = new HashMap<>(); + if (dataset.next()) { + int index = 0; + for (FilterFuncColumn ffc : columns) { + for (String col : ffc.cols()) { + Object o = dataset.getObject(++index); + Number rs = ffc.getDefvalue(); + if (o != null) rs = (Number) o; + map.put(ffc.col(col), rs); + } + } + } + dataset.close(); + return map; + }); + } + + //------------------------ getNumberResultCompose ----------------------- + @Override + public Number getNumberResult(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return cache.getNumberResult(func, defVal, column, node); + } + } + return getNumberResultCompose(info, entityClass, func, defVal, column, node).join(); + } + + @Override + public CompletableFuture getNumberResultAsync(final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return CompletableFuture.completedFuture(cache.getNumberResult(func, defVal, column, node)); + } + } + if (isAsync()) return getNumberResultCompose(info, entityClass, func, defVal, column, node); + return CompletableFuture.supplyAsync(() -> getNumberResultCompose(info, entityClass, func, defVal, column, node).join(), getExecutor()); + } + + protected CompletableFuture getNumberResultCompose(final EntityInfo info, final Class entityClass, final FilterFunc func, final Number defVal, final String column, final FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + final String sql = "SELECT " + func.getColumn((column == null || column.isEmpty() ? "*" : info.getSQLColumn("a", column))) + " FROM " + info.getTable(node) + " a" + + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(entityClass.getSimpleName() + " getnumberresult sql=" + sql); + return getNumberResultDB(info, sql, defVal, column); + } + + @Local + protected CompletableFuture getNumberResultDBApply(EntityInfo info, CompletableFuture future, Number defVal, String column) { + return future.thenApply((DataResultSet dataset) -> { + Number rs = defVal; + if (dataset.next()) { + Object o = dataset.getObject(1); + if (o != null) rs = (Number) o; + } + dataset.close(); + return rs; + }); + } + + //------------------------ queryColumnMapCompose ------------------------ + @Override + public Map queryColumnMap(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return cache.queryColumnMap(keyColumn, func, funcColumn, node); + } + } + return (Map) queryColumnMapCompose(info, keyColumn, func, funcColumn, node).join(); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return CompletableFuture.completedFuture(cache.queryColumnMap(keyColumn, func, funcColumn, node)); + } + } + if (isAsync()) return queryColumnMapCompose(info, keyColumn, func, funcColumn, node); + return CompletableFuture.supplyAsync(() -> (Map) queryColumnMapCompose(info, keyColumn, func, funcColumn, node).join(), getExecutor()); + } + + protected CompletableFuture> queryColumnMapCompose(final EntityInfo info, final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + final String keySqlColumn = info.getSQLColumn(null, keyColumn); + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + final String funcSqlColumn = func == null ? info.getSQLColumn("a", funcColumn) : func.getColumn((funcColumn == null || funcColumn.isEmpty() ? "*" : info.getSQLColumn("a", funcColumn))); + final String sql = "SELECT a." + keySqlColumn + ", " + funcSqlColumn + + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + " GROUP BY a." + keySqlColumn; + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " querycolumnmap sql=" + sql); + return queryColumnMapDB(info, sql, keyColumn); + } + + @Local + protected CompletableFuture> queryColumnMapDBApply(EntityInfo info, CompletableFuture future, final String keyColumn) { + return future.thenApply((DataResultSet dataset) -> { + Map rs = new LinkedHashMap<>(); + while (dataset.next()) { + rs.put((K) dataset.getObject(1), (N) dataset.getObject(2)); + } + dataset.close(); + return rs; + }); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { + Map map = queryColumnMap(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); + final Map rs = new LinkedHashMap<>(); + map.forEach((keys, values) -> rs.put(keys[0], values)); + return rs; + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String groupByColumn, final FilterNode node) { + CompletableFuture> future = queryColumnMapAsync(entityClass, funcNodes, Utility.ofArray(groupByColumn), node); + return future.thenApply(map -> { + final Map rs = new LinkedHashMap<>(); + map.forEach((keys, values) -> rs.put(keys[0], values)); + return rs; + }); + } + + @Override + public Map queryColumnMap(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return cache.queryColumnMap(funcNodes, groupByColumns, node); + } + } + return (Map) queryColumnMapCompose(info, funcNodes, groupByColumns, node).join(); + } + + @Override + public CompletableFuture> queryColumnMapAsync(final Class entityClass, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { + final EntityInfo info = loadEntityInfo(entityClass); + final EntityCache cache = info.getCache(); + if (cache != null && (isOnlyCache(info) || cache.isFullLoaded())) { + if (node == null || isCacheUseable(node, this)) { + return CompletableFuture.completedFuture(cache.queryColumnMap(funcNodes, groupByColumns, node)); + } + } + if (isAsync()) return queryColumnMapCompose(info, funcNodes, groupByColumns, node); + return CompletableFuture.supplyAsync(() -> (Map) queryColumnMapCompose(info, funcNodes, groupByColumns, node).join(), getExecutor()); + } + + protected CompletableFuture> queryColumnMapCompose(final EntityInfo info, final ColumnNode[] funcNodes, final String[] groupByColumns, final FilterNode node) { + final StringBuilder groupBySqlColumns = new StringBuilder(); + if (groupByColumns != null && groupByColumns.length > 0) { + for (int i = 0; i < groupByColumns.length; i++) { + if (groupBySqlColumns.length() > 0) groupBySqlColumns.append(", "); + groupBySqlColumns.append(info.getSQLColumn("a", groupByColumns[i])); + } + } + final StringBuilder funcSqlColumns = new StringBuilder(); + for (int i = 0; i < funcNodes.length; i++) { + if (funcSqlColumns.length() > 0) funcSqlColumns.append(", "); + if (funcNodes[i] instanceof ColumnFuncNode) { + funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnFuncNode) funcNodes[i], sqlFormatter)); + } else { + funcSqlColumns.append(info.formatSQLValue((Attribute) null, "a", (ColumnNodeValue) funcNodes[i], sqlFormatter)); + } + } + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final Set haset = new HashSet<>(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, haset, info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + String sql = "SELECT "; + if (groupBySqlColumns.length() > 0) sql += groupBySqlColumns + ", "; + sql += funcSqlColumns + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (groupBySqlColumns.length() > 0) sql += " GROUP BY " + groupBySqlColumns; + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " querycolumnmap sql=" + sql); + return queryColumnMapDB(info, sql, funcNodes, groupByColumns); + } + + @Local + protected CompletableFuture> queryColumnMapDBApply(EntityInfo info, CompletableFuture future, final ColumnNode[] funcNodes, final String[] groupByColumns) { + return future.thenApply((DataResultSet dataset) -> { + Map rs = new LinkedHashMap<>(); + while (dataset.next()) { + int index = 0; + Serializable[] keys = new Serializable[groupByColumns.length]; + for (int i = 0; i < keys.length; i++) { + keys[i] = (Serializable) dataset.getObject(++index); + } + Number[] vals = new Number[funcNodes.length]; + for (int i = 0; i < vals.length; i++) { + vals[i] = (Number) dataset.getObject(++index); + } + rs.put(keys, vals); + } + dataset.close(); + return rs; + }); + } + + //----------------------------- find ----------------------------- + @Override + public T[] finds(Class clazz, final SelectColumn selects, Serializable... pks) { + return findsAsync(clazz, selects, pks).join(); + } + + @Override + public CompletableFuture findsAsync(Class clazz, final SelectColumn selects, Serializable... pks) { + final EntityInfo info = loadEntityInfo(clazz); + if (pks == null || pks.length == 0) return CompletableFuture.completedFuture(info.getArrayer().apply(0)); + final EntityCache cache = info.getCache(); + if (cache != null) { + T[] rs = selects == null ? cache.finds(pks) : cache.finds(selects, pks); + if (cache.isFullLoaded() || rs != null) return CompletableFuture.completedFuture(rs); + } + return findsComposeAsync(info, selects, pks); + } + + protected CompletableFuture findsComposeAsync(final EntityInfo info, final SelectColumn selects, Serializable... pks) { + final Attribute primary = info.getPrimary(); + return queryListAsync(info.getType(), selects, null, FilterNode.create(info.getPrimarySQLColumn(), FilterExpress.IN, pks)).thenApply(list -> { + T[] rs = info.getArrayer().apply(pks.length); + for (int i = 0; i < rs.length; i++) { + T t = null; + Serializable pk = pks[i]; + for (T item : list) { + if (pk.equals(primary.get(item))) { + t = item; + break; + } + } + rs[i] = t; + } + return rs; + }); + } + + @Override + public T find(Class clazz, final SelectColumn selects, Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); + if (cache.isFullLoaded() || rs != null) return rs; + } + return findCompose(info, selects, pk).join(); + } + + @Override + public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + T rs = selects == null ? cache.find(pk) : cache.find(selects, pk); + if (cache.isFullLoaded() || rs != null) return CompletableFuture.completedFuture(rs); + } + if (isAsync()) return findCompose(info, selects, pk); + return CompletableFuture.supplyAsync(() -> findCompose(info, selects, pk).join(), getExecutor()); + } + + protected CompletableFuture findCompose(final EntityInfo info, final SelectColumn selects, Serializable pk) { + String column = info.getPrimarySQLColumn(); + final String sql = "SELECT " + info.getQueryColumns(null, selects) + " FROM " + info.getTable(pk) + " WHERE " + column + "=" + info.formatSQLValue(column, pk, sqlFormatter); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + return findDB(info, sql, true, selects); + } + + @Override + public T find(final Class clazz, final SelectColumn selects, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null && cache.isFullLoaded() && (node == null || isCacheUseable(node, this))) return cache.find(selects, node); + return this.findCompose(info, selects, node).join(); + } + + @Override + public CompletableFuture findAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null && cache.isFullLoaded() && (node == null || isCacheUseable(node, this))) { + return CompletableFuture.completedFuture(cache.find(selects, node)); + } + if (isAsync()) return this.findCompose(info, selects, node); + return CompletableFuture.supplyAsync(() -> this.findCompose(info, selects, node).join(), getExecutor()); + } + + protected CompletableFuture findCompose(final EntityInfo info, final SelectColumn selects, final FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + final String sql = "SELECT " + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + return findDB(info, sql, false, selects); + } + + @Local + protected CompletableFuture findDBApply(EntityInfo info, CompletableFuture future, boolean onlypk, SelectColumn selects) { + return future.thenApply((DataResultSet pgset) -> { + T rs = pgset.next() ? (onlypk && selects == null ? getEntityValue(info, null, pgset) : getEntityValue(info, selects, pgset)) : null; + pgset.close(); + return rs; + }); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, pk); + if (cache.isFullLoaded() || val != null) return val; + } + return findColumnCompose(info, column, defValue, pk).join(); + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, pk); + if (cache.isFullLoaded() || val != null) return CompletableFuture.completedFuture(val); + } + if (isAsync()) return findColumnCompose(info, column, defValue, pk); + return CompletableFuture.supplyAsync(() -> findColumnCompose(info, column, defValue, pk).join(), getExecutor()); + } + + protected CompletableFuture findColumnCompose(final EntityInfo info, String column, final Serializable defValue, final Serializable pk) { + final String sql = "SELECT " + info.getSQLColumn(null, column) + " FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + return findColumnDB(info, sql, true, column, defValue); + } + + @Override + public Serializable findColumn(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, node); + if (cache.isFullLoaded() || val != null) return val; + } + return this.findColumnCompose(info, column, defValue, node).join(); + } + + @Override + public CompletableFuture findColumnAsync(final Class clazz, final String column, final Serializable defValue, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + Serializable val = cache.findColumn(column, defValue, node); + if (cache.isFullLoaded() || val != null) return CompletableFuture.completedFuture(val); + } + if (isAsync()) return this.findColumnCompose(info, column, defValue, node); + return CompletableFuture.supplyAsync(() -> this.findColumnCompose(info, column, defValue, node).join(), getExecutor()); + } + + protected CompletableFuture findColumnCompose(final EntityInfo info, String column, final Serializable defValue, final FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + final String sql = "SELECT " + info.getSQLColumn("a", column) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " find sql=" + sql); + return findColumnDB(info, sql, false, column, defValue); + } + + @Local + protected CompletableFuture findColumnDBApply(EntityInfo info, CompletableFuture future, boolean onlypk, String column, Serializable defValue) { + return future.thenApply((DataResultSet dataset) -> { + Serializable val = defValue; + if (dataset.next()) { + final Attribute attr = info.getAttribute(column); + val = dataset.getObject(attr, 1, null); + } + dataset.close(); + return val == null ? defValue : val; + }); + } + + //---------------------------- existsCompose ---------------------------- + @Override + public boolean exists(Class clazz, Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(pk); + if (rs || cache.isFullLoaded()) return rs; + } + return existsCompose(info, pk).join(); + } + + @Override + public CompletableFuture existsAsync(final Class clazz, final Serializable pk) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(pk); + if (rs || cache.isFullLoaded()) return CompletableFuture.completedFuture(rs); + } + if (isAsync()) return existsCompose(info, pk); + return CompletableFuture.supplyAsync(() -> existsCompose(info, pk).join(), getExecutor()); + } + + protected CompletableFuture existsCompose(final EntityInfo info, Serializable pk) { + final String sql = "SELECT COUNT(*) FROM " + info.getTable(pk) + " WHERE " + info.getPrimarySQLColumn() + "=" + info.formatSQLValue(info.getPrimarySQLColumn(), pk, sqlFormatter); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); + return existsDB(info, sql, true); + } + + @Override + public boolean exists(final Class clazz, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(node); + if (rs || cache.isFullLoaded()) return rs; + } + return this.existsCompose(info, node).join(); + } + + @Override + public CompletableFuture existsAsync(final Class clazz, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (cache != null) { + boolean rs = cache.exists(node); + if (rs || cache.isFullLoaded()) return CompletableFuture.completedFuture(rs); + } + if (isAsync()) return this.existsCompose(info, node); + return CompletableFuture.supplyAsync(() -> this.existsCompose(info, node).join(), getExecutor()); + } + + protected CompletableFuture existsCompose(final EntityInfo info, FilterNode node) { + final Map joinTabalis = node == null ? null : node.getJoinTabalis(); + final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); + final CharSequence where = node == null ? null : node.createSQLExpress(this, info, joinTabalis); + final String sql = "SELECT COUNT(" + info.getPrimarySQLColumn("a") + ") FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); + if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists sql=" + sql); + return existsDB(info, sql, false); + } + + @Local + protected CompletableFuture existsDBApply(EntityInfo info, CompletableFuture future, boolean onlypk) { + return future.thenApply((DataResultSet pgset) -> { + boolean rs = pgset.next() ? (((Number) pgset.getObject(1)).intValue() > 0) : false; + pgset.close(); + return rs; + }); + } + + //-----------------------list set---------------------------- + @Override + public Set queryColumnSet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + final Set list = querySet(clazz, SelectColumn.includes(selectedColumn), flipper, node); + final Set rs = new LinkedHashSet<>(); + if (list.isEmpty()) return rs; + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + } + + @Override + public CompletableFuture> queryColumnSetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + return querySetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Set list) -> { + final Set rs = new LinkedHashSet<>(); + if (list.isEmpty()) return rs; + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + }); + } + + @Override + public List queryColumnList(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + final List list = queryList(clazz, SelectColumn.includes(selectedColumn), flipper, node); + final List rs = new ArrayList<>(); + if (list.isEmpty()) return rs; + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + } + + @Override + public CompletableFuture> queryColumnListAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + return queryListAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((List list) -> { + final List rs = new ArrayList<>(); + if (list.isEmpty()) return rs; + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + for (T t : list) { + rs.add(selected.get(t)); + } + return rs; + }); + } + + @Override + public Sheet queryColumnSheet(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + Sheet sheet = querySheet(clazz, SelectColumn.includes(selectedColumn), flipper, node); + final Sheet rs = new Sheet<>(); + if (sheet.isEmpty()) return rs; + rs.setTotal(sheet.getTotal()); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + final List list = new ArrayList<>(); + for (T t : sheet.getRows()) { + list.add(selected.get(t)); + } + rs.setRows(list); + return rs; + } + + @Override + public CompletableFuture> queryColumnSheetAsync(final String selectedColumn, final Class clazz, final Flipper flipper, final FilterNode node) { + return querySheetAsync(clazz, SelectColumn.includes(selectedColumn), flipper, node).thenApply((Sheet sheet) -> { + final Sheet rs = new Sheet<>(); + if (sheet.isEmpty()) return rs; + rs.setTotal(sheet.getTotal()); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute selected = (Attribute) info.getAttribute(selectedColumn); + final List list = new ArrayList<>(); + for (T t : sheet.getRows()) { + list.add(selected.get(t)); + } + rs.setRows(list); + return rs; + }); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param keyStream 主键Stream + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final SelectColumn selects, final Stream keyStream) { + if (keyStream == null) return new LinkedHashMap<>(); + final EntityInfo info = loadEntityInfo(clazz); + final ArrayList ids = new ArrayList<>(); + keyStream.forEach(k -> ids.add(k)); + final Attribute primary = info.getPrimary(); + List rs = queryList(clazz, FilterNode.create(primary.field(), ids)); + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) return new LinkedHashMap<>(); + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final Stream keyStream) { + if (keyStream == null) return CompletableFuture.completedFuture(new LinkedHashMap<>()); + final EntityInfo info = loadEntityInfo(clazz); + final ArrayList pks = new ArrayList<>(); + keyStream.forEach(k -> pks.add(k)); + final Attribute primary = info.getPrimary(); + return queryListAsync(clazz, FilterNode.create(primary.field(), pks)).thenApply((List rs) -> { + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) return new LinkedHashMap<>(); + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + }); + } + + /** + * 查询符合过滤条件记录的Map集合, 主键值为key
    + * 等价SQL: SELECT * FROM {table} WHERE {column} = {key} ORDER BY {flipper.sort} LIMIT {flipper.limit}
    + * + * @param 主键泛型 + * @param Entity泛型 + * @param clazz Entity类 + * @param selects 指定字段 + * @param node FilterNode + * + * @return Entity的集合 + */ + @Override + public Map queryMap(final Class clazz, final SelectColumn selects, final FilterNode node) { + List rs = queryList(clazz, selects, node); + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) return new LinkedHashMap<>(); + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + } + + @Override + public CompletableFuture> queryMapAsync(final Class clazz, final SelectColumn selects, final FilterNode node) { + return queryListAsync(clazz, selects, node).thenApply((List rs) -> { + final EntityInfo info = loadEntityInfo(clazz); + final Attribute primary = info.getPrimary(); + Map map = new LinkedHashMap<>(); + if (rs.isEmpty()) return new LinkedHashMap<>(); + for (T item : rs) { + map.put((K) primary.get(item), item); + } + return map; + }); + } + + @Override + public Set querySet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return new LinkedHashSet<>(querySheetCompose(true, false, true, clazz, selects, flipper, node).join().list(true)); + } + + @Override + public CompletableFuture> querySetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheetCompose(true, false, true, clazz, selects, flipper, node).thenApply((rs) -> new LinkedHashSet<>(rs.list(true))); + } + + @Override + public List queryList(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheetCompose(true, false, false, clazz, selects, flipper, node).join().list(true); + } + + @Override + public CompletableFuture> queryListAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheetCompose(true, false, false, clazz, selects, flipper, node).thenApply((rs) -> rs.list(true)); + } + + @Override + public Sheet querySheet(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheetCompose(true, true, false, clazz, selects, flipper, node).join(); + } + + @Override + public CompletableFuture> querySheetAsync(final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + if (isAsync()) return querySheetCompose(true, true, false, clazz, selects, flipper, node); + return CompletableFuture.supplyAsync(() -> querySheetCompose(true, true, false, clazz, selects, flipper, node).join(), getExecutor()); + } + + protected CompletableFuture> querySheetCompose(final boolean readcache, final boolean needtotal, final boolean distinct, final Class clazz, final SelectColumn selects, final Flipper flipper, final FilterNode node) { + final EntityInfo info = loadEntityInfo(clazz); + final EntityCache cache = info.getCache(); + if (readcache && cache != null && cache.isFullLoaded()) { + if (node == null || isCacheUseable(node, this)) { + if (info.isLoggable(logger, Level.FINEST, " cache query predicate = ")) logger.finest(clazz.getSimpleName() + " cache query predicate = " + (node == null ? null : createPredicate(node, cache))); + return CompletableFuture.completedFuture(cache.querySheet(needtotal, distinct, selects, flipper, node)); + } + } + return querySheetDB(info, readcache, needtotal, distinct, selects, flipper, node); + } + + protected static enum UpdateMode { + INSERT, DELETE, UPDATE, CLEAR, DROP, ALTER, OTHER; + } +} diff --git a/src/org/redkale/source/DistributeTable.java b/src/main/java/org/redkale/source/DistributeTable.java similarity index 96% rename from src/org/redkale/source/DistributeTable.java rename to src/main/java/org/redkale/source/DistributeTable.java index c4345a256..da70af068 100644 --- a/src/org/redkale/source/DistributeTable.java +++ b/src/main/java/org/redkale/source/DistributeTable.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * Entity分库分表的注解,需要结合DistributeTableStrategy使用
    - * 标记为 @DistributeTable的Entity类视为需要进行分库分表操作
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Target({TYPE}) -@Retention(RUNTIME) -public @interface DistributeTable { - - Class strategy(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Entity分库分表的注解,需要结合DistributeTableStrategy使用
    + * 标记为 @DistributeTable的Entity类视为需要进行分库分表操作
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Target({TYPE}) +@Retention(RUNTIME) +public @interface DistributeTable { + + Class strategy(); +} diff --git a/src/org/redkale/source/DistributeTableStrategy.java b/src/main/java/org/redkale/source/DistributeTableStrategy.java similarity index 96% rename from src/org/redkale/source/DistributeTableStrategy.java rename to src/main/java/org/redkale/source/DistributeTableStrategy.java index 5bce05353..a1146fbcb 100644 --- a/src/org/redkale/source/DistributeTableStrategy.java +++ b/src/main/java/org/redkale/source/DistributeTableStrategy.java @@ -1,57 +1,57 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; - -/** - * 分表分库策略,结合@DistributeTable使用
    - * 不能与@Cacheable同时使用
    - * 使用分表分库功能重点是主键的生成策略,不同场景生成策略不一样
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Entity类型 - */ -public interface DistributeTableStrategy { - - /** - * 获取对象的表名
    - * 查询单个对象(DataSource.find)时调用本方法获取表名
    - * - * @param table 模板表的表名 - * @param primary 记录主键 - * - * @return 带库名的全表名 - */ - public String getTable(String table, Serializable primary); - - /** - * 获取对象的表名
    - * 新增对象或更新单个对象(DataSource.insert、DataSource.update)时调用本方法获取表名
    - * - * @param table 模板表的表名 - * @param bean 实体对象 - * - * @return 带库名的全表名 - */ - public String getTable(String table, T bean); - - /** - * 获取对象的表名
    - * 查询、修改、删除对象(DataSource.find、DataSource.query、DataSource.delete、DataSource.update)时调用本方法获取表名
    - * 注意: 需保证FilterNode过滤的结果集合必须在一个数据库表中
    - * - * @param table 模板表的表名 - * @param node 过滤条件 - * - * @return 带库名的全表名 - */ - public String getTable(String table, FilterNode node); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; + +/** + * 分表分库策略,结合@DistributeTable使用
    + * 不能与@Cacheable同时使用
    + * 使用分表分库功能重点是主键的生成策略,不同场景生成策略不一样
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Entity类型 + */ +public interface DistributeTableStrategy { + + /** + * 获取对象的表名
    + * 查询单个对象(DataSource.find)时调用本方法获取表名
    + * + * @param table 模板表的表名 + * @param primary 记录主键 + * + * @return 带库名的全表名 + */ + public String getTable(String table, Serializable primary); + + /** + * 获取对象的表名
    + * 新增对象或更新单个对象(DataSource.insert、DataSource.update)时调用本方法获取表名
    + * + * @param table 模板表的表名 + * @param bean 实体对象 + * + * @return 带库名的全表名 + */ + public String getTable(String table, T bean); + + /** + * 获取对象的表名
    + * 查询、修改、删除对象(DataSource.find、DataSource.query、DataSource.delete、DataSource.update)时调用本方法获取表名
    + * 注意: 需保证FilterNode过滤的结果集合必须在一个数据库表中
    + * + * @param table 模板表的表名 + * @param node 过滤条件 + * + * @return 带库名的全表名 + */ + public String getTable(String table, FilterNode node); + +} diff --git a/src/org/redkale/source/EntityCache.java b/src/main/java/org/redkale/source/EntityCache.java similarity index 92% rename from src/org/redkale/source/EntityCache.java rename to src/main/java/org/redkale/source/EntityCache.java index 7254a7802..6de3d1823 100644 --- a/src/org/redkale/source/EntityCache.java +++ b/src/main/java/org/redkale/source/EntityCache.java @@ -1,998 +1,1078 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.lang.reflect.Array; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.logging.*; -import java.util.stream.*; -import javax.persistence.*; -import static org.redkale.source.FilterFunc.*; -import org.redkale.util.*; - -/** - * Entity数据的缓存类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Entity类的泛型 - */ -@SuppressWarnings("unchecked") -public final class EntityCache { - - //日志 - private static final Logger logger = Logger.getLogger(EntityCache.class.getName()); - - //主键与对象的键值对 - private ConcurrentHashMap map = new ConcurrentHashMap(); - - // CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%; - private Collection list = new ConcurrentLinkedQueue(); - - //Flipper.sort转换成Comparator的缓存 - private final Map> sortComparators = new ConcurrentHashMap<>(); - - //Entity类 - private final Class type; - - //接口返回的对象是否需要复制一份 - private final boolean needcopy; - - //Entity构建器 - private final Creator creator; - - //主键字段 - private final Attribute primary; - - //新增时的复制器, 排除了标记为@Transient的字段 - private final Reproduce newReproduce; - - //修改时的复制器, 排除了标记为@Transient或@Column(updatable=false)的字段 - private final Reproduce chgReproduce; - - //是否已经全量加载过 - private volatile boolean fullloaded; - - private final AtomicBoolean loading = new AtomicBoolean(); - - //Entity信息 - final EntityInfo info; - - //@Cacheable的定时更新秒数,为0表示不定时更新 - final int interval; - - //@Cacheable的定时器 - private ScheduledThreadPoolExecutor scheduler; - - private CompletableFuture> loadFuture; - - public EntityCache(final EntityInfo info, final Cacheable c) { - this.info = info; - this.interval = c == null ? 0 : c.interval(); - this.type = info.getType(); - this.creator = info.getCreator(); - this.primary = info.primary; - VirtualEntity ve = info.getType().getAnnotation(VirtualEntity.class); - boolean direct = c != null && c.direct(); - if (!direct) direct = ve != null && ve.direct(); - this.needcopy = !direct; - this.newReproduce = Reproduce.create(type, type, (m) -> { - try { - return type.getDeclaredField(m).getAnnotation(Transient.class) == null; - } catch (Exception e) { - return true; - } - }); - this.chgReproduce = Reproduce.create(type, type, (m) -> { - try { - java.lang.reflect.Field field = type.getDeclaredField(m); - if (field.getAnnotation(Transient.class) != null) return false; - Column column = field.getAnnotation(Column.class); - return (column == null || column.updatable()); - } catch (Exception e) { - return true; - } - }); - } - - public CompletableFuture> fullLoadAsync() { - if (this.fullloaded) return this.loadFuture; - if (loading.getAndSet(true)) return this.loadFuture; - if (info.fullloader == null) { - this.list = new ConcurrentLinkedQueue(); - this.map = new ConcurrentHashMap(); - this.fullloaded = true; - loading.set(false); - return this.loadFuture; - } - this.fullloaded = false; - CompletableFuture allFuture = info.fullloader.apply(info.source, info); - this.loadFuture = (CompletableFuture) allFuture; - if (allFuture == null) { - this.list = new ConcurrentLinkedQueue(); - this.map = new ConcurrentHashMap(); - this.fullloaded = true; - loading.set(false); - return this.loadFuture; - } - if (this.interval > 0 && this.scheduler == null && info.fullloader != null) { - this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-EntityCache-" + type + "-Thread"); - t.setDaemon(true); - return t; - }); - this.scheduler.scheduleAtFixedRate(() -> { - try { - ConcurrentHashMap newmap2 = new ConcurrentHashMap(); - List all2 = info.fullloader.apply(info.source, info).join(); - if (all2 != null) { - all2.stream().filter(x -> x != null).forEach(x -> { - newmap2.put(this.primary.get(x), x); - }); - } - this.list = all2 == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all2); - this.map = newmap2; - } catch (Throwable t) { - logger.log(Level.SEVERE, type + " schedule(interval=" + interval + "s) Cacheable error", t); - } - }, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS); - } - allFuture.whenComplete((l, t) -> { - if (t != null) { - loading.set(false); - return; - } - List all = l; - ConcurrentHashMap newmap = new ConcurrentHashMap(); - if (all != null) { - all.stream().filter(x -> x != null).forEach(x -> { - newmap.put(this.primary.get(x), x); - }); - } - this.list = new ConcurrentLinkedQueue(all); - this.map = newmap; - this.fullloaded = true; - loading.set(false); - }); - return this.loadFuture; - } - - public Class getType() { - return type; - } - - public int clear() { - this.fullloaded = false; - this.list = new ConcurrentLinkedQueue(); - this.map = new ConcurrentHashMap(); - if (this.scheduler != null) { - this.scheduler.shutdownNow(); - this.scheduler = null; - } - return 1; - } - - public boolean isFullLoaded() { - return fullloaded; - } - - public T find(Serializable pk) { - if (pk == null) return null; - T rs = map.get(pk); - return rs == null ? null : (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs); - } - - public T find(final SelectColumn selects, final Serializable pk) { - if (pk == null) return null; - T rs = map.get(pk); - if (rs == null) return null; - if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs); - T t = this.creator.create(); - for (Attribute attr : this.info.attributes) { - if (selects.test(attr.field())) attr.set(t, attr.get(rs)); - } - return t; - } - - public T find(final SelectColumn selects, FilterNode node) { - final Predicate filter = node == null ? null : node.createPredicate(this); - Stream stream = this.list.stream(); - if (filter != null) stream = stream.filter(filter); - Optional opt = stream.findFirst(); - if (!opt.isPresent()) return null; - if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), opt.get()) : opt.get()); - T rs = opt.get(); - T t = this.creator.create(); - for (Attribute attr : this.info.attributes) { - if (selects.test(attr.field())) attr.set(t, attr.get(rs)); - } - return t; - } - - public Serializable findColumn(final String column, final Serializable defValue, final Serializable pk) { - if (pk == null) return defValue; - T rs = map.get(pk); - if (rs == null) return defValue; - for (Attribute attr : this.info.attributes) { - if (column.equals(attr.field())) { - Serializable val = (Serializable) attr.get(rs); - return val == null ? defValue : val; - } - } - return defValue; - } - - public Serializable findColumn(final String column, final Serializable defValue, FilterNode node) { - final Predicate filter = node == null ? null : node.createPredicate(this); - Stream stream = this.list.stream(); - if (filter != null) stream = stream.filter(filter); - Optional opt = stream.findFirst(); - if (!opt.isPresent()) return defValue; - T rs = opt.get(); - for (Attribute attr : this.info.attributes) { - if (column.equals(attr.field())) { - Serializable val = (Serializable) attr.get(rs); - return val == null ? defValue : val; - } - } - return defValue; - } - - public boolean exists(Serializable pk) { - if (pk == null) return false; - final Class atype = this.primary.type(); - if (pk.getClass() != atype && pk instanceof Number) { - if (atype == int.class || atype == Integer.class) { - pk = ((Number) pk).intValue(); - } else if (atype == long.class || atype == Long.class) { - pk = ((Number) pk).longValue(); - } else if (atype == short.class || atype == Short.class) { - pk = ((Number) pk).shortValue(); - } else if (atype == float.class || atype == Float.class) { - pk = ((Number) pk).floatValue(); - } else if (atype == byte.class || atype == Byte.class) { - pk = ((Number) pk).byteValue(); - } else if (atype == double.class || atype == Double.class) { - pk = ((Number) pk).doubleValue(); - } else if (atype == AtomicInteger.class) { - pk = new AtomicInteger(((Number) pk).intValue()); - } else if (atype == AtomicLong.class) { - pk = new AtomicLong(((Number) pk).longValue()); - } - } - return this.map.containsKey(pk); - } - - public boolean exists(FilterNode node) { - final Predicate filter = node == null ? null : node.createPredicate(this); - Stream stream = this.list.stream(); - if (filter != null) stream = stream.filter(filter); - return stream.findFirst().isPresent(); - } - - public boolean exists(final Predicate filter) { - return (filter != null) && this.list.stream().filter(filter).findFirst().isPresent(); - } - - public Map queryColumnMap(final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { - final Attribute keyAttr = info.getAttribute(keyColumn); - final Predicate filter = node == null ? null : node.createPredicate(this); - final Attribute funcAttr = funcColumn == null ? null : info.getAttribute(funcColumn); - Stream stream = this.list.stream(); - if (filter != null) stream = stream.filter(filter); - Collector collector = null; - final Class valtype = funcAttr == null ? null : funcAttr.type(); - if (func != null) { - switch (func) { - case AVG: - if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) { - collector = (Collector) Collectors.averagingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue()); - } else { - collector = (Collector) Collectors.averagingLong((T t) -> ((Number) funcAttr.get(t)).longValue()); - } - break; - case COUNT: - collector = (Collector) Collectors.counting(); - break; - case DISTINCTCOUNT: - collector = (Collector) Collectors.mapping((t) -> funcAttr.get(t), Collectors.toSet()); - break; - case MAX: - case MIN: - Comparator comp = (o1, o2) -> o1 == null ? (o2 == null ? 0 : -1) : ((Comparable) funcAttr.get(o1)).compareTo(funcAttr.get(o2)); - collector = (Collector) ((func == MAX) ? Collectors.maxBy(comp) : Collectors.minBy(comp)); - break; - case SUM: - if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) { - collector = (Collector) Collectors.summingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue()); - } else { - collector = (Collector) Collectors.summingLong((T t) -> ((Number) funcAttr.get(t)).longValue()); - } - break; - } - } - Map rs = collector == null ? stream.collect(Collectors.toMap(t -> keyAttr.get(t), t -> funcAttr.get(t), (key1, key2) -> key2)) - : stream.collect(Collectors.groupingBy(t -> keyAttr.get(t), LinkedHashMap::new, collector)); - if (func == MAX || func == MIN) { - Map rs2 = new LinkedHashMap(); - rs.forEach((x, y) -> { - if (((Optional) y).isPresent()) rs2.put(x, funcAttr.get((T) ((Optional) y).get())); - }); - rs = rs2; - } else if (func == DISTINCTCOUNT) { - Map rs2 = new LinkedHashMap(); - rs.forEach((x, y) -> rs2.put(x, ((Set) y).size() + 0L)); - rs = rs2; - } - return rs; - } - - public Map queryColumnMap(final ColumnNode[] funcNodes, final String[] groupByColumns, FilterNode node) { - final Predicate filter = node == null ? null : node.createPredicate(this); - Stream stream = this.list.stream(); - if (filter != null) stream = stream.filter(filter); - final Attribute[] attrs = new Attribute[groupByColumns.length]; - for (int i = 0; i < groupByColumns.length; i++) { - attrs[i] = info.getAttribute(groupByColumns[i]); - } - final Map valmap = new HashMap<>(); - Function func = t -> { - StringBuilder sb = new StringBuilder(); - final Serializable[] vals = new Serializable[attrs.length]; - for (int i = 0; i < attrs.length; i++) { - vals[i] = attrs[i].get(t); - sb.append((char) 20).append(vals[i]); - } - final String key = sb.toString(); - if (!valmap.containsKey(key)) valmap.put(key, vals); - return valmap.get(key); - }; - Map> listmap = stream.collect(Collectors.groupingBy(func)); - final Map rsmap = new HashMap<>(listmap.size()); - listmap.forEach((k, l) -> rsmap.put(k, queryColumnNumbers(l, funcNodes))); - return rsmap; - } - - private Number[] queryColumnNumbers(final List list, final ColumnNode[] funcNodes) { - if (true) throw new UnsupportedOperationException("Not supported yet."); - Number[] rs = new Number[funcNodes.length]; - for (int i = 0; i < rs.length; i++) { - rs[i] = queryColumnNumber(list, funcNodes[i]); - } - return rs; - } - - private Number queryColumnNumber(final List list, final ColumnNode funcNode) { - if (funcNode instanceof ColumnFuncNode) { - return queryColumnNumber(list, (ColumnFuncNode) funcNode); - } else if (funcNode instanceof ColumnNodeValue) { - return queryColumnNumber(list, (ColumnNodeValue) funcNode); - } else { - return null; - } - } - - private Number queryColumnNumber(final List list, final ColumnFuncNode funcNode) { - if (funcNode.getValue() instanceof String) { - final Attribute attr = info.getAttribute((String) funcNode.getValue()); - final Function attrFunc = x -> (Number) attr.get(x); - return getNumberResult(list, funcNode.getFunc(), null, attr.type(), attrFunc, (FilterNode) null); - } - Number num = null; - if (funcNode.getValue() instanceof ColumnFuncNode) { - num = queryColumnNumber(list, (ColumnFuncNode) funcNode.getValue()); - } else if (funcNode.getValue() instanceof ColumnNodeValue) { - num = queryColumnNumber(list, (ColumnNodeValue) funcNode.getValue()); - } - return num; - } - - private Number queryColumnNumber(final List list, final ColumnNodeValue nodeValue) { - return null; - } - - private Number getNumberResult(final Collection entityList, final FilterFunc func, final Number defResult, final Class attrType, final Function attrFunc, final FilterNode node) { - final Predicate filter = node == null ? null : node.createPredicate(this); - Stream stream = entityList.stream(); - if (filter != null) stream = stream.filter(filter); - switch (func) { - case AVG: - if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { - OptionalDouble rs = stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).average(); - return rs.isPresent() ? (int) rs.getAsDouble() : defResult; - } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class) { - OptionalDouble rs = stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).average(); - return rs.isPresent() ? (long) rs.getAsDouble() : defResult; - } else if (attrType == short.class || attrType == Short.class) { - OptionalDouble rs = stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).average(); - return rs.isPresent() ? (short) rs.getAsDouble() : defResult; - } else if (attrType == float.class || attrType == Float.class) { - OptionalDouble rs = stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).average(); - return rs.isPresent() ? (float) rs.getAsDouble() : defResult; - } else if (attrType == double.class || attrType == Double.class) { - OptionalDouble rs = stream.mapToDouble(x -> (Double) attrFunc.apply(x)).average(); - return rs.isPresent() ? rs.getAsDouble() : defResult; - } - throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); - case COUNT: - return stream.count(); - case DISTINCTCOUNT: - return stream.map(x -> attrFunc.apply(x)).distinct().count(); - - case MAX: - if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { - OptionalInt rs = stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).max(); - return rs.isPresent() ? rs.getAsInt() : defResult; - } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class) { - OptionalLong rs = stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).max(); - return rs.isPresent() ? rs.getAsLong() : defResult; - } else if (attrType == short.class || attrType == Short.class) { - OptionalInt rs = stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).max(); - return rs.isPresent() ? (short) rs.getAsInt() : defResult; - } else if (attrType == float.class || attrType == Float.class) { - OptionalDouble rs = stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).max(); - return rs.isPresent() ? (float) rs.getAsDouble() : defResult; - } else if (attrType == double.class || attrType == Double.class) { - OptionalDouble rs = stream.mapToDouble(x -> (Double) attrFunc.apply(x)).max(); - return rs.isPresent() ? rs.getAsDouble() : defResult; - } - throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); - - case MIN: - if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { - OptionalInt rs = stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).min(); - return rs.isPresent() ? rs.getAsInt() : defResult; - } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class) { - OptionalLong rs = stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).min(); - return rs.isPresent() ? rs.getAsLong() : defResult; - } else if (attrType == short.class || attrType == Short.class) { - OptionalInt rs = stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).min(); - return rs.isPresent() ? (short) rs.getAsInt() : defResult; - } else if (attrType == float.class || attrType == Float.class) { - OptionalDouble rs = stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).min(); - return rs.isPresent() ? (float) rs.getAsDouble() : defResult; - } else if (attrType == double.class || attrType == Double.class) { - OptionalDouble rs = stream.mapToDouble(x -> (Double) attrFunc.apply(x)).min(); - return rs.isPresent() ? rs.getAsDouble() : defResult; - } - throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); - - case SUM: - if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { - return stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).sum(); - } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class) { - return stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).sum(); - } else if (attrType == short.class || attrType == Short.class) { - return (short) stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).sum(); - } else if (attrType == float.class || attrType == Float.class) { - return (float) stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).sum(); - } else if (attrType == double.class || attrType == Double.class) { - return stream.mapToDouble(x -> (Double) attrFunc.apply(x)).sum(); - } - throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); - } - return defResult; - } - - public Number getNumberResult(final FilterFunc func, final Number defResult, final String column, final FilterNode node) { - final Attribute attr = column == null ? null : info.getAttribute(column); //COUNT的column=null - final Function attrFunc = attr == null ? null : x -> (Number) attr.get(x); - return getNumberResult(this.list, func, defResult, attr == null ? null : attr.type(), attrFunc, node); - } - - public Sheet querySheet(final SelectColumn selects, final Flipper flipper, final FilterNode node) { - return querySheet(true, false, selects, flipper, node); - } - - protected Stream distinctStream(Stream stream, final List> keyattrs) { - if (keyattrs == null) return stream; - final Set keys = new HashSet<>(); - Predicate filter = t -> { - StringBuilder sb = new StringBuilder(); - for (Attribute attr : keyattrs) { - sb.append(attr.get(t)); - } - String key = sb.toString(); - if (keys.contains(key)) return false; - keys.add(key); - return true; - }; - return stream.filter(filter); - } - - public Sheet querySheet(final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, FilterNode node) { - final Predicate filter = node == null ? null : node.createPredicate(this); - final Comparator comparator = createComparator(flipper); - long total = 0; - List> keyattrs = null; - if (distinct) { - final List> attrs = new ArrayList<>(); - info.forEachAttribute((k, v) -> { - if (selects == null || selects.test(k)) attrs.add(v); - }); - keyattrs = attrs; - } - if (needtotal) { - Stream stream = this.list.stream(); - if (filter != null) stream = stream.filter(filter); - if (distinct) stream = distinctStream(stream, keyattrs); - total = stream.count(); - } - if (needtotal && total == 0) return new Sheet<>(0, new ArrayList()); - Stream stream = this.list.stream(); - if (filter != null) stream = stream.filter(filter); - if (distinct) stream = distinctStream(stream, keyattrs); - if (comparator != null) stream = stream.sorted(comparator); - if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset()); - if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit()); - final List rs = new ArrayList<>(); - if (selects == null) { - Consumer action = x -> rs.add(needcopy ? newReproduce.apply(creator.create(), x) : x); - if (comparator != null) { - stream.forEachOrdered(action); - } else { - stream.forEach(action); - } - } else { - final List> attrs = new ArrayList<>(); - info.forEachAttribute((k, v) -> { - if (selects.test(k)) attrs.add(v); - }); - Consumer action = x -> { - final T item = creator.create(); - for (Attribute attr : attrs) { - attr.set(item, attr.get(x)); - } - rs.add(item); - }; - if (comparator != null) { - stream.forEachOrdered(action); - } else { - stream.forEach(action); - } - } - if (!needtotal) total = rs.size(); - return new Sheet<>(total, rs); - } - - public int insert(T entity) { - if (entity == null) return 0; - final T rs = newReproduce.apply(this.creator.create(), entity); //确保同一主键值的map与list中的对象必须共用。 - T old = this.map.putIfAbsent(this.primary.get(rs), rs); - if (old == null) { - this.list.add(rs); - return 1; - } else { - logger.log(Level.WARNING, this.type + " cache repeat insert data: " + entity); - return 0; - } - } - - public int delete(final Serializable pk) { - if (pk == null) return 0; - final T rs = this.map.remove(pk); - if (rs == null) return 0; - this.list.remove(rs); - return 1; - } - - public Serializable[] delete(final Flipper flipper, final FilterNode node) { - if (node == null || this.list.isEmpty()) return new Serializable[0]; - final Comparator comparator = createComparator(flipper); - Stream stream = this.list.stream().filter(node.createPredicate(this)); - if (comparator != null) stream = stream.sorted(comparator); - if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset()); - if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit()); - Object[] rms = stream.toArray(); - Serializable[] ids = new Serializable[rms.length]; - int i = -1; - for (Object o : rms) { - final T t = (T) o; - ids[++i] = this.primary.get(t); - this.map.remove(ids[i]); - this.list.remove(t); - } - return ids; - } - - public int drop() { - return clear(); - } - - public int update(final T entity) { - if (entity == null) return 0; - T rs = this.map.get(this.primary.get(entity)); - if (rs == null) return 0; - synchronized (rs) { - this.chgReproduce.apply(rs, entity); - } - return 1; - } - - public T update(final T entity, Collection> attrs) { - if (entity == null) return entity; - T rs = this.map.get(this.primary.get(entity)); - if (rs == null) return rs; - synchronized (rs) { - for (Attribute attr : attrs) { - attr.set(rs, attr.get(entity)); - } - } - return rs; - } - - public T[] update(final T entity, final Collection> attrs, final FilterNode node) { - if (entity == null || node == null) return (T[]) Array.newInstance(type, 0); - T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); - for (T rs : rms) { - synchronized (rs) { - for (Attribute attr : attrs) { - attr.set(rs, attr.get(entity)); - } - } - } - return rms; - } - - public T update(final Serializable pk, Attribute attr, final V fieldValue) { - if (pk == null) return null; - T rs = this.map.get(pk); - if (rs != null) attr.set(rs, fieldValue); - return rs; - } - - public T[] update(Attribute attr, final V fieldValue, final FilterNode node) { - if (attr == null || node == null) return (T[]) Array.newInstance(type, 0); - T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); - for (T rs : rms) { - attr.set(rs, fieldValue); - } - return rms; - } - - public T updateColumn(final Serializable pk, List> attrs, final List values) { - if (pk == null || attrs == null || attrs.isEmpty()) return null; - T rs = this.map.get(pk); - if (rs == null) return rs; - synchronized (rs) { - for (int i = 0; i < attrs.size(); i++) { - ColumnValue cv = values.get(i); - updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue()); - } - } - return rs; - } - - public T[] updateColumn(final FilterNode node, final Flipper flipper, List> attrs, final List values) { - if (attrs == null || attrs.isEmpty() || node == null) return (T[]) Array.newInstance(type, 0); - Stream stream = this.list.stream(); - final Comparator comparator = createComparator(flipper); - if (comparator != null) stream = stream.sorted(comparator); - if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit()); - T[] rms = stream.filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); - for (T rs : rms) { - synchronized (rs) { - for (int i = 0; i < attrs.size(); i++) { - ColumnValue cv = values.get(i); - updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue()); - } - } - } - return rms; - } - - public T updateColumnOr(final Serializable pk, Attribute attr, final long orvalue) { - if (pk == null) return null; - T rs = this.map.get(pk); - if (rs == null) return rs; - synchronized (rs) { - return updateColumn(attr, rs, ColumnExpress.ORR, orvalue); - } - } - - public T updateColumnAnd(final Serializable pk, Attribute attr, final long andvalue) { - if (pk == null) return null; - T rs = this.map.get(pk); - if (rs == null) return rs; - synchronized (rs) { - return updateColumn(attr, rs, ColumnExpress.AND, andvalue); - } - } - - public T updateColumnIncrement(final Serializable pk, Attribute attr, final long incvalue) { - if (pk == null) return null; - T rs = this.map.get(pk); - if (rs == null) return rs; - synchronized (rs) { - return updateColumn(attr, rs, ColumnExpress.INC, incvalue); - } - } - - public T updateColumnDecrement(final Serializable pk, Attribute attr, final long incvalue) { - if (pk == null) return null; - T rs = this.map.get(pk); - if (rs == null) return rs; - synchronized (rs) { - return updateColumn(attr, rs, ColumnExpress.DEC, incvalue); - } - } - - private T updateColumn(Attribute attr, final T entity, final ColumnExpress express, Serializable val) { - final Class ft = attr.type(); - Number numb = null; - Serializable newval = null; - switch (express) { - case INC: - case DEC: - case MUL: - case DIV: - case MOD: - case AND: - case ORR: - numb = getValue((Number) attr.get(entity), express, val); - break; - case MOV: - if (val instanceof ColumnNodeValue) val = updateColumnNodeValue(attr, entity, (ColumnNodeValue) val); - newval = val; - if (val instanceof Number) numb = (Number) val; - break; - } - if (numb != null) { - if (ft == int.class || ft == Integer.class) { - newval = numb.intValue(); - } else if (ft == long.class || ft == Long.class) { - newval = numb.longValue(); - } else if (ft == short.class || ft == Short.class) { - newval = numb.shortValue(); - } else if (ft == float.class || ft == Float.class) { - newval = numb.floatValue(); - } else if (ft == double.class || ft == Double.class) { - newval = numb.doubleValue(); - } else if (ft == byte.class || ft == Byte.class) { - newval = numb.byteValue(); - } else if (ft == AtomicInteger.class) { - newval = new AtomicInteger(numb.intValue()); - } else if (ft == AtomicLong.class) { - newval = new AtomicLong(numb.longValue()); - } - } else { - if (ft == AtomicInteger.class && newval != null && newval.getClass() != AtomicInteger.class) { - newval = new AtomicInteger(((Number) newval).intValue()); - } else if (ft == AtomicLong.class && newval != null && newval.getClass() != AtomicLong.class) { - newval = new AtomicLong(((Number) newval).longValue()); - } - } - attr.set(entity, (V) newval); - return entity; - } - - private Serializable updateColumnNodeValue(Attribute attr, final T entity, ColumnNodeValue node) { - Serializable left = node.getLeft(); - if (left instanceof CharSequence) { - left = info.getUpdateAttribute(left.toString()).get(entity); - } else if (left instanceof ColumnNodeValue) { - left = updateColumnNodeValue(attr, entity, (ColumnNodeValue) left); - } - Serializable right = node.getRight(); - if (left instanceof CharSequence) { - right = info.getUpdateAttribute(right.toString()).get(entity); - } else if (left instanceof ColumnNodeValue) { - right = updateColumnNodeValue(attr, entity, (ColumnNodeValue) right); - } - return getValue((Number) left, node.getExpress(), right); - } - - private Number getValue(Number numb, final ColumnExpress express, Serializable val) { - switch (express) { - case INC: - if (numb == null) { - numb = (Number) val; - } else { - if (numb instanceof Float || ((Number) val) instanceof Float) { - numb = numb.floatValue() + ((Number) val).floatValue(); - } else if (numb instanceof Double || ((Number) val) instanceof Double) { - numb = numb.doubleValue() + ((Number) val).doubleValue(); - } else { - numb = numb.longValue() + ((Number) val).longValue(); - } - } - break; - case DEC: - if (numb == null) { - numb = (Number) val; - } else { - if (numb instanceof Float || ((Number) val) instanceof Float) { - numb = numb.floatValue() - ((Number) val).floatValue(); - } else if (numb instanceof Double || ((Number) val) instanceof Double) { - numb = numb.doubleValue() - ((Number) val).doubleValue(); - } else { - numb = numb.longValue() - ((Number) val).longValue(); - } - } - break; - case MUL: - if (numb == null) { - numb = 0; - } else { - numb = numb.longValue() * ((Number) val).floatValue(); - } - break; - case DIV: - if (numb == null) { - numb = 0; - } else { - numb = numb.longValue() / ((Number) val).floatValue(); - } - break; - case MOD: - if (numb == null) { - numb = 0; - } else { - numb = numb.longValue() % ((Number) val).intValue(); - } - break; - case AND: - if (numb == null) { - numb = 0; - } else { - numb = numb.longValue() & ((Number) val).longValue(); - } - break; - case ORR: - if (numb == null) { - numb = 0; - } else { - numb = numb.longValue() | ((Number) val).longValue(); - } - break; - } - return numb; - } - - public Attribute getAttribute(String fieldname) { - return info.getAttribute(fieldname); - } - - //------------------------------------------------------------------------------------------------------------------------------- - protected Comparator createComparator(Flipper flipper) { - if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) return null; - final String sort = flipper.getSort(); - Comparator comparator = this.sortComparators.get(sort); - if (comparator != null) return comparator; - for (String item : sort.split(",")) { - if (item.trim().isEmpty()) continue; - String[] sub = item.trim().split("\\s+"); - int pos = sub[0].indexOf('('); - Attribute attr; - if (pos <= 0) { - attr = getAttribute(sub[0]); - } else { //含SQL函数 - int pos2 = sub[0].lastIndexOf(')'); - final Attribute pattr = getAttribute(sub[0].substring(pos + 1, pos2)); - final String func = sub[0].substring(0, pos); - if ("ABS".equalsIgnoreCase(func)) { - Function getter = null; - if (pattr.type() == int.class || pattr.type() == Integer.class || pattr.type() == AtomicInteger.class) { - getter = x -> Math.abs(((Number) pattr.get((T) x)).intValue()); - } else if (pattr.type() == long.class || pattr.type() == Long.class || pattr.type() == AtomicLong.class) { - getter = x -> Math.abs(((Number) pattr.get((T) x)).longValue()); - } else if (pattr.type() == float.class || pattr.type() == Float.class) { - getter = x -> Math.abs(((Number) pattr.get((T) x)).floatValue()); - } else if (pattr.type() == double.class || pattr.type() == Double.class) { - getter = x -> Math.abs(((Number) pattr.get((T) x)).doubleValue()); - } else { - throw new RuntimeException("Flipper not supported sort illegal type by ABS (" + flipper.getSort() + ")"); - } - attr = (Attribute) Attribute.create(pattr.declaringClass(), pattr.field(), pattr.type(), getter, (o, v) -> pattr.set(o, v)); - } else if (func.isEmpty()) { - attr = pattr; - } else { - throw new RuntimeException("Flipper not supported sort illegal function (" + flipper.getSort() + ")"); - } - } - Comparator c = (sub.length > 1 && sub[1].equalsIgnoreCase("DESC")) ? (T o1, T o2) -> { - Comparable c1 = (Comparable) attr.get(o1); - Comparable c2 = (Comparable) attr.get(o2); - return c2 == null ? -1 : c2.compareTo(c1); - } : (T o1, T o2) -> { - Comparable c1 = (Comparable) attr.get(o1); - Comparable c2 = (Comparable) attr.get(o2); - return c1 == null ? -1 : c1.compareTo(c2); - }; - - if (comparator == null) { - comparator = c; - } else { - comparator = comparator.thenComparing(c); - } - } - this.sortComparators.put(sort, comparator); - return comparator; - } - - private static class UniqueSequence implements Serializable { - - private final Serializable[] value; - - public UniqueSequence(Serializable[] val) { - this.value = val; - } - - @Override - public int hashCode() { - return Arrays.deepHashCode(this.value); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final UniqueSequence other = (UniqueSequence) obj; - if (value.length != other.value.length) return false; - for (int i = 0; i < value.length; i++) { - if (!value[i].equals(other.value[i])) return false; - } - return true; - } - - } - - private static interface UniqueAttribute extends Predicate { - - public Serializable getValue(T bean); - - @Override - public boolean test(FilterNode node); - - public static UniqueAttribute create(final Attribute[] attributes) { - if (attributes.length == 1) { - final Attribute attribute = attributes[0]; - return new UniqueAttribute() { - - @Override - public Serializable getValue(T bean) { - return attribute.get(bean); - } - - @Override - public boolean test(FilterNode node) { - if (node == null || node.isOr()) return false; - if (!attribute.field().equals(node.column)) return false; - if (node.nodes == null) return true; - for (FilterNode n : node.nodes) { - if (!test(n)) return false; - } - return true; - } - }; - } else { - return new UniqueAttribute() { - - @Override - public Serializable getValue(T bean) { - final Serializable[] rs = new Serializable[attributes.length]; - for (int i = 0; i < rs.length; i++) { - rs[i] = attributes[i].get(bean); - } - return new UniqueSequence(rs); - } - - @Override - public boolean test(FilterNode node) { - return true; - } - }; - } - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.*; +import java.util.stream.*; +import javax.persistence.*; +import static org.redkale.source.FilterFunc.*; +import org.redkale.util.*; + +/** + * Entity数据的缓存类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Entity类的泛型 + */ +@SuppressWarnings("unchecked") +public final class EntityCache { + + //日志 + private static final Logger logger = Logger.getLogger(EntityCache.class.getName()); + + //主键与对象的键值对 + private ConcurrentHashMap map = new ConcurrentHashMap(); + + // CopyOnWriteArrayList 插入慢、查询快; 10w数据插入需要3.2秒; ConcurrentLinkedQueue 插入快、查询慢;10w数据查询需要 0.062秒, 查询慢40%; + private Collection list = new ConcurrentLinkedQueue(); + + //Flipper.sort转换成Comparator的缓存 + private final Map> sortComparators = new ConcurrentHashMap<>(); + + //Entity类 + private final Class type; + + //接口返回的对象是否需要复制一份 + private final boolean needcopy; + + //Entity构建器 + private final Creator creator; + + //Entity数值构建器 + private final IntFunction arrayer; + + //主键字段 + private final Attribute primary; + + //新增时的复制器, 排除了标记为@Transient的字段 + private final Reproduce newReproduce; + + //修改时的复制器, 排除了标记为@Transient或@Column(updatable=false)的字段 + private final Reproduce chgReproduce; + + //是否已经全量加载过 + private volatile boolean fullloaded; + + private final AtomicBoolean loading = new AtomicBoolean(); + + //Entity信息 + final EntityInfo info; + + //@Cacheable的定时更新秒数,为0表示不定时更新 + final int interval; + + //@Cacheable的定时器 + private ScheduledThreadPoolExecutor scheduler; + + private CompletableFuture> loadFuture; + + public EntityCache(final EntityInfo info, final Cacheable c) { + this.info = info; + this.interval = c == null ? 0 : c.interval(); + this.type = info.getType(); + this.arrayer = info.getArrayer(); + this.creator = info.getCreator(); + this.primary = info.primary; + VirtualEntity ve = info.getType().getAnnotation(VirtualEntity.class); + boolean direct = c != null && c.direct(); + if (!direct) direct = ve != null && ve.direct(); + this.needcopy = !direct; + this.newReproduce = Reproduce.create(type, type, (m) -> { + try { + return type.getDeclaredField(m).getAnnotation(Transient.class) == null; + } catch (Exception e) { + return true; + } + }); + this.chgReproduce = Reproduce.create(type, type, (m) -> { + try { + java.lang.reflect.Field field = type.getDeclaredField(m); + if (field.getAnnotation(Transient.class) != null) return false; + Column column = field.getAnnotation(Column.class); + return (column == null || column.updatable()); + } catch (Exception e) { + return true; + } + }); + } + + public CompletableFuture> fullLoadAsync() { + if (this.fullloaded) return this.loadFuture; + if (loading.getAndSet(true)) return this.loadFuture; + if (info.fullloader == null) { + this.list = new ConcurrentLinkedQueue(); + this.map = new ConcurrentHashMap(); + this.fullloaded = true; + loading.set(false); + return this.loadFuture; + } + this.fullloaded = false; + CompletableFuture allFuture = info.fullloader.apply(info.source, info); + this.loadFuture = (CompletableFuture) allFuture; + if (allFuture == null) { + this.list = new ConcurrentLinkedQueue(); + this.map = new ConcurrentHashMap(); + this.fullloaded = true; + loading.set(false); + return this.loadFuture; + } + if (this.interval > 0 && this.scheduler == null && info.fullloader != null) { + this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { + final Thread t = new Thread(r, "Redkale-EntityCache-" + type + "-Thread"); + t.setDaemon(true); + return t; + }); + this.scheduler.scheduleAtFixedRate(() -> { + try { + ConcurrentHashMap newmap2 = new ConcurrentHashMap(); + List all2 = info.fullloader.apply(info.source, info).join(); + if (all2 != null) { + all2.stream().filter(x -> x != null).forEach(x -> { + newmap2.put(this.primary.get(x), x); + }); + } + this.list = all2 == null ? new ConcurrentLinkedQueue() : new ConcurrentLinkedQueue(all2); + this.map = newmap2; + } catch (Throwable t) { + logger.log(Level.SEVERE, type + " schedule(interval=" + interval + "s) Cacheable error", t); + } + }, interval - System.currentTimeMillis() / 1000 % interval, interval, TimeUnit.SECONDS); + } + allFuture.whenComplete((l, t) -> { + if (t != null) { + loading.set(false); + return; + } + List all = l; + ConcurrentHashMap newmap = new ConcurrentHashMap(); + if (all != null) { + all.stream().filter(x -> x != null).forEach(x -> { + newmap.put(this.primary.get(x), x); + }); + } + this.list = new ConcurrentLinkedQueue(all); + this.map = newmap; + this.fullloaded = true; + loading.set(false); + }); + return this.loadFuture; + } + + public Class getType() { + return type; + } + + public int clear() { + this.fullloaded = false; + this.list = new ConcurrentLinkedQueue(); + this.map = new ConcurrentHashMap(); + if (this.scheduler != null) { + this.scheduler.shutdownNow(); + this.scheduler = null; + } + return 1; + } + + public boolean isFullLoaded() { + return fullloaded; + } + + public T find(Serializable pk) { + if (pk == null) return null; + T rs = map.get(pk); + return rs == null ? null : (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs); + } + + public T[] finds(Serializable... pks) { + if (pks == null || pks.length == 0) return arrayer.apply(0); + if (pks.length == 1) { + Class t = pks[0].getClass(); + if (t == int[].class) { + int[] ids = (int[]) pks[0]; + T[] array = arrayer.apply(ids.length); + for (int i = 0; i < array.length; i++) { + T rs = map.get(ids[i]); + array[i] = rs == null ? null : (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs); + } + return array; + } else if (t == long[].class) { + long[] ids = (long[]) pks[0]; + T[] array = arrayer.apply(ids.length); + for (int i = 0; i < array.length; i++) { + T rs = map.get(ids[i]); + array[i] = rs == null ? null : (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs); + } + return array; + } + } + T[] array = arrayer.apply(pks.length); + for (int i = 0; i < array.length; i++) { + T rs = map.get(pks[i]); + array[i] = rs == null ? null : (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs); + } + return array; + } + + public T find(final SelectColumn selects, final Serializable pk) { + if (pk == null) return null; + T rs = map.get(pk); + if (rs == null) return null; + if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), rs) : rs); + T t = this.creator.create(); + for (Attribute attr : this.info.attributes) { + if (selects.test(attr.field())) attr.set(t, attr.get(rs)); + } + return t; + } + + public T[] finds(final SelectColumn selects, Serializable... pks) { + if (pks == null || pks.length == 0) return arrayer.apply(0); + final Creator ctr = this.creator; + final Attribute[] attrs = this.info.attributes; + int size = pks.length; + int[] ids1 = null; + long[] ids2 = null; + if (size == 1) { + if (pks[0].getClass() == int[].class) { + ids1 = (int[]) pks[0]; + } else if (pks[0].getClass() == long[].class) { + ids2 = (long[]) pks[0]; + } + } + T[] array = arrayer.apply(size); + for (int i = 0; i < array.length; i++) { + Serializable id = ids1 == null ? (ids2 == null ? pks[i] : ids2[i]) : ids1[i]; + T rs = map.get(id); + if (rs == null) continue; + if (selects == null) { + if (needcopy) rs = newReproduce.apply(ctr.create(), rs); + } else { + T t = ctr.create(); + for (Attribute attr : attrs) { + if (selects.test(attr.field())) attr.set(t, attr.get(rs)); + } + rs = t; + } + array[i] = rs; + } + return array; + } + + public T find(final SelectColumn selects, FilterNode node) { + final Predicate filter = node == null ? null : node.createPredicate(this); + Stream stream = this.list.stream(); + if (filter != null) stream = stream.filter(filter); + Optional opt = stream.findFirst(); + if (!opt.isPresent()) return null; + if (selects == null) return (needcopy ? newReproduce.apply(this.creator.create(), opt.get()) : opt.get()); + T rs = opt.get(); + T t = this.creator.create(); + for (Attribute attr : this.info.attributes) { + if (selects.test(attr.field())) attr.set(t, attr.get(rs)); + } + return t; + } + + public Serializable findColumn(final String column, final Serializable defValue, final Serializable pk) { + if (pk == null) return defValue; + T rs = map.get(pk); + if (rs == null) return defValue; + for (Attribute attr : this.info.attributes) { + if (column.equals(attr.field())) { + Serializable val = (Serializable) attr.get(rs); + return val == null ? defValue : val; + } + } + return defValue; + } + + public Serializable findColumn(final String column, final Serializable defValue, FilterNode node) { + final Predicate filter = node == null ? null : node.createPredicate(this); + Stream stream = this.list.stream(); + if (filter != null) stream = stream.filter(filter); + Optional opt = stream.findFirst(); + if (!opt.isPresent()) return defValue; + T rs = opt.get(); + for (Attribute attr : this.info.attributes) { + if (column.equals(attr.field())) { + Serializable val = (Serializable) attr.get(rs); + return val == null ? defValue : val; + } + } + return defValue; + } + + public boolean exists(Serializable pk) { + if (pk == null) return false; + final Class atype = this.primary.type(); + if (pk.getClass() != atype && pk instanceof Number) { + if (atype == int.class || atype == Integer.class) { + pk = ((Number) pk).intValue(); + } else if (atype == long.class || atype == Long.class) { + pk = ((Number) pk).longValue(); + } else if (atype == short.class || atype == Short.class) { + pk = ((Number) pk).shortValue(); + } else if (atype == float.class || atype == Float.class) { + pk = ((Number) pk).floatValue(); + } else if (atype == byte.class || atype == Byte.class) { + pk = ((Number) pk).byteValue(); + } else if (atype == double.class || atype == Double.class) { + pk = ((Number) pk).doubleValue(); + } else if (atype == AtomicInteger.class) { + pk = new AtomicInteger(((Number) pk).intValue()); + } else if (atype == AtomicLong.class) { + pk = new AtomicLong(((Number) pk).longValue()); + } else if (atype == LongAdder.class) { + LongAdder la = new LongAdder(); + la.add(((Number) pk).longValue()); + pk = la; + } + } + return this.map.containsKey(pk); + } + + public boolean exists(FilterNode node) { + final Predicate filter = node == null ? null : node.createPredicate(this); + Stream stream = this.list.stream(); + if (filter != null) stream = stream.filter(filter); + return stream.findFirst().isPresent(); + } + + public boolean exists(final Predicate filter) { + return (filter != null) && this.list.stream().filter(filter).findFirst().isPresent(); + } + + public Map queryColumnMap(final String keyColumn, final FilterFunc func, final String funcColumn, FilterNode node) { + final Attribute keyAttr = info.getAttribute(keyColumn); + final Predicate filter = node == null ? null : node.createPredicate(this); + final Attribute funcAttr = funcColumn == null ? null : info.getAttribute(funcColumn); + Stream stream = this.list.stream(); + if (filter != null) stream = stream.filter(filter); + Collector collector = null; + final Class valtype = funcAttr == null ? null : funcAttr.type(); + if (func != null) { + switch (func) { + case AVG: + if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) { + collector = (Collector) Collectors.averagingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue()); + } else { + collector = (Collector) Collectors.averagingLong((T t) -> ((Number) funcAttr.get(t)).longValue()); + } + break; + case COUNT: + collector = (Collector) Collectors.counting(); + break; + case DISTINCTCOUNT: + collector = (Collector) Collectors.mapping((t) -> funcAttr.get(t), Collectors.toSet()); + break; + case MAX: + case MIN: + Comparator comp = (o1, o2) -> o1 == null ? (o2 == null ? 0 : -1) : ((Comparable) funcAttr.get(o1)).compareTo(funcAttr.get(o2)); + collector = (Collector) ((func == MAX) ? Collectors.maxBy(comp) : Collectors.minBy(comp)); + break; + case SUM: + if (valtype == float.class || valtype == Float.class || valtype == double.class || valtype == Double.class) { + collector = (Collector) Collectors.summingDouble((T t) -> ((Number) funcAttr.get(t)).doubleValue()); + } else { + collector = (Collector) Collectors.summingLong((T t) -> ((Number) funcAttr.get(t)).longValue()); + } + break; + } + } + Map rs = collector == null ? stream.collect(Collectors.toMap(t -> keyAttr.get(t), t -> funcAttr.get(t), (key1, key2) -> key2)) + : stream.collect(Collectors.groupingBy(t -> keyAttr.get(t), LinkedHashMap::new, collector)); + if (func == MAX || func == MIN) { + Map rs2 = new LinkedHashMap(); + rs.forEach((x, y) -> { + if (((Optional) y).isPresent()) rs2.put(x, funcAttr.get((T) ((Optional) y).get())); + }); + rs = rs2; + } else if (func == DISTINCTCOUNT) { + Map rs2 = new LinkedHashMap(); + rs.forEach((x, y) -> rs2.put(x, ((Set) y).size() + 0L)); + rs = rs2; + } + return rs; + } + + public Map queryColumnMap(final ColumnNode[] funcNodes, final String[] groupByColumns, FilterNode node) { + final Predicate filter = node == null ? null : node.createPredicate(this); + Stream stream = this.list.stream(); + if (filter != null) stream = stream.filter(filter); + final Attribute[] attrs = new Attribute[groupByColumns.length]; + for (int i = 0; i < groupByColumns.length; i++) { + attrs[i] = info.getAttribute(groupByColumns[i]); + } + final Map valmap = new HashMap<>(); + Function func = t -> { + StringBuilder sb = new StringBuilder(); + final Serializable[] vals = new Serializable[attrs.length]; + for (int i = 0; i < attrs.length; i++) { + vals[i] = attrs[i].get(t); + sb.append((char) 20).append(vals[i]); + } + final String key = sb.toString(); + if (!valmap.containsKey(key)) valmap.put(key, vals); + return valmap.get(key); + }; + Map> listmap = stream.collect(Collectors.groupingBy(func)); + final Map rsmap = new HashMap<>(listmap.size()); + listmap.forEach((k, l) -> rsmap.put(k, queryColumnNumbers(l, funcNodes))); + return rsmap; + } + + private Number[] queryColumnNumbers(final List list, final ColumnNode[] funcNodes) { + if (true) throw new UnsupportedOperationException("Not supported yet."); + Number[] rs = new Number[funcNodes.length]; + for (int i = 0; i < rs.length; i++) { + rs[i] = queryColumnNumber(list, funcNodes[i]); + } + return rs; + } + + private Number queryColumnNumber(final List list, final ColumnNode funcNode) { + if (funcNode instanceof ColumnFuncNode) { + return queryColumnNumber(list, (ColumnFuncNode) funcNode); + } else if (funcNode instanceof ColumnNodeValue) { + return queryColumnNumber(list, (ColumnNodeValue) funcNode); + } else { + return null; + } + } + + private Number queryColumnNumber(final List list, final ColumnFuncNode funcNode) { + if (funcNode.getValue() instanceof String) { + final Attribute attr = info.getAttribute((String) funcNode.getValue()); + final Function attrFunc = x -> (Number) attr.get(x); + return getNumberResult(list, funcNode.getFunc(), null, attr.type(), attrFunc, (FilterNode) null); + } + Number num = null; + if (funcNode.getValue() instanceof ColumnFuncNode) { + num = queryColumnNumber(list, (ColumnFuncNode) funcNode.getValue()); + } else if (funcNode.getValue() instanceof ColumnNodeValue) { + num = queryColumnNumber(list, (ColumnNodeValue) funcNode.getValue()); + } + return num; + } + + private Number queryColumnNumber(final List list, final ColumnNodeValue nodeValue) { + return null; + } + + private Number getNumberResult(final Collection entityList, final FilterFunc func, final Number defResult, final Class attrType, final Function attrFunc, final FilterNode node) { + final Predicate filter = node == null ? null : node.createPredicate(this); + Stream stream = entityList.stream(); + if (filter != null) stream = stream.filter(filter); + switch (func) { + case AVG: + if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { + OptionalDouble rs = stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).average(); + return rs.isPresent() ? (int) rs.getAsDouble() : defResult; + } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class || attrType == LongAdder.class) { + OptionalDouble rs = stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).average(); + return rs.isPresent() ? (long) rs.getAsDouble() : defResult; + } else if (attrType == short.class || attrType == Short.class) { + OptionalDouble rs = stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).average(); + return rs.isPresent() ? (short) rs.getAsDouble() : defResult; + } else if (attrType == float.class || attrType == Float.class) { + OptionalDouble rs = stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).average(); + return rs.isPresent() ? (float) rs.getAsDouble() : defResult; + } else if (attrType == double.class || attrType == Double.class) { + OptionalDouble rs = stream.mapToDouble(x -> (Double) attrFunc.apply(x)).average(); + return rs.isPresent() ? rs.getAsDouble() : defResult; + } + throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); + case COUNT: + return stream.count(); + case DISTINCTCOUNT: + return stream.map(x -> attrFunc.apply(x)).distinct().count(); + + case MAX: + if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { + OptionalInt rs = stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).max(); + return rs.isPresent() ? rs.getAsInt() : defResult; + } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class || attrType == LongAdder.class) { + OptionalLong rs = stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).max(); + return rs.isPresent() ? rs.getAsLong() : defResult; + } else if (attrType == short.class || attrType == Short.class) { + OptionalInt rs = stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).max(); + return rs.isPresent() ? (short) rs.getAsInt() : defResult; + } else if (attrType == float.class || attrType == Float.class) { + OptionalDouble rs = stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).max(); + return rs.isPresent() ? (float) rs.getAsDouble() : defResult; + } else if (attrType == double.class || attrType == Double.class) { + OptionalDouble rs = stream.mapToDouble(x -> (Double) attrFunc.apply(x)).max(); + return rs.isPresent() ? rs.getAsDouble() : defResult; + } + throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); + + case MIN: + if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { + OptionalInt rs = stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).min(); + return rs.isPresent() ? rs.getAsInt() : defResult; + } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class || attrType == LongAdder.class) { + OptionalLong rs = stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).min(); + return rs.isPresent() ? rs.getAsLong() : defResult; + } else if (attrType == short.class || attrType == Short.class) { + OptionalInt rs = stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).min(); + return rs.isPresent() ? (short) rs.getAsInt() : defResult; + } else if (attrType == float.class || attrType == Float.class) { + OptionalDouble rs = stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).min(); + return rs.isPresent() ? (float) rs.getAsDouble() : defResult; + } else if (attrType == double.class || attrType == Double.class) { + OptionalDouble rs = stream.mapToDouble(x -> (Double) attrFunc.apply(x)).min(); + return rs.isPresent() ? rs.getAsDouble() : defResult; + } + throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); + + case SUM: + if (attrType == int.class || attrType == Integer.class || attrType == AtomicInteger.class) { + return stream.mapToInt(x -> ((Number) attrFunc.apply(x)).intValue()).sum(); + } else if (attrType == long.class || attrType == Long.class || attrType == AtomicLong.class || attrType == LongAdder.class) { + return stream.mapToLong(x -> ((Number) attrFunc.apply(x)).longValue()).sum(); + } else if (attrType == short.class || attrType == Short.class) { + return (short) stream.mapToInt(x -> ((Short) attrFunc.apply(x)).intValue()).sum(); + } else if (attrType == float.class || attrType == Float.class) { + return (float) stream.mapToDouble(x -> ((Float) attrFunc.apply(x)).doubleValue()).sum(); + } else if (attrType == double.class || attrType == Double.class) { + return stream.mapToDouble(x -> (Double) attrFunc.apply(x)).sum(); + } + throw new RuntimeException("getNumberResult error(type:" + type + ", attr.type: " + attrType); + } + return defResult; + } + + public Number getNumberResult(final FilterFunc func, final Number defResult, final String column, final FilterNode node) { + final Attribute attr = column == null ? null : info.getAttribute(column); //COUNT的column=null + final Function attrFunc = attr == null ? null : x -> (Number) attr.get(x); + return getNumberResult(this.list, func, defResult, attr == null ? null : attr.type(), attrFunc, node); + } + + public Sheet querySheet(final SelectColumn selects, final Flipper flipper, final FilterNode node) { + return querySheet(true, false, selects, flipper, node); + } + + protected Stream distinctStream(Stream stream, final List> keyattrs) { + if (keyattrs == null) return stream; + final Set keys = new HashSet<>(); + Predicate filter = t -> { + StringBuilder sb = new StringBuilder(); + for (Attribute attr : keyattrs) { + sb.append(attr.get(t)); + } + String key = sb.toString(); + if (keys.contains(key)) return false; + keys.add(key); + return true; + }; + return stream.filter(filter); + } + + public Sheet querySheet(final boolean needtotal, final boolean distinct, final SelectColumn selects, final Flipper flipper, FilterNode node) { + final Predicate filter = node == null ? null : node.createPredicate(this); + final Comparator comparator = createComparator(flipper); + long total = 0; + List> keyattrs = null; + if (distinct) { + final List> attrs = new ArrayList<>(); + info.forEachAttribute((k, v) -> { + if (selects == null || selects.test(k)) attrs.add(v); + }); + keyattrs = attrs; + } + if (needtotal) { + Stream stream = this.list.stream(); + if (filter != null) stream = stream.filter(filter); + if (distinct) stream = distinctStream(stream, keyattrs); + total = stream.count(); + } + if (needtotal && total == 0) return new Sheet<>(0, new ArrayList()); + Stream stream = this.list.stream(); + if (filter != null) stream = stream.filter(filter); + if (distinct) stream = distinctStream(stream, keyattrs); + if (comparator != null) stream = stream.sorted(comparator); + if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset()); + if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit()); + final List rs = new ArrayList<>(); + if (selects == null) { + Consumer action = x -> rs.add(needcopy ? newReproduce.apply(creator.create(), x) : x); + if (comparator != null) { + stream.forEachOrdered(action); + } else { + stream.forEach(action); + } + } else { + final List> attrs = new ArrayList<>(); + info.forEachAttribute((k, v) -> { + if (selects.test(k)) attrs.add(v); + }); + Consumer action = x -> { + final T item = creator.create(); + for (Attribute attr : attrs) { + attr.set(item, attr.get(x)); + } + rs.add(item); + }; + if (comparator != null) { + stream.forEachOrdered(action); + } else { + stream.forEach(action); + } + } + if (!needtotal) total = rs.size(); + return new Sheet<>(total, rs); + } + + public int insert(T entity) { + if (entity == null) return 0; + final T rs = newReproduce.apply(this.creator.create(), entity); //确保同一主键值的map与list中的对象必须共用。 + T old = this.map.putIfAbsent(this.primary.get(rs), rs); + if (old == null) { + this.list.add(rs); + return 1; + } else { + logger.log(Level.WARNING, this.type + " cache repeat insert data: " + entity); + return 0; + } + } + + public int delete(final Serializable pk) { + if (pk == null) return 0; + final T rs = this.map.remove(pk); + if (rs == null) return 0; + this.list.remove(rs); + return 1; + } + + public Serializable[] delete(final Flipper flipper, final FilterNode node) { + if (node == null || this.list.isEmpty()) return new Serializable[0]; + final Comparator comparator = createComparator(flipper); + Stream stream = this.list.stream().filter(node.createPredicate(this)); + if (comparator != null) stream = stream.sorted(comparator); + if (flipper != null && flipper.getOffset() > 0) stream = stream.skip(flipper.getOffset()); + if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit()); + Object[] rms = stream.toArray(); + Serializable[] ids = new Serializable[rms.length]; + int i = -1; + for (Object o : rms) { + final T t = (T) o; + ids[++i] = this.primary.get(t); + this.map.remove(ids[i]); + this.list.remove(t); + } + return ids; + } + + public int drop() { + return clear(); + } + + public int update(final T entity) { + if (entity == null) return 0; + T rs = this.map.get(this.primary.get(entity)); + if (rs == null) return 0; + synchronized (rs) { + this.chgReproduce.apply(rs, entity); + } + return 1; + } + + public T update(final T entity, Collection> attrs) { + if (entity == null) return entity; + T rs = this.map.get(this.primary.get(entity)); + if (rs == null) return rs; + synchronized (rs) { + for (Attribute attr : attrs) { + attr.set(rs, attr.get(entity)); + } + } + return rs; + } + + public T[] update(final T entity, final Collection> attrs, final FilterNode node) { + if (entity == null || node == null) return (T[]) Array.newInstance(type, 0); + T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); + for (T rs : rms) { + synchronized (rs) { + for (Attribute attr : attrs) { + attr.set(rs, attr.get(entity)); + } + } + } + return rms; + } + + public T update(final Serializable pk, Attribute attr, final V fieldValue) { + if (pk == null) return null; + T rs = this.map.get(pk); + if (rs != null) attr.set(rs, fieldValue); + return rs; + } + + public T[] update(Attribute attr, final V fieldValue, final FilterNode node) { + if (attr == null || node == null) return (T[]) Array.newInstance(type, 0); + T[] rms = this.list.stream().filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); + for (T rs : rms) { + attr.set(rs, fieldValue); + } + return rms; + } + + public T updateColumn(final Serializable pk, List> attrs, final List values) { + if (pk == null || attrs == null || attrs.isEmpty()) return null; + T rs = this.map.get(pk); + if (rs == null) return rs; + synchronized (rs) { + for (int i = 0; i < attrs.size(); i++) { + ColumnValue cv = values.get(i); + updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue()); + } + } + return rs; + } + + public T[] updateColumn(final FilterNode node, final Flipper flipper, List> attrs, final List values) { + if (attrs == null || attrs.isEmpty() || node == null) return (T[]) Array.newInstance(type, 0); + Stream stream = this.list.stream(); + final Comparator comparator = createComparator(flipper); + if (comparator != null) stream = stream.sorted(comparator); + if (flipper != null && flipper.getLimit() > 0) stream = stream.limit(flipper.getLimit()); + T[] rms = stream.filter(node.createPredicate(this)).toArray(len -> (T[]) Array.newInstance(type, len)); + for (T rs : rms) { + synchronized (rs) { + for (int i = 0; i < attrs.size(); i++) { + ColumnValue cv = values.get(i); + updateColumn(attrs.get(i), rs, cv.getExpress(), cv.getValue()); + } + } + } + return rms; + } + + public T updateColumnOr(final Serializable pk, Attribute attr, final long orvalue) { + if (pk == null) return null; + T rs = this.map.get(pk); + if (rs == null) return rs; + synchronized (rs) { + return updateColumn(attr, rs, ColumnExpress.ORR, orvalue); + } + } + + public T updateColumnAnd(final Serializable pk, Attribute attr, final long andvalue) { + if (pk == null) return null; + T rs = this.map.get(pk); + if (rs == null) return rs; + synchronized (rs) { + return updateColumn(attr, rs, ColumnExpress.AND, andvalue); + } + } + + public T updateColumnIncrement(final Serializable pk, Attribute attr, final long incvalue) { + if (pk == null) return null; + T rs = this.map.get(pk); + if (rs == null) return rs; + synchronized (rs) { + return updateColumn(attr, rs, ColumnExpress.INC, incvalue); + } + } + + public T updateColumnDecrement(final Serializable pk, Attribute attr, final long incvalue) { + if (pk == null) return null; + T rs = this.map.get(pk); + if (rs == null) return rs; + synchronized (rs) { + return updateColumn(attr, rs, ColumnExpress.DEC, incvalue); + } + } + + private T updateColumn(Attribute attr, final T entity, final ColumnExpress express, Serializable val) { + final Class ft = attr.type(); + Number numb = null; + Serializable newval = null; + switch (express) { + case INC: + case DEC: + case MUL: + case DIV: + case MOD: + case AND: + case ORR: + numb = getValue((Number) attr.get(entity), express, val); + break; + case MOV: + if (val instanceof ColumnNodeValue) val = updateColumnNodeValue(attr, entity, (ColumnNodeValue) val); + newval = val; + if (val instanceof Number) numb = (Number) val; + break; + } + if (numb != null) { + if (ft == int.class || ft == Integer.class) { + newval = numb.intValue(); + } else if (ft == long.class || ft == Long.class) { + newval = numb.longValue(); + } else if (ft == short.class || ft == Short.class) { + newval = numb.shortValue(); + } else if (ft == float.class || ft == Float.class) { + newval = numb.floatValue(); + } else if (ft == double.class || ft == Double.class) { + newval = numb.doubleValue(); + } else if (ft == byte.class || ft == Byte.class) { + newval = numb.byteValue(); + } else if (ft == AtomicInteger.class) { + newval = new AtomicInteger(numb.intValue()); + } else if (ft == AtomicLong.class) { + newval = new AtomicLong(numb.longValue()); + } else if (ft == LongAdder.class) { + LongAdder la = new LongAdder(); + la.add(numb.longValue()); + newval = la; + } + } else { + if (ft == AtomicInteger.class && newval != null && newval.getClass() != AtomicInteger.class) { + newval = new AtomicInteger(((Number) newval).intValue()); + } else if (ft == AtomicLong.class && newval != null && newval.getClass() != AtomicLong.class) { + newval = new AtomicLong(((Number) newval).longValue()); + } else if (ft == LongAdder.class && newval != null && newval.getClass() != LongAdder.class) { + LongAdder la = new LongAdder(); + la.add(((Number) newval).longValue()); + newval = la; + } + } + attr.set(entity, (V) newval); + return entity; + } + + private Serializable updateColumnNodeValue(Attribute attr, final T entity, ColumnNodeValue node) { + Serializable left = node.getLeft(); + if (left instanceof CharSequence) { + left = info.getUpdateAttribute(left.toString()).get(entity); + if (node.getExpress() == ColumnExpress.MOV) return left; + } else if (left instanceof ColumnNodeValue) { + left = updateColumnNodeValue(attr, entity, (ColumnNodeValue) left); + } + Serializable right = node.getRight(); + if (left instanceof CharSequence) { + right = info.getUpdateAttribute(right.toString()).get(entity); + } else if (left instanceof ColumnNodeValue) { + right = updateColumnNodeValue(attr, entity, (ColumnNodeValue) right); + } + return getValue((Number) left, node.getExpress(), right); + } + + private Number getValue(Number numb, final ColumnExpress express, Serializable val) { + switch (express) { + case INC: + if (numb == null) { + numb = (Number) val; + } else { + if (numb instanceof Float || ((Number) val) instanceof Float) { + numb = numb.floatValue() + ((Number) val).floatValue(); + } else if (numb instanceof Double || ((Number) val) instanceof Double) { + numb = numb.doubleValue() + ((Number) val).doubleValue(); + } else { + numb = numb.longValue() + ((Number) val).longValue(); + } + } + break; + case DEC: + if (numb == null) { + numb = (Number) val; + } else { + if (numb instanceof Float || ((Number) val) instanceof Float) { + numb = numb.floatValue() - ((Number) val).floatValue(); + } else if (numb instanceof Double || ((Number) val) instanceof Double) { + numb = numb.doubleValue() - ((Number) val).doubleValue(); + } else { + numb = numb.longValue() - ((Number) val).longValue(); + } + } + break; + case MUL: + if (numb == null) { + numb = 0; + } else { + numb = numb.longValue() * ((Number) val).floatValue(); + } + break; + case DIV: + if (numb == null) { + numb = 0; + } else { + numb = numb.longValue() / ((Number) val).floatValue(); + } + break; + case MOD: + if (numb == null) { + numb = 0; + } else { + numb = numb.longValue() % ((Number) val).intValue(); + } + break; + case AND: + if (numb == null) { + numb = 0; + } else { + numb = numb.longValue() & ((Number) val).longValue(); + } + break; + case ORR: + if (numb == null) { + numb = 0; + } else { + numb = numb.longValue() | ((Number) val).longValue(); + } + break; + } + return numb; + } + + public Attribute getAttribute(String fieldname) { + return info.getAttribute(fieldname); + } + + //------------------------------------------------------------------------------------------------------------------------------- + protected Comparator createComparator(Flipper flipper) { + if (flipper == null || flipper.getSort() == null || flipper.getSort().isEmpty() || flipper.getSort().indexOf(';') >= 0 || flipper.getSort().indexOf('\n') >= 0) return null; + final String sort = flipper.getSort(); + Comparator comparator = this.sortComparators.get(sort); + if (comparator != null) return comparator; + for (String item : sort.split(",")) { + if (item.trim().isEmpty()) continue; + String[] sub = item.trim().split("\\s+"); + int pos = sub[0].indexOf('('); + Attribute attr; + if (pos <= 0) { + attr = getAttribute(sub[0]); + } else { //含SQL函数 + int pos2 = sub[0].lastIndexOf(')'); + final Attribute pattr = getAttribute(sub[0].substring(pos + 1, pos2)); + final String func = sub[0].substring(0, pos); + if ("ABS".equalsIgnoreCase(func)) { + Function getter = null; + if (pattr.type() == int.class || pattr.type() == Integer.class || pattr.type() == AtomicInteger.class) { + getter = x -> Math.abs(((Number) pattr.get((T) x)).intValue()); + } else if (pattr.type() == long.class || pattr.type() == Long.class || pattr.type() == AtomicLong.class || pattr.type() == LongAdder.class) { + getter = x -> Math.abs(((Number) pattr.get((T) x)).longValue()); + } else if (pattr.type() == float.class || pattr.type() == Float.class) { + getter = x -> Math.abs(((Number) pattr.get((T) x)).floatValue()); + } else if (pattr.type() == double.class || pattr.type() == Double.class) { + getter = x -> Math.abs(((Number) pattr.get((T) x)).doubleValue()); + } else { + throw new RuntimeException("Flipper not supported sort illegal type by ABS (" + flipper.getSort() + ")"); + } + attr = (Attribute) Attribute.create(pattr.declaringClass(), pattr.field(), pattr.type(), getter, (o, v) -> pattr.set(o, v)); + } else if (func.isEmpty()) { + attr = pattr; + } else { + throw new RuntimeException("Flipper not supported sort illegal function (" + flipper.getSort() + ")"); + } + } + Comparator c = (sub.length > 1 && sub[1].equalsIgnoreCase("DESC")) ? (T o1, T o2) -> { + Comparable c1 = (Comparable) attr.get(o1); + Comparable c2 = (Comparable) attr.get(o2); + return c2 == null ? -1 : c2.compareTo(c1); + } : (T o1, T o2) -> { + Comparable c1 = (Comparable) attr.get(o1); + Comparable c2 = (Comparable) attr.get(o2); + return c1 == null ? -1 : c1.compareTo(c2); + }; + + if (comparator == null) { + comparator = c; + } else { + comparator = comparator.thenComparing(c); + } + } + this.sortComparators.put(sort, comparator); + return comparator; + } + + private static class UniqueSequence implements Serializable { + + private final Serializable[] value; + + public UniqueSequence(Serializable[] val) { + this.value = val; + } + + @Override + public int hashCode() { + return Arrays.deepHashCode(this.value); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final UniqueSequence other = (UniqueSequence) obj; + if (value.length != other.value.length) return false; + for (int i = 0; i < value.length; i++) { + if (!value[i].equals(other.value[i])) return false; + } + return true; + } + + } + + private static interface UniqueAttribute extends Predicate { + + public Serializable getValue(T bean); + + @Override + public boolean test(FilterNode node); + + public static UniqueAttribute create(final Attribute[] attributes) { + if (attributes.length == 1) { + final Attribute attribute = attributes[0]; + return new UniqueAttribute() { + + @Override + public Serializable getValue(T bean) { + return attribute.get(bean); + } + + @Override + public boolean test(FilterNode node) { + if (node == null || node.isOr()) return false; + if (!attribute.field().equals(node.column)) return false; + if (node.nodes == null) return true; + for (FilterNode n : node.nodes) { + if (!test(n)) return false; + } + return true; + } + }; + } else { + return new UniqueAttribute() { + + @Override + public Serializable getValue(T bean) { + final Serializable[] rs = new Serializable[attributes.length]; + for (int i = 0; i < rs.length; i++) { + rs[i] = attributes[i].get(bean); + } + return new UniqueSequence(rs); + } + + @Override + public boolean test(FilterNode node) { + return true; + } + }; + } + } + } + +} diff --git a/src/org/redkale/source/EntityInfo.java b/src/main/java/org/redkale/source/EntityInfo.java similarity index 76% rename from src/org/redkale/source/EntityInfo.java rename to src/main/java/org/redkale/source/EntityInfo.java index f5a9f5ad8..bb493fd5b 100644 --- a/src/org/redkale/source/EntityInfo.java +++ b/src/main/java/org/redkale/source/EntityInfo.java @@ -1,1282 +1,1396 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.lang.reflect.*; -import java.sql.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.logging.*; -import javax.persistence.*; -import org.redkale.convert.json.*; -import org.redkale.util.*; - -/** - * Entity操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Entity类的泛型 - */ -@SuppressWarnings("unchecked") -public final class EntityInfo { - - private static final JsonConvert DEFAULT_JSON_CONVERT = JsonFactory.create().skipAllIgnore(true).getConvert(); - - //全局静态资源 - private static final ConcurrentHashMap entityInfos = new ConcurrentHashMap<>(); - - //日志 - private static final Logger logger = Logger.getLogger(EntityInfo.class.getSimpleName()); - - //Entity类名 - private final Class type; - - //类对应的数据表名, 如果是VirtualEntity 类, 则该字段为null - final String table; - - //JsonConvert - final JsonConvert jsonConvert; - - //Entity构建器 - private final Creator creator; - - //Entity构建器参数 - private final String[] constructorParameters; - - //Entity构建器参数Attribute - private final Attribute[] constructorAttributes; - - //Entity构建器参数Attribute - private final Attribute[] unconstructorAttributes; - - //主键 - final Attribute primary; - - //Entity缓存对象 - private final EntityCache cache; - - //用于存储绑定在EntityInfo上的对象 - private final ConcurrentHashMap subobjectMap = new ConcurrentHashMap<>(); - - //key是field的name, 不是sql字段。 - //存放所有与数据库对应的字段, 包括主键 - private final HashMap> attributeMap = new HashMap<>(); - - //存放所有与数据库对应的字段, 包括主键 - final Attribute[] attributes; - - //key是field的name, value是Column的别名,即数据库表的字段名 - //只有field.name 与 Column.name不同才存放在aliasmap里. - private final Map aliasmap; - - //key是field的name, value是CryptHandler - //字段都不存在CryptHandler时值因为为null,减少判断 - private final Map cryptmap; - - //所有可更新字段,即排除了主键字段和标记为@Column(updatable=false)的字段 - private final Map> updateAttributeMap = new HashMap<>(); - - //用于反向LIKE使用 - final String containSQL; - - //用于反向LIKE使用 - final String notcontainSQL; - - //用于判断表不存在的使用, 多个SQLState用;隔开 - private final String tablenotexistSqlstates; - - //用于复制表结构使用 - private final String tablecopySQL; - - //用于存在database.table_20160202类似这种分布式表 - private final Set tables = new CopyOnWriteArraySet<>(); - - //不能为null的字段名 - private final Set notNullColumns = new CopyOnWriteArraySet<>(); - - //分表 策略 - private final DistributeTableStrategy tableStrategy; - - //根据主键查找所有对象的SQL - private final String allQueryPrepareSQL; - - //根据主键查找单个对象的SQL, 含 ? - private final String findPrepareSQL; - - //根据主键查找单个对象的SQL, 含 $ - private final String findDollarPrepareSQL; - - //根据主键查找单个对象的SQL, 含 :name - private final String findNamesPrepareSQL; - - //数据库中所有字段 - private final Attribute[] queryAttributes; - - //新增SQL, 含 ?,即排除了自增长主键和标记为@Column(insertable=false)的字段 - private final String insertPrepareSQL; - - //新增SQL, 含 $,即排除了自增长主键和标记为@Column(insertable=false)的字段 - private final String insertDollarPrepareSQL; - - //新增SQL, 含 :name,即排除了自增长主键和标记为@Column(insertable=false)的字段 - private final String insertNamesPrepareSQL; - - //数据库中所有可新增字段 - final Attribute[] insertAttributes; - - //根据主键更新所有可更新字段的SQL,含 ? - private final String updatePrepareSQL; - - //根据主键更新所有可更新字段的SQL,含 $ - private final String updateDollarPrepareSQL; - - //根据主键更新所有可更新字段的SQL,含 :name - private final String updateNamesPrepareSQL; - - //数据库中所有可更新字段 - final Attribute[] updateAttributes; - - //根据主键删除记录的SQL,含 ? - private final String deletePrepareSQL; - - //根据主键删除记录的SQL,含 $ - private final String deleteDollarPrepareSQL; - - //根据主键删除记录的SQL,含 :name - private final String deleteNamesPrepareSQL; - - //日志级别,从LogLevel获取 - private final int logLevel; - - //日志控制 - private final Map excludeLogLevels; - - //Flipper.sort转换成以ORDER BY开头SQL的缓存 - private final Map sortOrderbySqls = new ConcurrentHashMap<>(); - - //所属的DataSource - final DataSource source; - - //全量数据的加载器 - final BiFunction> fullloader; - //------------------------------------------------------------ - - /** - * 加载EntityInfo - * - * @param clazz Entity类 - * @param cacheForbidden 是否禁用EntityCache - * @param conf 配置信息, persistence.xml中的property节点值 - * @param source DataSource,可为null - * @param fullloader 全量加载器,可为null - */ - static EntityInfo load(Class clazz, final boolean cacheForbidden, final Properties conf, - DataSource source, BiFunction> fullloader) { - EntityInfo rs = entityInfos.get(clazz); - if (rs != null && (rs.cache == null || rs.cache.isFullLoaded())) return rs; - synchronized (entityInfos) { - rs = entityInfos.get(clazz); - if (rs == null) { - rs = new EntityInfo(clazz, cacheForbidden, conf, source, fullloader); - entityInfos.put(clazz, rs); - } - if (rs.cache != null && !rs.isCacheFullLoaded()) { - if (fullloader == null) throw new IllegalArgumentException(clazz.getName() + " auto loader is illegal"); - rs.cache.fullLoadAsync(); - } - return rs; - } - } - - /** - * 获取Entity类对应的EntityInfo对象 - * - * @param 泛型 - * @param clazz Entity类 - * - * @return EntityInfo - */ - static EntityInfo get(Class clazz) { - return entityInfos.get(clazz); - } - - /** - * 构造函数 - * - * @param type Entity类 - * @param cacheForbidden 是否禁用EntityCache - * @param conf 配置信息, persistence.xml中的property节点值 - * @param source DataSource,可为null - * @param fullloader 全量加载器,可为null - */ - private EntityInfo(Class type, final boolean cacheForbidden, - Properties conf, DataSource source, BiFunction> fullloader) { - this.type = type; - this.source = source; - //--------------------------------------------- - - LogLevel ll = type.getAnnotation(LogLevel.class); - this.logLevel = ll == null ? Integer.MIN_VALUE : Level.parse(ll.value()).intValue(); - Map> logmap = new HashMap<>(); - for (LogExcludeLevel lel : type.getAnnotationsByType(LogExcludeLevel.class)) { - for (String onelevel : lel.levels()) { - int level = Level.parse(onelevel).intValue(); - HashSet set = logmap.get(level); - if (set == null) { - set = new HashSet<>(); - logmap.put(level, set); - } - for (String key : lel.keys()) { - set.add(key); - } - } - } - if (logmap.isEmpty()) { - this.excludeLogLevels = null; - } else { - this.excludeLogLevels = new HashMap<>(); - logmap.forEach((l, set) -> excludeLogLevels.put(l, set.toArray(new String[set.size()]))); - } - //--------------------------------------------- - Table t = type.getAnnotation(Table.class); - if (type.getAnnotation(VirtualEntity.class) != null || (source == null || "memory".equalsIgnoreCase(source.getType()))) { - this.table = null; - BiFunction> loader = null; - try { - VirtualEntity ve = type.getAnnotation(VirtualEntity.class); - if (ve != null) loader = ve.loader().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - logger.log(Level.SEVERE, type + " init @VirtualEntity.loader error", e); - } - this.fullloader = loader; - } else { - this.fullloader = fullloader; - if (t != null && !t.name().isEmpty() && t.name().indexOf('.') >= 0) throw new RuntimeException(type + " have illegal table.name on @Table"); - this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name()) : (t.catalog() + '.' + (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name())); - } - DistributeTable dt = type.getAnnotation(DistributeTable.class); - DistributeTableStrategy dts = null; - try { - dts = (dt == null) ? null : dt.strategy().getDeclaredConstructor().newInstance(); - } catch (Exception e) { - logger.log(Level.SEVERE, type + " init DistributeTableStrategy error", e); - } - this.tableStrategy = dts; - - this.creator = Creator.create(type); - ConstructorParameters cp = null; - try { - cp = this.creator.getClass().getMethod("create", Object[].class).getAnnotation(ConstructorParameters.class); - } catch (Exception e) { - logger.log(Level.SEVERE, type + " cannot find ConstructorParameters Creator", e); - } - this.constructorParameters = (cp == null || cp.value().length < 1) ? null : cp.value(); - Attribute idAttr0 = null; - Map cryptmap0 = null; - Map aliasmap0 = null; - Class cltmp = type; - Set fields = new HashSet<>(); - List> queryattrs = new ArrayList<>(); - List insertcols = new ArrayList<>(); - List> insertattrs = new ArrayList<>(); - List updatecols = new ArrayList<>(); - List> updateattrs = new ArrayList<>(); - Map> cryptCreatorMap = new HashMap<>(); - do { - for (Field field : cltmp.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (Modifier.isFinal(field.getModifiers())) continue; - if (field.getAnnotation(Transient.class) != null) continue; - if (fields.contains(field.getName())) continue; - final String fieldname = field.getName(); - final Column col = field.getAnnotation(Column.class); - final String sqlfield = col == null || col.name().isEmpty() ? fieldname : col.name(); - if (!fieldname.equals(sqlfield)) { - if (aliasmap0 == null) aliasmap0 = new HashMap<>(); - aliasmap0.put(fieldname, sqlfield); - } - final CryptColumn cpt = field.getAnnotation(CryptColumn.class); - CryptHandler cryptHandler = null; - if (cpt != null) { - if (cryptmap0 == null) cryptmap0 = new HashMap<>(); - cryptHandler = cryptCreatorMap.computeIfAbsent(cpt.handler(), c -> (Creator) Creator.create(cpt.handler())).create(); - cryptmap0.put(fieldname, cryptHandler); - } - Attribute attr; - try { - attr = Attribute.create(type, cltmp, field, cryptHandler); - } catch (RuntimeException e) { - continue; - } - if (field.getAnnotation(javax.persistence.Id.class) != null && idAttr0 == null) { - idAttr0 = attr; - insertcols.add(sqlfield); - insertattrs.add(attr); - } else { - if (col == null || col.insertable()) { - insertcols.add(sqlfield); - insertattrs.add(attr); - } - if (col == null || col.updatable()) { - updatecols.add(sqlfield); - updateattrs.add(attr); - updateAttributeMap.put(fieldname, attr); - } - if (col != null && !col.nullable()) { - notNullColumns.add(fieldname); - } - } - queryattrs.add(attr); - fields.add(fieldname); - attributeMap.put(fieldname, attr); - } - } while ((cltmp = cltmp.getSuperclass()) != Object.class); - if (idAttr0 == null) throw new RuntimeException(type.getName() + " have no primary column by @javax.persistence.Id"); - cltmp = type; - JsonConvert convert = DEFAULT_JSON_CONVERT; - do { - for (Method method : cltmp.getDeclaredMethods()) { - if (method.getAnnotation(SourceConvert.class) == null) continue; - if (!Modifier.isStatic(method.getModifiers())) throw new RuntimeException("@SourceConvert method(" + method + ") must be static"); - if (method.getReturnType() != JsonConvert.class) throw new RuntimeException("@SourceConvert method(" + method + ") must be return JsonConvert.class"); - if (method.getParameterCount() > 0) throw new RuntimeException("@SourceConvert method(" + method + ") must be 0 parameter"); - try { - method.setAccessible(true); - convert = (JsonConvert) method.invoke(null); - } catch (Exception e) { - throw new RuntimeException(method + " invoke error", e); - } - if (convert != null) break; - } - } while ((cltmp = cltmp.getSuperclass()) != Object.class); - this.jsonConvert = convert == null ? DEFAULT_JSON_CONVERT : convert; - - this.primary = idAttr0; - this.aliasmap = aliasmap0; - this.cryptmap = cryptmap0; - this.attributes = attributeMap.values().toArray(new Attribute[attributeMap.size()]); - this.queryAttributes = queryattrs.toArray(new Attribute[queryattrs.size()]); - this.insertAttributes = insertattrs.toArray(new Attribute[insertattrs.size()]); - this.updateAttributes = updateattrs.toArray(new Attribute[updateattrs.size()]); - if (this.constructorParameters == null) { - this.constructorAttributes = null; - this.unconstructorAttributes = null; - } else { - this.constructorAttributes = new Attribute[this.constructorParameters.length]; - List> unconstructorAttrs = new ArrayList<>(); - for (Attribute attr : queryAttributes) { - int pos = -1; - for (int i = 0; i < this.constructorParameters.length; i++) { - if (attr.field().equals(this.constructorParameters[i])) { - pos = i; - break; - } - } - if (pos >= 0) { - this.constructorAttributes[pos] = attr; - } else { - unconstructorAttrs.add(attr); - } - } - this.unconstructorAttributes = unconstructorAttrs.toArray(new Attribute[unconstructorAttrs.size()]); - } - if (table != null) { - StringBuilder insertsb = new StringBuilder(); - StringBuilder insertsbjdbc = new StringBuilder(); - StringBuilder insertsbdollar = new StringBuilder(); - StringBuilder insertsbnames = new StringBuilder(); - int index = 0; - for (String col : insertcols) { - if (index > 0) insertsb.append(','); - insertsb.append(col); - if (index > 0) { - insertsbjdbc.append(','); - insertsbdollar.append(','); - insertsbnames.append(','); - } - insertsbjdbc.append('?'); - insertsbdollar.append("$").append(++index); - insertsbnames.append(":").append(col); - } - this.insertPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbjdbc + ")"; - this.insertDollarPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbdollar + ")"; - this.insertNamesPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbnames + ")"; - StringBuilder updatesb = new StringBuilder(); - StringBuilder updatesbdollar = new StringBuilder(); - StringBuilder updatesbnames = new StringBuilder(); - index = 0; - for (String col : updatecols) { - if (index > 0) { - updatesb.append(", "); - updatesbdollar.append(", "); - updatesbnames.append(", "); - } - updatesb.append(col).append(" = ?"); - updatesbdollar.append(col).append(" = ").append("$").append(++index); - updatesbnames.append(col).append(" = :").append(col); - } - this.updatePrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesb + " WHERE " + getPrimarySQLColumn(null) + " = ?"; - this.updateDollarPrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesbdollar + " WHERE " + getPrimarySQLColumn(null) + " = $" + (++index); - this.updateNamesPrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesbnames + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null); - this.deletePrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = ?"; - this.deleteDollarPrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = $1"; - this.deleteNamesPrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null); - this.allQueryPrepareSQL = "SELECT * FROM " + table; - this.findPrepareSQL = "SELECT * FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = ?"; - this.findDollarPrepareSQL = "SELECT * FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = $1"; - this.findNamesPrepareSQL = "SELECT * FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null); - } else { - this.insertPrepareSQL = null; - this.updatePrepareSQL = null; - this.deletePrepareSQL = null; - this.findPrepareSQL = null; - this.allQueryPrepareSQL = null; - - this.insertDollarPrepareSQL = null; - this.updateDollarPrepareSQL = null; - this.deleteDollarPrepareSQL = null; - this.findDollarPrepareSQL = null; - - this.insertNamesPrepareSQL = null; - this.updateNamesPrepareSQL = null; - this.deleteNamesPrepareSQL = null; - this.findNamesPrepareSQL = null; - } - //----------------cache-------------- - Cacheable c = type.getAnnotation(Cacheable.class); - if (this.table == null || (!cacheForbidden && c != null && c.value())) { - this.cache = new EntityCache<>(this, c); - } else { - this.cache = null; - } - if (conf == null) conf = new Properties(); - this.containSQL = conf.getProperty(DataSources.JDBC_CONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) > 0"); - this.notcontainSQL = conf.getProperty(DataSources.JDBC_NOTCONTAIN_SQLTEMPLATE, "LOCATE(${keystr}, ${column}) = 0"); - - this.tablenotexistSqlstates = ";" + conf.getProperty(DataSources.JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02") + ";"; - this.tablecopySQL = conf.getProperty(DataSources.JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} LIKE ${oldtable}"); - } - - @SuppressWarnings("unchecked") - public V getSubobject(String name) { - return (V) this.subobjectMap.get(name); - } - - public void setSubobject(String name, Object value) { - this.subobjectMap.put(name, value); - } - - public void removeSubobject(String name) { - this.subobjectMap.remove(name); - } - - public void clearSubobjects() { - this.subobjectMap.clear(); - } - - /** - * 获取JsonConvert - * - * @return JsonConvert - */ - public JsonConvert getJsonConvert() { - return jsonConvert; - } - - /** - * 获取Entity缓存器 - * - * @return EntityCache - */ - public EntityCache getCache() { - return cache; - } - - /** - * 判断缓存器是否已经全量加载过 - * - * @return boolean - */ - public boolean isCacheFullLoaded() { - return cache != null && cache.isFullLoaded(); - } - - /** - * 获取Entity构建器 - * - * @return Creator - */ - public Creator getCreator() { - return creator; - } - - /** - * 获取Entity类名 - * - * @return Class - */ - public Class getType() { - return type; - } - - /** - * 判断Entity是否为虚拟类 - * - * @return boolean - */ - public boolean isVirtualEntity() { - return table == null; - } - - public DistributeTableStrategy getTableStrategy() { - return tableStrategy; - } - - public Object disTableLock() { - return tables; - } - - public boolean containsDisTable(String tablekey) { - return tables.contains(tablekey); - } - - public void addDisTable(String tablekey) { - tables.add(tablekey); - } - - public boolean removeDisTable(String tablekey) { - return tables.remove(tablekey); - } - - public String getTableNotExistSqlStates2() { - return tablenotexistSqlstates; - } - - public boolean isTableNotExist(String code) { - return tablenotexistSqlstates.contains(';' + code + ';'); - } - - public boolean isTableNotExist(SQLException e) { - if (e == null) return false; - return tablenotexistSqlstates.contains(';' + e.getSQLState() + ';'); - } - - public Attribute[] getInsertAttributes() { - return insertAttributes; - } - - public Attribute[] getUpdateAttributes() { - return updateAttributes; - } - - public Attribute[] getQueryAttributes() { - return queryAttributes; - } - - /** - * 获取Entity的QUERY SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getFindPrepareSQL(T bean) { - if (this.tableStrategy == null) return findPrepareSQL; - return findPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的QUERY SQL - * - * - * @return String - */ - public String getAllQueryPrepareSQL() { - return this.allQueryPrepareSQL; - } - - /** - * 获取Entity的QUERY SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getFindDollarPrepareSQL(T bean) { - if (this.tableStrategy == null) return findDollarPrepareSQL; - return findDollarPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的QUERY SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getFindNamesPrepareSQL(T bean) { - if (this.tableStrategy == null) return findNamesPrepareSQL; - return findNamesPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的INSERT SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getInsertPrepareSQL(T bean) { - if (this.tableStrategy == null) return insertPrepareSQL; - return insertPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的INSERT SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getInsertDollarPrepareSQL(T bean) { - if (this.tableStrategy == null) return insertDollarPrepareSQL; - return insertDollarPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的INSERT SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getInsertNamesPrepareSQL(T bean) { - if (this.tableStrategy == null) return insertNamesPrepareSQL; - return insertNamesPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的UPDATE SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getUpdatePrepareSQL(T bean) { - if (this.tableStrategy == null) return updatePrepareSQL; - return updatePrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的UPDATE SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getUpdateDollarPrepareSQL(T bean) { - if (this.tableStrategy == null) return updateDollarPrepareSQL; - return updateDollarPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的UPDATE SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getUpdateNamesPrepareSQL(T bean) { - if (this.tableStrategy == null) return updateNamesPrepareSQL; - return updateNamesPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的DELETE SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getDeletePrepareSQL(T bean) { - if (this.tableStrategy == null) return deletePrepareSQL; - return deletePrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的DELETE SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getDeleteDollarPrepareSQL(T bean) { - if (this.tableStrategy == null) return deleteDollarPrepareSQL; - return deleteDollarPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取Entity的DELETE SQL - * - * @param bean Entity对象 - * - * @return String - */ - public String getDeleteNamesPrepareSQL(T bean) { - if (this.tableStrategy == null) return deleteNamesPrepareSQL; - return deleteNamesPrepareSQL.replace("${newtable}", getTable(bean)); - } - - /** - * 获取查询字段列表 - * - * @param tabalis 表别名 - * @param selects 过滤字段 - * - * @return String - */ - public CharSequence getQueryColumns(String tabalis, SelectColumn selects) { - if (selects == null) return tabalis == null ? "*" : (tabalis + ".*"); - StringBuilder sb = new StringBuilder(); - for (Attribute attr : this.attributes) { - if (!selects.test(attr.field())) continue; - if (sb.length() > 0) sb.append(','); - sb.append(getSQLColumn(tabalis, attr.field())); - } - if (sb.length() == 0) sb.append('*'); - return sb; - } - - public String getTableCopySQL(String newTable) { - return tablecopySQL.replace("${newtable}", newTable).replace("${oldtable}", table); - } - - /** - * 根据主键值获取Entity的表名 - * - * @param primary Entity主键值 - * - * @return String - */ - public String getTable(Serializable primary) { - if (tableStrategy == null) return table; - String t = tableStrategy.getTable(table, primary); - return t == null || t.isEmpty() ? table : t; - } - - /** - * 根据过滤条件获取Entity的表名 - * - * @param node 过滤条件 - * - * @return String - */ - public String getTable(FilterNode node) { - if (tableStrategy == null) return table; - String t = tableStrategy.getTable(table, node); - return t == null || t.isEmpty() ? table : t; - } - - /** - * 根据Entity对象获取Entity的表名 - * - * @param bean Entity对象 - * - * @return String - */ - public String getTable(T bean) { - if (tableStrategy == null) return table; - String t = tableStrategy.getTable(table, bean); - return t == null || t.isEmpty() ? table : t; - } - - /** - * 获取主键字段的Attribute - * - * @return Attribute - */ - public Attribute getPrimary() { - return this.primary; - } - - /** - * 遍历数据库表对应的所有字段, 不包含@Transient字段 - * - * @param action BiConsumer - */ - public void forEachAttribute(BiConsumer> action) { - this.attributeMap.forEach(action); - } - - /** - * 根据Entity字段名获取字段的Attribute - * - * @param fieldname Class字段名 - * - * @return Attribute - */ - public Attribute getAttribute(String fieldname) { - if (fieldname == null) return null; - return this.attributeMap.get(fieldname); - } - - /** - * 根据Entity字段名获取可更新字段的Attribute - * - * @param fieldname Class字段名 - * - * @return Attribute - */ - public Attribute getUpdateAttribute(String fieldname) { - return this.updateAttributeMap.get(fieldname); - } - - /** - * 判断Entity类的字段名与表字段名s是否存在不一致的值 - * - * @return boolean - */ - public boolean isNoAlias() { - return this.aliasmap == null; - } - - /** - * 根据Flipper获取ORDER BY的SQL语句,不存在Flipper或sort字段返回空字符串 - * - * @param flipper 翻页对象 - * - * @return String - */ - protected String createSQLOrderby(Flipper flipper) { - if (flipper == null || flipper.getSort() == null) return ""; - final String sort = flipper.getSort(); - if (sort.isEmpty() || sort.indexOf(';') >= 0 || sort.indexOf('\n') >= 0) return ""; - String sql = this.sortOrderbySqls.get(sort); - if (sql != null) return sql; - final StringBuilder sb = new StringBuilder(); - sb.append(" ORDER BY "); - if (isNoAlias()) { - sb.append(sort); - } else { - boolean flag = false; - for (String item : sort.split(",")) { - if (item.isEmpty()) continue; - String[] sub = item.split("\\s+"); - if (flag) sb.append(','); - if (sub.length < 2 || sub[1].equalsIgnoreCase("ASC")) { - sb.append(getSQLColumn("a", sub[0])).append(" ASC"); - } else { - sb.append(getSQLColumn("a", sub[0])).append(" DESC"); - } - flag = true; - } - } - sql = sb.toString(); - this.sortOrderbySqls.put(sort, sql); - return sql; - } - - /** - * 根据field字段名获取数据库对应的字段名 - * - * @param tabalis 表别名 - * @param fieldname 字段名 - * - * @return String - */ - public String getSQLColumn(String tabalis, String fieldname) { - return this.aliasmap == null ? (tabalis == null ? fieldname : (tabalis + '.' + fieldname)) - : (tabalis == null ? aliasmap.getOrDefault(fieldname, fieldname) : (tabalis + '.' + aliasmap.getOrDefault(fieldname, fieldname))); - } - - /** - * 字段值转换成数据库的值 - * - * @param fieldname 字段名 - * @param fieldvalue 字段值 - * - * @return Object - */ - public Object getSQLValue(String fieldname, Serializable fieldvalue) { - if (fieldvalue == null && fieldname != null && isNotNullable(fieldname)) { - if (isNotNullJson(getAttribute(fieldname))) return ""; - } - if (this.cryptmap == null) return fieldvalue; - CryptHandler handler = this.cryptmap.get(fieldname); - if (handler == null) return fieldvalue; - return handler.encrypt(fieldvalue); - } - - /** - * 字段值转换成带转义的数据库的值 - * - * @param fieldname 字段名 - * @param fieldvalue 字段值 - * @param sqlFormatter 转义器 - * - * @return CharSequence - */ - public CharSequence formatSQLValue(String fieldname, Serializable fieldvalue, BiFunction sqlFormatter) { - Object val = getSQLValue(fieldname, fieldvalue); - return sqlFormatter == null ? formatToString(val) : sqlFormatter.apply(this, val); - } - - /** - * 字段值转换成带转义的数据库的值 - * - * @param value 字段值 - * @param sqlFormatter 转义器 - * - * @return CharSequence - */ - public CharSequence formatSQLValue(Object value, BiFunction sqlFormatter) { - return sqlFormatter == null ? formatToString(value) : sqlFormatter.apply(this, value); - } - - /** - * 字段值转换成数据库的值 - * - * @param 泛型 - * @param attr Attribute - * @param entity 记录对象 - * - * @return Object - */ - public Object getSQLValue(Attribute attr, T entity) { - Object val = attr.get(entity); - CryptHandler cryptHandler = attr.attach(); - if (cryptHandler != null) val = cryptHandler.encrypt(val); - return val; - } - - /** - * 字段值转换成带转义的数据库的值 - * - * @param 泛型 - * @param attr Attribute - * @param entity 记录对象 - * @param sqlFormatter 转义器 - * - * @return CharSequence - */ - public CharSequence formatSQLValue(Attribute attr, T entity, BiFunction sqlFormatter) { - Object val = getSQLValue(attr, entity); - return sqlFormatter == null ? formatToString(val) : sqlFormatter.apply(this, val); - } - - /** - * 数据库的值转换成数字段值 - * - * @param attr Attribute - * @param entity 记录对象 - * - * @return Object - */ - public Serializable getFieldValue(Attribute attr, T entity) { - Serializable val = attr.get(entity); - CryptHandler cryptHandler = attr.attach(); - if (cryptHandler != null) val = (Serializable) cryptHandler.decrypt(val); - return val; - } - - /** - * 获取主键字段的表字段名 - * - * @return String - */ - public String getPrimarySQLColumn() { - return getSQLColumn(null, this.primary.field()); - } - - /** - * 获取主键字段的带有表别名的表字段名 - * - * @param tabalis 表别名 - * - * @return String - */ - public String getPrimarySQLColumn(String tabalis) { - return getSQLColumn(tabalis, this.primary.field()); - } - - /** - * 拼接UPDATE给字段赋值的SQL片段 - * - * @param sqlColumn 表字段名 - * @param attr Attribute - * @param cv ColumnValue - * @param formatter 转义器 - * - * @return CharSequence - */ - protected CharSequence formatSQLValue(String sqlColumn, Attribute attr, final ColumnValue cv, BiFunction formatter) { - if (cv == null) return null; - Object val = cv.getValue(); - //ColumnNodeValue时 cv.getExpress() == ColumnExpress.MOV 只用于updateColumn - if (val instanceof ColumnNodeValue) return formatSQLValue(attr, null, (ColumnNodeValue) val, formatter); - if (val instanceof ColumnFuncNode) return formatSQLValue(attr, null, (ColumnFuncNode) val, formatter); - switch (cv.getExpress()) { - case INC: - return new StringBuilder().append(sqlColumn).append(" + ").append(val); - case DEC: - return new StringBuilder().append(sqlColumn).append(" - ").append(val); - case MUL: - return new StringBuilder().append(sqlColumn).append(" * ").append(val); - case DIV: - return new StringBuilder().append(sqlColumn).append(" / ").append(val); - case MOD: - return new StringBuilder().append(sqlColumn).append(" % ").append(val); - case AND: - return new StringBuilder().append(sqlColumn).append(" & ").append(val); - case ORR: - return new StringBuilder().append(sqlColumn).append(" | ").append(val); - case MOV: - CryptHandler handler = attr.attach(); - if (handler != null) val = handler.encrypt(val); - CharSequence rs = formatter == null ? formatToString(val) : formatter.apply(this, val); - if (rs == null && isNotNullJson(attr)) rs = ""; - return rs; - } - CryptHandler handler = attr.attach(); - if (handler != null) val = handler.encrypt(val); - return formatter == null ? formatToString(val) : formatter.apply(this, val); - } - - protected CharSequence formatSQLValue(Attribute attr, String tabalis, final ColumnFuncNode node, BiFunction formatter) { - if (node.getValue() instanceof ColumnNodeValue) { - return node.getFunc().getColumn(formatSQLValue(attr, tabalis, (ColumnNodeValue) node.getValue(), formatter).toString()); - } else { - return node.getFunc().getColumn(this.getSQLColumn(tabalis, String.valueOf(node.getValue()))); - } - } - - protected CharSequence formatSQLValue(Attribute attr, String tabalis, final ColumnNodeValue node, BiFunction formatter) { - Serializable left = node.getLeft(); - if (left instanceof CharSequence) { - left = this.getSQLColumn(tabalis, left.toString()); - } else if (left instanceof ColumnNodeValue) { - left = "(" + formatSQLValue(attr, tabalis, (ColumnNodeValue) left, formatter) + ")"; - } else if (left instanceof ColumnFuncNode) { - left = "(" + formatSQLValue(attr, tabalis, (ColumnFuncNode) left, formatter) + ")"; - } - Serializable right = node.getRight(); - if (right instanceof CharSequence) { - right = this.getSQLColumn(null, right.toString()); - } else if (left instanceof ColumnNodeValue) { - right = "(" + formatSQLValue(attr, tabalis, (ColumnNodeValue) right, formatter) + ")"; - } else if (left instanceof ColumnFuncNode) { - right = "(" + formatSQLValue(attr, tabalis, (ColumnFuncNode) right, formatter) + ")"; - } - switch (node.getExpress()) { - case INC: - return new StringBuilder().append(left).append(" + ").append(right); - case DEC: - return new StringBuilder().append(left).append(" - ").append(right); - case MUL: - return new StringBuilder().append(left).append(" * ").append(right); - case DIV: - return new StringBuilder().append(left).append(" / ").append(right); - case MOD: - return new StringBuilder().append(left).append(" % ").append(right); - case AND: - return new StringBuilder().append(left).append(" & ").append(right); - case ORR: - return new StringBuilder().append(left).append(" | ").append(right); - } - throw new IllegalArgumentException(node + " express cannot be null or MOV"); - } - - /** - * 获取所有数据表字段的Attribute, 不包含@Transient字段 - * - * @return Map - */ - protected Map> getAttributes() { - return attributeMap; - } - - /** - * 判断日志级别 - * - * @param logger Logger - * @param l Level - * - * @return boolean - */ - public boolean isLoggable(Logger logger, Level l) { - return logger.isLoggable(l) && l.intValue() >= this.logLevel; - } - - public boolean isNotNullable(String fieldname) { - return notNullColumns.contains(fieldname); - } - - public boolean isNotNullable(Attribute attr) { - return attr == null ? false : notNullColumns.contains(attr.field()); - } - - public boolean isNotNullJson(Attribute attr) { - if (attr == null) return false; - return notNullColumns.contains(attr.field()) - && !Number.class.isAssignableFrom(attr.type()) - && !CharSequence.class.isAssignableFrom(attr.type()) - && java.util.Date.class != attr.type() - && !attr.type().getName().startsWith("java.sql.") - && !attr.type().getName().startsWith("java.time."); - } - - /** - * 判断日志级别 - * - * @param logger Logger - * @param l Level - * @param str String - * - * @return boolean - */ - public boolean isLoggable(Logger logger, Level l, String str) { - boolean rs = logger.isLoggable(l) && l.intValue() >= this.logLevel; - if (this.excludeLogLevels == null || !rs || str == null) return rs; - String[] keys = this.excludeLogLevels.get(l.intValue()); - if (keys == null) return rs; - for (String key : keys) { - if (str.contains(key)) return false; - } - return rs; - } - - /** - * 将字段值序列化为可SQL的字符串 - * - * @param value 字段值 - * - * @return String - */ - private String formatToString(Object value) { - if (value == null) return null; - if (value instanceof CharSequence) { - return new StringBuilder().append('\'').append(value.toString().replace("'", "\\'")).append('\'').toString(); - } else if (!(value instanceof Number) && !(value instanceof java.util.Date) - && !value.getClass().getName().startsWith("java.sql.") && !value.getClass().getName().startsWith("java.time.")) { - return new StringBuilder().append('\'').append(jsonConvert.convertTo(value).replace("'", "\\'")).append('\'').toString(); - } - return String.valueOf(value); - } - - /** - * 将一行的ResultSet组装成一个Entity对象 - * - * @param sels 指定字段 - * @param set ResultSet - * - * @return Entity对象 - * @throws SQLException SQLException - */ - protected T getEntityValue(final SelectColumn sels, final ResultSet set) throws SQLException { - T obj; - Attribute[] attrs = this.queryAttributes; - if (this.constructorParameters == null) { - obj = creator.create(); - } else { - Object[] cps = new Object[this.constructorParameters.length]; - for (int i = 0; i < this.constructorAttributes.length; i++) { - Attribute attr = this.constructorAttributes[i]; - if (sels == null || sels.test(attr.field())) { - cps[i] = getFieldValue(attr, set); - } - } - obj = creator.create(cps); - attrs = this.unconstructorAttributes; - } - for (Attribute attr : attrs) { - if (sels == null || sels.test(attr.field())) { - attr.set(obj, getFieldValue(attr, set)); - } - } - return obj; - } - - protected Serializable getFieldValue(Attribute attr, final ResultSet set) throws SQLException { - return getFieldValue(attr, set, 0); - } - - protected Serializable getFieldValue(Attribute attr, final ResultSet set, int index) throws SQLException { - final Class t = attr.type(); - Serializable o; - if (t == byte[].class) { - Blob blob = index > 0 ? set.getBlob(index) : set.getBlob(this.getSQLColumn(null, attr.field())); - if (blob == null) { - o = null; - } else { //不支持超过2G的数据 - o = blob.getBytes(1, (int) blob.length()); - CryptHandler cryptHandler = attr.attach(); - if (cryptHandler != null) o = (Serializable) cryptHandler.decrypt(o); - } - } else { - o = (Serializable) (index > 0 ? set.getObject(index) : set.getObject(this.getSQLColumn(null, attr.field()))); - CryptHandler cryptHandler = attr.attach(); - if (cryptHandler != null) o = (Serializable) cryptHandler.decrypt(o); - if (t.isPrimitive()) { - if (o != null) { - if (t == int.class) { - o = ((Number) o).intValue(); - } else if (t == long.class) { - o = ((Number) o).longValue(); - } else if (t == short.class) { - o = ((Number) o).shortValue(); - } else if (t == float.class) { - o = ((Number) o).floatValue(); - } else if (t == double.class) { - o = ((Number) o).doubleValue(); - } else if (t == byte.class) { - o = ((Number) o).byteValue(); - } else if (t == char.class) { - o = (char) ((Number) o).intValue(); - } else if (t == boolean.class) { - o = (Boolean) o; - } - } else if (t == int.class) { - o = 0; - } else if (t == long.class) { - o = 0L; - } else if (t == short.class) { - o = (short) 0; - } else if (t == float.class) { - o = 0.0f; - } else if (t == double.class) { - o = 0.0d; - } else if (t == byte.class) { - o = (byte) 0; - } else if (t == boolean.class) { - o = false; - } else if (t == char.class) { - o = (char) 0; - } - } else if (t == AtomicInteger.class) { - if (o != null) { - o = new AtomicInteger(((Number) o).intValue()); - } else { - o = new AtomicInteger(); - } - } else if (t == AtomicLong.class) { - if (o != null) { - o = new AtomicLong(((Number) o).longValue()); - } else { - o = new AtomicLong(); - } - } else if (o != null && !t.isAssignableFrom(o.getClass()) && o instanceof CharSequence) { - o = ((CharSequence) o).length() == 0 ? null : jsonConvert.convertFrom(attr.genericType(), o.toString()); - } - } - return o; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.logging.*; +import javax.persistence.*; +import org.redkale.convert.json.*; +import org.redkale.util.*; + +/** + * Entity操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Entity类的泛型 + */ +@SuppressWarnings("unchecked") +public final class EntityInfo { + + private static final JsonConvert DEFAULT_JSON_CONVERT = JsonFactory.create().skipAllIgnore(true).getConvert(); + + //全局静态资源 + private static final ConcurrentHashMap entityInfos = new ConcurrentHashMap<>(); + + //日志 + private static final Logger logger = Logger.getLogger(EntityInfo.class.getSimpleName()); + + //Entity类名 + private final Class type; + + //类对应的数据表名, 如果是VirtualEntity 类, 则该字段为null + final String table; + + //JsonConvert + final JsonConvert jsonConvert; + + //Entity构建器 + private final Creator creator; + + //Entity数值构建器 + private final IntFunction arrayer; + + //Entity构建器参数 + private final String[] constructorParameters; + + //Entity构建器参数Attribute, 数组个数与constructorParameters相同 + final Attribute[] constructorAttributes; + + //Entity构建器参数Attribute + final Attribute[] unconstructorAttributes; + + //主键 + final Attribute primary; + + //DDL字段集合 + final EntityColumn[] ddlColumns; + + //Entity缓存对象 + private final EntityCache cache; + + //用于存储绑定在EntityInfo上的对象 + private final ConcurrentHashMap subobjectMap = new ConcurrentHashMap<>(); + + //key是field的name, 不是sql字段。 + //存放所有与数据库对应的字段, 包括主键 + private final HashMap> attributeMap = new HashMap<>(); + + //存放所有与数据库对应的字段, 包括主键 + final Attribute[] attributes; + + //key是field的name, value是Column的别名,即数据库表的字段名 + //只有field.name 与 Column.name不同才存放在aliasmap里. + private final Map aliasmap; + + //key是field的name, value是CryptHandler + //字段都不存在CryptHandler时值因为为null,减少判断 + private final Map cryptmap; + + //所有可更新字段,即排除了主键字段和标记为@Column(updatable=false)的字段 + private final Map> updateAttributeMap = new HashMap<>(); + + //用于存在database.table_20160202类似这种分布式表 + private final Set tables = new CopyOnWriteArraySet<>(); + + //不能为null的字段名 + private final Set notNullColumns = new CopyOnWriteArraySet<>(); + + //分表 策略 + private final DistributeTableStrategy tableStrategy; + + //根据主键查找所有对象的SQL + private final String allQueryPrepareSQL; + + //根据主键查找单个对象的SQL, 含 ? + private final String findQuestionPrepareSQL; + + //根据主键查找单个对象的SQL, 含 $ + private final String findDollarPrepareSQL; + + //根据主键查找单个对象的SQL, 含 :name + private final String findNamesPrepareSQL; + + //数据库中所有字段 + private final String[] querySqlColumns; + + private final String querySqlColumnSequence; + + private final String querySqlColumnSequenceA; + + //数据库中所有字段, 顺序必须与querySqlColumns、querySqlColumnSequence一致 + private final Attribute[] queryAttributes; + + //新增SQL, 含 ?,即排除了自增长主键和标记为@Column(insertable=false)的字段 + private final String insertQuestionPrepareSQL; + + //新增SQL, 含 $,即排除了自增长主键和标记为@Column(insertable=false)的字段 + private final String insertDollarPrepareSQL; + + //新增SQL, 含 :name,即排除了自增长主键和标记为@Column(insertable=false)的字段 + private final String insertNamesPrepareSQL; + + //数据库中所有可新增字段 + final Attribute[] insertAttributes; + + //根据主键更新所有可更新字段的SQL,含 ? + private final String updateQuestionPrepareSQL; + + //根据主键更新所有可更新字段的SQL,含 $ + private final String updateDollarPrepareSQL; + + //根据主键更新所有可更新字段的SQL,含 :name + private final String updateNamesPrepareSQL; + + //数据库中所有可更新字段 + final Attribute[] updateAttributes; + + //根据主键删除记录的SQL,含 ? + private final String deleteQuestionPrepareSQL; + + //根据主键删除记录的SQL,含 $ + private final String deleteDollarPrepareSQL; + + //根据主键删除记录的SQL,含 :name + private final String deleteNamesPrepareSQL; + + //日志级别,从LogLevel获取 + private final int logLevel; + + //日志控制 + private final Map excludeLogLevels; + + //Flipper.sort转换成以ORDER BY开头SQL的缓存 + private final Map sortOrderbySqls = new ConcurrentHashMap<>(); + + //所属的DataSource + final DataSource source; + + //全量数据的加载器 + final BiFunction> fullloader; + //------------------------------------------------------------ + + /** + * 加载EntityInfo + * + * @param clazz Entity类 + * @param cacheForbidden 是否禁用EntityCache + * @param conf 配置信息, persistence.xml中的property节点值 + * @param source DataSource,可为null + * @param fullloader 全量加载器,可为null + */ + static EntityInfo load(Class clazz, final boolean cacheForbidden, final Properties conf, + DataSource source, BiFunction> fullloader) { + EntityInfo rs = entityInfos.get(clazz); + if (rs != null && (rs.cache == null || rs.cache.isFullLoaded())) return rs; + synchronized (entityInfos) { + rs = entityInfos.get(clazz); + if (rs == null) { + rs = new EntityInfo(clazz, cacheForbidden, conf, source, fullloader); + entityInfos.put(clazz, rs); + } + if (rs.cache != null && !rs.isCacheFullLoaded()) { + if (fullloader == null) throw new IllegalArgumentException(clazz.getName() + " auto loader is illegal"); + rs.cache.fullLoadAsync(); + } + return rs; + } + } + + /** + * 获取Entity类对应的EntityInfo对象 + * + * @param 泛型 + * @param clazz Entity类 + * + * @return EntityInfo + */ + static EntityInfo get(Class clazz) { + return entityInfos.get(clazz); + } + + /** + * 给PrepareCompiler使用,用于预动态生成Attribute + * + * @since 2.5.0 + * @param 泛型 + * @param clazz Entity实体类 + * @param source 数据源 + * + * @return EntityInfo + */ + public static EntityInfo compile(Class clazz, DataSource source) { + return new EntityInfo<>(clazz, false, null, source, (BiFunction) null); + } + + /** + * 构造函数 + * + * @param type Entity类 + * @param cacheForbidden 是否禁用EntityCache + * @param conf 配置信息, persistence.xml中的property节点值 + * @param source DataSource,可为null + * @param fullloader 全量加载器,可为null + */ + private EntityInfo(Class type, final boolean cacheForbidden, + Properties conf, DataSource source, BiFunction> fullloader) { + this.type = type; + this.source = source; + //--------------------------------------------- + + LogLevel ll = type.getAnnotation(LogLevel.class); + this.logLevel = ll == null ? Integer.MIN_VALUE : Level.parse(ll.value()).intValue(); + Map> logmap = new HashMap<>(); + for (LogExcludeLevel lel : type.getAnnotationsByType(LogExcludeLevel.class)) { + for (String onelevel : lel.levels()) { + int level = Level.parse(onelevel).intValue(); + HashSet set = logmap.get(level); + if (set == null) { + set = new HashSet<>(); + logmap.put(level, set); + } + for (String key : lel.keys()) { + set.add(key); + } + } + } + if (logmap.isEmpty()) { + this.excludeLogLevels = null; + } else { + this.excludeLogLevels = new HashMap<>(); + logmap.forEach((l, set) -> excludeLogLevels.put(l, set.toArray(new String[set.size()]))); + } + //--------------------------------------------- + Table t = type.getAnnotation(Table.class); + if (type.getAnnotation(VirtualEntity.class) != null || (source == null || "memory".equalsIgnoreCase(source.getType()))) { + this.table = null; + BiFunction> loader = null; + try { + VirtualEntity ve = type.getAnnotation(VirtualEntity.class); + if (ve != null) { + loader = ve.loader().getDeclaredConstructor().newInstance(); + RedkaleClassLoader.putReflectionDeclaredConstructors(ve.loader(), ve.loader().getName()); + } + } catch (Exception e) { + logger.log(Level.SEVERE, type + " init @VirtualEntity.loader error", e); + } + this.fullloader = loader; + } else { + this.fullloader = fullloader; + if (t != null && !t.name().isEmpty() && t.name().indexOf('.') >= 0) throw new RuntimeException(type + " have illegal table.name on @Table"); + this.table = (t == null) ? type.getSimpleName().toLowerCase() : (t.catalog().isEmpty()) ? (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name()) : (t.catalog() + '.' + (t.name().isEmpty() ? type.getSimpleName().toLowerCase() : t.name())); + } + DistributeTable dt = type.getAnnotation(DistributeTable.class); + DistributeTableStrategy dts = null; + try { + dts = (dt == null) ? null : dt.strategy().getDeclaredConstructor().newInstance(); + if (dts != null) RedkaleClassLoader.putReflectionDeclaredConstructors(dt.strategy(), dt.strategy().getName()); + } catch (Exception e) { + logger.log(Level.SEVERE, type + " init DistributeTableStrategy error", e); + } + this.tableStrategy = dts; + + this.arrayer = Creator.arrayFunction(type); + this.creator = Creator.create(type); + ConstructorParameters cp = null; + try { + Method cm = this.creator.getClass().getMethod("create", Object[].class); + cp = cm.getAnnotation(ConstructorParameters.class); + RedkaleClassLoader.putReflectionPublicMethods(this.creator.getClass().getName()); + RedkaleClassLoader.putReflectionMethod(this.creator.getClass().getName(), cm); + } catch (Exception e) { + logger.log(Level.SEVERE, type + " cannot find ConstructorParameters Creator", e); + } + this.constructorParameters = (cp == null || cp.value().length < 1) ? null : cp.value(); + Attribute idAttr0 = null; + Map cryptmap0 = null; + Map aliasmap0 = null; + Class cltmp = type; + Set fields = new HashSet<>(); + List querycols = new ArrayList<>(); + List> queryattrs = new ArrayList<>(); + List insertcols = new ArrayList<>(); + List> insertattrs = new ArrayList<>(); + List updatecols = new ArrayList<>(); + List> updateattrs = new ArrayList<>(); + List> ddlList = new ArrayList<>(); + Map> cryptCreatorMap = new HashMap<>(); + do { + List ddl = new ArrayList<>(); + ddlList.add(ddl); + RedkaleClassLoader.putReflectionDeclaredFields(cltmp.getName()); + for (Field field : cltmp.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (Modifier.isFinal(field.getModifiers())) continue; + if (field.getAnnotation(Transient.class) != null) continue; + if (fields.contains(field.getName())) continue; + final String fieldname = field.getName(); + final Column col = field.getAnnotation(Column.class); + final String sqlfield = col == null || col.name().isEmpty() ? fieldname : col.name(); + if (!fieldname.equals(sqlfield)) { + if (aliasmap0 == null) aliasmap0 = new HashMap<>(); + aliasmap0.put(fieldname, sqlfield); + } + final CryptColumn cpt = field.getAnnotation(CryptColumn.class); + CryptHandler cryptHandler = null; + if (cpt != null) { + if (cryptmap0 == null) cryptmap0 = new HashMap<>(); + cryptHandler = cryptCreatorMap.computeIfAbsent(cpt.handler(), c -> (Creator) Creator.create(cpt.handler())).create(); + cryptmap0.put(fieldname, cryptHandler); + } + Attribute attr; + try { + attr = Attribute.create(type, cltmp, field, cryptHandler); + } catch (RuntimeException e) { + continue; + } + if (field.getAnnotation(javax.persistence.Id.class) != null && idAttr0 == null) { + idAttr0 = attr; + insertcols.add(sqlfield); + insertattrs.add(attr); + RedkaleClassLoader.putReflectionField(cltmp.getName(), field); + } else { + if (col == null || col.insertable()) { + insertcols.add(sqlfield); + insertattrs.add(attr); + } + if (col == null || col.updatable()) { + updatecols.add(sqlfield); + updateattrs.add(attr); + updateAttributeMap.put(fieldname, attr); + } + if (col != null && !col.nullable()) { + notNullColumns.add(fieldname); + } + } + ddl.add(new EntityColumn(field.getAnnotation(javax.persistence.Id.class) != null, col, attr.field(), attr.type(), field.getAnnotation(Comment.class))); + querycols.add(sqlfield); + queryattrs.add(attr); + fields.add(fieldname); + attributeMap.put(fieldname, attr); + } + } while ((cltmp = cltmp.getSuperclass()) != Object.class); + if (idAttr0 == null) throw new RuntimeException(type.getName() + " have no primary column by @javax.persistence.Id"); + cltmp = type; + JsonConvert convert = DEFAULT_JSON_CONVERT; + do { + for (Method method : cltmp.getDeclaredMethods()) { + if (method.getAnnotation(SourceConvert.class) == null) continue; + if (!Modifier.isStatic(method.getModifiers())) throw new RuntimeException("@SourceConvert method(" + method + ") must be static"); + if (method.getReturnType() != JsonConvert.class) throw new RuntimeException("@SourceConvert method(" + method + ") must be return JsonConvert.class"); + if (method.getParameterCount() > 0) throw new RuntimeException("@SourceConvert method(" + method + ") must be 0 parameter"); + try { + method.setAccessible(true); + convert = (JsonConvert) method.invoke(null); + } catch (Exception e) { + throw new RuntimeException(method + " invoke error", e); + } + if (convert != null) break; + } + } while ((cltmp = cltmp.getSuperclass()) != Object.class); + this.jsonConvert = convert == null ? DEFAULT_JSON_CONVERT : convert; + + this.primary = idAttr0; + this.aliasmap = aliasmap0; + this.cryptmap = cryptmap0; + List ddls = new ArrayList<>(); + Collections.reverse(ddlList); //父类的字段排在前面 + for (List ls : ddlList) { + ddls.addAll(ls); + } + this.ddlColumns = ddls.toArray(new EntityColumn[ddls.size()]); + this.attributes = attributeMap.values().toArray(new Attribute[attributeMap.size()]); + this.insertAttributes = insertattrs.toArray(new Attribute[insertattrs.size()]); + this.updateAttributes = updateattrs.toArray(new Attribute[updateattrs.size()]); + if (this.constructorParameters == null) { + this.constructorAttributes = null; + this.unconstructorAttributes = null; + } else { + this.constructorAttributes = new Attribute[this.constructorParameters.length]; + List> unconstructorAttrs = new ArrayList<>(); + List newquerycols1 = new ArrayList<>(); + List newquerycols2 = new ArrayList<>(); + for (Attribute attr : new ArrayList<>(queryattrs)) { + int pos = -1; + for (int i = 0; i < this.constructorParameters.length; i++) { + if (attr.field().equals(this.constructorParameters[i])) { + pos = i; + break; + } + } + if (pos >= 0) { + this.constructorAttributes[pos] = attr; + newquerycols1.add(querycols.get(queryattrs.indexOf(attr))); + } else { + unconstructorAttrs.add(attr); + newquerycols2.add(querycols.get(queryattrs.indexOf(attr))); + } + } + this.unconstructorAttributes = unconstructorAttrs.toArray(new Attribute[unconstructorAttrs.size()]); + newquerycols1.addAll(newquerycols2); + querycols = newquerycols1; + List> newqueryattrs = new ArrayList<>(); + newqueryattrs.addAll(List.of(constructorAttributes)); + newqueryattrs.addAll(unconstructorAttrs); + queryattrs = newqueryattrs; + } + this.querySqlColumns = querycols.toArray(new String[querycols.size()]); + this.querySqlColumnSequence = Utility.joining(querySqlColumns, ','); + this.querySqlColumnSequenceA = "a." + Utility.joining(querySqlColumns, ",a."); + this.queryAttributes = queryattrs.toArray(new Attribute[queryattrs.size()]); + if (table != null) { + StringBuilder querydb = new StringBuilder(); + int index = 0; + for (String col : querycols) { + if (index > 0) querydb.append(','); + querydb.append(col); + index++; + } + StringBuilder insertsb = new StringBuilder(); + StringBuilder insertsbquestion = new StringBuilder(); + StringBuilder insertsbdollar = new StringBuilder(); + StringBuilder insertsbnames = new StringBuilder(); + index = 0; + for (String col : insertcols) { + if (index > 0) insertsb.append(','); + insertsb.append(col); + if (index > 0) { + insertsbquestion.append(','); + insertsbdollar.append(','); + insertsbnames.append(','); + } + insertsbquestion.append('?'); + insertsbdollar.append("$").append(++index); + insertsbnames.append(":").append(col); + } + this.insertQuestionPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbquestion + ")"; + this.insertDollarPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbdollar + ")"; + this.insertNamesPrepareSQL = "INSERT INTO " + (this.tableStrategy == null ? table : "${newtable}") + "(" + insertsb + ") VALUES(" + insertsbnames + ")"; + StringBuilder updatesbquestion = new StringBuilder(); + StringBuilder updatesbdollar = new StringBuilder(); + StringBuilder updatesbnames = new StringBuilder(); + index = 0; + for (String col : updatecols) { + if (index > 0) { + updatesbquestion.append(", "); + updatesbdollar.append(", "); + updatesbnames.append(", "); + } + updatesbquestion.append(col).append(" = ?"); + updatesbdollar.append(col).append(" = ").append("$").append(++index); + updatesbnames.append(col).append(" = :").append(col); + } + this.updateQuestionPrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesbquestion + " WHERE " + getPrimarySQLColumn(null) + " = ?"; + this.updateDollarPrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesbdollar + " WHERE " + getPrimarySQLColumn(null) + " = $" + (++index); + this.updateNamesPrepareSQL = "UPDATE " + (this.tableStrategy == null ? table : "${newtable}") + " SET " + updatesbnames + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null); + this.deleteQuestionPrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = ?"; + this.deleteDollarPrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = $1"; + this.deleteNamesPrepareSQL = "DELETE FROM " + (this.tableStrategy == null ? table : "${newtable}") + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null); + this.allQueryPrepareSQL = "SELECT " + querydb + " FROM " + table; + this.findQuestionPrepareSQL = "SELECT " + querydb + " FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = ?"; + this.findDollarPrepareSQL = "SELECT " + querydb + " FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = $1"; + this.findNamesPrepareSQL = "SELECT " + querydb + " FROM " + table + " WHERE " + getPrimarySQLColumn(null) + " = :" + getPrimarySQLColumn(null); + } else { + this.allQueryPrepareSQL = null; + + this.insertQuestionPrepareSQL = null; + this.updateQuestionPrepareSQL = null; + this.deleteQuestionPrepareSQL = null; + this.findQuestionPrepareSQL = null; + + this.insertDollarPrepareSQL = null; + this.updateDollarPrepareSQL = null; + this.deleteDollarPrepareSQL = null; + this.findDollarPrepareSQL = null; + + this.insertNamesPrepareSQL = null; + this.updateNamesPrepareSQL = null; + this.deleteNamesPrepareSQL = null; + this.findNamesPrepareSQL = null; + } + //----------------cache-------------- + Cacheable c = type.getAnnotation(Cacheable.class); + if (this.table == null || (!cacheForbidden && c != null && c.value())) { + this.cache = new EntityCache<>(this, c); + } else { + this.cache = null; + } + } + + @SuppressWarnings("unchecked") + public V getSubobject(String name) { + return (V) this.subobjectMap.get(name); + } + + public void setSubobject(String name, Object value) { + this.subobjectMap.put(name, value); + } + + public void removeSubobject(String name) { + this.subobjectMap.remove(name); + } + + public void clearSubobjects() { + this.subobjectMap.clear(); + } + + /** + * 获取JsonConvert + * + * @return JsonConvert + */ + public JsonConvert getJsonConvert() { + return jsonConvert; + } + + /** + * 获取Entity缓存器 + * + * @return EntityCache + */ + public EntityCache getCache() { + return cache; + } + + /** + * 判断缓存器是否已经全量加载过 + * + * @return boolean + */ + public boolean isCacheFullLoaded() { + return cache != null && cache.isFullLoaded(); + } + + /** + * 获取Entity构建器 + * + * @return Creator + */ + public Creator getCreator() { + return creator; + } + + /** + * 获取Entity数组构建器 + * + * @return Creator + */ + public IntFunction getArrayer() { + return arrayer; + } + + /** + * 获取Entity类名 + * + * @return Class + */ + public Class getType() { + return type; + } + + /** + * 判断Entity是否为虚拟类 + * + * @return boolean + */ + public boolean isVirtualEntity() { + return table == null; + } + + public DistributeTableStrategy getTableStrategy() { + return tableStrategy; + } + + public Object disTableLock() { + return tables; + } + + public boolean containsDisTable(String tablekey) { + return tables.contains(tablekey); + } + + public void addDisTable(String tablekey) { + tables.add(tablekey); + } + + public boolean removeDisTable(String tablekey) { + return tables.remove(tablekey); + } + + public EntityColumn[] getDDLColumns() { + return ddlColumns; + } + + public Attribute[] getInsertAttributes() { + return insertAttributes; + } + + public Attribute[] getUpdateAttributes() { + return updateAttributes; + } + + public Attribute[] getQueryAttributes() { + return queryAttributes; + } + + /** + * 获取Entity的QUERY SQL + * + * @param pk 主键值 + * + * @return String + */ + public String getFindQuestionPrepareSQL(Serializable pk) { + if (this.tableStrategy == null) return findQuestionPrepareSQL; + return findQuestionPrepareSQL.replace("${newtable}", getTable(pk)); + } + + /** + * 获取Entity的QUERY SQL + * + * + * @return String + */ + public String getAllQueryPrepareSQL() { + return this.allQueryPrepareSQL; + } + + /** + * 获取Entity的QUERY SQL + * + * @param pk 主键值 + * + * @return String + */ + public String getFindDollarPrepareSQL(Serializable pk) { + if (this.tableStrategy == null) return findDollarPrepareSQL; + return findDollarPrepareSQL.replace("${newtable}", getTable(pk)); + } + + /** + * 获取Entity的QUERY SQL + * + * @param pk 主键值 + * + * @return String + */ + public String getFindNamesPrepareSQL(Serializable pk) { + if (this.tableStrategy == null) return findNamesPrepareSQL; + return findNamesPrepareSQL.replace("${newtable}", getTable(pk)); + } + + /** + * 获取Entity的INSERT SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getInsertQuestionPrepareSQL(T bean) { + if (this.tableStrategy == null) return insertQuestionPrepareSQL; + return insertQuestionPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的INSERT SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getInsertDollarPrepareSQL(T bean) { + if (this.tableStrategy == null) return insertDollarPrepareSQL; + return insertDollarPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的INSERT SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getInsertNamesPrepareSQL(T bean) { + if (this.tableStrategy == null) return insertNamesPrepareSQL; + return insertNamesPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的UPDATE SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getUpdateQuestionPrepareSQL(T bean) { + if (this.tableStrategy == null) return updateQuestionPrepareSQL; + return updateQuestionPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的UPDATE SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getUpdateDollarPrepareSQL(T bean) { + if (this.tableStrategy == null) return updateDollarPrepareSQL; + return updateDollarPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的UPDATE SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getUpdateNamesPrepareSQL(T bean) { + if (this.tableStrategy == null) return updateNamesPrepareSQL; + return updateNamesPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的DELETE SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getDeleteQuestionPrepareSQL(T bean) { + if (this.tableStrategy == null) return deleteQuestionPrepareSQL; + return deleteQuestionPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的DELETE SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getDeleteDollarPrepareSQL(T bean) { + if (this.tableStrategy == null) return deleteDollarPrepareSQL; + return deleteDollarPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取Entity的DELETE SQL + * + * @param bean Entity对象 + * + * @return String + */ + public String getDeleteNamesPrepareSQL(T bean) { + if (this.tableStrategy == null) return deleteNamesPrepareSQL; + return deleteNamesPrepareSQL.replace("${newtable}", getTable(bean)); + } + + /** + * 获取查询字段列表 + * + * @param tabalis 表别名 + * @param selects 过滤字段 + * + * @return String + */ + public CharSequence getQueryColumns(String tabalis, SelectColumn selects) { + if (selects == null) { + if (tabalis == null) return querySqlColumnSequence; + if ("a".equals(tabalis)) return querySqlColumnSequenceA; + return tabalis + "." + Utility.joining(querySqlColumns, "," + tabalis + "."); + } + StringBuilder sb = new StringBuilder(); + for (Attribute attr : this.attributes) { + if (!selects.test(attr.field())) continue; + if (sb.length() > 0) sb.append(','); + sb.append(getSQLColumn(tabalis, attr.field())); + } + if (sb.length() == 0) sb.append('*'); + return sb; + } + + public CharSequence getFullQueryColumns(String tabalis, SelectColumn selects) { + if (selects == null) { + if (tabalis == null) { + return querySqlColumnSequence; + } else { + StringBuilder sb = new StringBuilder(); + String s = tabalis + "."; + for (String col : querySqlColumns) { + if (sb.length() > 0) sb.append(','); + sb.append(s).append(col); + } + return sb; + } + } else { + StringBuilder sb = new StringBuilder(); + for (Attribute attr : this.attributes) { + if (!selects.test(attr.field())) continue; + if (sb.length() > 0) sb.append(','); + sb.append(getSQLColumn(tabalis, attr.field())); + } + if (sb.length() == 0) sb.append('*'); + return sb; + } + } + + public String getOriginTable() { + return table; + } + + /** + * 根据主键值获取Entity的表名 + * + * @param primary Entity主键值 + * + * @return String + */ + public String getTable(Serializable primary) { + if (tableStrategy == null) return table; + String t = tableStrategy.getTable(table, primary); + return t == null || t.isEmpty() ? table : t; + } + + /** + * 根据过滤条件获取Entity的表名 + * + * @param node 过滤条件 + * + * @return String + */ + public String getTable(FilterNode node) { + if (tableStrategy == null) return table; + String t = tableStrategy.getTable(table, node); + return t == null || t.isEmpty() ? table : t; + } + + /** + * 根据Entity对象获取Entity的表名 + * + * @param bean Entity对象 + * + * @return String + */ + public String getTable(T bean) { + if (tableStrategy == null) return table; + String t = tableStrategy.getTable(table, bean); + return t == null || t.isEmpty() ? table : t; + } + + /** + * 获取主键字段的Attribute + * + * @return Attribute + */ + public Attribute getPrimary() { + return this.primary; + } + + /** + * 遍历数据库表对应的所有字段, 不包含@Transient字段 + * + * @param action BiConsumer + */ + public void forEachAttribute(BiConsumer> action) { + this.attributeMap.forEach(action); + } + + /** + * 根据Entity字段名获取字段的Attribute + * + * @param fieldname Class字段名 + * + * @return Attribute + */ + public Attribute getAttribute(String fieldname) { + if (fieldname == null) return null; + return this.attributeMap.get(fieldname); + } + + /** + * 根据Entity字段名获取可更新字段的Attribute + * + * @param fieldname Class字段名 + * + * @return Attribute + */ + public Attribute getUpdateAttribute(String fieldname) { + return this.updateAttributeMap.get(fieldname); + } + + /** + * 判断Entity类的字段名与表字段名s是否存在不一致的值 + * + * @return boolean + */ + public boolean isNoAlias() { + return this.aliasmap == null; + } + + /** + * 根据Flipper获取ORDER BY的SQL语句,不存在Flipper或sort字段返回空字符串 + * + * @param flipper 翻页对象 + * + * @return String + */ + protected String createSQLOrderby(Flipper flipper) { + if (flipper == null || flipper.getSort() == null) return ""; + final String sort = flipper.getSort(); + if (sort.isEmpty() || sort.indexOf(';') >= 0 || sort.indexOf('\n') >= 0) return ""; + String sql = this.sortOrderbySqls.get(sort); + if (sql != null) return sql; + final StringBuilder sb = new StringBuilder(); + sb.append(" ORDER BY "); + if (isNoAlias()) { + sb.append(sort); + } else { + boolean flag = false; + for (String item : sort.split(",")) { + if (item.isEmpty()) continue; + String[] sub = item.split("\\s+"); + if (flag) sb.append(','); + if (sub.length < 2 || sub[1].equalsIgnoreCase("ASC")) { + sb.append(getSQLColumn("a", sub[0])).append(" ASC"); + } else { + sb.append(getSQLColumn("a", sub[0])).append(" DESC"); + } + flag = true; + } + } + sql = sb.toString(); + this.sortOrderbySqls.put(sort, sql); + return sql; + } + + /** + * 根据field字段名获取数据库对应的字段名 + * + * @param tabalis 表别名 + * @param fieldname 字段名 + * + * @return String + */ + public String getSQLColumn(String tabalis, String fieldname) { + return this.aliasmap == null ? (tabalis == null ? fieldname : (tabalis + '.' + fieldname)) + : (tabalis == null ? aliasmap.getOrDefault(fieldname, fieldname) : (tabalis + '.' + aliasmap.getOrDefault(fieldname, fieldname))); + } + + /** + * 字段值转换成数据库的值 + * + * @param fieldname 字段名 + * @param fieldvalue 字段值 + * + * @return Object + */ + public Object getSQLValue(String fieldname, Serializable fieldvalue) { + if (fieldvalue == null && fieldname != null && isNotNullable(fieldname)) { + if (isNotNullJson(getAttribute(fieldname))) return ""; + } + if (this.cryptmap == null) return fieldvalue; + CryptHandler handler = this.cryptmap.get(fieldname); + if (handler == null) return fieldvalue; + return handler.encrypt(fieldvalue); + } + + /** + * 字段值转换成带转义的数据库的值 + * + * @param fieldname 字段名 + * @param fieldvalue 字段值 + * @param sqlFormatter 转义器 + * + * @return CharSequence + */ + public CharSequence formatSQLValue(String fieldname, Serializable fieldvalue, BiFunction sqlFormatter) { + Object val = getSQLValue(fieldname, fieldvalue); + return sqlFormatter == null ? formatToString(val) : sqlFormatter.apply(this, val); + } + + /** + * 字段值转换成带转义的数据库的值 + * + * @param value 字段值 + * @param sqlFormatter 转义器 + * + * @return CharSequence + */ + public CharSequence formatSQLValue(Object value, BiFunction sqlFormatter) { + return sqlFormatter == null ? formatToString(value) : sqlFormatter.apply(this, value); + } + + /** + * 字段值转换成数据库的值 + * + * @param 泛型 + * @param attr Attribute + * @param entity 记录对象 + * + * @return Object + */ + public Object getSQLValue(Attribute attr, T entity) { + Object val = attr.get(entity); + CryptHandler cryptHandler = attr.attach(); + if (cryptHandler != null) val = cryptHandler.encrypt(val); + return val; + } + + /** + * 字段值转换成带转义的数据库的值 + * + * @param 泛型 + * @param attr Attribute + * @param entity 记录对象 + * @param sqlFormatter 转义器 + * + * @return CharSequence + */ + public CharSequence formatSQLValue(Attribute attr, T entity, BiFunction sqlFormatter) { + Object val = getSQLValue(attr, entity); + return sqlFormatter == null ? formatToString(val) : sqlFormatter.apply(this, val); + } + + /** + * 数据库的值转换成数字段值 + * + * @param attr Attribute + * @param entity 记录对象 + * + * @return Object + */ + public Serializable getFieldValue(Attribute attr, T entity) { + Serializable val = attr.get(entity); + CryptHandler cryptHandler = attr.attach(); + if (cryptHandler != null) val = (Serializable) cryptHandler.decrypt(val); + return val; + } + + /** + * 获取主键字段名 + * + * @return String + */ + public String getPrimaryColumn() { + return this.primary.field(); + } + + /** + * 获取主键字段的表字段名 + * + * @return String + */ + public String getPrimarySQLColumn() { + return getSQLColumn(null, this.primary.field()); + } + + /** + * 获取主键字段的带有表别名的表字段名 + * + * @param tabalis 表别名 + * + * @return String + */ + public String getPrimarySQLColumn(String tabalis) { + return getSQLColumn(tabalis, this.primary.field()); + } + + /** + * 拼接UPDATE给字段赋值的SQL片段 + * + * @param sqlColumn 表字段名 + * @param attr Attribute + * @param cv ColumnValue + * @param formatter 转义器 + * + * @return CharSequence + */ + protected CharSequence formatSQLValue(String sqlColumn, Attribute attr, final ColumnValue cv, BiFunction formatter) { + if (cv == null) return null; + Object val = cv.getValue(); + //ColumnNodeValue时 cv.getExpress() == ColumnExpress.MOV 只用于updateColumn + if (val instanceof ColumnNodeValue) return formatSQLValue(attr, null, (ColumnNodeValue) val, formatter); + if (val instanceof ColumnFuncNode) return formatSQLValue(attr, null, (ColumnFuncNode) val, formatter); + switch (cv.getExpress()) { + case INC: + return new StringBuilder().append(sqlColumn).append(" + ").append(val); + case DEC: + return new StringBuilder().append(sqlColumn).append(" - ").append(val); + case MUL: + return new StringBuilder().append(sqlColumn).append(" * ").append(val); + case DIV: + return new StringBuilder().append(sqlColumn).append(" / ").append(val); + case MOD: + return new StringBuilder().append(sqlColumn).append(" % ").append(val); + case AND: + return new StringBuilder().append(sqlColumn).append(" & ").append(val); + case ORR: + return new StringBuilder().append(sqlColumn).append(" | ").append(val); + case MOV: + CryptHandler handler = attr.attach(); + if (handler != null) val = handler.encrypt(val); + CharSequence rs = formatter == null ? formatToString(val) : formatter.apply(this, val); + if (rs == null && isNotNullJson(attr)) rs = ""; + return rs; + } + CryptHandler handler = attr.attach(); + if (handler != null) val = handler.encrypt(val); + return formatter == null ? formatToString(val) : formatter.apply(this, val); + } + + protected CharSequence formatSQLValue(Attribute attr, String tabalis, final ColumnFuncNode node, BiFunction formatter) { + if (node.getValue() instanceof ColumnNodeValue) { + return node.getFunc().getColumn(formatSQLValue(attr, tabalis, (ColumnNodeValue) node.getValue(), formatter).toString()); + } else { + return node.getFunc().getColumn(this.getSQLColumn(tabalis, String.valueOf(node.getValue()))); + } + } + + protected CharSequence formatSQLValue(Attribute attr, String tabalis, final ColumnNodeValue node, BiFunction formatter) { + Serializable left = node.getLeft(); + if (left instanceof CharSequence) { + left = this.getSQLColumn(tabalis, left.toString()); + if (node.getExpress() == ColumnExpress.MOV) return (String) left; + } else if (left instanceof ColumnNodeValue) { + left = "(" + formatSQLValue(attr, tabalis, (ColumnNodeValue) left, formatter) + ")"; + } else if (left instanceof ColumnFuncNode) { + left = "(" + formatSQLValue(attr, tabalis, (ColumnFuncNode) left, formatter) + ")"; + } + Serializable right = node.getRight(); + if (right instanceof CharSequence) { + right = this.getSQLColumn(null, right.toString()); + } else if (left instanceof ColumnNodeValue) { + right = "(" + formatSQLValue(attr, tabalis, (ColumnNodeValue) right, formatter) + ")"; + } else if (left instanceof ColumnFuncNode) { + right = "(" + formatSQLValue(attr, tabalis, (ColumnFuncNode) right, formatter) + ")"; + } + switch (node.getExpress()) { + case INC: + return new StringBuilder().append(left).append(" + ").append(right); + case DEC: + return new StringBuilder().append(left).append(" - ").append(right); + case MUL: + return new StringBuilder().append(left).append(" * ").append(right); + case DIV: + return new StringBuilder().append(left).append(" / ").append(right); + case MOD: + return new StringBuilder().append(left).append(" % ").append(right); + case AND: + return new StringBuilder().append(left).append(" & ").append(right); + case ORR: + return new StringBuilder().append(left).append(" | ").append(right); + } + throw new IllegalArgumentException(node + " express cannot be null or MOV"); + } + + /** + * 获取所有数据表字段的Attribute, 不包含@Transient字段 + * + * @return Map + */ + protected Map> getAttributes() { + return attributeMap; + } + + /** + * 判断日志级别 + * + * @param logger Logger + * @param l Level + * + * @return boolean + */ + public boolean isLoggable(Logger logger, Level l) { + return logger.isLoggable(l) && l.intValue() >= this.logLevel; + } + + public boolean isNotNullable(String fieldname) { + return notNullColumns.contains(fieldname); + } + + public boolean isNotNullable(Attribute attr) { + return attr == null ? false : notNullColumns.contains(attr.field()); + } + + public boolean isNotNullJson(Attribute attr) { + if (attr == null) return false; + return notNullColumns.contains(attr.field()) + && !Number.class.isAssignableFrom(attr.type()) + && !CharSequence.class.isAssignableFrom(attr.type()) + && boolean.class != attr.type() && Boolean.class != attr.type() + && byte[].class != attr.type() + && java.util.Date.class != attr.type() + && !attr.type().getName().startsWith("java.sql.") //避免引用import java.sql.* 减少模块依赖 + && !attr.type().getName().startsWith("java.time."); + } + + /** + * 判断日志级别 + * + * @param logger Logger + * @param l Level + * @param str String + * + * @return boolean + */ + public boolean isLoggable(Logger logger, Level l, String str) { + boolean rs = logger.isLoggable(l) && l.intValue() >= this.logLevel; + if (this.excludeLogLevels == null || !rs || str == null) return rs; + String[] keys = this.excludeLogLevels.get(l.intValue()); + if (keys == null) return rs; + for (String key : keys) { + if (str.contains(key)) return false; + } + return rs; + } + + /** + * 将字段值序列化为可SQL的字符串 + * + * @param value 字段值 + * + * @return String + */ + private String formatToString(Object value) { + if (value == null) return null; + if (value instanceof CharSequence) { + return new StringBuilder().append('\'').append(value.toString().replace("'", "\\'")).append('\'').toString(); + } else if (!(value instanceof Number) && !(value instanceof java.util.Date) + && !value.getClass().getName().startsWith("java.sql.") && !value.getClass().getName().startsWith("java.time.")) { + return new StringBuilder().append('\'').append(jsonConvert.convertTo(value).replace("'", "\\'")).append('\'').toString(); + } + return String.valueOf(value); + } + + /** + * 将一行的ResultSet组装成一个Entity对象 + * + * @param sels 指定字段 + * @param row ResultSet + * + * @return Entity对象 + */ + protected T getEntityValue(final SelectColumn sels, final DataResultSetRow row) { + if (row.wasNull()) return null; + T obj; + Attribute[] attrs = this.queryAttributes; + if (this.constructorParameters == null) { + obj = creator.create(); + } else { + Object[] cps = new Object[this.constructorParameters.length]; + for (int i = 0; i < this.constructorAttributes.length; i++) { + Attribute attr = this.constructorAttributes[i]; + if (sels == null || sels.test(attr.field())) { + cps[i] = getFieldValue(attr, row, 0); + } + } + obj = creator.create(cps); + attrs = this.unconstructorAttributes; + } + for (Attribute attr : attrs) { + if (sels == null || sels.test(attr.field())) { + attr.set(obj, getFieldValue(attr, row, 0)); + } + } + return obj; + } + + protected T getFullEntityValue(final DataResultSetRow row) { + return getEntityValue(constructorAttributes, constructorAttributes == null ? queryAttributes : unconstructorAttributes, row); + } + + /** + * 将一行的ResultSet组装成一个Entity对象 + * + * @param constructorAttrs 构建函数的Attribute数组, 大小必须与this.constructorAttributes相同 + * @param unconstructorAttrs 非构建函数的Attribute数组 + * @param row ResultSet + * + * @return Entity对象 + */ + protected T getEntityValue(final Attribute[] constructorAttrs, final Attribute[] unconstructorAttrs, final DataResultSetRow row) { + if (row.wasNull()) return null; + T obj; + int index = 0; + if (this.constructorParameters == null) { + obj = creator.create(); + } else { + Object[] cps = new Object[this.constructorParameters.length]; + for (int i = 0; i < constructorAttrs.length; i++) { + Attribute attr = constructorAttrs[i]; + if (attr == null) continue; + cps[i] = getFieldValue(attr, row, ++index); + } + obj = creator.create(cps); + } + if (unconstructorAttrs != null) { + for (Attribute attr : unconstructorAttrs) { + if (attr == null) continue; + attr.set(obj, getFieldValue(attr, row, ++index)); + } + } + return obj; + } + + protected Serializable getFieldValue(Attribute attr, final DataResultSetRow row, int index) { + return row.getObject(attr, index, index > 0 ? null : this.getSQLColumn(null, attr.field())); + } + + public static interface DataResultSetRow { + + //index从1开始 + public Object getObject(int index); + + public Object getObject(String column); + + //index从1开始 + default Serializable getObject(Attribute attr, int index, String column) { + return DataResultSet.getRowColumnValue(this, attr, index, column); + } + + //判断当前行值是否为null + public boolean wasNull(); + } + + public static class EntityColumn { + + public final boolean primary; //是否主键 + + public final String field; + + public final String column; + + public final Class type; + + public final String comment; + + public final boolean nullable; + + public final boolean unique; + + public final int length; + + public final int precision; + + public final int scale; + + protected EntityColumn(boolean primary, Column col, String name, Class type, Comment comment) { + this.primary = primary; + this.field = name; + this.column = col == null || col.name().isEmpty() ? name : col.name(); + this.type = type; + this.comment = (col == null || col.comment().isEmpty()) && comment != null && !comment.value().isEmpty() ? comment.value() : (col == null ? "" : col.comment()); + this.nullable = col == null ? false : col.nullable(); + this.unique = col == null ? false : col.unique(); + this.length = col == null ? 255 : col.length(); + this.precision = col == null ? 0 : col.precision(); + this.scale = col == null ? 0 : col.scale(); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/org/redkale/source/FilterBean.java b/src/main/java/org/redkale/source/FilterBean.java similarity index 93% rename from src/org/redkale/source/FilterBean.java rename to src/main/java/org/redkale/source/FilterBean.java index 1299b5b48..fdf03fc7c 100644 --- a/src/org/redkale/source/FilterBean.java +++ b/src/main/java/org/redkale/source/FilterBean.java @@ -1,20 +1,23 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -/** - * FilterBean用于过滤条件, 所有的FilterBean都必须可以转换成FilterNode
    - * - * 标记为@FilterColumn.ignore=true 的字段会被忽略, 不参与生成过滤条件
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface FilterBean { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import org.redkale.util.Bean; + +/** + * FilterBean用于过滤条件, 所有的FilterBean都必须可以转换成FilterNode
    + * + * 标记为@FilterColumn.ignore=true 的字段会被忽略, 不参与生成过滤条件
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Bean +public interface FilterBean { + +} diff --git a/src/org/redkale/source/FilterColumn.java b/src/main/java/org/redkale/source/FilterColumn.java similarity index 86% rename from src/org/redkale/source/FilterColumn.java rename to src/main/java/org/redkale/source/FilterColumn.java index 2e595e0e1..a8f48f4bc 100644 --- a/src/org/redkale/source/FilterColumn.java +++ b/src/main/java/org/redkale/source/FilterColumn.java @@ -1,72 +1,86 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - -/** - * 过滤字段标记 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({FIELD}) -@Retention(RUNTIME) -public @interface FilterColumn { - - /** - * 对应Entity Class中字段的名称, 而不是SQL字段名称 - * - * @return 字段名 - */ - String name() default ""; - - /** - * 当字段类型是Number时, 如果值>=least() 则需要过滤, 否则跳过该字段 - * - * @return 最小可过滤值 - */ - long least() default 1; - - /** - * 生成过滤条件时是否屏蔽该字段 - * - * @return 是否屏蔽该字段 - * @since 2.4.0 - */ - boolean ignore() default false; - - /** - * express的默认值根据字段类型的不同而不同:
    - * 数组 --> IN
    - * Range --> Between
    - * 其他 --> =
    - * - * @return 字段表达式 - */ - FilterExpress express() default FilterExpress.EQUAL; - - /** - * 当标记的字段类型是数组/Collection类型且express不是IN/NOTIN时,则构建过滤条件时会遍历字段值的元素来循环构建表达式,元素之间的关系是AND或OR由该值来确定 - * - * @return 数组元素间的表达式是否AND关系 - */ - boolean itemand() default true; - - /** - * 备注描述 - * - * @return 备注描述 - */ - String comment() default ""; - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; + +/** + * 过滤字段标记 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +public @interface FilterColumn { + + /** + * 对应Entity Class中字段的名称, 而不是SQL字段名称 + * + * @return 字段名 + */ + String name() default ""; + + /** + * 当字段类型是Number时, 如果值>=least() 则需要过滤, 否则跳过该字段 + * + * @return 最小可过滤值 + */ + long least() default 1; + + /** + * 生成过滤条件时是否屏蔽该字段 + * + * @return 是否屏蔽该字段 + * @since 2.4.0 + */ + boolean ignore() default false; + + /** + * express的默认值根据字段类型的不同而不同:
    + * 数组 --> IN
    + * Range --> Between
    + * 其他 --> =
    + * + * @return 字段表达式 + */ + FilterExpress express() default FilterExpress.EQUAL; + + /** + * 当标记的字段类型是数组/Collection类型且express不是IN/NOTIN时,则构建过滤条件时会遍历字段值的元素来循环构建表达式,元素之间的关系是AND或OR由该值来确定 + * + * @return 数组元素间的表达式是否AND关系 + */ + boolean itemand() default true; + + /** + * 判断字段是否必需,for OpenAPI Specification 3.1.0 + * + * @return 是否必需 + */ + boolean required() default false; + + /** + * for OpenAPI Specification 3.1.0 + * + * @return String + */ + String example() default ""; + + /** + * 备注描述 + * + * @return 备注描述 + */ + String comment() default ""; + +} diff --git a/src/org/redkale/source/FilterExpress.java b/src/main/java/org/redkale/source/FilterExpress.java similarity index 96% rename from src/org/redkale/source/FilterExpress.java rename to src/main/java/org/redkale/source/FilterExpress.java index 290ade443..a6e7fac8a 100644 --- a/src/org/redkale/source/FilterExpress.java +++ b/src/main/java/org/redkale/source/FilterExpress.java @@ -1,71 +1,71 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -/** - * 函数表达式, 均与SQL定义中的表达式相同 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public enum FilterExpress { - - EQUAL("="), - IGNORECASEEQUAL("="),//不区分大小写的 = - NOTEQUAL("<>"), - IGNORECASENOTEQUAL("="),//不区分大小写的 <> - GREATERTHAN(">"), - LESSTHAN("<"), - GREATERTHANOREQUALTO(">="), - LESSTHANOREQUALTO("<="), - STARTSWITH("LIKE"), - NOTSTARTSWITH("NOT LIKE"), - ENDSWITH("LIKE"), - NOTENDSWITH("NOT LIKE"), - LIKE("LIKE"), - NOTLIKE("NOT LIKE"), - IGNORECASELIKE("LIKE"), //不区分大小写的 LIKE - IGNORECASENOTLIKE("NOT LIKE"), //不区分大小写的 NOT LIKE - LENGTH_EQUAL("="), //字符串值的长度 - LENGTH_LESSTHAN("<"), //字符串值的长度 < - LENGTH_LESSTHANOREQUALTO("<="), //字符串值的长度 <= - LENGTH_GREATERTHAN(">"), //字符串值的长度 > - LENGTH_GREATERTHANOREQUALTO(">="), //字符串值的长度 >= - - CONTAIN("CONTAIN"), //包含, 相当于反向LIKE - NOTCONTAIN("NOT CONTAIN"), //不包含, 相当于反向LIKE - IGNORECASECONTAIN("CONTAIN"), //不区分大小写的 CONTAIN - IGNORECASENOTCONTAIN("NOT CONTAIN"), //不区分大小写的 NOT CONTAIN - - BETWEEN("BETWEEN"), - NOTBETWEEN("NOT BETWEEN"), - IN("IN"), - NOTIN("NOT IN"), - ISNULL("IS NULL"), - ISNOTNULL("IS NOT NULL"), - ISEMPTY("="),//值为空 - ISNOTEMPTY("<>"), //值不为空 - OPAND("&"), //与运算 > 0 - OPOR("|"), //或运算 > 0 - OPANDNO("&"), //与运算 == 0 - FV_MOD("%"), //取模运算,需要与FilterValue配合使用 - FV_DIV("DIV"), //整除运算,需要与FilterValue配合使用 - AND("AND"), - OR("OR"); - - private final String value; - - private FilterExpress(String v) { - this.value = v; - } - - public String value() { - return value; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +/** + * 函数表达式, 均与SQL定义中的表达式相同 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public enum FilterExpress { + + EQUAL("="), + IGNORECASEEQUAL("="),//不区分大小写的 = + NOTEQUAL("<>"), + IGNORECASENOTEQUAL("="),//不区分大小写的 <> + GREATERTHAN(">"), + LESSTHAN("<"), + GREATERTHANOREQUALTO(">="), + LESSTHANOREQUALTO("<="), + STARTSWITH("LIKE"), + NOTSTARTSWITH("NOT LIKE"), + ENDSWITH("LIKE"), + NOTENDSWITH("NOT LIKE"), + LIKE("LIKE"), + NOTLIKE("NOT LIKE"), + IGNORECASELIKE("LIKE"), //不区分大小写的 LIKE + IGNORECASENOTLIKE("NOT LIKE"), //不区分大小写的 NOT LIKE + LENGTH_EQUAL("="), //字符串值的长度 + LENGTH_LESSTHAN("<"), //字符串值的长度 < + LENGTH_LESSTHANOREQUALTO("<="), //字符串值的长度 <= + LENGTH_GREATERTHAN(">"), //字符串值的长度 > + LENGTH_GREATERTHANOREQUALTO(">="), //字符串值的长度 >= + + CONTAIN("CONTAIN"), //包含, 相当于反向LIKE + NOTCONTAIN("NOT CONTAIN"), //不包含, 相当于反向LIKE + IGNORECASECONTAIN("CONTAIN"), //不区分大小写的 CONTAIN + IGNORECASENOTCONTAIN("NOT CONTAIN"), //不区分大小写的 NOT CONTAIN + + BETWEEN("BETWEEN"), + NOTBETWEEN("NOT BETWEEN"), + IN("IN"), + NOTIN("NOT IN"), + ISNULL("IS NULL"), + ISNOTNULL("IS NOT NULL"), + ISEMPTY("="),//值为空 + ISNOTEMPTY("<>"), //值不为空 + OPAND("&"), //与运算 > 0 + OPOR("|"), //或运算 > 0 + OPANDNO("&"), //与运算 == 0 + FV_MOD("%"), //取模运算,需要与FilterValue配合使用 + FV_DIV("DIV"), //整除运算,需要与FilterValue配合使用 + AND("AND"), + OR("OR"); + + private final String value; + + private FilterExpress(String v) { + this.value = v; + } + + public String value() { + return value; + } + +} diff --git a/src/org/redkale/source/FilterFunc.java b/src/main/java/org/redkale/source/FilterFunc.java similarity index 95% rename from src/org/redkale/source/FilterFunc.java rename to src/main/java/org/redkale/source/FilterFunc.java index 756906cee..919a01909 100644 --- a/src/org/redkale/source/FilterFunc.java +++ b/src/main/java/org/redkale/source/FilterFunc.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -/** - * 常见的SQL聚合函数 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public enum FilterFunc { - AVG, //平均值 - COUNT, //总数 - DISTINCTCOUNT, //去重总数 - MAX, //最大值 - MIN, //最小值 - SUM; //求和 - - public String getColumn(String col) { - if (this == DISTINCTCOUNT) return "COUNT(DISTINCT " + col + ")"; - return this.name() + "(" + col + ")"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +/** + * 常见的SQL聚合函数 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public enum FilterFunc { + AVG, //平均值 + COUNT, //总数 + DISTINCTCOUNT, //去重总数 + MAX, //最大值 + MIN, //最小值 + SUM; //求和 + + public String getColumn(String col) { + if (this == DISTINCTCOUNT) return "COUNT(DISTINCT " + col + ")"; + return this.name() + "(" + col + ")"; + } +} diff --git a/src/org/redkale/source/FilterFuncColumn.java b/src/main/java/org/redkale/source/FilterFuncColumn.java similarity index 96% rename from src/org/redkale/source/FilterFuncColumn.java rename to src/main/java/org/redkale/source/FilterFuncColumn.java index d12032dd0..85daac7eb 100644 --- a/src/org/redkale/source/FilterFuncColumn.java +++ b/src/main/java/org/redkale/source/FilterFuncColumn.java @@ -1,93 +1,93 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.util.Arrays; - -/** - * FilterFuncColumn用于getNumberMap获取列表似数据, getNumberResult获取单字段值, getNumberMap获取多字段值 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class FilterFuncColumn implements java.io.Serializable { - - public static final String COLUMN_NULL = "*"; - - FilterFunc func; - - String[] columns; //为null,将使用*代替 - - Number defvalue; - - public FilterFuncColumn() { - } - - public static FilterFuncColumn create(final FilterFunc func) { - return new FilterFuncColumn(func); - } - - public static FilterFuncColumn create(final FilterFunc func, final String... columns) { - return new FilterFuncColumn(func, columns); - } - - public static FilterFuncColumn create(final FilterFunc func, final Number defvalue, final String... columns) { - return new FilterFuncColumn(func, defvalue, columns); - } - - public String[] cols() { - return columns == null || columns.length == 0 ? new String[]{COLUMN_NULL} : columns; - } - - public String col(String column) { - return column == null || column.isEmpty() ? COLUMN_NULL : column; - } - - public FilterFuncColumn(final FilterFunc func) { - this(func, (Number) null); - } - - public FilterFuncColumn(final FilterFunc func, final String... columns) { - this(func, null, columns); - } - - public FilterFuncColumn(final FilterFunc func, final Number defvalue, final String... columns) { - this.func = func; - this.defvalue = defvalue; - this.columns = columns; - } - - public FilterFunc getFunc() { - return func; - } - - public void setFunc(FilterFunc func) { - this.func = func; - } - - public String[] getColumns() { - return columns; - } - - public void setColumns(String[] columns) { - this.columns = columns; - } - - public Number getDefvalue() { - return defvalue; - } - - public void setDefvalue(Number defvalue) { - this.defvalue = defvalue; - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "{func:" + this.func + ", columns:" + Arrays.toString(this.columns) + ", defvalue:" + this.defvalue + "}"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.util.Arrays; + +/** + * FilterFuncColumn用于getNumberMap获取列表似数据, getNumberResult获取单字段值, getNumberMap获取多字段值 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class FilterFuncColumn implements java.io.Serializable { + + public static final String COLUMN_NULL = "*"; + + FilterFunc func; + + String[] columns; //为null,将使用*代替 + + Number defvalue; + + public FilterFuncColumn() { + } + + public static FilterFuncColumn create(final FilterFunc func) { + return new FilterFuncColumn(func); + } + + public static FilterFuncColumn create(final FilterFunc func, final String... columns) { + return new FilterFuncColumn(func, columns); + } + + public static FilterFuncColumn create(final FilterFunc func, final Number defvalue, final String... columns) { + return new FilterFuncColumn(func, defvalue, columns); + } + + public String[] cols() { + return columns == null || columns.length == 0 ? new String[]{COLUMN_NULL} : columns; + } + + public String col(String column) { + return column == null || column.isEmpty() ? COLUMN_NULL : column; + } + + public FilterFuncColumn(final FilterFunc func) { + this(func, (Number) null); + } + + public FilterFuncColumn(final FilterFunc func, final String... columns) { + this(func, null, columns); + } + + public FilterFuncColumn(final FilterFunc func, final Number defvalue, final String... columns) { + this.func = func; + this.defvalue = defvalue; + this.columns = columns; + } + + public FilterFunc getFunc() { + return func; + } + + public void setFunc(FilterFunc func) { + this.func = func; + } + + public String[] getColumns() { + return columns; + } + + public void setColumns(String[] columns) { + this.columns = columns; + } + + public Number getDefvalue() { + return defvalue; + } + + public void setDefvalue(Number defvalue) { + this.defvalue = defvalue; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{func:" + this.func + ", columns:" + Arrays.toString(this.columns) + ", defvalue:" + this.defvalue + "}"; + } +} diff --git a/src/org/redkale/source/FilterGroup.java b/src/main/java/org/redkale/source/FilterGroup.java similarity index 96% rename from src/org/redkale/source/FilterGroup.java rename to src/main/java/org/redkale/source/FilterGroup.java index 124379885..82b21b4da 100644 --- a/src/org/redkale/source/FilterGroup.java +++ b/src/main/java/org/redkale/source/FilterGroup.java @@ -1,69 +1,69 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import static java.lang.annotation.ElementType.FIELD; -import java.lang.annotation.*; - -/** - * 默认情况下FilterBean下的过滤字段之间是AND关系。
    - * 当需要使用OR或AND OR组合过滤查询时需要使用 FilterGroup。
    - * FilterGroup 的value 必须是[OR]或者[AND]开头, 多级需要用点.分隔。 (注: 暂时不支持多级)
    - * 示例一: - *

    - * public class TestFilterBean implements FilterBean {
    - *
    - *      private int id;
    - *
    - *      @FilterGroup("[OR]g1")
    - *      private String desc;
    - *
    - *      @FilterGroup("[OR]g1")
    - *      private String name;
    - * }
    - * 
    - * 转换的SQL语句为: WHERE id = ? AND (desc = ? OR name = ?) - * - * 示例二: - *
    - * public class TestFilterBean implements FilterBean {
    - *
    - *      private int id;
    - *
    - *      @FilterGroup("[OR]g1.[AND]subg1")
    - *      @FilterColumn(express = LIKE)
    - *      private String desc;
    - *
    - *      @FilterGroup("[OR]g1.[AND]subg1")
    - *      @FilterColumn(express = LIKE)
    - *      private String name;
    - *
    - *      @FilterGroup("[OR]g1.[OR]subg2")
    - *      private int age;
    - *
    - *      @FilterGroup("[OR]g1.[OR]subg2")
    - *      private int birthday;
    - * }
    - * 
    - * 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?))
    - * 因为默认是AND关系, @FilterGroup("") 等价于 @FilterGroup("[AND]")
    - * 所以示例二的@FilterGroup("[OR]g1.[AND]subg1") 可以简化为 @FilterGroup("[OR]g1.subg1")
    - */ -/** - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({FIELD}) -@Retention(RUNTIME) -public @interface FilterGroup { - - String value() default "[AND]"; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.FIELD; +import java.lang.annotation.*; + +/** + * 默认情况下FilterBean下的过滤字段之间是AND关系。
    + * 当需要使用OR或AND OR组合过滤查询时需要使用 FilterGroup。
    + * FilterGroup 的value 必须是[OR]或者[AND]开头, 多级需要用点.分隔。 (注: 暂时不支持多级)
    + * 示例一: + *

    + * public class TestFilterBean implements FilterBean {
    + *
    + *      private int id;
    + *
    + *      @FilterGroup("[OR]g1")
    + *      private String desc;
    + *
    + *      @FilterGroup("[OR]g1")
    + *      private String name;
    + * }
    + * 
    + * 转换的SQL语句为: WHERE id = ? AND (desc = ? OR name = ?) + * + * 示例二: + *
    + * public class TestFilterBean implements FilterBean {
    + *
    + *      private int id;
    + *
    + *      @FilterGroup("[OR]g1.[AND]subg1")
    + *      @FilterColumn(express = LIKE)
    + *      private String desc;
    + *
    + *      @FilterGroup("[OR]g1.[AND]subg1")
    + *      @FilterColumn(express = LIKE)
    + *      private String name;
    + *
    + *      @FilterGroup("[OR]g1.[OR]subg2")
    + *      private int age;
    + *
    + *      @FilterGroup("[OR]g1.[OR]subg2")
    + *      private int birthday;
    + * }
    + * 
    + * 转换的SQL语句为: WHERE id = ? AND ((desc LIKE ? AND name LIKE ?) OR (age = ? OR birthday = ?))
    + * 因为默认是AND关系, @FilterGroup("") 等价于 @FilterGroup("[AND]")
    + * 所以示例二的@FilterGroup("[OR]g1.[AND]subg1") 可以简化为 @FilterGroup("[OR]g1.subg1")
    + */ +/** + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +public @interface FilterGroup { + + String value() default "[AND]"; +} diff --git a/src/org/redkale/source/FilterJoinColumn.java b/src/main/java/org/redkale/source/FilterJoinColumn.java similarity index 97% rename from src/org/redkale/source/FilterJoinColumn.java rename to src/main/java/org/redkale/source/FilterJoinColumn.java index 528deb081..fe03ee3cb 100644 --- a/src/org/redkale/source/FilterJoinColumn.java +++ b/src/main/java/org/redkale/source/FilterJoinColumn.java @@ -1,54 +1,54 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 关联表过滤条件
    - * 关联关系表必须含主表, 不能是主表A关联表B,表B再关联表C,只能是主表A关联表B,主表A关联表C
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({FIELD}) -@Retention(RUNTIME) -public @interface FilterJoinColumn { - - /** - * 关联表 通常join表默认别名为b/c/d/...自增, 被join表默认别名为a - * - * @return 关联表 - */ - Class table(); - - /** - * - * 多个关联字段, 默认使用join表(b)的主键, join表与被join表(a)的字段必须一样
    - * 例如: SELECT a.* FROM user a INNER JOIN orderinfo b ON a.userid = b.userid AND a.usertype = b.usertype
    - * 那么注解为: @FilterJoinColumn(table = OrderInfo.class, columns = {"userid", "usertype"})
    - *

    - * columns中的字段名如果不一致,可以将两个字段名用=连接成一个字段名
    - * 例如: SELECT a.* FROM user a INNER JOIN orderinfo b ON a.userid = b.buyerid AND a.usertype = b.usertype
    - * 那么注解为: @FilterJoinColumn(table = OrderInfo.class, columns = {"userid=buyerid", "usertype"})
    - * - * @return 关联字段 - */ - String[] columns(); - - /** - * 备注描述 - * - * @return 备注描述 - */ - String comment() default ""; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 关联表过滤条件
    + * 关联关系表必须含主表, 不能是主表A关联表B,表B再关联表C,只能是主表A关联表B,主表A关联表C
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({FIELD}) +@Retention(RUNTIME) +public @interface FilterJoinColumn { + + /** + * 关联表 通常join表默认别名为b/c/d/...自增, 被join表默认别名为a + * + * @return 关联表 + */ + Class table(); + + /** + * + * 多个关联字段, 默认使用join表(b)的主键, join表与被join表(a)的字段必须一样
    + * 例如: SELECT a.* FROM user a INNER JOIN orderinfo b ON a.userid = b.userid AND a.usertype = b.usertype
    + * 那么注解为: @FilterJoinColumn(table = OrderInfo.class, columns = {"userid", "usertype"})
    + *

    + * columns中的字段名如果不一致,可以将两个字段名用=连接成一个字段名
    + * 例如: SELECT a.* FROM user a INNER JOIN orderinfo b ON a.userid = b.buyerid AND a.usertype = b.usertype
    + * 那么注解为: @FilterJoinColumn(table = OrderInfo.class, columns = {"userid=buyerid", "usertype"})
    + * + * @return 关联字段 + */ + String[] columns(); + + /** + * 备注描述 + * + * @return 备注描述 + */ + String comment() default ""; +} diff --git a/src/org/redkale/source/FilterJoinNode.java b/src/main/java/org/redkale/source/FilterJoinNode.java similarity index 96% rename from src/org/redkale/source/FilterJoinNode.java rename to src/main/java/org/redkale/source/FilterJoinNode.java index 16ad0dce8..8cd35934a 100644 --- a/src/org/redkale/source/FilterJoinNode.java +++ b/src/main/java/org/redkale/source/FilterJoinNode.java @@ -1,367 +1,367 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.*; -import static org.redkale.source.FilterExpress.EQUAL; -import org.redkale.util.*; - -/** - * @FilterJoinColumn对应的FilterNode对象 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class FilterJoinNode extends FilterNode { - - private Class joinClass; - - private EntityInfo joinEntity; //在调用 createSQLJoin 和 isCacheUseable 时会注入 - - private String[] joinColumns; - - public FilterJoinNode() { - } - - protected FilterJoinNode(Class joinClass, String[] joinColumns, String column, FilterExpress express, boolean itemand, Serializable value) { - Objects.requireNonNull(joinClass); - Objects.requireNonNull(joinColumns); - if (express == null && value != null) { - if (value instanceof Range) { - express = FilterExpress.BETWEEN; - } else if (value instanceof Collection) { - express = FilterExpress.IN; - } else if (value.getClass().isArray()) { - express = FilterExpress.IN; - } - } - this.joinClass = joinClass; - this.joinColumns = joinColumns; - this.column = column; - this.express = express == null ? EQUAL : express; - this.itemand = itemand; - this.value = value; - } - - protected FilterJoinNode(FilterJoinNode node) { - this(node.joinClass, node.joinColumns, node.column, node.express, node.itemand, node.value); - this.joinEntity = node.joinEntity; - this.or = node.or; - this.nodes = node.nodes; - } - - public static FilterJoinNode create(Class joinClass, String joinColumn, String column, Serializable value) { - return create(joinClass, new String[]{joinColumn}, column, value); - } - - public static FilterJoinNode create(Class joinClass, String joinColumn, String column, FilterExpress express, Serializable value) { - return create(joinClass, new String[]{joinColumn}, column, express, value); - } - - public static FilterJoinNode create(Class joinClass, String joinColumn, String column, FilterExpress express, boolean itemand, Serializable value) { - return create(joinClass, new String[]{joinColumn}, column, express, itemand, value); - } - - public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, Serializable value) { - return create(joinClass, joinColumns, column, null, value); - } - - public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, FilterExpress express, Serializable value) { - return create(joinClass, joinColumns, column, express, true, value); - } - - public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, FilterExpress express, boolean itemand, Serializable value) { - return new FilterJoinNode(joinClass, joinColumns, column, express, itemand, value); - } - - @Override - public FilterJoinNode copy() { - FilterJoinNode node = (FilterJoinNode) copy(new FilterJoinNode()); - node.joinClass = this.joinClass; - node.joinEntity = this.joinEntity; - if (this.joinColumns != null) { - node.joinColumns = new String[this.joinColumns.length]; - System.arraycopy(this.joinColumns, 0, node.joinColumns, 0, this.joinColumns.length); - } - return node; - } - - @Override - protected FilterNode any(final FilterNode node0, boolean signor) { - Objects.requireNonNull(node0); - if (!(node0 instanceof FilterJoinNode)) { - throw new IllegalArgumentException(this + (signor ? " or " : " and ") + " a node but " + String.valueOf(node0) + " is not a " + FilterJoinNode.class.getSimpleName()); - } - final FilterJoinNode node = (FilterJoinNode) node0; - if (this.nodes == null) { - this.nodes = new FilterNode[]{node}; - this.or = signor; - return this; - } - if (or == signor || this.column == null) { - this.nodes = Utility.append(this.nodes, node); - if (this.column == null) this.or = signor; - return this; - } - this.nodes = new FilterNode[]{new FilterJoinNode(this), node}; - this.column = null; - this.express = null; - this.itemand = true; - this.value = null; - this.joinClass = null; - this.joinEntity = null; - this.joinColumns = null; - this.or = signor; - return this; - } - - @Override - protected CharSequence createSQLExpress(final EntityInfo info, final Map joinTabalis) { - return super.createSQLExpress(this.joinEntity == null ? info : this.joinEntity, joinTabalis); - } - - @Override - protected Predicate createPredicate(final EntityCache cache) { - if (column == null && this.nodes == null) return null; - final EntityCache joinCache = this.joinEntity.getCache(); - final AtomicBoolean more = new AtomicBoolean(); - Predicate filter = createJoinPredicate(more); - Predicate rs = null; - if (filter == null && !more.get()) return rs; - if (filter != null) { - final Predicate inner = filter; - final String[][] localJoinColumns = new String[joinColumns.length][2]; - for (int i = 0; i < joinColumns.length; i++) { - int pos = joinColumns[i].indexOf('='); - if (pos > 0) { - localJoinColumns[i] = new String[]{joinColumns[i].substring(0, pos), joinColumns[i].substring(pos + 1)}; - } else { - localJoinColumns[i] = new String[]{joinColumns[i], joinColumns[i]}; - } - } - rs = new Predicate() { - - @Override - public boolean test(final T t) { - Predicate joinPredicate = null; - for (String[] localJoinColumn : localJoinColumns) { - final Serializable key = cache.getAttribute(localJoinColumn[0]).get(t); - final Attribute joinAttr = joinCache.getAttribute(localJoinColumn[1]); - Predicate p = (E e) -> key.equals(joinAttr.get(e)); - joinPredicate = joinPredicate == null ? p : joinPredicate.and(p); - } - return joinCache.exists(inner.and(joinPredicate)); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(" #-- ON ").append(localJoinColumns[0][0]).append("=").append(joinClass == null ? "null" : joinClass.getSimpleName()).append(".").append(localJoinColumns[0][1]); - for (int i = 1; i < localJoinColumns.length; i++) { - sb.append(" AND ").append(localJoinColumns[i][0]).append("=").append(joinClass == null ? "null" : joinClass.getSimpleName()).append(".").append(localJoinColumns[i][1]); - } - sb.append(" --# ").append(inner.toString()); - return sb.toString(); - } - }; - } - if (more.get()) { //存在不同Class的关联表 - if (this.nodes != null) { - for (FilterNode node : this.nodes) { - if (((FilterJoinNode) node).joinClass == this.joinClass) continue; - Predicate f = node.createPredicate(cache); - if (f == null) continue; - final Predicate one = rs; - final Predicate two = f; - rs = (rs == null) ? f : (or ? new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) || two.test(t); - } - - @Override - public String toString() { - return "(" + one + " OR " + two + ")"; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) && two.test(t); - } - - @Override - public String toString() { - return "(" + one + " AND " + two + ")"; - } - }); - } - } - } - return rs; - } - - private Predicate createJoinPredicate(final AtomicBoolean more) { - if (column == null && this.nodes == null) return null; - final EntityCache joinCache = this.joinEntity.getCache(); - Predicate filter = createElementPredicate(joinCache, true); - if (this.nodes != null) { - for (FilterNode node : this.nodes) { - if (((FilterJoinNode) node).joinClass != this.joinClass) { - more.set(true); - continue; - } - Predicate f = ((FilterJoinNode) node).createJoinPredicate(more); - if (f == null) continue; - final Predicate one = filter; - final Predicate two = f; - filter = (filter == null) ? f : (or ? new Predicate() { - - @Override - public boolean test(E t) { - return one.test(t) || two.test(t); - } - - @Override - public String toString() { - return "(" + one + " OR " + two + ")"; - } - } : new Predicate() { - - @Override - public boolean test(E t) { - return one.test(t) && two.test(t); - } - - @Override - public String toString() { - return "(" + one + " AND " + two + ")"; - } - }); - } - } - return filter; - } - - @Override - protected CharSequence createSQLJoin(final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { - boolean morejoin = false; - if (this.joinEntity == null) { - if (this.joinClass != null) this.joinEntity = func.apply(this.joinClass); - if (this.nodes != null) { - for (FilterNode node : this.nodes) { - if (node instanceof FilterJoinNode) { - FilterJoinNode joinNode = ((FilterJoinNode) node); - if (joinNode.joinClass != null) { - joinNode.joinEntity = func.apply(joinNode.joinClass); - if (this.joinClass != null && this.joinClass != joinNode.joinClass) morejoin = true; - } - } - } - } - } - StringBuilder sb = new StringBuilder(); - if (this.joinClass != null) { - CharSequence cs = createElementSQLJoin(update, joinTabalis, haset, info, this); - if (cs != null) sb.append(cs); - } - if (morejoin) { - Set set = new HashSet<>(); - if (this.joinClass != null) set.add(this.joinClass); - for (FilterNode node : this.nodes) { - if (node instanceof FilterJoinNode) { - FilterJoinNode joinNode = ((FilterJoinNode) node); - if (!set.contains(joinNode.joinClass)) { - CharSequence cs = createElementSQLJoin(update, joinTabalis, haset, info, joinNode); - if (cs != null) { - sb.append(cs); - set.add(joinNode.joinClass); - } - } - } - } - } - return sb; - } - - private static CharSequence createElementSQLJoin(final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info, final FilterJoinNode node) { - if (node.joinClass == null || (haset != null && haset.contains(joinTabalis.get(node.joinClass)))) return null; - StringBuilder sb = new StringBuilder(); - String[] joinColumns = node.joinColumns; - int pos = joinColumns[0].indexOf('='); - if (update) { - sb.append("[").append(node.joinEntity.getTable(node)).append(" ").append(joinTabalis.get(node.joinClass)).append(']'); - sb.append('{').append(info.getSQLColumn("a", pos > 0 ? joinColumns[0].substring(0, pos) : joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[0].substring(pos + 1) : joinColumns[0])); - for (int i = 1; i < joinColumns.length; i++) { - pos = joinColumns[i].indexOf('='); - sb.append(" AND ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[i].substring(0, pos) : joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[i].substring(pos + 1) : joinColumns[i])); - } - sb.append('}'); - } else { - sb.append(" INNER JOIN ").append(node.joinEntity.getTable(node)).append(" ").append(joinTabalis.get(node.joinClass)) - .append(" ON ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[0].substring(0, pos) : joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[0].substring(pos + 1) : joinColumns[0])); - for (int i = 1; i < joinColumns.length; i++) { - pos = joinColumns[i].indexOf('='); - sb.append(" AND ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[i].substring(0, pos) : joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[i].substring(pos + 1) : joinColumns[i])); - } - } - if (haset != null) haset.add(joinTabalis.get(node.joinClass)); - return sb; - } - - @Override - protected boolean isCacheUseable(final Function entityApplyer) { - if (this.joinEntity == null) this.joinEntity = entityApplyer.apply(this.joinClass); - if (!this.joinEntity.isCacheFullLoaded()) return false; - if (this.nodes == null) return true; - for (FilterNode node : this.nodes) { - if (!node.isCacheUseable(entityApplyer)) return false; - } - return true; - } - - @Override - protected void putJoinTabalis(Map map) { - if (this.joinClass != null && !map.containsKey(this.joinClass)) map.put(joinClass, String.valueOf((char) ('b' + map.size()))); - if (this.nodes == null) return; - for (FilterNode node : this.nodes) { - node.putJoinTabalis(map); - } - } - - @Override - protected final boolean isjoin() { - return true; - } - - @Override - public String toString() { - return toString(joinClass == null ? null : joinClass.getSimpleName()).toString(); - } - - public Class getJoinClass() { - return joinClass; - } - - public void setJoinClass(Class joinClass) { - this.joinClass = joinClass; - } - - public String[] getJoinColumns() { - return joinColumns; - } - - public void setJoinColumns(String[] joinColumns) { - this.joinColumns = joinColumns; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.*; +import static org.redkale.source.FilterExpress.EQUAL; +import org.redkale.util.*; + +/** + * @FilterJoinColumn对应的FilterNode对象 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class FilterJoinNode extends FilterNode { + + private Class joinClass; + + private EntityInfo joinEntity; //在调用 createSQLJoin 和 isCacheUseable 时会注入 + + private String[] joinColumns; + + public FilterJoinNode() { + } + + protected FilterJoinNode(Class joinClass, String[] joinColumns, String column, FilterExpress express, boolean itemand, Serializable value) { + Objects.requireNonNull(joinClass); + Objects.requireNonNull(joinColumns); + if (express == null && value != null) { + if (value instanceof Range) { + express = FilterExpress.BETWEEN; + } else if (value instanceof Collection) { + express = FilterExpress.IN; + } else if (value.getClass().isArray()) { + express = FilterExpress.IN; + } + } + this.joinClass = joinClass; + this.joinColumns = joinColumns; + this.column = column; + this.express = express == null ? EQUAL : express; + this.itemand = itemand; + this.value = value; + } + + protected FilterJoinNode(FilterJoinNode node) { + this(node.joinClass, node.joinColumns, node.column, node.express, node.itemand, node.value); + this.joinEntity = node.joinEntity; + this.or = node.or; + this.nodes = node.nodes; + } + + public static FilterJoinNode create(Class joinClass, String joinColumn, String column, Serializable value) { + return create(joinClass, new String[]{joinColumn}, column, value); + } + + public static FilterJoinNode create(Class joinClass, String joinColumn, String column, FilterExpress express, Serializable value) { + return create(joinClass, new String[]{joinColumn}, column, express, value); + } + + public static FilterJoinNode create(Class joinClass, String joinColumn, String column, FilterExpress express, boolean itemand, Serializable value) { + return create(joinClass, new String[]{joinColumn}, column, express, itemand, value); + } + + public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, Serializable value) { + return create(joinClass, joinColumns, column, null, value); + } + + public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, FilterExpress express, Serializable value) { + return create(joinClass, joinColumns, column, express, true, value); + } + + public static FilterJoinNode create(Class joinClass, String[] joinColumns, String column, FilterExpress express, boolean itemand, Serializable value) { + return new FilterJoinNode(joinClass, joinColumns, column, express, itemand, value); + } + + @Override + public FilterJoinNode copy() { + FilterJoinNode node = (FilterJoinNode) copy(new FilterJoinNode()); + node.joinClass = this.joinClass; + node.joinEntity = this.joinEntity; + if (this.joinColumns != null) { + node.joinColumns = new String[this.joinColumns.length]; + System.arraycopy(this.joinColumns, 0, node.joinColumns, 0, this.joinColumns.length); + } + return node; + } + + @Override + protected FilterNode any(final FilterNode node0, boolean signor) { + Objects.requireNonNull(node0); + if (!(node0 instanceof FilterJoinNode)) { + throw new IllegalArgumentException(this + (signor ? " or " : " and ") + " a node but " + String.valueOf(node0) + " is not a " + FilterJoinNode.class.getSimpleName()); + } + final FilterJoinNode node = (FilterJoinNode) node0; + if (this.nodes == null) { + this.nodes = new FilterNode[]{node}; + this.or = signor; + return this; + } + if (or == signor || this.column == null) { + this.nodes = Utility.append(this.nodes, node); + if (this.column == null) this.or = signor; + return this; + } + this.nodes = new FilterNode[]{new FilterJoinNode(this), node}; + this.column = null; + this.express = null; + this.itemand = true; + this.value = null; + this.joinClass = null; + this.joinEntity = null; + this.joinColumns = null; + this.or = signor; + return this; + } + + @Override + protected CharSequence createSQLExpress(DataSqlSource source, final EntityInfo info, final Map joinTabalis) { + return super.createSQLExpress(source, this.joinEntity == null ? info : this.joinEntity, joinTabalis); + } + + @Override + protected Predicate createPredicate(final EntityCache cache) { + if (column == null && this.nodes == null) return null; + final EntityCache joinCache = this.joinEntity.getCache(); + final AtomicBoolean more = new AtomicBoolean(); + Predicate filter = createJoinPredicate(more); + Predicate rs = null; + if (filter == null && !more.get()) return rs; + if (filter != null) { + final Predicate inner = filter; + final String[][] localJoinColumns = new String[joinColumns.length][2]; + for (int i = 0; i < joinColumns.length; i++) { + int pos = joinColumns[i].indexOf('='); + if (pos > 0) { + localJoinColumns[i] = new String[]{joinColumns[i].substring(0, pos), joinColumns[i].substring(pos + 1)}; + } else { + localJoinColumns[i] = new String[]{joinColumns[i], joinColumns[i]}; + } + } + rs = new Predicate() { + + @Override + public boolean test(final T t) { + Predicate joinPredicate = null; + for (String[] localJoinColumn : localJoinColumns) { + final Serializable key = cache.getAttribute(localJoinColumn[0]).get(t); + final Attribute joinAttr = joinCache.getAttribute(localJoinColumn[1]); + Predicate p = (E e) -> key.equals(joinAttr.get(e)); + joinPredicate = joinPredicate == null ? p : joinPredicate.and(p); + } + return joinCache.exists(inner.and(joinPredicate)); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(" #-- ON ").append(localJoinColumns[0][0]).append("=").append(joinClass == null ? "null" : joinClass.getSimpleName()).append(".").append(localJoinColumns[0][1]); + for (int i = 1; i < localJoinColumns.length; i++) { + sb.append(" AND ").append(localJoinColumns[i][0]).append("=").append(joinClass == null ? "null" : joinClass.getSimpleName()).append(".").append(localJoinColumns[i][1]); + } + sb.append(" --# ").append(inner.toString()); + return sb.toString(); + } + }; + } + if (more.get()) { //存在不同Class的关联表 + if (this.nodes != null) { + for (FilterNode node : this.nodes) { + if (((FilterJoinNode) node).joinClass == this.joinClass) continue; + Predicate f = node.createPredicate(cache); + if (f == null) continue; + final Predicate one = rs; + final Predicate two = f; + rs = (rs == null) ? f : (or ? new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) || two.test(t); + } + + @Override + public String toString() { + return "(" + one + " OR " + two + ")"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) && two.test(t); + } + + @Override + public String toString() { + return "(" + one + " AND " + two + ")"; + } + }); + } + } + } + return rs; + } + + private Predicate createJoinPredicate(final AtomicBoolean more) { + if (column == null && this.nodes == null) return null; + final EntityCache joinCache = this.joinEntity.getCache(); + Predicate filter = createElementPredicate(joinCache, true); + if (this.nodes != null) { + for (FilterNode node : this.nodes) { + if (((FilterJoinNode) node).joinClass != this.joinClass) { + more.set(true); + continue; + } + Predicate f = ((FilterJoinNode) node).createJoinPredicate(more); + if (f == null) continue; + final Predicate one = filter; + final Predicate two = f; + filter = (filter == null) ? f : (or ? new Predicate() { + + @Override + public boolean test(E t) { + return one.test(t) || two.test(t); + } + + @Override + public String toString() { + return "(" + one + " OR " + two + ")"; + } + } : new Predicate() { + + @Override + public boolean test(E t) { + return one.test(t) && two.test(t); + } + + @Override + public String toString() { + return "(" + one + " AND " + two + ")"; + } + }); + } + } + return filter; + } + + @Override + protected CharSequence createSQLJoin(final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { + boolean morejoin = false; + if (this.joinEntity == null) { + if (this.joinClass != null) this.joinEntity = func.apply(this.joinClass); + if (this.nodes != null) { + for (FilterNode node : this.nodes) { + if (node instanceof FilterJoinNode) { + FilterJoinNode joinNode = ((FilterJoinNode) node); + if (joinNode.joinClass != null) { + joinNode.joinEntity = func.apply(joinNode.joinClass); + if (this.joinClass != null && this.joinClass != joinNode.joinClass) morejoin = true; + } + } + } + } + } + StringBuilder sb = new StringBuilder(); + if (this.joinClass != null) { + CharSequence cs = createElementSQLJoin(update, joinTabalis, haset, info, this); + if (cs != null) sb.append(cs); + } + if (morejoin) { + Set set = new HashSet<>(); + if (this.joinClass != null) set.add(this.joinClass); + for (FilterNode node : this.nodes) { + if (node instanceof FilterJoinNode) { + FilterJoinNode joinNode = ((FilterJoinNode) node); + if (!set.contains(joinNode.joinClass)) { + CharSequence cs = createElementSQLJoin(update, joinTabalis, haset, info, joinNode); + if (cs != null) { + sb.append(cs); + set.add(joinNode.joinClass); + } + } + } + } + } + return sb; + } + + private static CharSequence createElementSQLJoin(final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info, final FilterJoinNode node) { + if (node.joinClass == null || (haset != null && haset.contains(joinTabalis.get(node.joinClass)))) return null; + StringBuilder sb = new StringBuilder(); + String[] joinColumns = node.joinColumns; + int pos = joinColumns[0].indexOf('='); + if (update) { + sb.append("[").append(node.joinEntity.getTable(node)).append(" ").append(joinTabalis.get(node.joinClass)).append(']'); + sb.append('{').append(info.getSQLColumn("a", pos > 0 ? joinColumns[0].substring(0, pos) : joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[0].substring(pos + 1) : joinColumns[0])); + for (int i = 1; i < joinColumns.length; i++) { + pos = joinColumns[i].indexOf('='); + sb.append(" AND ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[i].substring(0, pos) : joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[i].substring(pos + 1) : joinColumns[i])); + } + sb.append('}'); + } else { + sb.append(" INNER JOIN ").append(node.joinEntity.getTable(node)).append(" ").append(joinTabalis.get(node.joinClass)) + .append(" ON ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[0].substring(0, pos) : joinColumns[0])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[0].substring(pos + 1) : joinColumns[0])); + for (int i = 1; i < joinColumns.length; i++) { + pos = joinColumns[i].indexOf('='); + sb.append(" AND ").append(info.getSQLColumn("a", pos > 0 ? joinColumns[i].substring(0, pos) : joinColumns[i])).append(" = ").append(node.joinEntity.getSQLColumn(joinTabalis.get(node.joinClass), pos > 0 ? joinColumns[i].substring(pos + 1) : joinColumns[i])); + } + } + if (haset != null) haset.add(joinTabalis.get(node.joinClass)); + return sb; + } + + @Override + protected boolean isCacheUseable(final Function entityApplyer) { + if (this.joinEntity == null) this.joinEntity = entityApplyer.apply(this.joinClass); + if (!this.joinEntity.isCacheFullLoaded()) return false; + if (this.nodes == null) return true; + for (FilterNode node : this.nodes) { + if (!node.isCacheUseable(entityApplyer)) return false; + } + return true; + } + + @Override + protected void putJoinTabalis(Map map) { + if (this.joinClass != null && !map.containsKey(this.joinClass)) map.put(joinClass, String.valueOf((char) ('b' + map.size()))); + if (this.nodes == null) return; + for (FilterNode node : this.nodes) { + node.putJoinTabalis(map); + } + } + + @Override + protected final boolean isjoin() { + return true; + } + + @Override + public String toString() { + return toString(joinClass == null ? null : joinClass.getSimpleName()).toString(); + } + + public Class getJoinClass() { + return joinClass; + } + + public void setJoinClass(Class joinClass) { + this.joinClass = joinClass; + } + + public String[] getJoinColumns() { + return joinColumns; + } + + public void setJoinColumns(String[] joinColumns) { + this.joinColumns = joinColumns; + } + +} diff --git a/src/org/redkale/source/FilterKey.java b/src/main/java/org/redkale/source/FilterKey.java similarity index 96% rename from src/org/redkale/source/FilterKey.java rename to src/main/java/org/redkale/source/FilterKey.java index b0a1b52fb..20740a4f9 100644 --- a/src/org/redkale/source/FilterKey.java +++ b/src/main/java/org/redkale/source/FilterKey.java @@ -1,39 +1,39 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.util.Objects; -import org.redkale.util.ConstructorParameters; - -/** - * FilterKey主要用于自身字段间的表达式, 如: a.recordid = a.parentid , a.parentid就需要FilterKey来表示 new FilterKey("parentid") - *
    - * 注意:该类型不支持表达式:FV_XXX、BETWEEN、NOTBETWEEN、IN、NOTIN - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class FilterKey implements java.io.Serializable { - - private final String column; - - @ConstructorParameters({"column"}) - public FilterKey(String column) { - this.column = Objects.requireNonNull(column); - } - - public String getColumn() { - return column; - } - - @Override - public String toString() { - return "a." + getColumn(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.util.Objects; +import org.redkale.util.ConstructorParameters; + +/** + * FilterKey主要用于自身字段间的表达式, 如: a.recordid = a.parentid , a.parentid就需要FilterKey来表示 new FilterKey("parentid") + *
    + * 注意:该类型不支持表达式:FV_XXX、BETWEEN、NOTBETWEEN、IN、NOTIN + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class FilterKey implements java.io.Serializable { + + private final String column; + + @ConstructorParameters({"column"}) + public FilterKey(String column) { + this.column = Objects.requireNonNull(column); + } + + public String getColumn() { + return column; + } + + @Override + public String toString() { + return "a." + getColumn(); + } + +} diff --git a/src/org/redkale/source/FilterNode.java b/src/main/java/org/redkale/source/FilterNode.java similarity index 96% rename from src/org/redkale/source/FilterNode.java rename to src/main/java/org/redkale/source/FilterNode.java index 72fb62788..52b31cca1 100644 --- a/src/org/redkale/source/FilterNode.java +++ b/src/main/java/org/redkale/source/FilterNode.java @@ -1,2023 +1,2025 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.lang.reflect.Array; -import java.util.*; -import java.util.function.*; -import static org.redkale.source.FilterExpress.*; -import org.redkale.util.*; - -/** - * 注意:
    - * column的值以#开头的视为虚拟字段,不在过滤范围内
    - * 在调用 createSQLExpress 之前必须先调用 createSQLJoin
    - * 在调用 createPredicate 之前必须先调用 isCacheUseable
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class FilterNode { //FilterNode 不能实现Serializable接口, 否则DataSource很多重载接口会出现冲突 - - protected boolean readOnly; - - protected String column; - - protected FilterExpress express; - - protected Serializable value; - - protected boolean itemand; - - //---------------------------------------------- - protected boolean or; - - protected FilterNode[] nodes; - - public FilterNode() { - } - - protected FilterNode(String col, FilterExpress exp, boolean itemand, Serializable val) { - Objects.requireNonNull(col); - if (exp == null) { - if (val instanceof Range) { - exp = FilterExpress.BETWEEN; - } else if (val instanceof Collection) { - if (!((Collection) val).isEmpty()) { - Object subval = null; - for (Object obj : (Collection) val) { //取第一个值 - subval = obj; - break; - } - if (subval instanceof Range) { - exp = FilterExpress.BETWEEN; -// } else if (subval instanceof Collection) { -// exp = FilterExpress.IN; -// } else if (subval != null && val.getClass().isArray()) { -// exp = FilterExpress.IN; - } else { - exp = FilterExpress.IN; - } - } else { //空集合 - exp = FilterExpress.IN; - } - } else if (val != null && val.getClass().isArray()) { - Class comp = val.getClass().getComponentType(); - if (Range.class.isAssignableFrom(comp)) { - exp = FilterExpress.BETWEEN; - } else { - exp = FilterExpress.IN; - } - } - } - this.column = col; - this.express = exp == null ? EQUAL : exp; - this.itemand = itemand; - this.value = val; - } - - public FilterNode copy() { - return copy(new FilterNode()); - } - - protected FilterNode copy(FilterNode node) { - node.readOnly = this.readOnly; - node.column = this.column; - node.express = this.express; - node.value = this.value; - node.itemand = this.itemand; - node.or = this.or; - if (this.nodes != null) { - node.nodes = new FilterNode[this.nodes.length]; - for (int i = 0; i < node.nodes.length; i++) { - node.nodes[i] = this.nodes[i] == null ? null : this.nodes[i].copy(); - } - } - return node; - } - - public FilterNode asReadOnly() { - this.readOnly = true; - return this; - } - - public FilterNode readOnly(boolean readOnly) { - this.readOnly = readOnly; - return this; - } - - public long findLongValue(final String col, long defValue) { - Serializable val = findValue(col); - return val == null ? defValue : ((Number) val).longValue(); - } - - public int findIntValue(final String col, int defValue) { - Serializable val = findValue(col); - return val == null ? defValue : ((Number) val).intValue(); - } - - public String findStringValue(final String col) { - return (String) findValue(col); - } - - public Serializable findValue(final String col) { - if (this.column != null && this.column.equals(col)) return this.value; - if (this.nodes == null) return null; - for (FilterNode n : this.nodes) { - if (n == null) continue; - Serializable val = n.findValue(col); - if (val != null) return val; - } - return null; - } - - public final FilterNode and(FilterNode node) { - return any(node, false); - } - - public final FilterNode and(String column, Serializable value) { - return and(column, null, value); - } - - public final FilterNode and(String column, FilterExpress express, Serializable value) { - return and(column, express, true, value); - } - - public final FilterNode and(String column, FilterExpress express, boolean itemand, Serializable value) { - return and(new FilterNode(column, express, itemand, value)); - } - - public final FilterNode or(FilterNode node) { - return any(node, true); - } - - public final FilterNode or(String column, Serializable value) { - return or(column, null, value); - } - - public final FilterNode or(String column, FilterExpress express, Serializable value) { - return or(column, express, true, value); - } - - public final FilterNode or(String column, FilterExpress express, boolean itemand, Serializable value) { - return or(new FilterNode(column, express, itemand, value)); - } - - protected FilterNode any(FilterNode node, boolean signor) { - if (this.readOnly) throw new RuntimeException("FilterNode(" + this + ") is ReadOnly"); - Objects.requireNonNull(node); - if (this.column == null) { - this.column = node.column; - this.express = node.express; - this.itemand = node.itemand; - this.value = node.value; - return this; - } - if (this.nodes == null) { - this.nodes = new FilterNode[]{node}; - this.or = signor; - return this; - } - if (or == signor) { - this.nodes = Utility.append(this.nodes, node); - return this; - } - FilterNode newnode = new FilterNode(this.column, this.express, this.itemand, this.value); - newnode.or = this.or; - newnode.nodes = this.nodes; - this.nodes = new FilterNode[]{newnode, node}; - this.column = null; - this.express = null; - this.itemand = true; - this.or = signor; - this.value = null; - return this; - } - - /** - * 该方法需要重载 - * - * @param Entity类的泛型 - * @param func EntityInfo的加载器 - * @param update 是否用于更新的JOIN - * @param joinTabalis 关联表集合 - * @param haset 已拼接过的字段名 - * @param info Entity类的EntityInfo - * - * @return SQL的join语句 不存在返回null - */ - protected CharSequence createSQLJoin(final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { - if (joinTabalis == null || this.nodes == null) return null; - StringBuilder sb = null; - for (FilterNode node : this.nodes) { - CharSequence cs = node.createSQLJoin(func, update, joinTabalis, haset, info); - if (cs == null) continue; - if (sb == null) sb = new StringBuilder(); - sb.append(cs); - } - return sb; - } - - /** - * 该方法需要重载 - * - * @return 是否存在关联表 - */ - protected boolean isjoin() { - if (this.nodes == null) return false; - for (FilterNode node : this.nodes) { - if (node.isjoin()) return true; - } - return false; - } - - protected final Map getJoinTabalis() { - if (!isjoin()) return null; - Map map = new HashMap<>(); - putJoinTabalis(map); - return map; - } - - protected void putJoinTabalis(Map map) { - if (this.nodes == null) return; - for (FilterNode node : this.nodes) { - node.putJoinTabalis(map); - } - } - - /** - * 该方法需要重载 - * - * @param entityApplyer EntityInfo的加载器 - * - * @return 是否可以使用缓存 - */ - protected boolean isCacheUseable(final Function entityApplyer) { - if (this.nodes == null) return true; - for (FilterNode node : this.nodes) { - if (!node.isCacheUseable(entityApplyer)) return false; - } - return true; - } - - /** - * 该方法需要重载 - * - * @param Entity类的泛型 - * @param joinTabalis 关联表的集合 - * @param info EntityInfo - * - * @return JOIN的SQL语句 - */ - protected CharSequence createSQLExpress(final EntityInfo info, final Map joinTabalis) { - CharSequence sb0 = this.column == null || this.column.isEmpty() || this.column.charAt(0) == '#' || info == null - ? null : createElementSQLExpress(info, joinTabalis == null ? null : joinTabalis.get(info.getType())); - if (this.nodes == null) return sb0; - final StringBuilder rs = new StringBuilder(); - rs.append('('); - boolean more = false; - if (sb0 != null && sb0.length() > 2) { - more = true; - rs.append(sb0); - } - for (FilterNode node : this.nodes) { - CharSequence f = node.createSQLExpress(info, joinTabalis); - if (f == null || f.length() < 3) continue; - if (more) rs.append(or ? " OR " : " AND "); - rs.append(f); - more = true; - } - rs.append(')'); - if (rs.length() < 5) return null; - return rs; - } - - public static FilterNode create(String column, Serializable value) { - return create(column, null, value); - } - - public static FilterNode create(String column, FilterExpress express, Serializable value) { - return create(column, express, true, value); - } - - public static FilterNode create(String column, FilterExpress express, boolean itemand, Serializable value) { - return new FilterNode(column, express, itemand, value); - } - - private boolean needSplit(final Object val0) { - return needSplit(express, val0); - } - - private static boolean needSplit(final FilterExpress express, final Object val0) { - if (val0 == null) return false; - boolean items = express != IN && express != NOTIN; //是否数组集合的表达式 - if (!items) { - if (val0.getClass().isArray()) { - Class comp = val0.getClass().getComponentType(); - if (!(comp.isPrimitive() || CharSequence.class.isAssignableFrom(comp) || Number.class.isAssignableFrom(comp))) { - items = true; - } - } else if (val0 instanceof Collection) { - for (Object fv : (Collection) val0) { - if (fv == null) continue; - Class comp = fv.getClass(); - if (!(comp.isPrimitive() || CharSequence.class.isAssignableFrom(comp) || Number.class.isAssignableFrom(comp))) { - items = true; - } - break; //只需检测第一个值 - } - } - } - return items; - } - - protected final CharSequence createElementSQLExpress(final EntityInfo info, String talis) { - final Object val0 = getValue(); - if (needSplit(val0)) { - if (val0 instanceof Collection) { - StringBuilder sb = new StringBuilder(); - boolean more = ((Collection) val0).size() > 1; - if (more) sb.append('('); - for (Object fv : (Collection) val0) { - if (fv == null) continue; - CharSequence cs = createElementSQLExpress(info, talis, fv); - if (cs == null) continue; - if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); - sb.append(cs); - } - if (more) sb.append(')'); - return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 - } else if (val0.getClass().isArray()) { - StringBuilder sb = new StringBuilder(); - Object[] fvs = (Object[]) val0; - boolean more = fvs.length > 1; - if (more) sb.append('('); - for (Object fv : fvs) { - if (fv == null) continue; - CharSequence cs = createElementSQLExpress(info, talis, fv); - if (cs == null) continue; - if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); - sb.append(cs); - } - if (more) sb.append(')'); - return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 - } - } - return createElementSQLExpress(info, talis, val0); - - } - - private CharSequence createElementSQLExpress(final EntityInfo info, String talis, Object val0) { - if (column == null || this.column.isEmpty() || this.column.charAt(0) == '#') return null; - if (talis == null) talis = "a"; - if (express == ISNULL || express == ISNOTNULL) { - return new StringBuilder().append(info.getSQLColumn(talis, column)).append(' ').append(express.value()); - } - if (express == ISEMPTY || express == ISNOTEMPTY) { - return new StringBuilder().append(info.getSQLColumn(talis, column)).append(' ').append(express.value()).append(" ''"); - } - if (val0 == null) return null; - if (express == FV_MOD || express == FV_DIV) { - FilterValue fv = (FilterValue) val0; - return new StringBuilder().append(info.getSQLColumn(talis, column)).append(' ').append(express.value()).append(' ').append(fv.getOptvalue()) - .append(' ').append(fv.getExpress().value()).append(' ').append(fv.getDestvalue()); - } - final boolean fk = (val0 instanceof FilterKey); - CharSequence val = fk ? info.getSQLColumn(talis, ((FilterKey) val0).getColumn()) : formatToString(express, info.getSQLValue(column, (Serializable) val0)); - if (val == null) return null; - StringBuilder sb = new StringBuilder(32); - if (express == CONTAIN) return info.containSQL.replace("${column}", info.getSQLColumn(talis, column)).replace("${keystr}", val); - if (express == IGNORECASECONTAIN) return info.containSQL.replace("${column}", "LOWER(" + info.getSQLColumn(talis, column) + ")").replace("${keystr}", val); - if (express == NOTCONTAIN) return info.notcontainSQL.replace("${column}", info.getSQLColumn(talis, column)).replace("${keystr}", val); - if (express == IGNORECASENOTCONTAIN) return info.notcontainSQL.replace("${column}", "LOWER(" + info.getSQLColumn(talis, column) + ")").replace("${keystr}", val); - - if (express == LENGTH_EQUAL || express == LENGTH_LESSTHAN || express == LENGTH_LESSTHANOREQUALTO - || express == LENGTH_GREATERTHAN || express == LENGTH_GREATERTHANOREQUALTO) { - sb.append("LENGTH(").append(info.getSQLColumn(talis, column)).append(')'); - } else if (express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL || express == IGNORECASELIKE || express == IGNORECASENOTLIKE) { - sb.append("LOWER(").append(info.getSQLColumn(talis, column)).append(')'); - if (fk) val = "LOWER(" + info.getSQLColumn(talis, ((FilterKey) val0).getColumn()) + ')'; - } else { - sb.append(info.getSQLColumn(talis, column)); - } - sb.append(' '); - switch (express) { - case OPAND: - case OPOR: - sb.append(express.value()).append(' ').append(val).append(" > 0"); - break; - case OPANDNO: - sb.append(express.value()).append(' ').append(val).append(" = 0"); - break; - default: - sb.append(express.value()).append(' ').append(val); - break; - } - return sb; - } - - protected Predicate createPredicate(final EntityCache cache) { - if (cache == null || (column == null && this.nodes == null)) return null; - Predicate filter = createElementPredicate(cache, false); - if (this.nodes == null) return filter; - for (FilterNode node : this.nodes) { - Predicate f = node.createPredicate(cache); - if (f == null) continue; - final Predicate one = filter; - final Predicate two = f; - filter = (filter == null) ? f : (or ? new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) || two.test(t); - } - - @Override - public String toString() { - return "(" + one + " OR " + two + ")"; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) && two.test(t); - } - - @Override - public String toString() { - return "(" + one + " AND " + two + ")"; - } - }); - } - return filter; - } - - protected final Predicate createElementPredicate(final EntityCache cache, final boolean join) { - if (this.column == null || this.column.isEmpty() || this.column.charAt(0) == '#') return null; - return createElementPredicate(cache, join, cache.getAttribute(column)); - } - - @SuppressWarnings("unchecked") - protected final Predicate createElementPredicate(final EntityCache cache, final boolean join, final Attribute attr) { - final Object val0 = getValue(); - if (needSplit(val0)) { - if (val0 instanceof Collection) { - Predicate filter = null; - for (Object fv : (Collection) val0) { - if (fv == null) continue; - Predicate f = createElementPredicate(cache, join, attr, fv); - if (f == null) continue; - final Predicate one = filter; - final Predicate two = f; - filter = (filter == null) ? f : (!itemand ? new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) || two.test(t); - } - - @Override - public String toString() { - return "(" + one + " OR " + two + ")"; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) && two.test(t); - } - - @Override - public String toString() { - return "(" + one + " AND " + two + ")"; - } - }); - } - return filter; - } else if (val0.getClass().isArray()) { - final Class primtype = val0.getClass(); - Object val2 = val0; - int ix = -1; - if (primtype == boolean[].class) { - boolean[] bs = (boolean[]) val0; - Boolean[] ns = new Boolean[bs.length]; - for (boolean v : bs) { - ns[++ix] = v; - } - val2 = ns; - } else if (primtype == byte[].class) { - byte[] bs = (byte[]) val0; - Byte[] ns = new Byte[bs.length]; - for (byte v : bs) { - ns[++ix] = v; - } - val2 = ns; - } else if (primtype == short[].class) { - short[] bs = (short[]) val0; - Short[] ns = new Short[bs.length]; - for (short v : bs) { - ns[++ix] = v; - } - val2 = ns; - } else if (primtype == char[].class) { - char[] bs = (char[]) val0; - Character[] ns = new Character[bs.length]; - for (char v : bs) { - ns[++ix] = v; - } - val2 = ns; - } else if (primtype == int[].class) { - int[] bs = (int[]) val0; - Integer[] ns = new Integer[bs.length]; - for (int v : bs) { - ns[++ix] = v; - } - val2 = ns; - } else if (primtype == float[].class) { - float[] bs = (float[]) val0; - Float[] ns = new Float[bs.length]; - for (float v : bs) { - ns[++ix] = v; - } - val2 = ns; - } else if (primtype == long[].class) { - long[] bs = (long[]) val0; - Long[] ns = new Long[bs.length]; - for (long v : bs) { - ns[++ix] = v; - } - val2 = ns; - } else if (primtype == double[].class) { - double[] bs = (double[]) val0; - Double[] ns = new Double[bs.length]; - for (double v : bs) { - ns[++ix] = v; - } - val2 = ns; - } - Predicate filter = null; - for (Object fv : (Object[]) val2) { - if (fv == null) continue; - Predicate f = createElementPredicate(cache, join, attr, fv); - if (f == null) continue; - final Predicate one = filter; - final Predicate two = f; - filter = (filter == null) ? f : (!itemand ? new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) || two.test(t); - } - - @Override - public String toString() { - return "(" + one + " OR " + two + ")"; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return one.test(t) && two.test(t); - } - - @Override - public String toString() { - return "(" + one + " AND " + two + ")"; - } - }); - } - return filter; - } - } - return createElementPredicate(cache, join, attr, val0); - } - - @SuppressWarnings("unchecked") - protected final Predicate createElementPredicate(final EntityCache cache, final boolean join, final Attribute attr, Object val0) { - if (attr == null) return null; - final String field = join ? (cache.getType().getSimpleName() + "." + attr.field()) : attr.field(); - if (express == ISNULL) return new Predicate() { - - @Override - public boolean test(T t) { - return attr.get(t) == null; - } - - @Override - public String toString() { - return field + " = null"; - } - }; - if (express == ISNOTNULL) return new Predicate() { - - @Override - public boolean test(T t) { - return attr.get(t) != null; - } - - @Override - public String toString() { - return field + " != null"; - } - }; - if (express == ISEMPTY) return new Predicate() { - - @Override - public boolean test(T t) { - Object v = attr.get(t); - return v == null || v.toString().isEmpty(); - } - - @Override - public String toString() { - return field + " = ''"; - } - }; - if (express == ISNOTEMPTY) return new Predicate() { - - @Override - public boolean test(T t) { - Object v = attr.get(t); - return v != null && !v.toString().isEmpty(); - } - - @Override - public String toString() { - return field + " != ''"; - } - }; - if (val0 == null) return null; - - final Class atype = attr.type(); - final Class valtype = val0.getClass(); - if (atype != valtype && val0 instanceof Number) { - if (atype == int.class || atype == Integer.class) { - val0 = ((Number) val0).intValue(); - } else if (atype == long.class || atype == Long.class) { - val0 = ((Number) val0).longValue(); - } else if (atype == short.class || atype == Short.class) { - val0 = ((Number) val0).shortValue(); - } else if (atype == float.class || atype == Float.class) { - val0 = ((Number) val0).floatValue(); - } else if (atype == byte.class || atype == Byte.class) { - val0 = ((Number) val0).byteValue(); - } else if (atype == double.class || atype == Double.class) { - val0 = ((Number) val0).doubleValue(); - } - } else if (valtype.isArray()) { - final int len = Array.getLength(val0); - if (len == 0 && express == NOTIN) return null; - final Class compType = valtype.getComponentType(); - if (atype != compType && len > 0) { - if (!compType.isPrimitive() && Number.class.isAssignableFrom(compType)) throw new RuntimeException("param(" + val0 + ") type not match " + atype + " for column " + column); - if (atype == int.class || atype == Integer.class) { - int[] vs = new int[len]; - for (int i = 0; i < len; i++) { - vs[i] = ((Number) Array.get(val0, i)).intValue(); - } - val0 = vs; - } else if (atype == long.class || atype == Long.class) { - long[] vs = new long[len]; - for (int i = 0; i < len; i++) { - vs[i] = ((Number) Array.get(val0, i)).longValue(); - } - val0 = vs; - } else if (atype == short.class || atype == Short.class) { - short[] vs = new short[len]; - for (int i = 0; i < len; i++) { - vs[i] = ((Number) Array.get(val0, i)).shortValue(); - } - val0 = vs; - } else if (atype == float.class || atype == Float.class) { - float[] vs = new float[len]; - for (int i = 0; i < len; i++) { - vs[i] = ((Number) Array.get(val0, i)).floatValue(); - } - val0 = vs; - } else if (atype == byte.class || atype == Byte.class) { - byte[] vs = new byte[len]; - for (int i = 0; i < len; i++) { - vs[i] = ((Number) Array.get(val0, i)).byteValue(); - } - val0 = vs; - } else if (atype == double.class || atype == Double.class) { - double[] vs = new double[len]; - for (int i = 0; i < len; i++) { - vs[i] = ((Number) Array.get(val0, i)).doubleValue(); - } - val0 = vs; - } - } - } else if (val0 instanceof Collection) { - final Collection collection = (Collection) val0; - if (collection.isEmpty() && express == NOTIN) return null; - if (!collection.isEmpty()) { - Iterator it = collection.iterator(); - it.hasNext(); - Class fs = it.next().getClass(); - Class pfs = fs; - if (fs == Integer.class) { - pfs = int.class; - } else if (fs == Long.class) { - pfs = long.class; - } else if (fs == Short.class) { - pfs = short.class; - } else if (fs == Float.class) { - pfs = float.class; - } else if (fs == Byte.class) { - pfs = byte.class; - } else if (fs == Double.class) { - pfs = double.class; - } - if (Number.class.isAssignableFrom(fs) && atype != fs && atype != pfs) { //需要转换 - ArrayList list = new ArrayList(collection.size()); - if (atype == int.class || atype == Integer.class) { - for (Number num : (Collection) collection) { - list.add(num.intValue()); - } - } else if (atype == long.class || atype == Long.class) { - for (Number num : (Collection) collection) { - list.add(num.longValue()); - } - } else if (atype == short.class || atype == Short.class) { - for (Number num : (Collection) collection) { - list.add(num.shortValue()); - } - } else if (atype == float.class || atype == Float.class) { - for (Number num : (Collection) collection) { - list.add(num.floatValue()); - } - } else if (atype == byte.class || atype == Byte.class) { - for (Number num : (Collection) collection) { - list.add(num.byteValue()); - } - } else if (atype == double.class || atype == Double.class) { - for (Number num : (Collection) collection) { - list.add(num.doubleValue()); - } - } - val0 = list; - } - } - } - final Serializable val = (Serializable) val0; - final boolean fk = (val instanceof FilterKey); - final Attribute fkattr = fk ? cache.getAttribute(((FilterKey) val).getColumn()) : null; - if (fk && fkattr == null) throw new RuntimeException(cache.getType() + " not found column(" + ((FilterKey) val).getColumn() + ")"); - switch (express) { - case EQUAL: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return Objects.equals(fkattr.get(t), attr.get(t)); - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return val.equals(attr.get(t)); - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + formatToString(val); - } - }; - case IGNORECASEEQUAL: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - if (rs == null && rs2 == null) return true; - if (rs == null || rs2 == null) return false; - return Objects.equals(rs.toString().toLowerCase(), rs2.toString().toLowerCase()); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return false; - return val.toString().equalsIgnoreCase(rs.toString()); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(val); - } - }; - case NOTEQUAL: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return !Objects.equals(fkattr.get(t), attr.get(t)); - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return !val.equals(attr.get(t)); - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + formatToString(val); - } - }; - case IGNORECASENOTEQUAL: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - if (rs == null && rs2 == null) return false; - if (rs == null || rs2 == null) return true; - return !Objects.equals(rs.toString().toLowerCase(), rs2.toString().toLowerCase()); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return true; - return !val.toString().equalsIgnoreCase(rs.toString()); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(val); - } - }; - case GREATERTHAN: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) > 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) > 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + val; - } - }; - case LESSTHAN: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) < 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) < 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + val; - } - }; - case GREATERTHANOREQUALTO: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) >= 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) >= 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + val; - } - }; - case LESSTHANOREQUALTO: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) <= 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) <= 0; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + val; - } - }; - - case FV_MOD: - FilterValue fv0 = (FilterValue) val; - switch (fv0.getExpress()) { - case EQUAL: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) == fv0.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); - } - }; - case NOTEQUAL: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) != fv0.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); - } - }; - case GREATERTHAN: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) > fv0.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); - } - }; - case LESSTHAN: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) < fv0.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); - } - }; - case GREATERTHANOREQUALTO: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) >= fv0.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); - } - }; - case LESSTHANOREQUALTO: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) <= fv0.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); - } - }; - default: - throw new RuntimeException("(" + fv0 + ")'s express illegal, must be =, !=, <, >, <=, >="); - } - case FV_DIV: - FilterValue fv1 = (FilterValue) val; - switch (fv1.getExpress()) { - case EQUAL: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) == fv1.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); - } - }; - case NOTEQUAL: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) != fv1.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); - } - }; - case GREATERTHAN: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) > fv1.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); - } - }; - case LESSTHAN: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) < fv1.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); - } - }; - case GREATERTHANOREQUALTO: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) >= fv1.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); - } - }; - case LESSTHANOREQUALTO: - return new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) <= fv1.getDestvalue().longValue(); - } - - @Override - public String toString() { - return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); - } - }; - default: - throw new RuntimeException("(" + fv1 + ")'s express illegal, must be =, !=, <, >, <=, >="); - } - case OPAND: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() & ((Number) fkattr.get(t)).longValue()) > 0; - } - - @Override - public String toString() { - return field + " & " + fkattr.field() + " > 0"; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() & ((Number) val).longValue()) > 0; - } - - @Override - public String toString() { - return field + " & " + val + " > 0"; - } - }; - case OPOR: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() | ((Number) fkattr.get(t)).longValue()) > 0; - } - - @Override - public String toString() { - return field + " | " + fkattr.field() + " > 0"; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() | ((Number) val).longValue()) > 0; - } - - @Override - public String toString() { - return field + " | " + val + " > 0"; - } - }; - case OPANDNO: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() & ((Number) fkattr.get(t)).longValue()) == 0; - } - - @Override - public String toString() { - return field + " & " + fkattr.field() + " = 0"; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - return (((Number) attr.get(t)).longValue() & ((Number) val).longValue()) == 0; - } - - @Override - public String toString() { - return field + " & " + val + " = 0"; - } - }; - case LIKE: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs != null && rs2 != null && rs.toString().contains(rs2.toString()); - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs != null && rs.toString().contains(val.toString()); - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + formatToString(val); - } - }; - case STARTSWITH: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs != null && rs2 != null && rs.toString().startsWith(rs2.toString()); - } - - @Override - public String toString() { - return field + " STARTSWITH " + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs != null && rs.toString().startsWith(val.toString()); - } - - @Override - public String toString() { - return field + " STARTSWITH " + formatToString(val); - } - }; - case ENDSWITH: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs != null && rs2 != null && rs.toString().endsWith(rs2.toString()); - } - - @Override - public String toString() { - return field + " ENDSWITH " + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs != null && rs.toString().endsWith(val.toString()); - } - - @Override - public String toString() { - return field + " ENDSWITH " + formatToString(val); - } - }; - case IGNORECASELIKE: - if (fk) return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs != null && rs2 != null && rs.toString().toLowerCase().contains(rs2.toString().toLowerCase()); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; - } - }; - final String valstr = val.toString().toLowerCase(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs != null && rs.toString().toLowerCase().contains(valstr); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(valstr); - } - }; - case NOTSTARTSWITH: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs == null || rs2 == null || !rs.toString().startsWith(rs2.toString()); - } - - @Override - public String toString() { - return field + " NOT STARTSWITH " + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs == null || !rs.toString().startsWith(val.toString()); - } - - @Override - public String toString() { - return field + " NOT STARTSWITH " + formatToString(val); - } - }; - case NOTENDSWITH: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs == null || rs2 == null || !rs.toString().endsWith(rs2.toString()); - } - - @Override - public String toString() { - return field + " NOT ENDSWITH " + fkattr.field(); - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs == null || !rs.toString().endsWith(val.toString()); - } - - @Override - public String toString() { - return field + " NOT ENDSWITH " + formatToString(val); - } - }; - case IGNORECASENOTLIKE: - if (fk) return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs == null || rs2 == null || !rs.toString().toLowerCase().contains(rs2.toString().toLowerCase()); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; - } - }; - final String valstr2 = val.toString().toLowerCase(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs == null || !rs.toString().toLowerCase().contains(valstr2); - } - - @Override - public String toString() { - return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(valstr2); - } - }; - case LENGTH_EQUAL: - final int intval = ((Number) val).intValue(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return (rs == null && 0 == intval) || (rs != null && rs.toString().length() == intval); - } - - @Override - public String toString() { - return "LENGTH(" + field + ") " + express.value() + ' ' + intval; - } - }; - case LENGTH_LESSTHAN: - final int intval2 = ((Number) val).intValue(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return (rs == null && 0 < intval2) || (rs != null && rs.toString().length() < intval2); - } - - @Override - public String toString() { - return "LENGTH(" + field + ") " + express.value() + ' ' + intval2; - } - }; - case LENGTH_LESSTHANOREQUALTO: - final int intval3 = ((Number) val).intValue(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return (rs == null && 0 <= intval3) || (rs != null && rs.toString().length() <= intval3); - } - - @Override - public String toString() { - return "LENGTH(" + field + ") " + express.value() + ' ' + intval3; - } - }; - case LENGTH_GREATERTHAN: - final int intval4 = ((Number) val).intValue(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return (rs == null && 0 > intval4) || (rs != null && rs.toString().length() > intval4); - } - - @Override - public String toString() { - return "LENGTH(" + field + ") " + express.value() + ' ' + intval4; - } - }; - case LENGTH_GREATERTHANOREQUALTO: - final int intval5 = ((Number) val).intValue(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return (rs == null && 0 >= intval5) || (rs != null && rs.toString().length() >= intval5); - } - - @Override - public String toString() { - return "LENGTH(" + field + ") " + express.value() + ' ' + intval5; - } - }; - case CONTAIN: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs != null && rs2 != null && rs2.toString().contains(rs.toString()); - } - - @Override - public String toString() { - return fkattr.field() + ' ' + express.value() + ' ' + field; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs != null && val.toString().contains(rs.toString()); - } - - @Override - public String toString() { - return "" + formatToString(val) + ' ' + express.value() + ' ' + field; - } - }; - case IGNORECASECONTAIN: - if (fk) return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs != null && rs2 != null && rs2.toString().toLowerCase().contains(rs.toString().toLowerCase()); - } - - @Override - public String toString() { - return " LOWER(" + fkattr.field() + ") " + express.value() + ' ' + "LOWER(" + field + ") "; - } - }; - final String valstr3 = val.toString().toLowerCase(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs != null && valstr3.contains(rs.toString().toLowerCase()); - } - - @Override - public String toString() { - return "" + formatToString(valstr3) + express.value() + ' ' + "LOWER(" + field + ") "; - } - }; - case NOTCONTAIN: - return fk ? new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs == null || rs2 == null || !rs2.toString().contains(rs.toString()); - } - - @Override - public String toString() { - return fkattr.field() + ' ' + express.value() + ' ' + field; - } - } : new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs == null || !val.toString().contains(rs.toString()); - } - - @Override - public String toString() { - return "" + formatToString(val) + ' ' + express.value() + ' ' + field; - } - }; - case IGNORECASENOTCONTAIN: - if (fk) return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - Object rs2 = fkattr.get(t); - return rs == null || rs2 == null || !rs2.toString().toLowerCase().contains(rs.toString().toLowerCase()); - } - - @Override - public String toString() { - return " LOWER(" + fkattr.field() + ") " + express.value() + ' ' + "LOWER(" + field + ") "; - } - }; - final String valstr4 = val.toString().toLowerCase(); - return new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs == null || !valstr4.contains(rs.toString().toLowerCase()); - } - - @Override - public String toString() { - return "" + formatToString(valstr4) + express.value() + ' ' + "LOWER(" + field + ") "; - } - }; - case BETWEEN: - case NOTBETWEEN: - Range range = (Range) val; - final Comparable min = range.getMin(); - final Comparable max = range.getMax(); - if (express == BETWEEN) return new Predicate() { - - @Override - public boolean test(T t) { - Comparable rs = (Comparable) attr.get(t); - if (rs == null) return false; - if (min != null && min.compareTo(rs) >= 0) return false; - return !(max != null && max.compareTo(rs) <= 0); - } - - @Override - public String toString() { - return field + " BETWEEN " + min + " AND " + max; - } - }; - if (express == NOTBETWEEN) return new Predicate() { - - @Override - public boolean test(T t) { - Comparable rs = (Comparable) attr.get(t); - if (rs == null) return true; - if (min != null && min.compareTo(rs) >= 0) return true; - return (max != null && max.compareTo(rs) <= 0); - } - - @Override - public String toString() { - return field + " NOT BETWEEN " + min + " AND " + max; - } - }; - return null; - case IN: - case NOTIN: - Predicate filter; - if (val instanceof Collection) { - Collection array = (Collection) val; - if (array.isEmpty()) { //express 只会是 IN - filter = new Predicate() { - - @Override - public boolean test(T t) { - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + " []"; - } - }; - } else { - filter = new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - return rs != null && array.contains(rs); - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + val; - } - }; - } - } else { - Class type = val.getClass(); - if (Array.getLength(val) == 0) {//express 只会是 IN - filter = new Predicate() { - - @Override - public boolean test(T t) { - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + " []"; - } - }; - } else if (type == int[].class) { - filter = new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return false; - int k = (int) rs; - for (int v : (int[]) val) { - if (v == k) return true; - } - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + Arrays.toString((int[]) val); - } - }; - } else if (type == short[].class) { - filter = new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return false; - short k = (short) rs; - for (short v : (short[]) val) { - if (v == k) return true; - } - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + Arrays.toString((short[]) val); - } - }; - } else if (type == long[].class) { - filter = new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return false; - long k = (long) rs; - for (long v : (long[]) val) { - if (v == k) return true; - } - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + Arrays.toString((long[]) val); - } - }; - } else if (type == float[].class) { - filter = new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return false; - float k = (float) rs; - for (float v : (float[]) val) { - if (v == k) return true; - } - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + Arrays.toString((float[]) val); - } - }; - } else if (type == double[].class) { - filter = new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return false; - double k = (double) rs; - for (double v : (double[]) val) { - if (v == k) return true; - } - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + Arrays.toString((double[]) val); - } - }; - } else { - filter = new Predicate() { - - @Override - public boolean test(T t) { - Object rs = attr.get(t); - if (rs == null) return false; - for (Object v : (Object[]) val) { - if (rs.equals(v)) return true; - } - return false; - } - - @Override - public String toString() { - return field + ' ' + express.value() + ' ' + Arrays.toString((Object[]) val); - } - }; - } - } - if (express == NOTIN) { - final Predicate filter2 = filter; - filter = new Predicate() { - - @Override - public boolean test(T t) { - return !filter2.test(t); - } - - @Override - public String toString() { - return filter2.toString(); - } - }; - } - return filter; - } - return null; - } - - @Override - public String toString() { - return toString(null).toString(); - } - - protected StringBuilder toString(final String prefix) { - StringBuilder sb = new StringBuilder(); - StringBuilder element = toElementString(prefix); - boolean more = element != null && element.length() > 0 && this.nodes != null; - if (more) sb.append('('); - sb.append(element); - if (this.nodes != null) { - for (FilterNode node : this.nodes) { - String s = node.toString(); - if (s.length() < 1) continue; - if (sb.length() > 1) sb.append(or ? " OR " : " AND "); - sb.append(s); - } - } - if (more) sb.append(')'); - return sb; - } - - protected final StringBuilder toElementString(final String prefix) { - Serializable val0 = getValue(); - if (needSplit(val0)) { - if (val0 instanceof Collection) { - StringBuilder sb = new StringBuilder(); - boolean more = ((Collection) val0).size() > 1; - if (more) sb.append('('); - for (Object fv : (Collection) val0) { - if (fv == null) continue; - CharSequence cs = toElementString(prefix, fv); - if (cs == null) continue; - if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); - sb.append(cs); - } - if (more) sb.append(')'); - return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 - } else if (val0.getClass().isArray()) { - StringBuilder sb = new StringBuilder(); - Object[] fvs = (Object[]) val0; - boolean more = fvs.length > 1; - if (more) sb.append('('); - for (Object fv : fvs) { - if (fv == null) continue; - CharSequence cs = toElementString(prefix, fv); - if (cs == null) continue; - if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); - sb.append(cs); - } - if (more) sb.append(')'); - return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 - } - } - return toElementString(prefix, val0); - } - - protected final StringBuilder toElementString(final String prefix, Object ev) { - StringBuilder sb = new StringBuilder(); - if (column != null) { - String col = prefix == null ? column : (prefix + "." + column); - if (express == ISNULL || express == ISNOTNULL) { - sb.append(col).append(' ').append(express.value()); - } else if (express == ISEMPTY || express == ISNOTEMPTY) { - sb.append(col).append(' ').append(express.value()).append(" ''"); - } else if (ev != null) { - boolean lower = (express == IGNORECASELIKE || express == IGNORECASENOTLIKE || express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN); - sb.append(lower ? ("LOWER(" + col + ')') : col).append(' ').append(express.value()).append(' ').append(formatToString(express, ev)); - } - } - return sb; - } - - private static CharSequence formatToString(Object value) { - CharSequence sb = formatToString(null, value); - return sb == null ? null : sb.toString(); - } - - private static CharSequence formatToString(FilterExpress express, Object value) { - if (value == null) return null; - if (value instanceof Number) return String.valueOf(value); - if (value instanceof CharSequence) { - if (express == LIKE || express == NOTLIKE) { - value = "%" + value + '%'; - } else if (express == STARTSWITH || express == NOTSTARTSWITH) { - value = value + "%"; - } else if (express == ENDSWITH || express == NOTENDSWITH) { - value = "%" + value; - } else if (express == IGNORECASELIKE || express == IGNORECASENOTLIKE) { - value = "%" + value.toString().toLowerCase() + '%'; - } else if (express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN - || express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL) { - value = value.toString().toLowerCase(); - } - return new StringBuilder().append('\'').append(value.toString().replace("'", "\\'")).append('\''); - } else if (value instanceof Range) { - Range range = (Range) value; - boolean rangestring = range.getClass() == Range.StringRange.class; - StringBuilder sb = new StringBuilder(); - if (rangestring) { - sb.append('\'').append(range.getMin().toString().replace("'", "\\'")).append('\''); - } else { - sb.append(range.getMin()); - } - sb.append(" AND "); - if (rangestring) { - sb.append('\'').append(range.getMax().toString().replace("'", "\\'")).append('\''); - } else { - sb.append(range.getMax()); - } - return sb; - } else if (value.getClass().isArray()) { - int len = Array.getLength(value); - if (len == 0) return express == NOTIN ? null : new StringBuilder("(NULL)"); - if (len == 1) { - Object firstval = Array.get(value, 0); - if (firstval != null && firstval.getClass().isArray()) return formatToString(express, firstval); - } - StringBuilder sb = new StringBuilder(); - sb.append('('); - for (int i = 0; i < len; i++) { - Object o = Array.get(value, i); - if (sb.length() > 1) sb.append(','); - if (o instanceof CharSequence) { - sb.append('\'').append(o.toString().replace("'", "\\'")).append('\''); - } else { - sb.append(o); - } - } - return sb.append(')'); - } else if (value instanceof Collection) { - Collection c = (Collection) value; - if (c.isEmpty()) return express == NOTIN ? null : new StringBuilder("(NULL)"); - StringBuilder sb = new StringBuilder(); - sb.append('('); - for (Object o : c) { - if (sb.length() > 1) sb.append(','); - if (o instanceof CharSequence) { - sb.append('\'').append(o.toString().replace("'", "\\'")).append('\''); - } else { - sb.append(o); - } - } - return sb.append(')'); - } - return String.valueOf(value); - } - - public final Serializable getValue() { - return value; - } - - public final void setValue(Serializable value) { - this.value = value; - } - - public boolean isReadOnly() { - return readOnly; - } - - public void setReadOnly(boolean readOnly) { - this.readOnly = readOnly; - } - - public final boolean isOr() { - return or; - } - - public final void setOr(boolean or) { - this.or = or; - } - - public final String getColumn() { - return column; - } - - public final void setColumn(String column) { - this.column = column; - } - - public final FilterExpress getExpress() { - return express; - } - - public final void setExpress(FilterExpress express) { - this.express = express; - } - - public final boolean isItemand() { - return itemand; - } - - public final void setItemand(boolean itemand) { - this.itemand = itemand; - } - - public final FilterNode[] getNodes() { - return nodes; - } - - public final void setNodes(FilterNode[] nodes) { - this.nodes = nodes; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.*; +import java.util.function.*; +import static org.redkale.source.FilterExpress.*; +import org.redkale.util.*; + +/** + * 注意:
    + * column的值以#开头的视为虚拟字段,不在过滤范围内
    + * 在调用 createSQLExpress 之前必须先调用 createSQLJoin
    + * 在调用 createPredicate 之前必须先调用 isCacheUseable
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class FilterNode { //FilterNode 不能实现Serializable接口, 否则DataSource很多重载接口会出现冲突 + + protected boolean readOnly; + + protected String column; + + protected FilterExpress express; + + protected Serializable value; + + protected boolean itemand; + + //---------------------------------------------- + protected boolean or; + + protected FilterNode[] nodes; + + public FilterNode() { + } + + protected FilterNode(String col, FilterExpress exp, boolean itemand, Serializable val) { + Objects.requireNonNull(col); + if (exp == null) { + if (val instanceof Range) { + exp = FilterExpress.BETWEEN; + } else if (val instanceof Collection) { + if (!((Collection) val).isEmpty()) { + Object subval = null; + for (Object obj : (Collection) val) { //取第一个值 + subval = obj; + break; + } + if (subval instanceof Range) { + exp = FilterExpress.BETWEEN; +// } else if (subval instanceof Collection) { +// exp = FilterExpress.IN; +// } else if (subval != null && val.getClass().isArray()) { +// exp = FilterExpress.IN; + } else { + exp = FilterExpress.IN; + } + } else { //空集合 + exp = FilterExpress.IN; + } + } else if (val != null && val.getClass().isArray()) { + Class comp = val.getClass().getComponentType(); + if (Range.class.isAssignableFrom(comp)) { + exp = FilterExpress.BETWEEN; + } else { + exp = FilterExpress.IN; + } + } + } + this.column = col; + this.express = exp == null ? EQUAL : exp; + this.itemand = itemand; + this.value = val; + } + + public FilterNode copy() { + return copy(new FilterNode()); + } + + protected FilterNode copy(FilterNode node) { + node.readOnly = this.readOnly; + node.column = this.column; + node.express = this.express; + node.value = this.value; + node.itemand = this.itemand; + node.or = this.or; + if (this.nodes != null) { + node.nodes = new FilterNode[this.nodes.length]; + for (int i = 0; i < node.nodes.length; i++) { + node.nodes[i] = this.nodes[i] == null ? null : this.nodes[i].copy(); + } + } + return node; + } + + public FilterNode asReadOnly() { + this.readOnly = true; + return this; + } + + public FilterNode readOnly(boolean readOnly) { + this.readOnly = readOnly; + return this; + } + + public long findLongValue(final String col, long defValue) { + Serializable val = findValue(col); + return val == null ? defValue : ((Number) val).longValue(); + } + + public int findIntValue(final String col, int defValue) { + Serializable val = findValue(col); + return val == null ? defValue : ((Number) val).intValue(); + } + + public String findStringValue(final String col) { + return (String) findValue(col); + } + + public Serializable findValue(final String col) { + if (this.column != null && this.column.equals(col)) return this.value; + if (this.nodes == null) return null; + for (FilterNode n : this.nodes) { + if (n == null) continue; + Serializable val = n.findValue(col); + if (val != null) return val; + } + return null; + } + + public final FilterNode and(FilterNode node) { + return any(node, false); + } + + public final FilterNode and(String column, Serializable value) { + return and(column, null, value); + } + + public final FilterNode and(String column, FilterExpress express, Serializable value) { + return and(column, express, true, value); + } + + public final FilterNode and(String column, FilterExpress express, boolean itemand, Serializable value) { + return and(new FilterNode(column, express, itemand, value)); + } + + public final FilterNode or(FilterNode node) { + return any(node, true); + } + + public final FilterNode or(String column, Serializable value) { + return or(column, null, value); + } + + public final FilterNode or(String column, FilterExpress express, Serializable value) { + return or(column, express, true, value); + } + + public final FilterNode or(String column, FilterExpress express, boolean itemand, Serializable value) { + return or(new FilterNode(column, express, itemand, value)); + } + + protected FilterNode any(FilterNode node, boolean signor) { + if (this.readOnly) throw new RuntimeException("FilterNode(" + this + ") is ReadOnly"); + Objects.requireNonNull(node); + if (this.column == null) { + this.column = node.column; + this.express = node.express; + this.itemand = node.itemand; + this.value = node.value; + return this; + } + if (this.nodes == null) { + this.nodes = new FilterNode[]{node}; + this.or = signor; + return this; + } + if (or == signor) { + this.nodes = Utility.append(this.nodes, node); + return this; + } + FilterNode newnode = new FilterNode(this.column, this.express, this.itemand, this.value); + newnode.or = this.or; + newnode.nodes = this.nodes; + this.nodes = new FilterNode[]{newnode, node}; + this.column = null; + this.express = null; + this.itemand = true; + this.or = signor; + this.value = null; + return this; + } + + /** + * 该方法需要重载 + * + * @param Entity类的泛型 + * @param func EntityInfo的加载器 + * @param update 是否用于更新的JOIN + * @param joinTabalis 关联表集合 + * @param haset 已拼接过的字段名 + * @param info Entity类的EntityInfo + * + * @return SQL的join语句 不存在返回null + */ + protected CharSequence createSQLJoin(final Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { + if (joinTabalis == null || this.nodes == null) return null; + StringBuilder sb = null; + for (FilterNode node : this.nodes) { + CharSequence cs = node.createSQLJoin(func, update, joinTabalis, haset, info); + if (cs == null) continue; + if (sb == null) sb = new StringBuilder(); + sb.append(cs); + } + return sb; + } + + /** + * 该方法需要重载 + * + * @return 是否存在关联表 + */ + protected boolean isjoin() { + if (this.nodes == null) return false; + for (FilterNode node : this.nodes) { + if (node.isjoin()) return true; + } + return false; + } + + protected final Map getJoinTabalis() { + if (!isjoin()) return null; + Map map = new HashMap<>(); + putJoinTabalis(map); + return map; + } + + protected void putJoinTabalis(Map map) { + if (this.nodes == null) return; + for (FilterNode node : this.nodes) { + node.putJoinTabalis(map); + } + } + + /** + * 该方法需要重载 + * + * @param entityApplyer EntityInfo的加载器 + * + * @return 是否可以使用缓存 + */ + protected boolean isCacheUseable(final Function entityApplyer) { + if (this.nodes == null) return true; + for (FilterNode node : this.nodes) { + if (!node.isCacheUseable(entityApplyer)) return false; + } + return true; + } + + /** + * 该方法需要重载 + * + * @param source DataSqlSource + * @param Entity类的泛型 + * @param joinTabalis 关联表的集合 + * @param info EntityInfo + * + * @return JOIN的SQL语句 + */ + protected CharSequence createSQLExpress(DataSqlSource source, final EntityInfo info, final Map joinTabalis) { + CharSequence sb0 = this.column == null || this.column.isEmpty() || this.column.charAt(0) == '#' || info == null + ? null : createElementSQLExpress(source, info, joinTabalis == null ? null : joinTabalis.get(info.getType())); + if (this.nodes == null) return sb0; + final StringBuilder rs = new StringBuilder(); + rs.append('('); + boolean more = false; + if (sb0 != null && sb0.length() > 2) { + more = true; + rs.append(sb0); + } + for (FilterNode node : this.nodes) { + CharSequence f = node.createSQLExpress(source, info, joinTabalis); + if (f == null || f.length() < 3) continue; + if (more) rs.append(or ? " OR " : " AND "); + rs.append(f); + more = true; + } + rs.append(')'); + if (rs.length() < 5) return null; + return rs; + } + + public static FilterNode create(String column, Serializable value) { + return create(column, null, value); + } + + public static FilterNode create(String column, FilterExpress express, Serializable value) { + return create(column, express, true, value); + } + + public static FilterNode create(String column, FilterExpress express, boolean itemand, Serializable value) { + return new FilterNode(column, express, itemand, value); + } + + private boolean needSplit(final Object val0) { + return needSplit(express, val0); + } + + private static boolean needSplit(final FilterExpress express, final Object val0) { + if (val0 == null) return false; + boolean items = express != IN && express != NOTIN; //是否数组集合的表达式 + if (!items) { + if (val0.getClass().isArray()) { + Class comp = val0.getClass().getComponentType(); + if (comp == java.io.Serializable.class) comp = ((Object[]) val0)[0].getClass(); + if (!(comp.isPrimitive() || CharSequence.class.isAssignableFrom(comp) || Number.class.isAssignableFrom(comp))) { + items = true; + } + } else if (val0 instanceof Collection) { + for (Object fv : (Collection) val0) { + if (fv == null) continue; + Class comp = fv.getClass(); + if (!(comp.isPrimitive() || CharSequence.class.isAssignableFrom(comp) || Number.class.isAssignableFrom(comp))) { + items = true; + } + break; //只需检测第一个值 + } + } + } + return items; + } + + protected final CharSequence createElementSQLExpress(DataSqlSource source, final EntityInfo info, String talis) { + final Object val0 = getValue(); + if (needSplit(val0)) { + if (val0 instanceof Collection) { + StringBuilder sb = new StringBuilder(); + boolean more = ((Collection) val0).size() > 1; + if (more) sb.append('('); + for (Object fv : (Collection) val0) { + if (fv == null) continue; + CharSequence cs = createElementSQLExpress(source, info, talis, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } else if (val0.getClass().isArray()) { + StringBuilder sb = new StringBuilder(); + Object[] fvs = (Object[]) val0; + boolean more = fvs.length > 1; + if (more) sb.append('('); + for (Object fv : fvs) { + if (fv == null) continue; + CharSequence cs = createElementSQLExpress(source, info, talis, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } + } + return createElementSQLExpress(source, info, talis, val0); + + } + + private CharSequence createElementSQLExpress(DataSqlSource source, final EntityInfo info, String talis, Object val0) { + if (column == null || this.column.isEmpty() || this.column.charAt(0) == '#') return null; + if (talis == null) talis = "a"; + if (express == ISNULL || express == ISNOTNULL) { + return new StringBuilder().append(info.getSQLColumn(talis, column)).append(' ').append(express.value()); + } + if (express == ISEMPTY || express == ISNOTEMPTY) { + return new StringBuilder().append(info.getSQLColumn(talis, column)).append(' ').append(express.value()).append(" ''"); + } + if (val0 == null) return null; + if (express == FV_MOD || express == FV_DIV) { + FilterValue fv = (FilterValue) val0; + return new StringBuilder().append(info.getSQLColumn(talis, column)).append(' ').append(express.value()).append(' ').append(fv.getOptvalue()) + .append(' ').append(fv.getExpress().value()).append(' ').append(fv.getDestvalue()); + } + final boolean fk = (val0 instanceof FilterKey); + CharSequence val = fk ? info.getSQLColumn(talis, ((FilterKey) val0).getColumn()) : formatToString(express, info.getSQLValue(column, (Serializable) val0)); + if (val == null) return null; + StringBuilder sb = new StringBuilder(32); + if (express == CONTAIN) return source.containSQL.replace("${column}", info.getSQLColumn(talis, column)).replace("${keystr}", val); + if (express == IGNORECASECONTAIN) return source.containSQL.replace("${column}", "LOWER(" + info.getSQLColumn(talis, column) + ")").replace("${keystr}", val); + if (express == NOTCONTAIN) return source.notcontainSQL.replace("${column}", info.getSQLColumn(talis, column)).replace("${keystr}", val); + if (express == IGNORECASENOTCONTAIN) return source.notcontainSQL.replace("${column}", "LOWER(" + info.getSQLColumn(talis, column) + ")").replace("${keystr}", val); + + if (express == LENGTH_EQUAL || express == LENGTH_LESSTHAN || express == LENGTH_LESSTHANOREQUALTO + || express == LENGTH_GREATERTHAN || express == LENGTH_GREATERTHANOREQUALTO) { + sb.append("LENGTH(").append(info.getSQLColumn(talis, column)).append(')'); + } else if (express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL || express == IGNORECASELIKE || express == IGNORECASENOTLIKE) { + sb.append("LOWER(").append(info.getSQLColumn(talis, column)).append(')'); + if (fk) val = "LOWER(" + info.getSQLColumn(talis, ((FilterKey) val0).getColumn()) + ')'; + } else { + sb.append(info.getSQLColumn(talis, column)); + } + sb.append(' '); + switch (express) { + case OPAND: + case OPOR: + sb.append(express.value()).append(' ').append(val).append(" > 0"); + break; + case OPANDNO: + sb.append(express.value()).append(' ').append(val).append(" = 0"); + break; + default: + sb.append(express.value()).append(' ').append(val); + break; + } + return sb; + } + + protected Predicate createPredicate(final EntityCache cache) { + if (cache == null || (column == null && this.nodes == null)) return null; + Predicate filter = createElementPredicate(cache, false); + if (this.nodes == null) return filter; + for (FilterNode node : this.nodes) { + Predicate f = node.createPredicate(cache); + if (f == null) continue; + final Predicate one = filter; + final Predicate two = f; + filter = (filter == null) ? f : (or ? new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) || two.test(t); + } + + @Override + public String toString() { + return "(" + one + " OR " + two + ")"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) && two.test(t); + } + + @Override + public String toString() { + return "(" + one + " AND " + two + ")"; + } + }); + } + return filter; + } + + protected final Predicate createElementPredicate(final EntityCache cache, final boolean join) { + if (this.column == null || this.column.isEmpty() || this.column.charAt(0) == '#') return null; + return createElementPredicate(cache, join, cache.getAttribute(column)); + } + + @SuppressWarnings("unchecked") + protected final Predicate createElementPredicate(final EntityCache cache, final boolean join, final Attribute attr) { + final Object val0 = getValue(); + if (needSplit(val0)) { + if (val0 instanceof Collection) { + Predicate filter = null; + for (Object fv : (Collection) val0) { + if (fv == null) continue; + Predicate f = createElementPredicate(cache, join, attr, fv); + if (f == null) continue; + final Predicate one = filter; + final Predicate two = f; + filter = (filter == null) ? f : (!itemand ? new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) || two.test(t); + } + + @Override + public String toString() { + return "(" + one + " OR " + two + ")"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) && two.test(t); + } + + @Override + public String toString() { + return "(" + one + " AND " + two + ")"; + } + }); + } + return filter; + } else if (val0.getClass().isArray()) { + final Class primtype = val0.getClass(); + Object val2 = val0; + int ix = -1; + if (primtype == boolean[].class) { + boolean[] bs = (boolean[]) val0; + Boolean[] ns = new Boolean[bs.length]; + for (boolean v : bs) { + ns[++ix] = v; + } + val2 = ns; + } else if (primtype == byte[].class) { + byte[] bs = (byte[]) val0; + Byte[] ns = new Byte[bs.length]; + for (byte v : bs) { + ns[++ix] = v; + } + val2 = ns; + } else if (primtype == short[].class) { + short[] bs = (short[]) val0; + Short[] ns = new Short[bs.length]; + for (short v : bs) { + ns[++ix] = v; + } + val2 = ns; + } else if (primtype == char[].class) { + char[] bs = (char[]) val0; + Character[] ns = new Character[bs.length]; + for (char v : bs) { + ns[++ix] = v; + } + val2 = ns; + } else if (primtype == int[].class) { + int[] bs = (int[]) val0; + Integer[] ns = new Integer[bs.length]; + for (int v : bs) { + ns[++ix] = v; + } + val2 = ns; + } else if (primtype == float[].class) { + float[] bs = (float[]) val0; + Float[] ns = new Float[bs.length]; + for (float v : bs) { + ns[++ix] = v; + } + val2 = ns; + } else if (primtype == long[].class) { + long[] bs = (long[]) val0; + Long[] ns = new Long[bs.length]; + for (long v : bs) { + ns[++ix] = v; + } + val2 = ns; + } else if (primtype == double[].class) { + double[] bs = (double[]) val0; + Double[] ns = new Double[bs.length]; + for (double v : bs) { + ns[++ix] = v; + } + val2 = ns; + } + Predicate filter = null; + for (Object fv : (Object[]) val2) { + if (fv == null) continue; + Predicate f = createElementPredicate(cache, join, attr, fv); + if (f == null) continue; + final Predicate one = filter; + final Predicate two = f; + filter = (filter == null) ? f : (!itemand ? new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) || two.test(t); + } + + @Override + public String toString() { + return "(" + one + " OR " + two + ")"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return one.test(t) && two.test(t); + } + + @Override + public String toString() { + return "(" + one + " AND " + two + ")"; + } + }); + } + return filter; + } + } + return createElementPredicate(cache, join, attr, val0); + } + + @SuppressWarnings("unchecked") + protected final Predicate createElementPredicate(final EntityCache cache, final boolean join, final Attribute attr, Object val0) { + if (attr == null) return null; + final String field = join ? (cache.getType().getSimpleName() + "." + attr.field()) : attr.field(); + if (express == ISNULL) return new Predicate() { + + @Override + public boolean test(T t) { + return attr.get(t) == null; + } + + @Override + public String toString() { + return field + " = null"; + } + }; + if (express == ISNOTNULL) return new Predicate() { + + @Override + public boolean test(T t) { + return attr.get(t) != null; + } + + @Override + public String toString() { + return field + " != null"; + } + }; + if (express == ISEMPTY) return new Predicate() { + + @Override + public boolean test(T t) { + Object v = attr.get(t); + return v == null || v.toString().isEmpty(); + } + + @Override + public String toString() { + return field + " = ''"; + } + }; + if (express == ISNOTEMPTY) return new Predicate() { + + @Override + public boolean test(T t) { + Object v = attr.get(t); + return v != null && !v.toString().isEmpty(); + } + + @Override + public String toString() { + return field + " != ''"; + } + }; + if (val0 == null) return null; + + final Class atype = attr.type(); + final Class valtype = val0.getClass(); + if (atype != valtype && val0 instanceof Number) { + if (atype == int.class || atype == Integer.class) { + val0 = ((Number) val0).intValue(); + } else if (atype == long.class || atype == Long.class) { + val0 = ((Number) val0).longValue(); + } else if (atype == short.class || atype == Short.class) { + val0 = ((Number) val0).shortValue(); + } else if (atype == float.class || atype == Float.class) { + val0 = ((Number) val0).floatValue(); + } else if (atype == byte.class || atype == Byte.class) { + val0 = ((Number) val0).byteValue(); + } else if (atype == double.class || atype == Double.class) { + val0 = ((Number) val0).doubleValue(); + } + } else if (valtype.isArray()) { + final int len = Array.getLength(val0); + if (len == 0 && express == NOTIN) return null; + final Class compType = valtype.getComponentType(); + if (atype != compType && len > 0) { + if (!compType.isPrimitive() && Number.class.isAssignableFrom(compType)) throw new RuntimeException("param(" + val0 + ") type not match " + atype + " for column " + column); + if (atype == int.class || atype == Integer.class) { + int[] vs = new int[len]; + for (int i = 0; i < len; i++) { + vs[i] = ((Number) Array.get(val0, i)).intValue(); + } + val0 = vs; + } else if (atype == long.class || atype == Long.class) { + long[] vs = new long[len]; + for (int i = 0; i < len; i++) { + vs[i] = ((Number) Array.get(val0, i)).longValue(); + } + val0 = vs; + } else if (atype == short.class || atype == Short.class) { + short[] vs = new short[len]; + for (int i = 0; i < len; i++) { + vs[i] = ((Number) Array.get(val0, i)).shortValue(); + } + val0 = vs; + } else if (atype == float.class || atype == Float.class) { + float[] vs = new float[len]; + for (int i = 0; i < len; i++) { + vs[i] = ((Number) Array.get(val0, i)).floatValue(); + } + val0 = vs; + } else if (atype == byte.class || atype == Byte.class) { + byte[] vs = new byte[len]; + for (int i = 0; i < len; i++) { + vs[i] = ((Number) Array.get(val0, i)).byteValue(); + } + val0 = vs; + } else if (atype == double.class || atype == Double.class) { + double[] vs = new double[len]; + for (int i = 0; i < len; i++) { + vs[i] = ((Number) Array.get(val0, i)).doubleValue(); + } + val0 = vs; + } + } + } else if (val0 instanceof Collection) { + final Collection collection = (Collection) val0; + if (collection.isEmpty() && express == NOTIN) return null; + if (!collection.isEmpty()) { + Iterator it = collection.iterator(); + it.hasNext(); + Class fs = it.next().getClass(); + Class pfs = fs; + if (fs == Integer.class) { + pfs = int.class; + } else if (fs == Long.class) { + pfs = long.class; + } else if (fs == Short.class) { + pfs = short.class; + } else if (fs == Float.class) { + pfs = float.class; + } else if (fs == Byte.class) { + pfs = byte.class; + } else if (fs == Double.class) { + pfs = double.class; + } + if (Number.class.isAssignableFrom(fs) && atype != fs && atype != pfs) { //需要转换 + ArrayList list = new ArrayList(collection.size()); + if (atype == int.class || atype == Integer.class) { + for (Number num : (Collection) collection) { + list.add(num.intValue()); + } + } else if (atype == long.class || atype == Long.class) { + for (Number num : (Collection) collection) { + list.add(num.longValue()); + } + } else if (atype == short.class || atype == Short.class) { + for (Number num : (Collection) collection) { + list.add(num.shortValue()); + } + } else if (atype == float.class || atype == Float.class) { + for (Number num : (Collection) collection) { + list.add(num.floatValue()); + } + } else if (atype == byte.class || atype == Byte.class) { + for (Number num : (Collection) collection) { + list.add(num.byteValue()); + } + } else if (atype == double.class || atype == Double.class) { + for (Number num : (Collection) collection) { + list.add(num.doubleValue()); + } + } + val0 = list; + } + } + } + final Serializable val = (Serializable) val0; + final boolean fk = (val instanceof FilterKey); + final Attribute fkattr = fk ? cache.getAttribute(((FilterKey) val).getColumn()) : null; + if (fk && fkattr == null) throw new RuntimeException(cache.getType() + " not found column(" + ((FilterKey) val).getColumn() + ")"); + switch (express) { + case EQUAL: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return Objects.equals(fkattr.get(t), attr.get(t)); + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return val.equals(attr.get(t)); + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + formatToString(val); + } + }; + case IGNORECASEEQUAL: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + if (rs == null && rs2 == null) return true; + if (rs == null || rs2 == null) return false; + return Objects.equals(rs.toString().toLowerCase(), rs2.toString().toLowerCase()); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return false; + return val.toString().equalsIgnoreCase(rs.toString()); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(val); + } + }; + case NOTEQUAL: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return !Objects.equals(fkattr.get(t), attr.get(t)); + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return !val.equals(attr.get(t)); + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + formatToString(val); + } + }; + case IGNORECASENOTEQUAL: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + if (rs == null && rs2 == null) return false; + if (rs == null || rs2 == null) return true; + return !Objects.equals(rs.toString().toLowerCase(), rs2.toString().toLowerCase()); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return true; + return !val.toString().equalsIgnoreCase(rs.toString()); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(val); + } + }; + case GREATERTHAN: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) > 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) > 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + val; + } + }; + case LESSTHAN: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) < 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) < 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + val; + } + }; + case GREATERTHANOREQUALTO: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) >= 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) >= 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + val; + } + }; + case LESSTHANOREQUALTO: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo((Comparable) fkattr.get(t)) <= 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return ((Comparable) attr.get(t)).compareTo(((Comparable) val)) <= 0; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + val; + } + }; + + case FV_MOD: + FilterValue fv0 = (FilterValue) val; + switch (fv0.getExpress()) { + case EQUAL: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) == fv0.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); + } + }; + case NOTEQUAL: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) != fv0.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); + } + }; + case GREATERTHAN: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) > fv0.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); + } + }; + case LESSTHAN: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) < fv0.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); + } + }; + case GREATERTHANOREQUALTO: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) >= fv0.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); + } + }; + case LESSTHANOREQUALTO: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() % fv0.getOptvalue().longValue()) <= fv0.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv0.getOptvalue() + " " + fv0.getExpress().value() + " " + fv0.getDestvalue(); + } + }; + default: + throw new RuntimeException("(" + fv0 + ")'s express illegal, must be =, !=, <, >, <=, >="); + } + case FV_DIV: + FilterValue fv1 = (FilterValue) val; + switch (fv1.getExpress()) { + case EQUAL: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) == fv1.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); + } + }; + case NOTEQUAL: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) != fv1.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); + } + }; + case GREATERTHAN: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) > fv1.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); + } + }; + case LESSTHAN: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) < fv1.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); + } + }; + case GREATERTHANOREQUALTO: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) >= fv1.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); + } + }; + case LESSTHANOREQUALTO: + return new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() / fv1.getOptvalue().longValue()) <= fv1.getDestvalue().longValue(); + } + + @Override + public String toString() { + return field + " " + express.value() + " " + fv1.getOptvalue() + " " + fv1.getExpress().value() + " " + fv1.getDestvalue(); + } + }; + default: + throw new RuntimeException("(" + fv1 + ")'s express illegal, must be =, !=, <, >, <=, >="); + } + case OPAND: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() & ((Number) fkattr.get(t)).longValue()) > 0; + } + + @Override + public String toString() { + return field + " & " + fkattr.field() + " > 0"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() & ((Number) val).longValue()) > 0; + } + + @Override + public String toString() { + return field + " & " + val + " > 0"; + } + }; + case OPOR: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() | ((Number) fkattr.get(t)).longValue()) > 0; + } + + @Override + public String toString() { + return field + " | " + fkattr.field() + " > 0"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() | ((Number) val).longValue()) > 0; + } + + @Override + public String toString() { + return field + " | " + val + " > 0"; + } + }; + case OPANDNO: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() & ((Number) fkattr.get(t)).longValue()) == 0; + } + + @Override + public String toString() { + return field + " & " + fkattr.field() + " = 0"; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + return (((Number) attr.get(t)).longValue() & ((Number) val).longValue()) == 0; + } + + @Override + public String toString() { + return field + " & " + val + " = 0"; + } + }; + case LIKE: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs != null && rs2 != null && rs.toString().contains(rs2.toString()); + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs != null && rs.toString().contains(val.toString()); + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + formatToString(val); + } + }; + case STARTSWITH: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs != null && rs2 != null && rs.toString().startsWith(rs2.toString()); + } + + @Override + public String toString() { + return field + " STARTSWITH " + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs != null && rs.toString().startsWith(val.toString()); + } + + @Override + public String toString() { + return field + " STARTSWITH " + formatToString(val); + } + }; + case ENDSWITH: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs != null && rs2 != null && rs.toString().endsWith(rs2.toString()); + } + + @Override + public String toString() { + return field + " ENDSWITH " + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs != null && rs.toString().endsWith(val.toString()); + } + + @Override + public String toString() { + return field + " ENDSWITH " + formatToString(val); + } + }; + case IGNORECASELIKE: + if (fk) return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs != null && rs2 != null && rs.toString().toLowerCase().contains(rs2.toString().toLowerCase()); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; + } + }; + final String valstr = val.toString().toLowerCase(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs != null && rs.toString().toLowerCase().contains(valstr); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(valstr); + } + }; + case NOTSTARTSWITH: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs == null || rs2 == null || !rs.toString().startsWith(rs2.toString()); + } + + @Override + public String toString() { + return field + " NOT STARTSWITH " + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs == null || !rs.toString().startsWith(val.toString()); + } + + @Override + public String toString() { + return field + " NOT STARTSWITH " + formatToString(val); + } + }; + case NOTENDSWITH: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs == null || rs2 == null || !rs.toString().endsWith(rs2.toString()); + } + + @Override + public String toString() { + return field + " NOT ENDSWITH " + fkattr.field(); + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs == null || !rs.toString().endsWith(val.toString()); + } + + @Override + public String toString() { + return field + " NOT ENDSWITH " + formatToString(val); + } + }; + case IGNORECASENOTLIKE: + if (fk) return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs == null || rs2 == null || !rs.toString().toLowerCase().contains(rs2.toString().toLowerCase()); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + " LOWER(" + fkattr.field() + ')'; + } + }; + final String valstr2 = val.toString().toLowerCase(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs == null || !rs.toString().toLowerCase().contains(valstr2); + } + + @Override + public String toString() { + return "LOWER(" + field + ") " + express.value() + ' ' + formatToString(valstr2); + } + }; + case LENGTH_EQUAL: + final int intval = ((Number) val).intValue(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return (rs == null && 0 == intval) || (rs != null && rs.toString().length() == intval); + } + + @Override + public String toString() { + return "LENGTH(" + field + ") " + express.value() + ' ' + intval; + } + }; + case LENGTH_LESSTHAN: + final int intval2 = ((Number) val).intValue(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return (rs == null && 0 < intval2) || (rs != null && rs.toString().length() < intval2); + } + + @Override + public String toString() { + return "LENGTH(" + field + ") " + express.value() + ' ' + intval2; + } + }; + case LENGTH_LESSTHANOREQUALTO: + final int intval3 = ((Number) val).intValue(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return (rs == null && 0 <= intval3) || (rs != null && rs.toString().length() <= intval3); + } + + @Override + public String toString() { + return "LENGTH(" + field + ") " + express.value() + ' ' + intval3; + } + }; + case LENGTH_GREATERTHAN: + final int intval4 = ((Number) val).intValue(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return (rs == null && 0 > intval4) || (rs != null && rs.toString().length() > intval4); + } + + @Override + public String toString() { + return "LENGTH(" + field + ") " + express.value() + ' ' + intval4; + } + }; + case LENGTH_GREATERTHANOREQUALTO: + final int intval5 = ((Number) val).intValue(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return (rs == null && 0 >= intval5) || (rs != null && rs.toString().length() >= intval5); + } + + @Override + public String toString() { + return "LENGTH(" + field + ") " + express.value() + ' ' + intval5; + } + }; + case CONTAIN: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs != null && rs2 != null && rs2.toString().contains(rs.toString()); + } + + @Override + public String toString() { + return fkattr.field() + ' ' + express.value() + ' ' + field; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs != null && val.toString().contains(rs.toString()); + } + + @Override + public String toString() { + return "" + formatToString(val) + ' ' + express.value() + ' ' + field; + } + }; + case IGNORECASECONTAIN: + if (fk) return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs != null && rs2 != null && rs2.toString().toLowerCase().contains(rs.toString().toLowerCase()); + } + + @Override + public String toString() { + return " LOWER(" + fkattr.field() + ") " + express.value() + ' ' + "LOWER(" + field + ") "; + } + }; + final String valstr3 = val.toString().toLowerCase(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs != null && valstr3.contains(rs.toString().toLowerCase()); + } + + @Override + public String toString() { + return "" + formatToString(valstr3) + express.value() + ' ' + "LOWER(" + field + ") "; + } + }; + case NOTCONTAIN: + return fk ? new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs == null || rs2 == null || !rs2.toString().contains(rs.toString()); + } + + @Override + public String toString() { + return fkattr.field() + ' ' + express.value() + ' ' + field; + } + } : new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs == null || !val.toString().contains(rs.toString()); + } + + @Override + public String toString() { + return "" + formatToString(val) + ' ' + express.value() + ' ' + field; + } + }; + case IGNORECASENOTCONTAIN: + if (fk) return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + Object rs2 = fkattr.get(t); + return rs == null || rs2 == null || !rs2.toString().toLowerCase().contains(rs.toString().toLowerCase()); + } + + @Override + public String toString() { + return " LOWER(" + fkattr.field() + ") " + express.value() + ' ' + "LOWER(" + field + ") "; + } + }; + final String valstr4 = val.toString().toLowerCase(); + return new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs == null || !valstr4.contains(rs.toString().toLowerCase()); + } + + @Override + public String toString() { + return "" + formatToString(valstr4) + express.value() + ' ' + "LOWER(" + field + ") "; + } + }; + case BETWEEN: + case NOTBETWEEN: + Range range = (Range) val; + final Comparable min = range.getMin(); + final Comparable max = range.getMax(); + if (express == BETWEEN) return new Predicate() { + + @Override + public boolean test(T t) { + Comparable rs = (Comparable) attr.get(t); + if (rs == null) return false; + if (min != null && min.compareTo(rs) >= 0) return false; + return !(max != null && max.compareTo(rs) <= 0); + } + + @Override + public String toString() { + return field + " BETWEEN " + min + " AND " + max; + } + }; + if (express == NOTBETWEEN) return new Predicate() { + + @Override + public boolean test(T t) { + Comparable rs = (Comparable) attr.get(t); + if (rs == null) return true; + if (min != null && min.compareTo(rs) >= 0) return true; + return (max != null && max.compareTo(rs) <= 0); + } + + @Override + public String toString() { + return field + " NOT BETWEEN " + min + " AND " + max; + } + }; + return null; + case IN: + case NOTIN: + Predicate filter; + if (val instanceof Collection) { + Collection array = (Collection) val; + if (array.isEmpty()) { //express 只会是 IN + filter = new Predicate() { + + @Override + public boolean test(T t) { + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + " []"; + } + }; + } else { + filter = new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + return rs != null && array.contains(rs); + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + val; + } + }; + } + } else { + Class type = val.getClass(); + if (Array.getLength(val) == 0) {//express 只会是 IN + filter = new Predicate() { + + @Override + public boolean test(T t) { + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + " []"; + } + }; + } else if (type == int[].class) { + filter = new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return false; + int k = (int) rs; + for (int v : (int[]) val) { + if (v == k) return true; + } + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + Arrays.toString((int[]) val); + } + }; + } else if (type == short[].class) { + filter = new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return false; + short k = (short) rs; + for (short v : (short[]) val) { + if (v == k) return true; + } + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + Arrays.toString((short[]) val); + } + }; + } else if (type == long[].class) { + filter = new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return false; + long k = (long) rs; + for (long v : (long[]) val) { + if (v == k) return true; + } + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + Arrays.toString((long[]) val); + } + }; + } else if (type == float[].class) { + filter = new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return false; + float k = (float) rs; + for (float v : (float[]) val) { + if (v == k) return true; + } + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + Arrays.toString((float[]) val); + } + }; + } else if (type == double[].class) { + filter = new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return false; + double k = (double) rs; + for (double v : (double[]) val) { + if (v == k) return true; + } + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + Arrays.toString((double[]) val); + } + }; + } else { + filter = new Predicate() { + + @Override + public boolean test(T t) { + Object rs = attr.get(t); + if (rs == null) return false; + for (Object v : (Object[]) val) { + if (rs.equals(v)) return true; + } + return false; + } + + @Override + public String toString() { + return field + ' ' + express.value() + ' ' + Arrays.toString((Object[]) val); + } + }; + } + } + if (express == NOTIN) { + final Predicate filter2 = filter; + filter = new Predicate() { + + @Override + public boolean test(T t) { + return !filter2.test(t); + } + + @Override + public String toString() { + return filter2.toString(); + } + }; + } + return filter; + } + return null; + } + + @Override + public String toString() { + return toString(null).toString(); + } + + protected StringBuilder toString(final String prefix) { + StringBuilder sb = new StringBuilder(); + StringBuilder element = toElementString(prefix); + boolean more = element != null && element.length() > 0 && this.nodes != null; + if (more) sb.append('('); + sb.append(element); + if (this.nodes != null) { + for (FilterNode node : this.nodes) { + String s = node.toString(); + if (s.length() < 1) continue; + if (sb.length() > 1) sb.append(or ? " OR " : " AND "); + sb.append(s); + } + } + if (more) sb.append(')'); + return sb; + } + + protected final StringBuilder toElementString(final String prefix) { + Serializable val0 = getValue(); + if (needSplit(val0)) { + if (val0 instanceof Collection) { + StringBuilder sb = new StringBuilder(); + boolean more = ((Collection) val0).size() > 1; + if (more) sb.append('('); + for (Object fv : (Collection) val0) { + if (fv == null) continue; + CharSequence cs = toElementString(prefix, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } else if (val0.getClass().isArray()) { + StringBuilder sb = new StringBuilder(); + Object[] fvs = (Object[]) val0; + boolean more = fvs.length > 1; + if (more) sb.append('('); + for (Object fv : fvs) { + if (fv == null) continue; + CharSequence cs = toElementString(prefix, fv); + if (cs == null) continue; + if (sb.length() > 2) sb.append(itemand ? " AND " : " OR "); + sb.append(cs); + } + if (more) sb.append(')'); + return sb.length() > 3 ? sb : null; //若sb的值只是(),则不过滤 + } + } + return toElementString(prefix, val0); + } + + protected final StringBuilder toElementString(final String prefix, Object ev) { + StringBuilder sb = new StringBuilder(); + if (column != null) { + String col = prefix == null ? column : (prefix + "." + column); + if (express == ISNULL || express == ISNOTNULL) { + sb.append(col).append(' ').append(express.value()); + } else if (express == ISEMPTY || express == ISNOTEMPTY) { + sb.append(col).append(' ').append(express.value()).append(" ''"); + } else if (ev != null) { + boolean lower = (express == IGNORECASELIKE || express == IGNORECASENOTLIKE || express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN); + sb.append(lower ? ("LOWER(" + col + ')') : col).append(' ').append(express.value()).append(' ').append(formatToString(express, ev)); + } + } + return sb; + } + + private static CharSequence formatToString(Object value) { + CharSequence sb = formatToString(null, value); + return sb == null ? null : sb.toString(); + } + + private static CharSequence formatToString(FilterExpress express, Object value) { + if (value == null) return null; + if (value instanceof Number) return String.valueOf(value); + if (value instanceof CharSequence) { + if (express == LIKE || express == NOTLIKE) { + value = "%" + value + '%'; + } else if (express == STARTSWITH || express == NOTSTARTSWITH) { + value = value + "%"; + } else if (express == ENDSWITH || express == NOTENDSWITH) { + value = "%" + value; + } else if (express == IGNORECASELIKE || express == IGNORECASENOTLIKE) { + value = "%" + value.toString().toLowerCase() + '%'; + } else if (express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN + || express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL) { + value = value.toString().toLowerCase(); + } + return new StringBuilder().append('\'').append(value.toString().replace("'", "\\'")).append('\''); + } else if (value instanceof Range) { + Range range = (Range) value; + boolean rangestring = range.getClass() == Range.StringRange.class; + StringBuilder sb = new StringBuilder(); + if (rangestring) { + sb.append('\'').append(range.getMin().toString().replace("'", "\\'")).append('\''); + } else { + sb.append(range.getMin()); + } + sb.append(" AND "); + if (rangestring) { + sb.append('\'').append(range.getMax().toString().replace("'", "\\'")).append('\''); + } else { + sb.append(range.getMax()); + } + return sb; + } else if (value.getClass().isArray()) { + int len = Array.getLength(value); + if (len == 0) return express == NOTIN ? null : new StringBuilder("(NULL)"); + if (len == 1) { + Object firstval = Array.get(value, 0); + if (firstval != null && firstval.getClass().isArray()) return formatToString(express, firstval); + } + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (int i = 0; i < len; i++) { + Object o = Array.get(value, i); + if (sb.length() > 1) sb.append(','); + if (o instanceof CharSequence) { + sb.append('\'').append(o.toString().replace("'", "\\'")).append('\''); + } else { + sb.append(o); + } + } + return sb.append(')'); + } else if (value instanceof Collection) { + Collection c = (Collection) value; + if (c.isEmpty()) return express == NOTIN ? null : new StringBuilder("(NULL)"); + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (Object o : c) { + if (sb.length() > 1) sb.append(','); + if (o instanceof CharSequence) { + sb.append('\'').append(o.toString().replace("'", "\\'")).append('\''); + } else { + sb.append(o); + } + } + return sb.append(')'); + } + return String.valueOf(value); + } + + public final Serializable getValue() { + return value; + } + + public final void setValue(Serializable value) { + this.value = value; + } + + public boolean isReadOnly() { + return readOnly; + } + + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + public final boolean isOr() { + return or; + } + + public final void setOr(boolean or) { + this.or = or; + } + + public final String getColumn() { + return column; + } + + public final void setColumn(String column) { + this.column = column; + } + + public final FilterExpress getExpress() { + return express; + } + + public final void setExpress(FilterExpress express) { + this.express = express; + } + + public final boolean isItemand() { + return itemand; + } + + public final void setItemand(boolean itemand) { + this.itemand = itemand; + } + + public final FilterNode[] getNodes() { + return nodes; + } + + public final void setNodes(FilterNode[] nodes) { + this.nodes = nodes; + } + +} diff --git a/src/org/redkale/source/FilterNodeBean.java b/src/main/java/org/redkale/source/FilterNodeBean.java similarity index 94% rename from src/org/redkale/source/FilterNodeBean.java rename to src/main/java/org/redkale/source/FilterNodeBean.java index 279e867db..d4ed4b6a1 100644 --- a/src/org/redkale/source/FilterNodeBean.java +++ b/src/main/java/org/redkale/source/FilterNodeBean.java @@ -1,378 +1,384 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import javax.persistence.Transient; -import static org.redkale.source.FilterExpress.*; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param FilterBean泛型 - */ -public final class FilterNodeBean implements Comparable> { - - private static final ConcurrentHashMap beanodes = new ConcurrentHashMap<>(); - - private Attribute beanAttr; - - private String column; - - private FilterExpress express; - - private boolean itemand; - - private boolean or; - - private FilterNodeBean[] nodeBeans; - - //-----------------join table-------------------------- - private Class joinClass; - - private String[] joinColumns; - - //---------------------------------------------------- - private long least; - - private boolean string; - - private boolean number; - - public FilterNodeBean(FilterNodeBean bean) { - this.beanAttr = bean == null ? null : bean.beanAttr; - this.column = bean == null ? null : bean.column; - this.express = bean == null ? null : bean.express; - this.itemand = bean == null ? true : bean.itemand; - this.joinClass = bean == null ? null : bean.joinClass; - this.joinColumns = bean == null ? null : bean.joinColumns; - this.least = bean == null ? 1 : bean.least; - this.string = bean == null ? false : bean.string; - this.number = bean == null ? false : bean.number; - this.or = bean == null ? false : bean.or; - this.nodeBeans = bean == null ? null : bean.nodeBeans; - } - - private FilterNodeBean(final FilterJoinColumn joinCol, final FilterColumn filterCol, final Attribute attr, final Type genericType) { - this.beanAttr = attr; - this.joinClass = joinCol == null ? null : joinCol.table(); - this.joinColumns = joinCol == null ? null : joinCol.columns(); - - final Class type = attr.type(); - this.column = (filterCol != null && !filterCol.name().isEmpty()) ? filterCol.name() : attr.field(); - - FilterExpress exp = filterCol == null ? null : filterCol.express(); - Class compType = type.getComponentType(); - if (Collection.class.isAssignableFrom(type) && genericType instanceof ParameterizedType) { - Type pt = ((ParameterizedType) genericType).getActualTypeArguments()[0]; - if (pt instanceof Class) compType = (Class) pt; - } - if ((exp == null || exp == EQUAL) && (type.isArray() || Collection.class.isAssignableFrom(type))) { - if (compType != null && Range.class.isAssignableFrom(compType)) { - if (AND != exp) exp = OR; - } else if (NOTIN != exp) { - exp = IN; - } - } else if (Range.class.isAssignableFrom(type)) { - if (NOTBETWEEN != exp) exp = BETWEEN; - } - if (exp == null) exp = EQUAL; - this.express = exp; - - this.least = filterCol == null ? 1 : filterCol.least(); - this.number = (type.isPrimitive() && type != boolean.class) || Number.class.isAssignableFrom(type); - this.string = CharSequence.class.isAssignableFrom(type); - } - - private FilterNodeBean or(FilterNodeBean node) { - return any(node, true); - } - - private FilterNodeBean and(FilterNodeBean node) { - return any(node, false); - } - - private FilterNodeBean any(FilterNodeBean node, boolean signor) { - Objects.requireNonNull(node); - if (this.column == null) { - this.beanAttr = node.beanAttr; - this.column = node.column; - this.express = node.express; - this.itemand = node.itemand; - this.joinClass = node.joinClass; - this.joinColumns = node.joinColumns; - this.least = node.least; - this.string = node.string; - this.number = node.number; - return this; - } - if (this.nodeBeans == null) { - this.nodeBeans = new FilterNodeBean[]{node}; - this.or = signor; - return this; - } - if (or == signor) { - this.nodeBeans = Utility.append(this.nodeBeans, node); - return this; - } - this.nodeBeans = new FilterNodeBean[]{new FilterNodeBean(this), node}; - this.column = null; - this.or = signor; - return this; - } - - public static FilterNode createFilterNode(final FilterBean bean) { - if (bean == null) return null; - return load(bean.getClass()).create(bean); - } - - private FilterNode create(final T bean) { - if (bean == null || beanAttr == null) return null; - FilterNode node = null; - final Serializable val = beanAttr.get(bean); - if (column != null && val != null) { - boolean skip = false; - if (string && ((CharSequence) val).length() == 0) { //空字符串不需要进行过滤 - skip = true; - } else if (number && ((Number) val).longValue() < least) { //数值小于过滤下值限则不需要过滤 - skip = true; - } - if (!skip) { - if (this.joinClass == null) { - node = FilterNode.create(column, express, itemand, val); - } else { - node = FilterJoinNode.create(joinClass, joinColumns, column, express, itemand, val); - } - } - } - if (this.nodeBeans == null) return node; - for (final FilterNodeBean fnb : this.nodeBeans) { - FilterNode n = fnb.create(bean); - if (n == null) continue; - node = node == null ? n : ((!(n instanceof FilterJoinNode)) ? n.any(node, or) : node.any(n, or)); - } - return node; - } - - private static FilterNodeBean createFilterNodeBean(final Class clazz) { - final Set fields = new HashSet<>(); - final Map nodemap = new LinkedHashMap(); - Class cltmp = clazz; - do { - for (final Field field : cltmp.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (fields.contains(field.getName())) continue; - if (field.getAnnotation(Transient.class) != null) continue; - if (field.getAnnotation(FilterColumn.class) != null && field.getAnnotation(FilterColumn.class).ignore()) continue; - - final boolean pubmod = Modifier.isPublic(field.getModifiers()); - - char[] chars = field.getName().toCharArray(); - chars[0] = Character.toUpperCase(chars[0]); - final Class t = field.getType(); - Method getter = null; - try { - getter = cltmp.getMethod(((t == boolean.class || t == Boolean.class) ? "is" : "get") + new String(chars)); - } catch (Exception ex) { - if (t == Boolean.class) { - try { - getter = cltmp.getMethod("get" + new String(chars)); - } catch (Exception ex2) { - if (!pubmod) continue; - } - } else if (!pubmod) continue; - } - fields.add(field.getName()); - - final Attribute beanAttr = pubmod ? Attribute.create(field) : Attribute.create(getter, null); - FilterNodeBean nodeBean = new FilterNodeBean(field.getAnnotation(FilterJoinColumn.class), field.getAnnotation(FilterColumn.class), beanAttr, field.getGenericType()); - - //------------------------------------ - { - FilterGroup[] refs = field.getAnnotationsByType(FilterGroup.class); - String[] groups = new String[refs.length]; - for (int i = 0; i < refs.length; i++) { - groups[i] = refs[i].value(); - } - if (groups.length == 0) groups = new String[]{"[AND]"}; - for (String key : groups) { - if (!key.startsWith("[AND]") && !key.startsWith("[OR]")) { - throw new RuntimeException(field + "'s FilterGroup.value(" + key + ") illegal, must be [AND] or [OR] startsWith"); - } - FilterNodeBean node = nodemap.get(key); - if (node == null) { - nodemap.put(key, nodeBean); - } else if (nodeBean.joinClass == null && node.joinClass != null) { //非joinNode 关联 joinNode - nodemap.put(key, nodeBean.any(node, key.substring(key.lastIndexOf('.') + 1).contains("[OR]"))); - } else { - node.any(nodeBean, key.substring(key.lastIndexOf('.') + 1).contains("[OR]")); - } - } - } - } - } while ((cltmp = cltmp.getSuperclass()) != Object.class); - final Map linkes = new LinkedHashMap<>(); - nodemap.forEach((k, v) -> { - String[] keys = k.split("\\."); - LinkNode link = linkes.get(keys[0]); - if (link == null) { - linkes.put(keys[0], new LinkNode(k, v)); - } else { - link.put(keys, 0, v); - } - }); - FilterNodeBean rs = null; - for (LinkNode link : linkes.values()) { - FilterNodeBean f = link.createFilterNodeBean(); - if (f == null) continue; - rs = rs == null ? f : rs.and(f); - } - if (rs != null && rs.nodeBeans != null) Arrays.sort(rs.nodeBeans); - return rs == null ? new FilterNodeBean(null) : rs; - } - - @Override - public int compareTo(FilterNodeBean o) { - if (this.joinClass == null && o.joinClass == null) return 0; - if (this.joinClass != null && o.joinClass != null) return 0; - return this.joinClass == null ? -1 : 1; - } - - private static class LinkNode { - - public final boolean or; - - public final String key; - - public final List beans = new ArrayList<>(); - - public final Map nexts = new LinkedHashMap<>(); - - public LinkNode(String keyString, FilterNodeBean node) { - String[] keys = keyString.split("\\."); - this.key = keys[0]; - this.or = this.key.contains("[OR]"); - put(keys, 0, node); - } - - public LinkNode(String[] keyStrings, int pos, FilterNodeBean node) { - this.key = keyStrings[pos]; - this.or = this.key.contains("[OR]"); - put(keyStrings, pos, node); - } - - public FilterNodeBean createFilterNodeBean() { - FilterNodeBean node = null; - for (FilterNodeBean bean : beans) { - node = node == null ? bean : node.any(bean, or); - } - for (LinkNode link : nexts.values()) { - FilterNodeBean f = link.createFilterNodeBean(); - if (f == null) continue; - node = node == null ? f : node.any(f, or); - } - return node; - } - - public final void put(final String[] keys, int pos, final FilterNodeBean node) { - if (keys.length == pos + 1 && this.key.equals(keys[pos])) { - this.beans.add(node); - return; - } - LinkNode link = nexts.get(keys[pos + 1]); - if (link == null) { - nexts.put(keys[pos + 1], new LinkNode(keys, pos + 1, node)); - } else { - link.put(keys, pos + 1, node); - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("{key = '").append(key).append("', or = ").append(or); - if (!beans.isEmpty()) { - sb.append(", beans = [\r\n"); - for (FilterNodeBean bean : this.beans) { - sb.append(" ").append(bean).append("\r\n"); - } - sb.append("]"); - } - if (!nexts.isEmpty()) { - sb.append(", nexts = [\r\n"); - for (LinkNode link : this.nexts.values()) { - sb.append(" ").append(link).append("\r\n"); - } - sb.append("]"); - } - sb.append("}"); - return sb.toString(); - } - } - - private static FilterNodeBean load(Class clazz) { - FilterNodeBean rs = beanodes.get(clazz); - if (rs != null) return rs; - synchronized (beanodes) { - rs = beanodes.get(clazz); - if (rs == null) { - rs = createFilterNodeBean(clazz); - beanodes.put(clazz, rs); - } - return rs; - } - } - - @Override - public String toString() { - return toString(joinClass == null ? null : joinClass.getSimpleName()).toString(); - } - - protected StringBuilder toString(final String prefix) { - StringBuilder sb = new StringBuilder(); - StringBuilder element = toElementString(prefix); - boolean more = element.length() > 0 && this.nodeBeans != null; - if (more) sb.append('('); - sb.append(element); - if (this.nodeBeans != null) { - for (FilterNodeBean node : this.nodeBeans) { - String s = node.toString(); - if (s.length() < 1) continue; - if (sb.length() > 1) sb.append(or ? " OR " : " AND "); - sb.append(s); - } - } - if (more) sb.append(')'); - return sb; - } - - protected final StringBuilder toElementString(final String prefix) { - StringBuilder sb = new StringBuilder(); - if (column != null) { - String col = prefix == null ? column : (prefix + "." + column); - if (express == ISNULL || express == ISNOTNULL) { - sb.append(col).append(' ').append(express.value()); - } else if (express == ISEMPTY || express == ISNOTEMPTY) { - sb.append(col).append(' ').append(express.value()).append(" ''"); - } else if (express == LENGTH_EQUAL || express == LENGTH_LESSTHAN || express == LENGTH_LESSTHANOREQUALTO - || express == LENGTH_GREATERTHAN || express == LENGTH_GREATERTHANOREQUALTO) { - sb.append("LENGTH(").append(col).append(") ").append(express.value()).append(" ?"); - } else { - boolean lower = (express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL || express == IGNORECASELIKE - || express == IGNORECASENOTLIKE || express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN); - sb.append(lower ? ("LOWER(" + col + ')') : col).append(' ').append(express.value()).append(" ?"); - } - } - return sb; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import javax.persistence.Transient; +import static org.redkale.source.FilterExpress.*; +import org.redkale.util.*; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param FilterBean泛型 + */ +public final class FilterNodeBean implements Comparable> { + + private static final ConcurrentHashMap beanodes = new ConcurrentHashMap<>(); + + private Attribute beanAttr; + + private String column; + + private FilterExpress express; + + private boolean itemand; + + private boolean or; + + private FilterNodeBean[] nodeBeans; + + //-----------------join table-------------------------- + private Class joinClass; + + private String[] joinColumns; + + //---------------------------------------------------- + private long least; + + private boolean string; + + private boolean number; + + public FilterNodeBean(FilterNodeBean bean) { + this.beanAttr = bean == null ? null : bean.beanAttr; + this.column = bean == null ? null : bean.column; + this.express = bean == null ? null : bean.express; + this.itemand = bean == null ? true : bean.itemand; + this.joinClass = bean == null ? null : bean.joinClass; + this.joinColumns = bean == null ? null : bean.joinColumns; + this.least = bean == null ? 1 : bean.least; + this.string = bean == null ? false : bean.string; + this.number = bean == null ? false : bean.number; + this.or = bean == null ? false : bean.or; + this.nodeBeans = bean == null ? null : bean.nodeBeans; + } + + private FilterNodeBean(final FilterJoinColumn joinCol, final FilterColumn filterCol, final Attribute attr, final Type genericType) { + this.beanAttr = attr; + this.joinClass = joinCol == null ? null : joinCol.table(); + this.joinColumns = joinCol == null ? null : joinCol.columns(); + + final Class type = attr.type(); + this.column = (filterCol != null && !filterCol.name().isEmpty()) ? filterCol.name() : attr.field(); + + FilterExpress exp = filterCol == null ? null : filterCol.express(); + Class compType = type.getComponentType(); + if (Collection.class.isAssignableFrom(type) && genericType instanceof ParameterizedType) { + Type pt = ((ParameterizedType) genericType).getActualTypeArguments()[0]; + if (pt instanceof Class) compType = (Class) pt; + } + if ((exp == null || exp == EQUAL) && (type.isArray() || Collection.class.isAssignableFrom(type))) { + if (compType != null && Range.class.isAssignableFrom(compType)) { + if (AND != exp) exp = OR; + } else if (NOTIN != exp) { + exp = IN; + } + } else if (Range.class.isAssignableFrom(type)) { + if (NOTBETWEEN != exp) exp = BETWEEN; + } + if (exp == null) exp = EQUAL; + this.express = exp; + + this.least = filterCol == null ? 1 : filterCol.least(); + this.number = (type.isPrimitive() && type != boolean.class) || Number.class.isAssignableFrom(type); + this.string = CharSequence.class.isAssignableFrom(type); + } + + private FilterNodeBean or(FilterNodeBean node) { + return any(node, true); + } + + private FilterNodeBean and(FilterNodeBean node) { + return any(node, false); + } + + private FilterNodeBean any(FilterNodeBean node, boolean signor) { + Objects.requireNonNull(node); + if (this.column == null) { + this.beanAttr = node.beanAttr; + this.column = node.column; + this.express = node.express; + this.itemand = node.itemand; + this.joinClass = node.joinClass; + this.joinColumns = node.joinColumns; + this.least = node.least; + this.string = node.string; + this.number = node.number; + return this; + } + if (this.nodeBeans == null) { + this.nodeBeans = new FilterNodeBean[]{node}; + this.or = signor; + return this; + } + if (or == signor) { + this.nodeBeans = Utility.append(this.nodeBeans, node); + return this; + } + this.nodeBeans = new FilterNodeBean[]{new FilterNodeBean(this), node}; + this.column = null; + this.or = signor; + return this; + } + + public static FilterNode createFilterNode(final FilterBean bean) { + if (bean == null) return null; + return load(bean.getClass()).create(bean); + } + + public static FilterNodeBean load(Class clazz) { + FilterNodeBean rs = beanodes.get(clazz); + if (rs != null) return rs; + synchronized (beanodes) { + rs = beanodes.get(clazz); + if (rs == null) { + rs = createFilterNodeBean(clazz); + beanodes.put(clazz, rs); + } + return rs; + } + } + + private FilterNode create(final T bean) { + if (bean == null || beanAttr == null) return null; + FilterNode node = null; + final Serializable val = beanAttr.get(bean); + if (column != null && val != null) { + boolean skip = false; + if (string && ((CharSequence) val).length() == 0) { //空字符串不需要进行过滤 + skip = true; + } else if (number && ((Number) val).longValue() < least) { //数值小于过滤下值限则不需要过滤 + skip = true; + } + if (!skip) { + if (this.joinClass == null) { + node = FilterNode.create(column, express, itemand, val); + } else { + node = FilterJoinNode.create(joinClass, joinColumns, column, express, itemand, val); + } + } + } + if (this.nodeBeans == null) return node; + for (final FilterNodeBean fnb : this.nodeBeans) { + FilterNode n = fnb.create(bean); + if (n == null) continue; + node = node == null ? n : ((!(n instanceof FilterJoinNode)) ? n.any(node, or) : node.any(n, or)); + } + return node; + } + + private static FilterNodeBean createFilterNodeBean(final Class clazz) { + final Set fields = new HashSet<>(); + final Map nodemap = new LinkedHashMap(); + Class cltmp = clazz; + do { + for (final Field field : cltmp.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (fields.contains(field.getName())) continue; + if (field.getAnnotation(Transient.class) != null) continue; + if (field.getAnnotation(FilterColumn.class) != null && field.getAnnotation(FilterColumn.class).ignore()) continue; + + final boolean pubmod = Modifier.isPublic(field.getModifiers()); + + char[] chars = field.getName().toCharArray(); + chars[0] = Character.toUpperCase(chars[0]); + final Class t = field.getType(); + Method getter = null; + try { + getter = cltmp.getMethod(((t == boolean.class || t == Boolean.class) ? "is" : "get") + new String(chars)); + } catch (NoSuchMethodException ex) { + try { + getter = cltmp.getMethod(field.getName()); + } catch (NoSuchMethodException ex2) { + if (t == Boolean.class) { + try { + getter = cltmp.getMethod("get" + new String(chars)); + } catch (Exception ex3) { + if (!pubmod) continue; + } + } else if (!pubmod) { + continue; + } + } + } + fields.add(field.getName()); + + final Attribute beanAttr = pubmod ? Attribute.create(field) : Attribute.create(getter, null); + FilterNodeBean nodeBean = new FilterNodeBean(field.getAnnotation(FilterJoinColumn.class), field.getAnnotation(FilterColumn.class), beanAttr, field.getGenericType()); + + //------------------------------------ + { + FilterGroup[] refs = field.getAnnotationsByType(FilterGroup.class); + String[] groups = new String[refs.length]; + for (int i = 0; i < refs.length; i++) { + groups[i] = refs[i].value(); + } + if (groups.length == 0) groups = new String[]{"[AND]"}; + for (String key : groups) { + if (!key.startsWith("[AND]") && !key.startsWith("[OR]")) { + throw new RuntimeException(field + "'s FilterGroup.value(" + key + ") illegal, must be [AND] or [OR] startsWith"); + } + FilterNodeBean node = nodemap.get(key); + if (node == null) { + nodemap.put(key, nodeBean); + } else if (nodeBean.joinClass == null && node.joinClass != null) { //非joinNode 关联 joinNode + nodemap.put(key, nodeBean.any(node, key.substring(key.lastIndexOf('.') + 1).contains("[OR]"))); + } else { + node.any(nodeBean, key.substring(key.lastIndexOf('.') + 1).contains("[OR]")); + } + } + } + } + } while ((cltmp = cltmp.getSuperclass()) != Object.class); + final Map linkes = new LinkedHashMap<>(); + nodemap.forEach((k, v) -> { + String[] keys = k.split("\\."); + LinkNode link = linkes.get(keys[0]); + if (link == null) { + linkes.put(keys[0], new LinkNode(k, v)); + } else { + link.put(keys, 0, v); + } + }); + FilterNodeBean rs = null; + for (LinkNode link : linkes.values()) { + FilterNodeBean f = link.createFilterNodeBean(); + if (f == null) continue; + rs = rs == null ? f : rs.and(f); + } + if (rs != null && rs.nodeBeans != null) Arrays.sort(rs.nodeBeans); + return rs == null ? new FilterNodeBean(null) : rs; + } + + @Override + public int compareTo(FilterNodeBean o) { + if (this.joinClass == null && o.joinClass == null) return 0; + if (this.joinClass != null && o.joinClass != null) return 0; + return this.joinClass == null ? -1 : 1; + } + + private static class LinkNode { + + public final boolean or; + + public final String key; + + public final List beans = new ArrayList<>(); + + public final Map nexts = new LinkedHashMap<>(); + + public LinkNode(String keyString, FilterNodeBean node) { + String[] keys = keyString.split("\\."); + this.key = keys[0]; + this.or = this.key.contains("[OR]"); + put(keys, 0, node); + } + + public LinkNode(String[] keyStrings, int pos, FilterNodeBean node) { + this.key = keyStrings[pos]; + this.or = this.key.contains("[OR]"); + put(keyStrings, pos, node); + } + + public FilterNodeBean createFilterNodeBean() { + FilterNodeBean node = null; + for (FilterNodeBean bean : beans) { + node = node == null ? bean : node.any(bean, or); + } + for (LinkNode link : nexts.values()) { + FilterNodeBean f = link.createFilterNodeBean(); + if (f == null) continue; + node = node == null ? f : node.any(f, or); + } + return node; + } + + public final void put(final String[] keys, int pos, final FilterNodeBean node) { + if (keys.length == pos + 1 && this.key.equals(keys[pos])) { + this.beans.add(node); + return; + } + LinkNode link = nexts.get(keys[pos + 1]); + if (link == null) { + nexts.put(keys[pos + 1], new LinkNode(keys, pos + 1, node)); + } else { + link.put(keys, pos + 1, node); + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("{key = '").append(key).append("', or = ").append(or); + if (!beans.isEmpty()) { + sb.append(", beans = [\r\n"); + for (FilterNodeBean bean : this.beans) { + sb.append(" ").append(bean).append("\r\n"); + } + sb.append("]"); + } + if (!nexts.isEmpty()) { + sb.append(", nexts = [\r\n"); + for (LinkNode link : this.nexts.values()) { + sb.append(" ").append(link).append("\r\n"); + } + sb.append("]"); + } + sb.append("}"); + return sb.toString(); + } + } + + @Override + public String toString() { + return toString(joinClass == null ? null : joinClass.getSimpleName()).toString(); + } + + protected StringBuilder toString(final String prefix) { + StringBuilder sb = new StringBuilder(); + StringBuilder element = toElementString(prefix); + boolean more = element.length() > 0 && this.nodeBeans != null; + if (more) sb.append('('); + sb.append(element); + if (this.nodeBeans != null) { + for (FilterNodeBean node : this.nodeBeans) { + String s = node.toString(); + if (s.length() < 1) continue; + if (sb.length() > 1) sb.append(or ? " OR " : " AND "); + sb.append(s); + } + } + if (more) sb.append(')'); + return sb; + } + + protected final StringBuilder toElementString(final String prefix) { + StringBuilder sb = new StringBuilder(); + if (column != null) { + String col = prefix == null ? column : (prefix + "." + column); + if (express == ISNULL || express == ISNOTNULL) { + sb.append(col).append(' ').append(express.value()); + } else if (express == ISEMPTY || express == ISNOTEMPTY) { + sb.append(col).append(' ').append(express.value()).append(" ''"); + } else if (express == LENGTH_EQUAL || express == LENGTH_LESSTHAN || express == LENGTH_LESSTHANOREQUALTO + || express == LENGTH_GREATERTHAN || express == LENGTH_GREATERTHANOREQUALTO) { + sb.append("LENGTH(").append(col).append(") ").append(express.value()).append(" ?"); + } else { + boolean lower = (express == IGNORECASEEQUAL || express == IGNORECASENOTEQUAL || express == IGNORECASELIKE + || express == IGNORECASENOTLIKE || express == IGNORECASECONTAIN || express == IGNORECASENOTCONTAIN); + sb.append(lower ? ("LOWER(" + col + ')') : col).append(' ').append(express.value()).append(" ?"); + } + } + return sb; + } +} diff --git a/src/org/redkale/source/FilterValue.java b/src/main/java/org/redkale/source/FilterValue.java similarity index 96% rename from src/org/redkale/source/FilterValue.java rename to src/main/java/org/redkale/source/FilterValue.java index 5b62bd33b..d19515863 100644 --- a/src/org/redkale/source/FilterValue.java +++ b/src/main/java/org/redkale/source/FilterValue.java @@ -1,70 +1,70 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -/** - * FilterValue主要用于复杂的表达式。
    - * 例如: col / 10 = 3 、MOD(col, 8) > 0 这些都不是单独一个数值能表达的,因此需要FilterValue 才构建 8 、 > 、0 组合值。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class FilterValue implements java.io.Serializable { - - private Number optvalue; - - private FilterExpress express; - - private Number destvalue; - - public FilterValue() { - } - - public FilterValue(Number optvalue, Number destvalue) { - this(optvalue, FilterExpress.EQUAL, destvalue); - } - - public FilterValue(Number optvalue, FilterExpress express) { - this(optvalue, express, 0); - } - - public FilterValue(Number optvalue, FilterExpress express, Number destvalue) { - this.optvalue = optvalue; - this.express = express; - this.destvalue = destvalue; - } - - public Number getOptvalue() { - return optvalue == null ? 0 : optvalue; - } - - public void setOptvalue(Number optvalue) { - this.optvalue = optvalue; - } - - public FilterExpress getExpress() { - return express == null ? FilterExpress.EQUAL : express; - } - - public void setExpress(FilterExpress express) { - this.express = express; - } - - public Number getDestvalue() { - return destvalue == null ? 0 : destvalue; - } - - public void setDestvalue(Number destvalue) { - this.destvalue = destvalue; - } - - @Override - public String toString() { - return FilterValue.class.getSimpleName() + "[optvalue=" + getOptvalue() + ", express=" + getExpress() + ", destvalue=" + getDestvalue() + "]"; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +/** + * FilterValue主要用于复杂的表达式。
    + * 例如: col / 10 = 3 、MOD(col, 8) > 0 这些都不是单独一个数值能表达的,因此需要FilterValue 才构建 8 、 > 、0 组合值。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class FilterValue implements java.io.Serializable { + + private Number optvalue; + + private FilterExpress express; + + private Number destvalue; + + public FilterValue() { + } + + public FilterValue(Number optvalue, Number destvalue) { + this(optvalue, FilterExpress.EQUAL, destvalue); + } + + public FilterValue(Number optvalue, FilterExpress express) { + this(optvalue, express, 0); + } + + public FilterValue(Number optvalue, FilterExpress express, Number destvalue) { + this.optvalue = optvalue; + this.express = express; + this.destvalue = destvalue; + } + + public Number getOptvalue() { + return optvalue == null ? 0 : optvalue; + } + + public void setOptvalue(Number optvalue) { + this.optvalue = optvalue; + } + + public FilterExpress getExpress() { + return express == null ? FilterExpress.EQUAL : express; + } + + public void setExpress(FilterExpress express) { + this.express = express; + } + + public Number getDestvalue() { + return destvalue == null ? 0 : destvalue; + } + + public void setDestvalue(Number destvalue) { + this.destvalue = destvalue; + } + + @Override + public String toString() { + return FilterValue.class.getSimpleName() + "[optvalue=" + getOptvalue() + ", express=" + getExpress() + ", destvalue=" + getDestvalue() + "]"; + } +} diff --git a/src/org/redkale/source/Flipper.java b/src/main/java/org/redkale/source/Flipper.java similarity index 84% rename from src/org/redkale/source/Flipper.java rename to src/main/java/org/redkale/source/Flipper.java index 01f6f8f05..563b1e36f 100644 --- a/src/org/redkale/source/Flipper.java +++ b/src/main/java/org/redkale/source/Flipper.java @@ -1,156 +1,177 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import org.redkale.convert.ConvertColumn; -import org.redkale.util.Comment; - -/** - * 翻页对象, offset从0开始, limit必须大于0 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class Flipper implements Serializable, Cloneable { - - public static int DEFAULT_LIMIT = 20; - - @ConvertColumn(index = 1) - @Comment("记录行的偏移量,从0开始") - private int offset = 0; - - @ConvertColumn(index = 2) - @Comment("每页多少行") - private int limit = DEFAULT_LIMIT; - - @ConvertColumn(index = 3) - @Comment("排序字段, 可多字段排序") - private String sort = ""; - - public Flipper() { - } - - public Flipper(int limit) { - this.limit = limit > 0 ? limit : 0; - } - - public Flipper(String sortColumn) { - this.sort = sortColumn; - } - - public Flipper(int limit, int offset) { - this.limit = limit > 0 ? limit : 0; - this.offset = offset < 0 ? 0 : offset; - } - - public Flipper(int limit, String sortColumn) { - this.limit = limit > 0 ? limit : 0; - this.sort = sortColumn; - } - - public Flipper(int limit, int offset, String sortColumn) { - this.limit = limit > 0 ? limit : 0; - this.offset = offset < 0 ? 0 : offset; - this.sort = sortColumn; - } - - public Flipper copyTo(Flipper copy) { - if (copy == null) return copy; - copy.offset = this.offset; - copy.limit = this.limit; - copy.sort = this.sort; - return copy; - } - - public Flipper copyFrom(Flipper copy) { - if (copy == null) return this; - this.offset = copy.offset; - this.limit = copy.limit; - this.sort = copy.sort; - return this; - } - - public Flipper next() { - this.offset = getOffset() + this.limit; - return this; - } - - @Override - @SuppressWarnings("CloneDoesntCallSuperClone") - public Flipper clone() { - return this.copyTo(new Flipper()); - } - - @Override - public String toString() { - return this.getClass().getSimpleName() + "{offset:" + this.offset + ", limit:" + this.limit + ", sort:" + this.sort + "}"; - } - - public int getLimit() { - return limit; - } - - public void setLimit(int limit) { - this.limit = limit; - } - - public Flipper limit(int limit) { - setLimit(limit); - return this; - } - - public Flipper maxLimit(int maxlimit) { - setLimit(Math.max(1, Math.min(maxlimit, limit))); - return this; - } - - public Flipper unlimit() { - this.limit = 0; - return this; - } - - public int getOffset() { - return offset; - } - - public void setOffset(int offset) { - this.offset = offset < 0 ? 0 : offset; - } - - public Flipper offset(int offset) { - setOffset(offset); - return this; - } - - public String getSort() { - return sort; - } - - public void setSort(String sort) { - this.sort = sort == null ? "" : sort.trim(); - } - - public Flipper sort(String sort) { - setSort(sort); - return this; - } - - public static Flipper sortIfAbsent(Flipper flipper, String sort) { - if (flipper != null) return flipper.sortIfAbsent(sort); - return flipper; - } - - public Flipper sortIfAbsent(String sort) { - if (this.sort == null || this.sort.isEmpty()) { - this.sort = sort; - } - return this; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.io.Serializable; +import java.util.Objects; +import org.redkale.convert.ConvertColumn; +import org.redkale.util.Comment; + +/** + * 翻页对象, offset从0开始, limit必须大于0 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class Flipper implements Serializable, Cloneable { + + public static int DEFAULT_LIMIT = 20; + + @ConvertColumn(index = 1) + @Comment("记录行的偏移量,从0开始") + private int offset = 0; + + @ConvertColumn(index = 2) + @Comment("每页多少行") + private int limit = DEFAULT_LIMIT; + + @ConvertColumn(index = 3) + @Comment("排序字段, 可多字段排序") + private String sort = ""; + + public Flipper() { + } + + public Flipper(int limit) { + this.limit = limit > 0 ? limit : 0; + } + + public Flipper(String sortColumn) { + this.sort = sortColumn; + } + + public Flipper(int limit, int offset) { + this.limit = limit > 0 ? limit : 0; + this.offset = offset < 0 ? 0 : offset; + } + + public Flipper(int limit, String sortColumn) { + this.limit = limit > 0 ? limit : 0; + this.sort = sortColumn; + } + + public Flipper(int limit, int offset, String sortColumn) { + this.limit = limit > 0 ? limit : 0; + this.offset = offset < 0 ? 0 : offset; + this.sort = sortColumn; + } + + public Flipper copyTo(Flipper copy) { + if (copy == null) return copy; + copy.offset = this.offset; + copy.limit = this.limit; + copy.sort = this.sort; + return copy; + } + + public Flipper copyFrom(Flipper copy) { + if (copy == null) return this; + this.offset = copy.offset; + this.limit = copy.limit; + this.sort = copy.sort; + return this; + } + + public Flipper next() { + this.offset = getOffset() + this.limit; + return this; + } + + @Override + @SuppressWarnings("CloneDoesntCallSuperClone") + public Flipper clone() { + return this.copyTo(new Flipper()); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "{offset:" + this.offset + ", limit:" + this.limit + ", sort:" + this.sort + "}"; + } + + @Override + public int hashCode() { + int hash = 5; + hash = 37 * hash + this.offset; + hash = 37 * hash + this.limit; + hash = 37 * hash + Objects.hashCode(this.sort); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final Flipper other = (Flipper) obj; + if (this.offset != other.offset) return false; + if (this.limit != other.limit) return false; + return Objects.equals(this.sort, other.sort); + } + + public int getLimit() { + return limit; + } + + public void setLimit(int limit) { + this.limit = limit; + } + + public Flipper limit(int limit) { + setLimit(limit); + return this; + } + + public Flipper maxLimit(int maxlimit) { + setLimit(Math.max(1, Math.min(maxlimit, limit))); + return this; + } + + public Flipper unlimit() { + this.limit = 0; + return this; + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset < 0 ? 0 : offset; + } + + public Flipper offset(int offset) { + setOffset(offset); + return this; + } + + public String getSort() { + return sort; + } + + public void setSort(String sort) { + this.sort = sort == null ? "" : sort.trim(); + } + + public Flipper sort(String sort) { + setSort(sort); + return this; + } + + public static Flipper sortIfAbsent(Flipper flipper, String sort) { + if (flipper != null) return flipper.sortIfAbsent(sort); + return flipper; + } + + public Flipper sortIfAbsent(String sort) { + if (this.sort == null || this.sort.isEmpty()) { + this.sort = sort; + } + return this; + } + +} diff --git a/src/org/redkale/source/Range.java b/src/main/java/org/redkale/source/Range.java similarity index 95% rename from src/org/redkale/source/Range.java rename to src/main/java/org/redkale/source/Range.java index 69cb83278..575f15709 100644 --- a/src/org/redkale/source/Range.java +++ b/src/main/java/org/redkale/source/Range.java @@ -1,387 +1,387 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.util.function.*; -import org.redkale.convert.ConvertColumn; -import org.redkale.util.Utility; - -/** - * - * 取值范围,包含两边的值 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Comparable的子类型 - */ -public interface Range extends java.io.Serializable, Predicate { - - public E getMin(); - - public E getMax(); - - public static ByteRange create(byte min, byte max) { - return new ByteRange(min, max); - } - - public static ShortRange create(short min, short max) { - return new ShortRange(min, max); - } - - public static IntRange create(int min, int max) { - return new IntRange(min, max); - } - - public static LongRange create(long min, long max) { - return new LongRange(min, max); - } - - public static FloatRange create(float min, float max) { - return new FloatRange(min, max); - } - - public static DoubleRange create(double min, double max) { - return new DoubleRange(min, max); - } - - public static StringRange create(String min, String max) { - return new StringRange(min, max); - } - - public static final class ByteRange implements Range { - - @ConvertColumn(index = 1) - private Byte min = Byte.MIN_VALUE; - - @ConvertColumn(index = 2) - private Byte max = Byte.MAX_VALUE; - - public ByteRange() { - } - - public ByteRange(Byte min, Byte max) { - if (min != null) this.min = min; - if (max != null) this.max = max; - } - - @Override - public Byte getMin() { - return min; - } - - @Override - public Byte getMax() { - return max; - } - - public void setMin(Byte min) { - if (min != null) this.min = min; - } - - public void setMax(Byte max) { - if (max != null) this.max = max; - } - - @Override - public boolean test(Byte t) { - if (max < min && max <= 0) return t >= min; - return t >= min && t <= max; - } - - @Override - public String toString() { - return "{min:" + min + ", max:" + max + "}"; - } - - } - - public static final class ShortRange implements Range { - - @ConvertColumn(index = 1) - private Short min = Short.MIN_VALUE; - - @ConvertColumn(index = 2) - private Short max = Short.MAX_VALUE; - - public ShortRange() { - } - - public ShortRange(Short min, Short max) { - if (min != null) this.min = min; - if (max != null) this.max = max; - } - - @Override - public Short getMin() { - return min; - } - - @Override - public Short getMax() { - return max; - } - - public void setMin(Short min) { - if (min != null) this.min = min; - } - - public void setMax(Short max) { - if (max != null) this.max = max; - } - - @Override - public boolean test(Short t) { - if (max < min && max <= 0) return t >= min; - return t >= min && t <= max; - } - - @Override - public String toString() { - return "{min:" + min + ", max:" + max + "}"; - } - } - - public static final class IntRange implements Range { - - @ConvertColumn(index = 1) - private Integer min = Integer.MIN_VALUE; - - @ConvertColumn(index = 2) - private Integer max = Integer.MAX_VALUE; - - public IntRange() { - } - - public IntRange(Integer min, Integer max) { - if (min != null) this.min = min; - if (max != null) this.max = max; - } - - @Override - public Integer getMin() { - return min; - } - - @Override - public Integer getMax() { - return max; - } - - public void setMin(Integer min) { - if (min != null) this.min = min; - } - - public void setMax(Integer max) { - if (max != null) this.max = max; - } - - @Override - public boolean test(Integer t) { - if (max < min && max <= 0) return t >= min; - return t >= min && t <= max; - } - - @Override - public String toString() { - return "{min:" + min + ", max:" + max + "}"; - } - } - - public static final class LongRange implements Range { - - @ConvertColumn(index = 1) - private Long min = Long.MIN_VALUE; - - @ConvertColumn(index = 2) - private Long max = Long.MAX_VALUE; - - public LongRange() { - } - - public LongRange(Long min, Long max) { - if (min != null) this.min = min; - if (max != null) this.max = max; - } - - public static LongRange todayRange() { - long min = Utility.midnight(); - return new LongRange(min, min + 24 * 60 * 60 * 1000 - 1); - } - - public static LongRange yesterdayRange() { - long min = Utility.midnight(System.currentTimeMillis() - 24 * 60 * 60 * 1000); - return new LongRange(min, min + 24 * 60 * 60 * 1000 - 1); - } - - @Override - public Long getMin() { - return min; - } - - @Override - public Long getMax() { - return max; - } - - public void setMin(Long min) { - if (min != null) this.min = min; - } - - public void setMax(Long max) { - if (max != null) this.max = max; - } - - @Override - public boolean test(Long t) { - if (max < min && max <= 0) return t >= min; - return t >= min && t <= max; - } - - @Override - public String toString() { - return "{min:" + min + ", max:" + max + "}"; - } - } - - public static final class FloatRange implements Range { - - @ConvertColumn(index = 1) - private Float min = Float.MIN_VALUE; - - @ConvertColumn(index = 2) - private Float max = Float.MAX_VALUE; - - public FloatRange() { - } - - public FloatRange(Float min, Float max) { - if (min != null) this.min = min; - if (max != null) this.max = max; - } - - @Override - public Float getMin() { - return min; - } - - @Override - public Float getMax() { - return max; - } - - public void setMin(Float min) { - if (min != null) this.min = min; - } - - public void setMax(Float max) { - if (max != null) this.max = max; - } - - @Override - public boolean test(Float t) { - if (max < min && max <= 0) return t >= min; - return t >= min && t <= max; - } - - @Override - public String toString() { - return "{min:" + min + ", max:" + max + "}"; - } - } - - public static final class DoubleRange implements Range { - - @ConvertColumn(index = 1) - private Double min = Double.MIN_VALUE; - - @ConvertColumn(index = 2) - private Double max = Double.MAX_VALUE; - - public DoubleRange() { - } - - public DoubleRange(Double min, Double max) { - if (min != null) this.min = min; - if (max != null) this.max = max; - } - - @Override - public Double getMin() { - return min; - } - - @Override - public Double getMax() { - return max; - } - - public void setMin(Double min) { - if (min != null) this.min = min; - } - - public void setMax(Double max) { - if (max != null) this.max = max; - } - - @Override - public boolean test(Double t) { - if (max < min && max <= 0) return t >= min; - return t >= min && t <= max; - } - - @Override - public String toString() { - return "{min:" + min + ", max:" + max + "}"; - } - } - - public static final class StringRange implements Range { - - @ConvertColumn(index = 1) - private String min = ""; - - @ConvertColumn(index = 2) - private String max = ""; - - public StringRange() { - } - - public StringRange(String min, String max) { - this.min = min; - this.max = max; - } - - @Override - public String getMin() { - return min; - } - - @Override - public String getMax() { - return max; - } - - public void setMin(String min) { - if (min != null) this.min = min; - } - - public void setMax(String max) { - if (max != null) this.max = max; - } - - @Override - public boolean test(String t) { - return t.compareTo(min) >= 0 && t.compareTo(max) <= 0; - } - - @Override - public String toString() { - return "{min:'" + min + "', max:'" + max + "'}"; - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.util.function.*; +import org.redkale.convert.ConvertColumn; +import org.redkale.util.Utility; + +/** + * + * 取值范围,包含两边的值 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Comparable的子类型 + */ +public interface Range extends java.io.Serializable, Predicate { + + public E getMin(); + + public E getMax(); + + public static ByteRange create(byte min, byte max) { + return new ByteRange(min, max); + } + + public static ShortRange create(short min, short max) { + return new ShortRange(min, max); + } + + public static IntRange create(int min, int max) { + return new IntRange(min, max); + } + + public static LongRange create(long min, long max) { + return new LongRange(min, max); + } + + public static FloatRange create(float min, float max) { + return new FloatRange(min, max); + } + + public static DoubleRange create(double min, double max) { + return new DoubleRange(min, max); + } + + public static StringRange create(String min, String max) { + return new StringRange(min, max); + } + + public static final class ByteRange implements Range { + + @ConvertColumn(index = 1) + private Byte min = Byte.MIN_VALUE; + + @ConvertColumn(index = 2) + private Byte max = Byte.MAX_VALUE; + + public ByteRange() { + } + + public ByteRange(Byte min, Byte max) { + if (min != null) this.min = min; + if (max != null) this.max = max; + } + + @Override + public Byte getMin() { + return min; + } + + @Override + public Byte getMax() { + return max; + } + + public void setMin(Byte min) { + if (min != null) this.min = min; + } + + public void setMax(Byte max) { + if (max != null) this.max = max; + } + + @Override + public boolean test(Byte t) { + if (max < min && max <= 0) return t >= min; + return t >= min && t <= max; + } + + @Override + public String toString() { + return "{min:" + min + ", max:" + max + "}"; + } + + } + + public static final class ShortRange implements Range { + + @ConvertColumn(index = 1) + private Short min = Short.MIN_VALUE; + + @ConvertColumn(index = 2) + private Short max = Short.MAX_VALUE; + + public ShortRange() { + } + + public ShortRange(Short min, Short max) { + if (min != null) this.min = min; + if (max != null) this.max = max; + } + + @Override + public Short getMin() { + return min; + } + + @Override + public Short getMax() { + return max; + } + + public void setMin(Short min) { + if (min != null) this.min = min; + } + + public void setMax(Short max) { + if (max != null) this.max = max; + } + + @Override + public boolean test(Short t) { + if (max < min && max <= 0) return t >= min; + return t >= min && t <= max; + } + + @Override + public String toString() { + return "{min:" + min + ", max:" + max + "}"; + } + } + + public static final class IntRange implements Range { + + @ConvertColumn(index = 1) + private Integer min = Integer.MIN_VALUE; + + @ConvertColumn(index = 2) + private Integer max = Integer.MAX_VALUE; + + public IntRange() { + } + + public IntRange(Integer min, Integer max) { + if (min != null) this.min = min; + if (max != null) this.max = max; + } + + @Override + public Integer getMin() { + return min; + } + + @Override + public Integer getMax() { + return max; + } + + public void setMin(Integer min) { + if (min != null) this.min = min; + } + + public void setMax(Integer max) { + if (max != null) this.max = max; + } + + @Override + public boolean test(Integer t) { + if (max < min && max <= 0) return t >= min; + return t >= min && t <= max; + } + + @Override + public String toString() { + return "{min:" + min + ", max:" + max + "}"; + } + } + + public static final class LongRange implements Range { + + @ConvertColumn(index = 1) + private Long min = Long.MIN_VALUE; + + @ConvertColumn(index = 2) + private Long max = Long.MAX_VALUE; + + public LongRange() { + } + + public LongRange(Long min, Long max) { + if (min != null) this.min = min; + if (max != null) this.max = max; + } + + public static LongRange todayRange() { + long min = Utility.midnight(); + return new LongRange(min, min + 24 * 60 * 60 * 1000 - 1); + } + + public static LongRange yesterdayRange() { + long min = Utility.midnight(System.currentTimeMillis() - 24 * 60 * 60 * 1000); + return new LongRange(min, min + 24 * 60 * 60 * 1000 - 1); + } + + @Override + public Long getMin() { + return min; + } + + @Override + public Long getMax() { + return max; + } + + public void setMin(Long min) { + if (min != null) this.min = min; + } + + public void setMax(Long max) { + if (max != null) this.max = max; + } + + @Override + public boolean test(Long t) { + if (max < min && max <= 0) return t >= min; + return t >= min && t <= max; + } + + @Override + public String toString() { + return "{min:" + min + ", max:" + max + "}"; + } + } + + public static final class FloatRange implements Range { + + @ConvertColumn(index = 1) + private Float min = Float.MIN_VALUE; + + @ConvertColumn(index = 2) + private Float max = Float.MAX_VALUE; + + public FloatRange() { + } + + public FloatRange(Float min, Float max) { + if (min != null) this.min = min; + if (max != null) this.max = max; + } + + @Override + public Float getMin() { + return min; + } + + @Override + public Float getMax() { + return max; + } + + public void setMin(Float min) { + if (min != null) this.min = min; + } + + public void setMax(Float max) { + if (max != null) this.max = max; + } + + @Override + public boolean test(Float t) { + if (max < min && max <= 0) return t >= min; + return t >= min && t <= max; + } + + @Override + public String toString() { + return "{min:" + min + ", max:" + max + "}"; + } + } + + public static final class DoubleRange implements Range { + + @ConvertColumn(index = 1) + private Double min = Double.MIN_VALUE; + + @ConvertColumn(index = 2) + private Double max = Double.MAX_VALUE; + + public DoubleRange() { + } + + public DoubleRange(Double min, Double max) { + if (min != null) this.min = min; + if (max != null) this.max = max; + } + + @Override + public Double getMin() { + return min; + } + + @Override + public Double getMax() { + return max; + } + + public void setMin(Double min) { + if (min != null) this.min = min; + } + + public void setMax(Double max) { + if (max != null) this.max = max; + } + + @Override + public boolean test(Double t) { + if (max < min && max <= 0) return t >= min; + return t >= min && t <= max; + } + + @Override + public String toString() { + return "{min:" + min + ", max:" + max + "}"; + } + } + + public static final class StringRange implements Range { + + @ConvertColumn(index = 1) + private String min = ""; + + @ConvertColumn(index = 2) + private String max = ""; + + public StringRange() { + } + + public StringRange(String min, String max) { + this.min = min; + this.max = max; + } + + @Override + public String getMin() { + return min; + } + + @Override + public String getMax() { + return max; + } + + public void setMin(String min) { + if (min != null) this.min = min; + } + + public void setMax(String max) { + if (max != null) this.max = max; + } + + @Override + public boolean test(String t) { + return t.compareTo(min) >= 0 && t.compareTo(max) <= 0; + } + + @Override + public String toString() { + return "{min:'" + min + "', max:'" + max + "'}"; + } + } +} diff --git a/src/org/redkale/source/SearchBean.java b/src/main/java/org/redkale/source/SearchBean.java similarity index 95% rename from src/org/redkale/source/SearchBean.java rename to src/main/java/org/redkale/source/SearchBean.java index 6ffdc765e..da09d38f1 100644 --- a/src/org/redkale/source/SearchBean.java +++ b/src/main/java/org/redkale/source/SearchBean.java @@ -1,398 +1,400 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.util.*; -import org.redkale.convert.ConvertColumn; -import org.redkale.convert.json.JsonConvert; - -/** - * SearchFilterBean用于搜索条件, 所有的FilterBean都必须可以转换成FilterNode
    - * - * 不被标记为@javax.persistence.Transient 的字段均视为过滤条件
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.4.0 - */ -public interface SearchBean extends java.io.Serializable { - - public static final String SEARCH_FILTER_NAME = "#search"; - - public static SearchSimpleBean create() { - return new SearchSimpleBean(); - } - - public static SearchSimpleBean create(String keyword, String... fields) { - return new SearchSimpleBean(keyword, fields); - } - - /** - * 需要搜索的index集合,无值则使用当前entity类 - * - * @return Class[] - */ - public Class[] searchClasses(); - - /** - * 搜索字段集合, 必须字段值 - * - * @return String[] - */ - public String[] searchFields(); - - /** - * 搜索关键字, 必须字段值 - * - * @return String - */ - public String searchKeyword(); - - /** - * 搜索分词器,可以为空 - * - * @return String - */ - public String searchAnalyzer(); - - /** - * 扩展的信息 - * - * @return Map - */ - default Map extras() { - return null; - } - - /** - * 高亮显示 - * - * @return SearchHighlightBean - */ - public SearchHighlightBean highlight(); - - public static interface SearchHighlightBean { - - public static SearchSimpleHighlightBean create() { - return new SearchSimpleHighlightBean(); - } - - public String preTag(); - - public String postTag(); - - public String boundaryLocale(); - - public int fragmentSize(); - - default int fragmentCount() { - return 1; - } - - default Map extras() { - return null; - } - } - - public static class SearchSimpleBean implements SearchBean { - - @ConvertColumn(index = 1) - @FilterColumn(ignore = true) - private Class[] classes; - - @ConvertColumn(index = 2) - @FilterColumn(ignore = true) - private String[] fields; - - @ConvertColumn(index = 3) - @FilterColumn(ignore = true) - private String keyword; - - @ConvertColumn(index = 4) - @FilterColumn(ignore = true) - private String analyzer; - - @ConvertColumn(index = 5) - @FilterColumn(ignore = true) - private SearchHighlightBean highlight; - - @ConvertColumn(index = 6) - @FilterColumn(ignore = true) - private Map extras; - - public SearchSimpleBean() { - } - - public SearchSimpleBean(String keyword, String... fields) { - this.keyword = keyword; - this.fields = fields; - if (fields == null || fields.length < 1) throw new IllegalArgumentException("fields is empty"); - } - - public SearchSimpleBean keyword(String keyword) { - this.keyword = keyword; - return this; - } - - public SearchSimpleBean analyzer(String analyzer) { - this.analyzer = analyzer; - return this; - } - - public SearchSimpleBean fields(String... fields) { - if (fields == null || fields.length < 1) throw new IllegalArgumentException("fields is empty"); - this.fields = fields; - return this; - } - - public SearchSimpleBean classes(Class[] classes) { - this.classes = classes; - return this; - } - - public SearchSimpleBean highlight(SearchHighlightBean highlight) { - this.highlight = highlight; - return this; - } - - public SearchSimpleBean extras(Map map) { - this.extras = map; - return this; - } - - public SearchSimpleBean extras(String key, Object value) { - if (this.extras == null) this.extras = new LinkedHashMap<>(); - this.extras.put(key, value); - return this; - } - - @Override - public String searchKeyword() { - return keyword; - } - - @Override - public Class[] searchClasses() { - return classes; - } - - @Override - public String[] searchFields() { - return fields; - } - - @Override - public Map extras() { - return extras; - } - - @Override - public String searchAnalyzer() { - return analyzer; - } - - @Override - public SearchHighlightBean highlight() { - return highlight; - } - - public Class[] getClasses() { - return classes; - } - - public void setClasses(Class[] classes) { - this.classes = classes; - } - - public String[] getFields() { - return fields; - } - - public void setFields(String[] fields) { - this.fields = fields; - } - - public String getKeyword() { - return keyword; - } - - public void setKeyword(String keyword) { - this.keyword = keyword; - } - - public String getAnalyzer() { - return analyzer; - } - - public void setAnalyzer(String analyzer) { - this.analyzer = analyzer; - } - - public SearchHighlightBean getHighlight() { - return highlight; - } - - public void setHighlight(SearchHighlightBean highlight) { - this.highlight = highlight; - } - - public Map getExtras() { - return extras; - } - - public void setExtras(Map extras) { - this.extras = extras; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - } - - public static class SearchSimpleHighlightBean implements SearchHighlightBean { - - @ConvertColumn(index = 1) - private String preTag; - - @ConvertColumn(index = 2) - private String postTag; - - @ConvertColumn(index = 3) - private String boundaryLocale; - - @ConvertColumn(index = 4) - private int fragmentSize = 100; - - @ConvertColumn(index = 5) - private int fragmentCount = 1; - - @ConvertColumn(index = 6) - @FilterColumn(ignore = true) - private Map extras; - - public SearchSimpleHighlightBean tag(String preTag, String postTag) { - this.preTag = preTag; - this.postTag = postTag; - return this; - } - - public SearchSimpleHighlightBean boundaryLocale(String boundaryLocale) { - this.boundaryLocale = boundaryLocale; - return this; - } - - public SearchSimpleHighlightBean fragmentSize(int fragmentSize) { - this.fragmentSize = fragmentSize; - return this; - } - - public SearchSimpleHighlightBean fragmentCount(int fragmentCount) { - this.fragmentCount = fragmentCount; - return this; - } - - public SearchSimpleHighlightBean extras(Map map) { - this.extras = map; - return this; - } - - public SearchSimpleHighlightBean extras(String key, Object value) { - if (this.extras == null) this.extras = new LinkedHashMap<>(); - this.extras.put(key, value); - return this; - } - - @Override - public Map extras() { - return extras; - } - - @Override - public String preTag() { - return preTag; - } - - @Override - public String postTag() { - return postTag; - } - - @Override - public String boundaryLocale() { - return boundaryLocale; - } - - @Override - public int fragmentSize() { - return fragmentSize; - } - - @Override - public int fragmentCount() { - return fragmentCount; - } - - public String getPreTag() { - return preTag; - } - - public void setPreTag(String preTag) { - this.preTag = preTag; - } - - public String getPostTag() { - return postTag; - } - - public void setPostTag(String postTag) { - this.postTag = postTag; - } - - public String getBoundaryLocale() { - return boundaryLocale; - } - - public void setBoundaryLocale(String boundaryLocale) { - this.boundaryLocale = boundaryLocale; - } - - public int getFragmentSize() { - return fragmentSize; - } - - public void setFragmentSize(int fragmentSize) { - this.fragmentSize = fragmentSize; - } - - public int getFragmentCount() { - return fragmentCount; - } - - public void setFragmentCount(int fragmentCount) { - this.fragmentCount = fragmentCount; - } - - public Map getExtras() { - return extras; - } - - public void setExtras(Map extras) { - this.extras = extras; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.util.*; +import org.redkale.convert.*; +import org.redkale.convert.json.JsonConvert; + +/** + * SearchFilterBean用于搜索条件, 所有的FilterBean都必须可以转换成FilterNode
    + * + * 不被标记为@javax.persistence.Transient 的字段均视为过滤条件
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.4.0 + */ +@ConvertImpl(SearchBean.SearchSimpleBean.class) +public interface SearchBean extends java.io.Serializable { + + public static final String SEARCH_FILTER_NAME = "#search"; + + public static SearchSimpleBean create() { + return new SearchSimpleBean(); + } + + public static SearchSimpleBean create(String keyword, String... fields) { + return new SearchSimpleBean(keyword, fields); + } + + /** + * 需要搜索的index集合,无值则使用当前entity类 + * + * @return Class[] + */ + public Class[] searchClasses(); + + /** + * 搜索字段集合, 必须字段值 + * + * @return String[] + */ + public String[] searchFields(); + + /** + * 搜索关键字, 必须字段值 + * + * @return String + */ + public String searchKeyword(); + + /** + * 搜索分词器,可以为空 + * + * @return String + */ + public String searchAnalyzer(); + + /** + * 扩展的信息 + * + * @return Map + */ + default Map extras() { + return null; + } + + /** + * 高亮显示 + * + * @return SearchHighlightBean + */ + public SearchHighlightBean highlight(); + + @ConvertImpl(SearchBean.SearchSimpleHighlightBean.class) + public static interface SearchHighlightBean { + + public static SearchSimpleHighlightBean create() { + return new SearchSimpleHighlightBean(); + } + + public String preTag(); + + public String postTag(); + + public String boundaryLocale(); + + public int fragmentSize(); + + default int fragmentCount() { + return 1; + } + + default Map extras() { + return null; + } + } + + public static class SearchSimpleBean implements SearchBean { + + @ConvertColumn(index = 1) + @FilterColumn(ignore = true) + private Class[] classes; + + @ConvertColumn(index = 2) + @FilterColumn(ignore = true) + private String[] fields; + + @ConvertColumn(index = 3) + @FilterColumn(ignore = true) + private String keyword; + + @ConvertColumn(index = 4) + @FilterColumn(ignore = true) + private String analyzer; + + @ConvertColumn(index = 5) + @FilterColumn(ignore = true) + private SearchHighlightBean highlight; + + @ConvertColumn(index = 6) + @FilterColumn(ignore = true) + private Map extras; + + public SearchSimpleBean() { + } + + public SearchSimpleBean(String keyword, String... fields) { + this.keyword = keyword; + this.fields = fields; + if (fields == null || fields.length < 1) throw new IllegalArgumentException("fields is empty"); + } + + public SearchSimpleBean keyword(String keyword) { + this.keyword = keyword; + return this; + } + + public SearchSimpleBean analyzer(String analyzer) { + this.analyzer = analyzer; + return this; + } + + public SearchSimpleBean fields(String... fields) { + if (fields == null || fields.length < 1) throw new IllegalArgumentException("fields is empty"); + this.fields = fields; + return this; + } + + public SearchSimpleBean classes(Class[] classes) { + this.classes = classes; + return this; + } + + public SearchSimpleBean highlight(SearchHighlightBean highlight) { + this.highlight = highlight; + return this; + } + + public SearchSimpleBean extras(Map map) { + this.extras = map; + return this; + } + + public SearchSimpleBean extras(String key, Object value) { + if (this.extras == null) this.extras = new LinkedHashMap<>(); + this.extras.put(key, value); + return this; + } + + @Override + public String searchKeyword() { + return keyword; + } + + @Override + public Class[] searchClasses() { + return classes; + } + + @Override + public String[] searchFields() { + return fields; + } + + @Override + public Map extras() { + return extras; + } + + @Override + public String searchAnalyzer() { + return analyzer; + } + + @Override + public SearchHighlightBean highlight() { + return highlight; + } + + public Class[] getClasses() { + return classes; + } + + public void setClasses(Class[] classes) { + this.classes = classes; + } + + public String[] getFields() { + return fields; + } + + public void setFields(String[] fields) { + this.fields = fields; + } + + public String getKeyword() { + return keyword; + } + + public void setKeyword(String keyword) { + this.keyword = keyword; + } + + public String getAnalyzer() { + return analyzer; + } + + public void setAnalyzer(String analyzer) { + this.analyzer = analyzer; + } + + public SearchHighlightBean getHighlight() { + return highlight; + } + + public void setHighlight(SearchHighlightBean highlight) { + this.highlight = highlight; + } + + public Map getExtras() { + return extras; + } + + public void setExtras(Map extras) { + this.extras = extras; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + } + + public static class SearchSimpleHighlightBean implements SearchHighlightBean { + + @ConvertColumn(index = 1) + private String preTag; + + @ConvertColumn(index = 2) + private String postTag; + + @ConvertColumn(index = 3) + private String boundaryLocale; + + @ConvertColumn(index = 4) + private int fragmentSize = 100; + + @ConvertColumn(index = 5) + private int fragmentCount = 1; + + @ConvertColumn(index = 6) + @FilterColumn(ignore = true) + private Map extras; + + public SearchSimpleHighlightBean tag(String preTag, String postTag) { + this.preTag = preTag; + this.postTag = postTag; + return this; + } + + public SearchSimpleHighlightBean boundaryLocale(String boundaryLocale) { + this.boundaryLocale = boundaryLocale; + return this; + } + + public SearchSimpleHighlightBean fragmentSize(int fragmentSize) { + this.fragmentSize = fragmentSize; + return this; + } + + public SearchSimpleHighlightBean fragmentCount(int fragmentCount) { + this.fragmentCount = fragmentCount; + return this; + } + + public SearchSimpleHighlightBean extras(Map map) { + this.extras = map; + return this; + } + + public SearchSimpleHighlightBean extras(String key, Object value) { + if (this.extras == null) this.extras = new LinkedHashMap<>(); + this.extras.put(key, value); + return this; + } + + @Override + public Map extras() { + return extras; + } + + @Override + public String preTag() { + return preTag; + } + + @Override + public String postTag() { + return postTag; + } + + @Override + public String boundaryLocale() { + return boundaryLocale; + } + + @Override + public int fragmentSize() { + return fragmentSize; + } + + @Override + public int fragmentCount() { + return fragmentCount; + } + + public String getPreTag() { + return preTag; + } + + public void setPreTag(String preTag) { + this.preTag = preTag; + } + + public String getPostTag() { + return postTag; + } + + public void setPostTag(String postTag) { + this.postTag = postTag; + } + + public String getBoundaryLocale() { + return boundaryLocale; + } + + public void setBoundaryLocale(String boundaryLocale) { + this.boundaryLocale = boundaryLocale; + } + + public int getFragmentSize() { + return fragmentSize; + } + + public void setFragmentSize(int fragmentSize) { + this.fragmentSize = fragmentSize; + } + + public int getFragmentCount() { + return fragmentCount; + } + + public void setFragmentCount(int fragmentCount) { + this.fragmentCount = fragmentCount; + } + + public Map getExtras() { + return extras; + } + + public void setExtras(Map extras) { + this.extras = extras; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + } + +} diff --git a/src/org/redkale/source/SearchColumn.java b/src/main/java/org/redkale/source/SearchColumn.java similarity index 95% rename from src/org/redkale/source/SearchColumn.java rename to src/main/java/org/redkale/source/SearchColumn.java index d6670865b..5c93e5e13 100644 --- a/src/org/redkale/source/SearchColumn.java +++ b/src/main/java/org/redkale/source/SearchColumn.java @@ -1,87 +1,87 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.lang.annotation.Target; -import java.lang.annotation.Retention; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * - * 搜索引擎的数据Entity依附在setter、getter方法、字段进行简单的配置
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.4.0 - */ -@Target({FIELD}) -@Retention(RUNTIME) -public @interface SearchColumn { - - //高亮显示参数 - public static class HighLights { - - public static final String HIGHLIGHT_NAME_ID = "#[id]"; - - public static final String HIGHLIGHT_NAME_INDEX = "#[index]"; - - } - - /** - * 是否全文搜索 - * - * @return boolean - */ - boolean text() default false; - - /** - * 高亮对应的Column.name字段名,被标记的字段为虚拟字段,不会映射表中的字段
    - * 被标记的字段必须是String类型
    - * 有值时,ignore必须为true - * - * @return String - */ - String highlight() default ""; - - /** - * 解析/存储时是否屏蔽该字段 - * - * @return boolean - */ - boolean ignore() default false; - - /** - * 设置索引参数 - * - * @return String - */ - String options() default ""; - - /** - * 内容是否html格式 - * - * @return boolean - */ - boolean html() default false; - - /** - * 设置索引分词器 - * - * @return String - */ - String analyzer() default ""; - - /** - * 设置搜索索引分词器 - * - * @return String - */ - String searchAnalyzer() default ""; - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.lang.annotation.Target; +import java.lang.annotation.Retention; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * + * 搜索引擎的数据Entity依附在setter、getter方法、字段进行简单的配置
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.4.0 + */ +@Target({FIELD}) +@Retention(RUNTIME) +public @interface SearchColumn { + + //高亮显示参数 + public static class HighLights { + + public static final String HIGHLIGHT_NAME_ID = "#[id]"; + + public static final String HIGHLIGHT_NAME_INDEX = "#[index]"; + + } + + /** + * 是否全文搜索 + * + * @return boolean + */ + boolean text() default false; + + /** + * 高亮对应的Column.name字段名,被标记的字段为虚拟字段,不会映射表中的字段
    + * 被标记的字段必须是String类型
    + * 有值时,ignore必须为true + * + * @return String + */ + String highlight() default ""; + + /** + * 解析/存储时是否屏蔽该字段 + * + * @return boolean + */ + boolean ignore() default false; + + /** + * 设置索引参数 + * + * @return String + */ + String options() default ""; + + /** + * 内容是否html格式 + * + * @return boolean + */ + boolean html() default false; + + /** + * 设置索引分词器 + * + * @return String + */ + String analyzer() default ""; + + /** + * 设置搜索索引分词器 + * + * @return String + */ + String searchAnalyzer() default ""; + +} diff --git a/src/org/redkale/source/SearchSource.java b/src/main/java/org/redkale/source/SearchSource.java similarity index 96% rename from src/org/redkale/source/SearchSource.java rename to src/main/java/org/redkale/source/SearchSource.java index f32b7d15e..d6b178101 100644 --- a/src/org/redkale/source/SearchSource.java +++ b/src/main/java/org/redkale/source/SearchSource.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.util.concurrent.CompletableFuture; - -/** - * - * 搜索引擎的数据源, 接口与DataSource基本一致。
    - * 返回类型为CompletableFuture的接口为异步接口 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.4.0 - */ -public interface SearchSource extends DataSource { - - public int updateMapping(final Class clazz); - - public CompletableFuture updateMappingAsync(final Class clazz); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.util.concurrent.CompletableFuture; + +/** + * + * 搜索引擎的数据源, 接口与DataSource基本一致。
    + * 返回类型为CompletableFuture的接口为异步接口 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.4.0 + */ +public interface SearchSource extends DataSource { + + public int updateMapping(final Class clazz); + + public CompletableFuture updateMappingAsync(final Class clazz); +} diff --git a/src/org/redkale/source/SourceConvert.java b/src/main/java/org/redkale/source/SourceConvert.java similarity index 96% rename from src/org/redkale/source/SourceConvert.java rename to src/main/java/org/redkale/source/SourceConvert.java index 3c7a4b7ab..60ef324d7 100644 --- a/src/org/redkale/source/SourceConvert.java +++ b/src/main/java/org/redkale/source/SourceConvert.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 用于定制Source操作JSON字段的转换策略。
    - * 只能依附在Entity类的静态无参数方法上, 且返回值必须是JsonConvert。
    - * 注意: 如果一个类有两个静态方法标记为@SourceConvert, 框架只会识别第一个。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -public @interface SourceConvert { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 用于定制Source操作JSON字段的转换策略。
    + * 只能依附在Entity类的静态无参数方法上, 且返回值必须是JsonConvert。
    + * 注意: 如果一个类有两个静态方法标记为@SourceConvert, 框架只会识别第一个。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface SourceConvert { + +} diff --git a/src/org/redkale/source/VirtualEntity.java b/src/main/java/org/redkale/source/VirtualEntity.java similarity index 96% rename from src/org/redkale/source/VirtualEntity.java rename to src/main/java/org/redkale/source/VirtualEntity.java index 344b0748c..b27ca642d 100644 --- a/src/org/redkale/source/VirtualEntity.java +++ b/src/main/java/org/redkale/source/VirtualEntity.java @@ -1,53 +1,53 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.*; - -/** - * VirtualEntity表示虚拟的数据实体类, 通常Entity都会映射到数据库中的某个表,而标记为@VirtualEntity的Entity类只存在EntityCache中 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target(TYPE) -@Retention(RUNTIME) -public @interface VirtualEntity { - - /** - * DataSource是否直接返回对象的真实引用, 而不是copy一份 - * - * @return boolean - */ - boolean direct() default false; - - /** - * 初始化时数据的加载器 - * - * @return Class - */ - Class>> loader() default DefaultFunctionLoader.class; - - /** - * 默认全量加载器 - * - */ - public static class DefaultFunctionLoader implements BiFunction> { - - @Override - public CompletableFuture apply(DataSource source, EntityInfo info) { - return null; - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.source; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.*; + +/** + * VirtualEntity表示虚拟的数据实体类, 通常Entity都会映射到数据库中的某个表,而标记为@VirtualEntity的Entity类只存在EntityCache中 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target(TYPE) +@Retention(RUNTIME) +public @interface VirtualEntity { + + /** + * DataSource是否直接返回对象的真实引用, 而不是copy一份 + * + * @return boolean + */ + boolean direct() default false; + + /** + * 初始化时数据的加载器 + * + * @return Class + */ + Class>> loader() default DefaultFunctionLoader.class; + + /** + * 默认全量加载器 + * + */ + public static class DefaultFunctionLoader implements BiFunction> { + + @Override + public CompletableFuture apply(DataSource source, EntityInfo info) { + return null; + } + } +} diff --git a/src/org/redkale/source/package-info.java b/src/main/java/org/redkale/source/package-info.java similarity index 95% rename from src/org/redkale/source/package-info.java rename to src/main/java/org/redkale/source/package-info.java index 47fa2d01a..0fdd99524 100644 --- a/src/org/redkale/source/package-info.java +++ b/src/main/java/org/redkale/source/package-info.java @@ -1,4 +1,4 @@ -/** - * 数据源(数据库、缓存)操作包 - */ -package org.redkale.source; +/** + * 数据源(数据库、缓存)操作包 + */ +package org.redkale.source; diff --git a/src/main/java/org/redkale/util/AnonymousUnsafe.java b/src/main/java/org/redkale/util/AnonymousUnsafe.java new file mode 100644 index 000000000..a744baa7d --- /dev/null +++ b/src/main/java/org/redkale/util/AnonymousUnsafe.java @@ -0,0 +1,443 @@ +//package org.redkale.util; +// +//import java.lang.reflect.Field; +//import java.nio.ByteBuffer; +// +//public class AnonymousUnsafe implements Unsafe { +// +// private final sun.misc.Unsafe unsafe; +// +// public AnonymousUnsafe(Object obj) { +// this.unsafe = (sun.misc.Unsafe) obj; +// } +// +// @Override +// public int getInt(Object o, long offset) { +// return unsafe.getInt(o, offset); +// } +// +// @Override +// public void putInt(Object o, long offset, int x) { +// unsafe.putInt(o, offset, x); +// } +// +// @Override +// public Object getObject(Object o, long offset) { +// return unsafe.getObject(o, offset); +// } +// +// @Override +// public void putObject(Object o, long offset, Object x) { +// unsafe.putObject(o, offset, x); +// } +// +// @Override +// public boolean getBoolean(Object o, long offset) { +// return unsafe.getBoolean(o, offset); +// } +// +// @Override +// public void putBoolean(Object o, long offset, boolean x) { +// unsafe.putBoolean(o, offset, x); +// } +// +// @Override +// public byte getByte(Object o, long offset) { +// return unsafe.getByte(o, offset); +// } +// +// @Override +// public void putByte(Object o, long offset, byte x) { +// unsafe.putByte(o, offset, x); +// } +// +// @Override +// public short getShort(Object o, long offset) { +// return unsafe.getShort(o, offset); +// } +// +// @Override +// public void putShort(Object o, long offset, short x) { +// unsafe.putShort(o, offset, x); +// } +// +// @Override +// public char getChar(Object o, long offset) { +// return unsafe.getChar(o, offset); +// } +// +// @Override +// public void putChar(Object o, long offset, char x) { +// unsafe.putChar(o, offset, x); +// } +// +// @Override +// public long getLong(Object o, long offset) { +// return unsafe.getLong(o, offset); +// } +// +// @Override +// public void putLong(Object o, long offset, long x) { +// unsafe.putLong(o, offset, x); +// } +// +// @Override +// public float getFloat(Object o, long offset) { +// return unsafe.getFloat(o, offset); +// } +// +// @Override +// public void putFloat(Object o, long offset, float x) { +// unsafe.putFloat(o, offset, x); +// } +// +// @Override +// public double getDouble(Object o, long offset) { +// return unsafe.getDouble(o, offset); +// } +// +// @Override +// public void putDouble(Object o, long offset, double x) { +// unsafe.putDouble(o, offset, x); +// } +// +// @Override +// public byte getByte(long address) { +// return unsafe.getByte(address); +// } +// +// @Override +// public void putByte(long address, byte x) { +// unsafe.putByte(address, x); +// } +// +// @Override +// public short getShort(long address) { +// return unsafe.getShort(address); +// } +// +// @Override +// public void putShort(long address, short x) { +// unsafe.putShort(address, x); +// } +// +// @Override +// public char getChar(long address) { +// return unsafe.getChar(address); +// } +// +// @Override +// public void putChar(long address, char x) { +// unsafe.putChar(address, x); +// } +// +// @Override +// public int getInt(long address) { +// return unsafe.getInt(address); +// } +// +// @Override +// public void putInt(long address, int x) { +// unsafe.putInt(address, x); +// } +// +// @Override +// public long getLong(long address) { +// return unsafe.getLong(address); +// } +// +// @Override +// public void putLong(long address, long x) { +// unsafe.putLong(address, x); +// } +// +// @Override +// public float getFloat(long address) { +// return unsafe.getFloat(address); +// } +// +// @Override +// public void putFloat(long address, float x) { +// unsafe.putFloat(address, x); +// } +// +// @Override +// public double getDouble(long address) { +// return unsafe.getDouble(address); +// } +// +// @Override +// public void putDouble(long address, double x) { +// unsafe.putDouble(address, x); +// } +// +// @Override +// public long getAddress(long address) { +// return unsafe.getAddress(address); +// } +// +// @Override +// public void putAddress(long address, long x) { +// unsafe.putAddress(address, x); +// } +// +// @Override +// public long allocateMemory(long bytes) { +// return unsafe.allocateMemory(bytes); +// } +// +// @Override +// public long reallocateMemory(long address, long bytes) { +// return unsafe.reallocateMemory(address, bytes); +// } +// +// @Override +// public void setMemory(Object o, long offset, long bytes, byte value) { +// unsafe.setMemory(o, offset, bytes, value); +// } +// +// @Override +// public void setMemory(long address, long bytes, byte value) { +// unsafe.setMemory(address, bytes, value); +// } +// +// @Override +// public void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes) { +// unsafe.copyMemory(srcBase, srcOffset, destBase, destOffset, bytes); +// } +// +// @Override +// public void copyMemory(long srcAddress, long destAddress, long bytes) { +// unsafe.copyMemory(srcAddress, destAddress, bytes); +// } +// +// @Override +// public void freeMemory(long address) { +// unsafe.freeMemory(address); +// } +// +// @Override +// public long objectFieldOffset(Field f) { +// return unsafe.objectFieldOffset(f); +// } +// +// @Override +// public long staticFieldOffset(Field f) { +// return unsafe.staticFieldOffset(f); +// } +// +// @Override +// public Object staticFieldBase(Field f) { +// return unsafe.staticFieldBase(f); +// } +// +// @Override +// public int arrayBaseOffset(Class arrayClass) { +// return unsafe.arrayBaseOffset(arrayClass); +// } +// +// @Override +// public int arrayIndexScale(Class arrayClass) { +// return unsafe.arrayIndexScale(arrayClass); +// } +// +// @Override +// public int addressSize() { +// return unsafe.addressSize(); +// } +// +// @Override +// public int pageSize() { +// return unsafe.pageSize(); +// } +// +// @Override +// public Object allocateInstance(Class cls) throws InstantiationException { +// return unsafe.allocateInstance(cls); +// } +// +// @Override +// public void throwException(Throwable ee) { +// unsafe.throwException(ee); +// } +// +// @Override +// public boolean compareAndSwapObject(Object o, long offset, Object expected, Object x) { +// return unsafe.compareAndSwapObject(o, offset, expected, x); +// } +// +// @Override +// public boolean compareAndSwapInt(Object o, long offset, int expected, int x) { +// return unsafe.compareAndSwapInt(o, offset, expected, x); +// } +// +// @Override +// public boolean compareAndSwapLong(Object o, long offset, long expected, long x) { +// return unsafe.compareAndSwapLong(o, offset, expected, x); +// } +// +// @Override +// public Object getObjectVolatile(Object o, long offset) { +// return unsafe.getObjectVolatile(o, offset); +// } +// +// @Override +// public void putObjectVolatile(Object o, long offset, Object x) { +// unsafe.putObjectVolatile(o, offset, x); +// } +// +// @Override +// public int getIntVolatile(Object o, long offset) { +// return unsafe.getIntVolatile(o, offset); +// } +// +// @Override +// public void putIntVolatile(Object o, long offset, int x) { +// unsafe.putIntVolatile(o, offset, x); +// } +// +// @Override +// public boolean getBooleanVolatile(Object o, long offset) { +// return unsafe.getBooleanVolatile(o, offset); +// } +// +// @Override +// public void putBooleanVolatile(Object o, long offset, boolean x) { +// unsafe.putBooleanVolatile(o, offset, x); +// } +// +// @Override +// public byte getByteVolatile(Object o, long offset) { +// return unsafe.getByteVolatile(o, offset); +// } +// +// @Override +// public void putByteVolatile(Object o, long offset, byte x) { +// unsafe.putByteVolatile(o, offset, x); +// } +// +// @Override +// public short getShortVolatile(Object o, long offset) { +// return unsafe.getShortVolatile(o, offset); +// } +// +// @Override +// public void putShortVolatile(Object o, long offset, short x) { +// unsafe.putShortVolatile(o, offset, x); +// } +// +// @Override +// public char getCharVolatile(Object o, long offset) { +// return unsafe.getCharVolatile(o, offset); +// } +// +// @Override +// public void putCharVolatile(Object o, long offset, char x) { +// unsafe.putCharVolatile(o, offset, x); +// } +// +// @Override +// public long getLongVolatile(Object o, long offset) { +// return unsafe.getLongVolatile(o, offset); +// } +// +// @Override +// public void putLongVolatile(Object o, long offset, long x) { +// unsafe.putLongVolatile(o, offset, x); +// } +// +// @Override +// public float getFloatVolatile(Object o, long offset) { +// return unsafe.getFloatVolatile(o, offset); +// } +// +// @Override +// public void putFloatVolatile(Object o, long offset, float x) { +// unsafe.putFloatVolatile(o, offset, x); +// } +// +// @Override +// public double getDoubleVolatile(Object o, long offset) { +// return unsafe.getDoubleVolatile(o, offset); +// } +// +// @Override +// public void putDoubleVolatile(Object o, long offset, double x) { +// unsafe.putDoubleVolatile(o, offset, x); +// } +// +// @Override +// public void putOrderedObject(Object o, long offset, Object x) { +// unsafe.putOrderedObject(o, offset, x); +// } +// +// @Override +// public void putOrderedInt(Object o, long offset, int x) { +// unsafe.putOrderedInt(o, offset, x); +// } +// +// @Override +// public void putOrderedLong(Object o, long offset, long x) { +// unsafe.putOrderedLong(o, offset, x); +// } +// +// @Override +// public void unpark(Object thread) { +// unsafe.unpark(thread); +// } +// +// @Override +// public void park(boolean isAbsolute, long time) { +// unsafe.park(isAbsolute, time); +// } +// +// @Override +// public int getLoadAverage(double[] loadavg, int nelems) { +// return unsafe.getLoadAverage(loadavg, nelems); +// } +// +// @Override +// public int getAndAddInt(Object o, long offset, int delta) { +// return unsafe.getAndAddInt(o, offset, delta); +// } +// +// @Override +// public long getAndAddLong(Object o, long offset, long delta) { +// return unsafe.getAndAddLong(o, offset, delta); +// } +// +// @Override +// public int getAndSetInt(Object o, long offset, int newValue) { +// return unsafe.getAndSetInt(o, offset, newValue); +// } +// +// @Override +// public long getAndSetLong(Object o, long offset, long newValue) { +// return unsafe.getAndSetLong(o, offset, newValue); +// } +// +// @Override +// public Object getAndSetObject(Object o, long offset, Object newValue) { +// return unsafe.getAndSetObject(o, offset, newValue); +// } +// +// @Override +// public void loadFence() { +// unsafe.loadFence(); +// } +// +// @Override +// public void storeFence() { +// unsafe.storeFence(); +// } +// +// @Override +// public void fullFence() { +// unsafe.fullFence(); +// } +// +// @Override +// public void invokeCleaner(ByteBuffer directBuffer) { +// unsafe.invokeCleaner(directBuffer); +// } +//} diff --git a/src/org/redkale/util/AnyValue.java b/src/main/java/org/redkale/util/AnyValue.java similarity index 86% rename from src/org/redkale/util/AnyValue.java rename to src/main/java/org/redkale/util/AnyValue.java index d364873b2..794b05293 100644 --- a/src/org/redkale/util/AnyValue.java +++ b/src/main/java/org/redkale/util/AnyValue.java @@ -1,706 +1,778 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.reflect.Array; -import java.util.*; -import java.util.function.*; -import org.redkale.convert.ConvertDisabled; - -/** - * 该类提供类似JSONObject的数据结构,主要用于读取xml配置文件和http-header存储 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public abstract class AnyValue { - - /** - * 可读写的AnyValue默认实现类 - * - * @author zhangjx - */ - @SuppressWarnings("unchecked") - public static final class DefaultAnyValue extends AnyValue { - - /** - * 区分name大小写的比较策略 - * - */ - public static final BiPredicate EQUALS = (name1, name2) -> name1.equals(name2); - - /** - * 不区分name大小写的比较策略 - */ - public static final BiPredicate EQUALSIGNORE = (name1, name2) -> name1.equalsIgnoreCase(name2); - - private boolean ignoreCase; - - private BiPredicate predicate; - - private Entry[] stringEntrys = new Entry[0]; - - private Entry[] anyEntrys = new Entry[0]; - - /** - * 创建空的DefaultAnyValue对象 - * - * @return DefaultAnyValue对象 - */ - public static final DefaultAnyValue create() { - return new DefaultAnyValue(); - } - - /** - * 创建含name-value值的DefaultAnyValue对象 - * - * @param name name - * @param value value值 - * - * @return DefaultAnyValue对象 - */ - public static final DefaultAnyValue create(String name, Number value) { - DefaultAnyValue conf = new DefaultAnyValue(); - conf.addValue(name, value); - return conf; - } - - /** - * 创建含name-value值的DefaultAnyValue对象 - * - * @param name name - * @param value value值 - * - * @return DefaultAnyValue对象 - */ - public static final DefaultAnyValue create(String name, String value) { - DefaultAnyValue conf = new DefaultAnyValue(); - conf.addValue(name, value); - return conf; - } - - /** - * 创建含name-value值的DefaultAnyValue对象 - * - * @param name name - * @param value value值 - * - * @return DefaultAnyValue对象 - */ - public static final DefaultAnyValue create(String name, AnyValue value) { - DefaultAnyValue conf = new DefaultAnyValue(); - conf.addValue(name, value); - return conf; - } - - /** - * 创建一个区分大小写比较策略的DefaultAnyValue对象 - * - */ - public DefaultAnyValue() { - this(false); - } - - /** - * 创建DefaultAnyValue对象 - * - * @param ignoreCase name是否不区分大小写 - */ - public DefaultAnyValue(boolean ignoreCase) { - this.ignoreCase = ignoreCase; - this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS; - } - - /** - * 创建共享此内容的DefaultAnyValue对象 - * - * @return DefaultAnyValue对象 - */ - public DefaultAnyValue duplicate() { - DefaultAnyValue rs = new DefaultAnyValue(this.ignoreCase); - rs.stringEntrys = this.stringEntrys; - rs.anyEntrys = this.anyEntrys; - return rs; - } - - //去重 - public DefaultAnyValue addAllStringSet(final AnyValue av) { - if (av == null) return this; - final Entry[] strings = av.getStringEntrys(); - if (strings == null) return this; - for (Entry en : strings) { - if (getValue(en.name) == null) this.addValue(en.name, en.value); - } - return this; - } - - public DefaultAnyValue addAll(final AnyValue av) { - if (av == null) return this; - if (av instanceof DefaultAnyValue) { - final DefaultAnyValue adv = (DefaultAnyValue) av; - if (adv.stringEntrys != null) { - for (Entry en : adv.stringEntrys) { - this.addValue(en.name, en.value); - } - } - if (adv.anyEntrys != null) { - for (Entry en : adv.anyEntrys) { - this.addValue(en.name, en.value); - } - } - } else { - final Entry[] strings = av.getStringEntrys(); - if (strings != null) { - for (Entry en : strings) { - this.addValue(en.name, en.value); - } - } - final Entry[] anys = av.getAnyEntrys(); - if (anys != null) { - for (Entry en : anys) { - this.addValue(en.name, en.value); - } - } - } - return this; - } - - public DefaultAnyValue setAll(final AnyValue av) { - if (av == null) return this; - if (av instanceof DefaultAnyValue) { - final DefaultAnyValue adv = (DefaultAnyValue) av; - if (adv.stringEntrys != null) { - for (Entry en : adv.stringEntrys) { - this.setValue(en.name, en.value); - } - } - if (adv.anyEntrys != null) { - for (Entry en : adv.anyEntrys) { - this.setValue(en.name, en.value); - } - } - } else { - final Entry[] strings = av.getStringEntrys(); - if (strings != null) { - for (Entry en : strings) { - this.setValue(en.name, en.value); - } - } - final Entry[] anys = av.getAnyEntrys(); - if (anys != null) { - for (Entry en : anys) { - this.setValue(en.name, en.value); - } - } - } - return this; - } - - @Override - public void forEach(BiConsumer stringConsumer) { - forEach(stringConsumer, null); - } - - @Override - public void forEach(BiConsumer stringConsumer, BiConsumer anyConsumer) { - if (stringConsumer != null) { - for (Entry en : stringEntrys) { - stringConsumer.accept(en.name, en.value); - } - } - if (anyConsumer != null) { - for (Entry en : (Entry[]) anyEntrys) { - anyConsumer.accept(en.name, en.value); - } - } - } - - @Override - public Entry[] getStringEntrys() { - return stringEntrys; - } - - public void setStringEntrys(Entry[] stringEntrys) { - this.stringEntrys = stringEntrys; - } - - @Override - public Entry[] getAnyEntrys() { - return (Entry[]) (Entry[]) anyEntrys; - } - - public void setAnyEntrys(Entry[] anyEntrys) { - this.anyEntrys = anyEntrys; - } - - public boolean isIgnoreCase() { - return ignoreCase; - } - - public void setIgnoreCase(boolean ignoreCase) { - this.ignoreCase = ignoreCase; - if (this.predicate == null) { - this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS; - } - } - - @Override - @ConvertDisabled - public String[] getNames() { - Set set = new LinkedHashSet<>(); - for (Entry en : this.stringEntrys) { - set.add(en.name); - } - for (Entry en : this.anyEntrys) { - set.add(en.name); - } - return set.toArray(new String[set.size()]); - } - - @Override - public String[] getValues(String... names) { - return Entry.getValues(this.predicate, String.class, this.stringEntrys, names); - } - - @Override - public AnyValue[] getAnyValues(String... names) { - return Entry.getValues(this.predicate, DefaultAnyValue.class, this.anyEntrys, names); - } - - @Override - public String toString() { - return toString(0); - } - - public DefaultAnyValue clear() { - if (this.stringEntrys != null && this.stringEntrys.length > 0) this.stringEntrys = new Entry[0]; - if (this.anyEntrys != null && this.anyEntrys.length > 0) this.anyEntrys = new Entry[0]; - return this; - } - - public DefaultAnyValue setValue(String name, String value) { - if (name == null) return this; - if (getValue(name) == null) { - this.addValue(name, value); - } else { - for (Entry en : this.stringEntrys) { - if (predicate.test(en.name, name)) { - en.value = value; - return this; - } - } - } - return this; - } - - public DefaultAnyValue setValue(String name, AnyValue value) { - if (name == null) return this; - if (getValue(name) == null) { - this.addValue(name, value); - } else { - for (Entry en : this.anyEntrys) { - if (predicate.test(en.name, name)) { - en.value = (DefaultAnyValue) value; - return this; - } - } - } - return this; - } - - public DefaultAnyValue put(String name, boolean value) { - return addValue(name, String.valueOf(value)); - } - - public DefaultAnyValue put(String name, Number value) { - return addValue(name, String.valueOf(value)); - } - - public DefaultAnyValue put(String name, String value) { - this.stringEntrys = Utility.append(this.stringEntrys, new Entry(name, value)); - return this; - } - - public DefaultAnyValue addValue(String name, boolean value) { - return addValue(name, String.valueOf(value)); - } - - public DefaultAnyValue addValue(String name, Number value) { - return addValue(name, String.valueOf(value)); - } - - public DefaultAnyValue addValue(String name, String value) { - this.stringEntrys = Utility.append(this.stringEntrys, new Entry(name, value)); - return this; - } - - public DefaultAnyValue addValue(String name, AnyValue value) { - if (name == null || value == null) return this; - this.anyEntrys = Utility.append(this.anyEntrys, new Entry(name, value)); - return this; - } - - public DefaultAnyValue removeValue(String name, AnyValue value) { - if (name == null || value == null || this.anyEntrys == null) return this; - this.anyEntrys = Utility.remove(this.anyEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value)); - return this; - } - - public DefaultAnyValue removeValue(String name, String value) { - if (name == null || value == null || this.stringEntrys == null) return this; - this.stringEntrys = Utility.remove(this.stringEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value)); - return this; - } - - @Override - public AnyValue getAnyValue(String name) { - for (Entry en : this.anyEntrys) { - if (predicate.test(en.name, name)) { - return en.value; - } - } - return null; - } - - @Override - public String get(String name) { - return getValue(name); - } - - @Override - public String getValue(String name) { - for (Entry en : this.stringEntrys) { - if (predicate.test(en.name, name)) { - return en.value; - } - } - return null; - } - - @Override - public String[] getValues(String name) { - return Entry.getValues(this.predicate, String.class, this.stringEntrys, name); - } - - @Override - public AnyValue[] getAnyValues(String name) { - return Entry.getValues(this.predicate, DefaultAnyValue.class, this.anyEntrys, name); - } - - } - - public static final class Entry { - - public final String name; - - T value; - - @ConstructorParameters({"name", "value"}) - public Entry(String name0, T value0) { - this.name = name0; - this.value = value0; - } - - public String getName() { - return name; - } - - public T getValue() { - return value; - } - - static T[] getValues(BiPredicate comparison, Class clazz, Entry[] entitys, String name) { - int len = 0; - for (Entry en : entitys) { - if (comparison.test(en.name, name)) { - ++len; - } - } - if (len == 0) return (T[]) Array.newInstance(clazz, len); - T[] rs = (T[]) Array.newInstance(clazz, len); - int i = 0; - for (Entry en : entitys) { - if (comparison.test(en.name, name)) { - rs[i++] = en.value; - } - } - return rs; - } - - static T[] getValues(BiPredicate comparison, Class clazz, Entry[] entitys, String... names) { - int len = 0; - for (Entry en : entitys) { - for (String name : names) { - if (comparison.test(en.name, name)) { - ++len; - break; - } - } - } - if (len == 0) return (T[]) Array.newInstance(clazz, len); - T[] rs = (T[]) Array.newInstance(clazz, len); - int i = 0; - for (Entry en : entitys) { - for (String name : names) { - if (comparison.test(en.name, name)) { - rs[i++] = en.value; - break; - } - } - } - return rs; - } - } - - public static DefaultAnyValue create() { - return new DefaultAnyValue(); - } - - public String toString(int indent) { //indent: 缩进长度 - if (indent < 0) indent = 0; - char[] chars = new char[indent]; - Arrays.fill(chars, ' '); - final String space = new String(chars); - StringBuilder sb = new StringBuilder(); - sb.append("{\r\n"); - for (Entry en : getStringEntrys()) { - sb.append(space).append(" '").append(en.name).append("': '").append(en.value).append("',\r\n"); - } - for (Entry en : getAnyEntrys()) { - sb.append(space).append(" '").append(en.name).append("': '").append(en.value.toString(indent + 4)).append("',\r\n"); - } - sb.append(space).append('}'); - return sb.toString(); - } - - public abstract void forEach(BiConsumer stringConsumer); - - public abstract void forEach(BiConsumer stringConsumer, BiConsumer anyConsumer); - - public abstract Entry[] getStringEntrys(); - - public abstract Entry[] getAnyEntrys(); - - public abstract String[] getNames(); - - public abstract String[] getValues(String name); - - public abstract String[] getValues(String... names); - - public abstract AnyValue[] getAnyValues(String name); - - public abstract AnyValue[] getAnyValues(String... names); - - public abstract AnyValue getAnyValue(String name); - - public abstract String getValue(String name); - - public abstract String get(String name); - - public boolean getBoolValue(String name) { - return Boolean.parseBoolean(getValue(name)); - } - - public boolean getBoolValue(String name, boolean defaultValue) { - String value = getValue(name); - return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value); - } - - public byte getByteValue(String name) { - return Byte.parseByte(getValue(name)); - } - - public byte getByteValue(String name, byte defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Byte.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public byte getByteValue(int radix, String name, byte defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Byte.decode(value) : Byte.parseByte(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public char getCharValue(String name) { - return getValue(name).charAt(0); - } - - public char getCharValue(String name, char defaultValue) { - String value = getValue(name); - return value == null || value.length() == 0 ? defaultValue : value.charAt(0); - } - - public short getShortValue(String name) { - return Short.decode(getValue(name)); - } - - public short getShortValue(String name, short defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Short.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public short getShortValue(int radix, String name, short defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getIntValue(String name) { - return Integer.decode(getValue(name)); - } - - public int getIntValue(String name, int defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Integer.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public int getIntValue(int radix, String name, int defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public long getLongValue(String name) { - return Long.decode(getValue(name)); - } - - public long getLongValue(String name, long defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Long.decode(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public long getLongValue(int radix, String name, long defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix)); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public float getFloatValue(String name) { - return Float.parseFloat(getValue(name)); - } - - public float getFloatValue(String name, float defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Float.parseFloat(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public double getDoubleValue(String name) { - return Double.parseDouble(getValue(name)); - } - - public double getDoubleValue(String name, double defaultValue) { - String value = getValue(name); - if (value == null || value.length() == 0) return defaultValue; - try { - return Double.parseDouble(value); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public String getValue(String name, String defaultValue) { - String value = getValue(name); - return value == null ? defaultValue : value; - } - - public String getOrDefault(String name, String defaultValue) { - String value = getValue(name); - return value == null ? defaultValue : value; - } - - @Override - public boolean equals(Object other) { - if (!(other instanceof AnyValue)) return false; - AnyValue conf = (AnyValue) other; - if (!equals(this.getStringEntrys(), conf.getStringEntrys())) return false; - return equals(this.getAnyEntrys(), conf.getAnyEntrys()); - } - - private static boolean equals(Entry[] entry1, Entry[] entry2) { - if ((entry1 == null || entry1.length == 0) && (entry2 == null || entry2.length == 0)) return true; - if (entry1.length != entry2.length) return false; - for (int i = 0; i < entry1.length; i++) { - if (!entry1[i].name.equals(entry2[i].name)) return false; - if (!entry1[i].getValue().equals(entry2[i].getValue())) return false; - } - return true; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 19 * hash + Arrays.deepHashCode(this.getStringEntrys()); - hash = 19 * hash + Arrays.deepHashCode(this.getAnyEntrys()); - return hash; - } - - public String toXML(String rootName) { - return toXMLString(new StringBuilder("\r\n\r\n"), rootName, this, 0).toString(); - } - - protected static StringBuilder toXMLString(StringBuilder sb, String nodeName, AnyValue conf, int indent) { //indent: 缩进长度 - if (indent < 0) indent = 0; - char[] chars = new char[indent]; - Arrays.fill(chars, ' '); - final String space = new String(chars); - Entry[] anys = conf.getAnyEntrys(); - sb.append(space).append('<').append(nodeName); - for (Entry en : conf.getStringEntrys()) { - sb.append(' ').append(en.name).append("=\"").append(en.value).append("\""); - } - if (anys == null || anys.length == 0) return sb.append("/>\r\n\r\n"); - sb.append(">\r\n\r\n"); - for (Entry en : conf.getAnyEntrys()) { - toXMLString(sb, en.name, en.getValue(), indent + 4); - } - return sb.append(space).append("\r\n\r\n"); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.io.*; +import java.nio.charset.*; +import java.util.*; +import java.util.function.*; +import org.redkale.convert.ConvertDisabled; + +/** + * 该类提供类似JSONObject的数据结构,主要用于读取xml配置文件和http-header存储 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public abstract class AnyValue { + + /** + * xml的文本节点name + */ + public static final String XML_TEXT_NODE_NAME = ""; + + /** + * 可读写的AnyValue默认实现类 + * + * @author zhangjx + */ + @SuppressWarnings("unchecked") + public static final class DefaultAnyValue extends AnyValue { + + /** + * 区分name大小写的比较策略 + * + */ + public static final BiPredicate EQUALS = (name1, name2) -> name1.equals(name2); + + /** + * 不区分name大小写的比较策略 + */ + public static final BiPredicate EQUALSIGNORE = (name1, name2) -> name1.equalsIgnoreCase(name2); + + private boolean ignoreCase; + + private BiPredicate predicate; + + private Entry[] stringEntrys = new Entry[0]; + + private Entry[] anyEntrys = new Entry[0]; + + /** + * 创建空的DefaultAnyValue对象 + * + * @return DefaultAnyValue对象 + */ + public static final DefaultAnyValue create() { + return new DefaultAnyValue(); + } + + /** + * 创建含name-value值的DefaultAnyValue对象 + * + * @param name name + * @param value value值 + * + * @return DefaultAnyValue对象 + */ + public static final DefaultAnyValue create(String name, Number value) { + DefaultAnyValue conf = new DefaultAnyValue(); + conf.addValue(name, value); + return conf; + } + + /** + * 创建含name-value值的DefaultAnyValue对象 + * + * @param name name + * @param value value值 + * + * @return DefaultAnyValue对象 + */ + public static final DefaultAnyValue create(String name, String value) { + DefaultAnyValue conf = new DefaultAnyValue(); + conf.addValue(name, value); + return conf; + } + + /** + * 创建含name-value值的DefaultAnyValue对象 + * + * @param name name + * @param value value值 + * + * @return DefaultAnyValue对象 + */ + public static final DefaultAnyValue create(String name, AnyValue value) { + DefaultAnyValue conf = new DefaultAnyValue(); + conf.addValue(name, value); + return conf; + } + + /** + * 创建一个区分大小写比较策略的DefaultAnyValue对象 + * + */ + public DefaultAnyValue() { + this(false); + } + + /** + * 创建DefaultAnyValue对象 + * + * @param ignoreCase name是否不区分大小写 + */ + public DefaultAnyValue(boolean ignoreCase) { + this.ignoreCase = ignoreCase; + this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS; + } + + /** + * 创建共享此内容的DefaultAnyValue对象 + * + * @return DefaultAnyValue对象 + */ + public DefaultAnyValue duplicate() { + DefaultAnyValue rs = new DefaultAnyValue(this.ignoreCase); + rs.stringEntrys = this.stringEntrys; + rs.anyEntrys = this.anyEntrys; + return rs; + } + + //去重 + public DefaultAnyValue addAllStringSet(final AnyValue av) { + if (av == null) return this; + final Entry[] strings = av.getStringEntrys(); + if (strings == null) return this; + for (Entry en : strings) { + if (getValue(en.name) == null) this.addValue(en.name, en.value); + } + return this; + } + + public DefaultAnyValue addAll(final AnyValue av) { + if (av == null) return this; + if (av instanceof DefaultAnyValue) { + final DefaultAnyValue adv = (DefaultAnyValue) av; + if (adv.stringEntrys != null) { + for (Entry en : adv.stringEntrys) { + this.addValue(en.name, en.value); + } + } + if (adv.anyEntrys != null) { + for (Entry en : adv.anyEntrys) { + this.addValue(en.name, en.value); + } + } + } else { + final Entry[] strings = av.getStringEntrys(); + if (strings != null) { + for (Entry en : strings) { + this.addValue(en.name, en.value); + } + } + final Entry[] anys = av.getAnyEntrys(); + if (anys != null) { + for (Entry en : anys) { + this.addValue(en.name, en.value); + } + } + } + return this; + } + + public DefaultAnyValue setAll(final AnyValue av) { + if (av == null) return this; + if (av instanceof DefaultAnyValue) { + final DefaultAnyValue adv = (DefaultAnyValue) av; + if (adv.stringEntrys != null) { + for (Entry en : adv.stringEntrys) { + this.setValue(en.name, en.value); + } + } + if (adv.anyEntrys != null) { + for (Entry en : adv.anyEntrys) { + this.setValue(en.name, en.value); + } + } + } else { + final Entry[] strings = av.getStringEntrys(); + if (strings != null) { + for (Entry en : strings) { + this.setValue(en.name, en.value); + } + } + final Entry[] anys = av.getAnyEntrys(); + if (anys != null) { + for (Entry en : anys) { + this.setValue(en.name, en.value); + } + } + } + return this; + } + + @Override + public void forEach(BiConsumer stringConsumer) { + forEach(stringConsumer, null); + } + + @Override + public void forEach(BiConsumer stringConsumer, BiConsumer anyConsumer) { + if (stringConsumer != null) { + for (Entry en : stringEntrys) { + stringConsumer.accept(en.name, en.value); + } + } + if (anyConsumer != null) { + for (Entry en : (Entry[]) anyEntrys) { + anyConsumer.accept(en.name, en.value); + } + } + } + + @Override + public Entry[] getStringEntrys() { + return stringEntrys; + } + + public void setStringEntrys(Entry[] stringEntrys) { + this.stringEntrys = stringEntrys; + } + + @Override + public Entry[] getAnyEntrys() { + return (Entry[]) (Entry[]) anyEntrys; + } + + public void setAnyEntrys(Entry[] anyEntrys) { + this.anyEntrys = anyEntrys; + } + + public boolean isIgnoreCase() { + return ignoreCase; + } + + public void setIgnoreCase(boolean ignoreCase) { + this.ignoreCase = ignoreCase; + if (this.predicate == null) { + this.predicate = ignoreCase ? EQUALSIGNORE : EQUALS; + } + } + + @Override + @ConvertDisabled + public String[] getNames() { + Set set = new LinkedHashSet<>(); + for (Entry en : this.stringEntrys) { + set.add(en.name); + } + for (Entry en : this.anyEntrys) { + set.add(en.name); + } + return set.toArray(new String[set.size()]); + } + + @Override + public String[] getValues(String... names) { + return Entry.getStringValues(this.predicate, this.stringEntrys, names); + } + + @Override + public AnyValue[] getAnyValues(String... names) { + return Entry.getAnyValueValues(this.predicate, this.anyEntrys, names); + } + + @Override + public String[] getValues(String name) { + return Entry.getStringValues(this.predicate, this.stringEntrys, name); + } + + @Override + public AnyValue[] getAnyValues(String name) { + return Entry.getAnyValueValues(this.predicate, this.anyEntrys, name); + } + + @Override + public String toString() { + return toString(0); + } + + public DefaultAnyValue clear() { + if (this.stringEntrys != null && this.stringEntrys.length > 0) this.stringEntrys = new Entry[0]; + if (this.anyEntrys != null && this.anyEntrys.length > 0) this.anyEntrys = new Entry[0]; + return this; + } + + public DefaultAnyValue setValue(String name, String value) { + if (name == null) return this; + if (getValue(name) == null) { + this.addValue(name, value); + } else { + for (Entry en : this.stringEntrys) { + if (predicate.test(en.name, name)) { + en.value = value; + return this; + } + } + } + return this; + } + + public DefaultAnyValue setValue(String name, AnyValue value) { + if (name == null) return this; + if (getValue(name) == null) { + this.addValue(name, value); + } else { + for (Entry en : this.anyEntrys) { + if (predicate.test(en.name, name)) { + en.value = (DefaultAnyValue) value; + return this; + } + } + } + return this; + } + + public DefaultAnyValue put(String name, boolean value) { + return addValue(name, String.valueOf(value)); + } + + public DefaultAnyValue put(String name, Number value) { + return addValue(name, String.valueOf(value)); + } + + public DefaultAnyValue put(String name, String value) { + this.stringEntrys = Utility.append(this.stringEntrys, new Entry(name, value)); + return this; + } + + public DefaultAnyValue addValue(String name, boolean value) { + return addValue(name, String.valueOf(value)); + } + + public DefaultAnyValue addValue(String name, Number value) { + return addValue(name, String.valueOf(value)); + } + + public DefaultAnyValue addValue(String name, String value) { + this.stringEntrys = Utility.append(this.stringEntrys, new Entry(name, value)); + return this; + } + + public DefaultAnyValue addValue(String name, AnyValue value) { + if (name == null || value == null) return this; + this.anyEntrys = Utility.append(this.anyEntrys, new Entry(name, value)); + return this; + } + + public DefaultAnyValue removeValue(String name, AnyValue value) { + if (name == null || value == null || this.anyEntrys == null) return this; + this.anyEntrys = Utility.remove(this.anyEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value)); + return this; + } + + public DefaultAnyValue removeValue(String name, String value) { + if (name == null || value == null || this.stringEntrys == null) return this; + this.stringEntrys = Utility.remove(this.stringEntrys, (t) -> name.equals(((Entry) t).name) && ((Entry) t).getValue().equals(value)); + return this; + } + + @Override + public AnyValue getAnyValue(String name) { + for (Entry en : this.anyEntrys) { + if (predicate.test(en.name, name)) { + return en.value; + } + } + return null; + } + + @Override + public String get(String name) { + return getValue(name); + } + + @Override + public String getValue(String name) { + for (Entry en : this.stringEntrys) { + if (predicate.test(en.name, name)) { + return en.value; + } + } + return null; + } + + } + + public static final class Entry { + + public final String name; + + T value; + + @ConstructorParameters({"name", "value"}) + public Entry(String name0, T value0) { + this.name = name0; + this.value = value0; + } + + public String getName() { + return name; + } + + public T getValue() { + return value; + } + + static String[] getStringValues(BiPredicate comparison, Entry[] entitys, String name) { + int len = 0; + for (Entry en : entitys) { + if (comparison.test(en.name, name)) { + ++len; + } + } + if (len == 0) return new String[len]; + String[] rs = new String[len]; + int i = 0; + for (Entry en : entitys) { + if (comparison.test(en.name, name)) { + rs[i++] = en.value; + } + } + return rs; + } + + static AnyValue[] getAnyValueValues(BiPredicate comparison, Entry[] entitys, String name) { + int len = 0; + for (Entry en : entitys) { + if (comparison.test(en.name, name)) { + ++len; + } + } + if (len == 0) return new AnyValue[len]; + AnyValue[] rs = new AnyValue[len]; + int i = 0; + for (Entry en : entitys) { + if (comparison.test(en.name, name)) { + rs[i++] = en.value; + } + } + return rs; + } + + static String[] getStringValues(BiPredicate comparison, Entry[] entitys, String... names) { + int len = 0; + for (Entry en : entitys) { + for (String name : names) { + if (comparison.test(en.name, name)) { + ++len; + break; + } + } + } + if (len == 0) return new String[len]; + String[] rs = new String[len]; + int i = 0; + for (Entry en : entitys) { + for (String name : names) { + if (comparison.test(en.name, name)) { + rs[i++] = en.value; + break; + } + } + } + return rs; + } + + static AnyValue[] getAnyValueValues(BiPredicate comparison, Entry[] entitys, String... names) { + int len = 0; + for (Entry en : entitys) { + for (String name : names) { + if (comparison.test(en.name, name)) { + ++len; + break; + } + } + } + if (len == 0) return new AnyValue[len]; + AnyValue[] rs = new AnyValue[len]; + int i = 0; + for (Entry en : entitys) { + for (String name : names) { + if (comparison.test(en.name, name)) { + rs[i++] = en.value; + break; + } + } + } + return rs; + } + } + + public static DefaultAnyValue create() { + return new DefaultAnyValue(); + } + + public static AnyValue loadFromXml(String text) throws IOException { + return new XmlReader(text).read(); + } + + public static AnyValue loadFromXml(InputStream in) throws IOException { + return loadFromXml(in, StandardCharsets.UTF_8); + } + + public static AnyValue loadFromXml(InputStream in, Charset charset) throws IOException { + return new XmlReader(Utility.read(in, charset)).read(); + } + + public static AnyValue loadFromXml(String text, BiFunction attrFunc) throws IOException { + return new XmlReader(text).attrFunc(attrFunc).read(); + } + + public static AnyValue loadFromXml(InputStream in, BiFunction attrFunc) throws IOException { + return loadFromXml(in, StandardCharsets.UTF_8, attrFunc); + } + + public static AnyValue loadFromXml(InputStream in, Charset charset, BiFunction attrFunc) throws IOException { + return new XmlReader(Utility.read(in, charset)).attrFunc(attrFunc).read(); + } + + public String toString(int indent) { //indent: 缩进长度 + if (indent < 0) indent = 0; + char[] chars = new char[indent]; + Arrays.fill(chars, ' '); + final String space = new String(chars); + StringBuilder sb = new StringBuilder(); + sb.append("{\r\n"); + for (Entry en : getStringEntrys()) { + sb.append(space).append(" '").append(en.name).append("': '").append(en.value).append("',\r\n"); + } + for (Entry en : getAnyEntrys()) { + sb.append(space).append(" '").append(en.name).append("': '").append(en.value.toString(indent + 4)).append("',\r\n"); + } + sb.append(space).append('}'); + return sb.toString(); + } + + public abstract void forEach(BiConsumer stringConsumer); + + public abstract void forEach(BiConsumer stringConsumer, BiConsumer anyConsumer); + + public abstract Entry[] getStringEntrys(); + + public abstract Entry[] getAnyEntrys(); + + public abstract String[] getNames(); + + public abstract String[] getValues(String name); + + public abstract String[] getValues(String... names); + + public abstract AnyValue[] getAnyValues(String name); + + public abstract AnyValue[] getAnyValues(String... names); + + public abstract AnyValue getAnyValue(String name); + + public abstract String getValue(String name); + + public abstract String get(String name); + + public boolean getBoolValue(String name) { + return Boolean.parseBoolean(getValue(name)); + } + + public boolean getBoolValue(String name, boolean defaultValue) { + String value = getValue(name); + return value == null || value.length() == 0 ? defaultValue : Boolean.parseBoolean(value); + } + + public byte getByteValue(String name) { + return Byte.parseByte(getValue(name)); + } + + public byte getByteValue(String name, byte defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Byte.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public byte getByteValue(int radix, String name, byte defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Byte.decode(value) : Byte.parseByte(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public char getCharValue(String name) { + return getValue(name).charAt(0); + } + + public char getCharValue(String name, char defaultValue) { + String value = getValue(name); + return value == null || value.length() == 0 ? defaultValue : value.charAt(0); + } + + public short getShortValue(String name) { + return Short.decode(getValue(name)); + } + + public short getShortValue(String name, short defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Short.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public short getShortValue(int radix, String name, short defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Short.decode(value) : Short.parseShort(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public int getIntValue(String name) { + return Integer.decode(getValue(name)); + } + + public int getIntValue(String name, int defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Integer.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public int getIntValue(int radix, String name, int defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Integer.decode(value) : Integer.parseInt(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public long getLongValue(String name) { + return Long.decode(getValue(name)); + } + + public long getLongValue(String name, long defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Long.decode(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public long getLongValue(int radix, String name, long defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return (radix == 10 ? Long.decode(value) : Long.parseLong(value, radix)); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public float getFloatValue(String name) { + return Float.parseFloat(getValue(name)); + } + + public float getFloatValue(String name, float defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public double getDoubleValue(String name) { + return Double.parseDouble(getValue(name)); + } + + public double getDoubleValue(String name, double defaultValue) { + String value = getValue(name); + if (value == null || value.length() == 0) return defaultValue; + try { + return Double.parseDouble(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + public String getValue(String name, String defaultValue) { + String value = getValue(name); + return value == null ? defaultValue : value; + } + + public String getOrDefault(String name, String defaultValue) { + String value = getValue(name); + return value == null ? defaultValue : value; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof AnyValue)) return false; + AnyValue conf = (AnyValue) other; + if (!equals(this.getStringEntrys(), conf.getStringEntrys())) return false; + return equals(this.getAnyEntrys(), conf.getAnyEntrys()); + } + + private static boolean equals(Entry[] entry1, Entry[] entry2) { + if ((entry1 == null || entry1.length == 0) && (entry2 == null || entry2.length == 0)) return true; + if (entry1.length != entry2.length) return false; + for (int i = 0; i < entry1.length; i++) { + if (!entry1[i].name.equals(entry2[i].name)) return false; + if (!entry1[i].getValue().equals(entry2[i].getValue())) return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 19 * hash + Arrays.deepHashCode(this.getStringEntrys()); + hash = 19 * hash + Arrays.deepHashCode(this.getAnyEntrys()); + return hash; + } + + public String toXML(String rootName) { + return toXMLString(new StringBuilder("\r\n\r\n"), rootName, this, 0).toString(); + } + + protected static StringBuilder toXMLString(StringBuilder sb, String nodeName, AnyValue conf, int indent) { //indent: 缩进长度 + if (indent < 0) indent = 0; + char[] chars = new char[indent]; + Arrays.fill(chars, ' '); + final String space = new String(chars); + Entry[] anys = conf.getAnyEntrys(); + sb.append(space).append('<').append(nodeName); + for (Entry en : conf.getStringEntrys()) { + sb.append(' ').append(en.name).append("=\"").append(en.value).append("\""); + } + if (anys == null || anys.length == 0) return sb.append("/>\r\n\r\n"); + sb.append(">\r\n\r\n"); + for (Entry en : conf.getAnyEntrys()) { + toXMLString(sb, en.name, en.getValue(), indent + 4); + } + return sb.append(space).append("\r\n\r\n"); + } + +} diff --git a/src/org/redkale/util/Attribute.java b/src/main/java/org/redkale/util/Attribute.java similarity index 88% rename from src/org/redkale/util/Attribute.java rename to src/main/java/org/redkale/util/Attribute.java index 504f42ba0..f4a4bc193 100644 --- a/src/org/redkale/util/Attribute.java +++ b/src/main/java/org/redkale/util/Attribute.java @@ -1,1040 +1,1115 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.reflect.TypeVariable; -import java.util.*; -import java.util.function.*; -import org.redkale.asm.*; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; -import static org.redkale.asm.Opcodes.*; - -/** - * 该类实现动态映射一个JavaBean类中成员对应的getter、setter方法; 代替低效的反射实现方式。 - *

    - *  public class Record {
    - *
    - *      private String name;
    - *
    - *      public String getName() {
    - *          return name;
    - *      }
    - *
    - *      public void setName(String name) {
    - *          this.name = name;
    - *      }
    - *  }
    - * 
    - * 获取name的 Attribute : - *
    - *  Attribute<Record, String> nameAction = Attribute.create(Record.class.getDeclaredField("name"));
    - * 
    - * 等价于: - *
    - *  Attribute<Record, String> nameAction = new Attribute<Record, String>() {
    - *
    - *      private java.lang.reflect.Type _gtype = String.class;
    - *
    - *      private java.lang.Object _attach;
    - *
    - *      @Override
    - *      public String field() {
    - *          return "name";
    - *      }
    - *
    - *      @Override
    - *      public String get(Record obj) {
    - *          return obj.getName();
    - *      }
    - *
    - *      @Override
    - *      public void set(Record obj, String value) {
    - *          obj.setName(value);
    - *      }
    - *
    - *      @Override
    - *      public Class type() {
    - *          return String.class;
    - *      }
    - *
    - *      @Override
    - *      public java.lang.reflect.Type genericType() {
    - *          return _gtype;
    - *      }
    - *
    - *      @Override
    - *      public Object attach() {
    - *          return _attach;
    - *      }
    - *
    - *      @Override
    - *      public Class declaringClass() {
    - *          return Record.class;
    - *      }
    - *  };
    - * 
    - *

    - * 映射Field时,field必须满足以下条件之一:
    - * 1、field属性是public且非final
    - * 2、至少存在对应的getter、setter方法中的一个
    - * 当不存在getter方法时,get操作固定返回null
    - * 当不存在setter方法时,set操作为空方法
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 字段依附的类 - * @param 字段的数据类型 - */ -@SuppressWarnings("unchecked") -public interface Attribute { - - /** - * 返回字段的数据类型 - * - * @return 字段的数据类型 - */ - public Class type(); - - /** - * 返回字段的数据泛型 - * - * @return 字段的数据泛型 - */ - default java.lang.reflect.Type genericType() { - return type(); - } - - /** - * 返回字段依附的类名 - * - * @return 依附的类名 - */ - public Class declaringClass(); - - /** - * 返回字段名 - * - * @return 字段名 - */ - public String field(); - - /** - * 获取指定对象的该字段的值 - * - * @param obj 指定对象 - * - * @return 字段的值 - */ - public F get(T obj); - - /** - * 给指定对象的该字段赋值 - * - * @param obj 指定对象 - * @param value 字段新值 - */ - public void set(T obj, F value); - - /** - * 附加对象 - * - * @param 泛型 - * - * @return 附加对象 - */ - default E attach() { - return null; - } - - /** - * 根据一个Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param field 字段,如果该字段不存在则抛异常 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(final java.lang.reflect.Field field) { - return create((Class) field.getDeclaringClass(), field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); - } - - /** - * 根据一个Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param field 字段,如果该字段不存在则抛异常 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(final java.lang.reflect.Field field, Object attach) { - return create((Class) field.getDeclaringClass(), field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); - } - - /** - * 根据一个Field和field的别名生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param fieldalias 别名 - * @param field 字段,如果该字段不存在则抛异常 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(String fieldalias, final java.lang.reflect.Field field) { - return create((Class) field.getDeclaringClass(), fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); - } - - /** - * 根据一个Field和field的别名生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param fieldalias 别名 - * @param field 字段,如果该字段不存在则抛异常 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(String fieldalias, final java.lang.reflect.Field field, Object attach) { - return create((Class) field.getDeclaringClass(), fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); - } - - /** - * 根据一个Class和field真实名称生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldname 字段名,如果该字段不存在则抛异常 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final String fieldname) { - try { - return create(clazz, fieldname, (Class) null, clazz.getDeclaredField(fieldname), (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); - } catch (NoSuchFieldException | SecurityException ex) { - throw new RuntimeException(ex); - } - } - - /** - * 根据一个Class和field真实名称生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldname 字段名,如果该字段不存在则抛异常 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final String fieldname, Object attach) { - try { - return create(clazz, fieldname, (Class) null, clazz.getDeclaredField(fieldname), (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); - } catch (NoSuchFieldException | SecurityException ex) { - throw new RuntimeException(ex); - } - } - - /** - * 根据一个Class和Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param field 字段,如果该字段不存在则抛异常 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final java.lang.reflect.Field field) { - return create(clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); - } - - /** - * 根据一个Class和Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param subclass 指定依附的子类 - * @param clazz 指定依附的类 - * @param field 字段,如果该字段不存在则抛异常 - * - * @return Attribute对象 - */ - public static Attribute create(Class subclass, Class clazz, final java.lang.reflect.Field field) { - return create(subclass, clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); - } - - /** - * 根据一个Class和Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param field 字段,如果该字段不存在则抛异常 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final java.lang.reflect.Field field, Object attach) { - return create(clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); - } - - /** - * 根据一个Class和Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param subclass 指定依附的子类 - * @param clazz 指定依附的类 - * @param field 字段,如果该字段不存在则抛异常 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(Class subclass, Class clazz, final java.lang.reflect.Field field, Object attach) { - return create(subclass, clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); - } - - /** - * 根据一个Class、field别名和Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param field 字段,如果该字段不存在则抛异常 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Field field) { - return create(clazz, fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); - } - - /** - * 根据一个Class、field别名和Field生成 Attribute 对象。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param field 字段,如果该字段不存在则抛异常 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Field field, Object attach) { - return create(clazz, fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); - } - - /** - * 根据一个getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param getter getter方法 - * @param setter setter方法 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(final java.lang.reflect.Method getter, final java.lang.reflect.Method setter) { - return create((Class) (getter == null ? setter.getDeclaringClass() : getter.getDeclaringClass()), (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, null); - } - - /** - * 根据一个getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(final java.lang.reflect.Method getter, final java.lang.reflect.Method setter, Object attach) { - return create((Class) (getter == null ? setter.getDeclaringClass() : getter.getDeclaringClass()), (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, attach); - } - - /** - * 根据Class、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param getter getter方法 - * @param setter setter方法 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter) { - return create(clazz, (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, null); - } - - /** - * 根据Class、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter, Object attach) { - return create(clazz, (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, attach); - } - - /** - * 根据Class生成getter、setter方法都存在的字段对应的 Attribute 对象数组。 - * - * @param 依附类的类型 - * @param clazz 指定依附的类 - * - * @return Attribute对象数组 - */ - public static Attribute[] create(Class clazz) { - List> list = new ArrayList<>(); - for (java.lang.reflect.Field field : clazz.getFields()) { - if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue; - if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) continue; - list.add(create(clazz, field)); - } - for (java.lang.reflect.Method setter : clazz.getDeclaredMethods()) { - if (java.lang.reflect.Modifier.isStatic(setter.getModifiers())) continue; - if (!setter.getName().startsWith("set")) continue; - if (setter.getReturnType() != void.class) continue; - if (setter.getParameterCount() != 1) continue; - Class t = setter.getParameterTypes()[0]; - String prefix = t == boolean.class || t == Boolean.class ? "is" : "get"; - java.lang.reflect.Method getter = null; - try { - getter = clazz.getMethod(setter.getName().replaceFirst("set", prefix)); - } catch (Exception e) { - continue; - } - list.add(create(clazz, getter, setter)); - } - return list.toArray(new Attribute[list.size()]); - } - - /** - * 根据Class生成getter方法对应的 Attribute 对象数组。 - * - * @param 依附类的类型 - * @param clazz 指定依附的类 - * - * @return Attribute对象数组 - */ - public static Attribute[] createGetters(Class clazz) { - List> list = new ArrayList<>(); - for (java.lang.reflect.Field field : clazz.getFields()) { - if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue; - if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) continue; - list.add(create(clazz, field)); - } - for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) { - if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) continue; - if (!method.getName().startsWith("get") && !method.getName().startsWith("is")) continue; - if (method.getReturnType() == void.class) continue; - if (method.getParameterCount() != 0) continue; - list.add(create(clazz, method, null)); - } - return list.toArray(new Attribute[list.size()]); - } - - /** - * 根据Class生成setter方法对应的 Attribute 对象数组。 - * - * @param 依附类的类型 - * @param clazz 指定依附的类 - * - * @return Attribute对象数组 - */ - public static Attribute[] createSetters(Class clazz) { - List> list = new ArrayList<>(); - for (java.lang.reflect.Field field : clazz.getFields()) { - if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue; - if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) continue; - list.add(create(clazz, field)); - } - for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) { - if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) continue; - if (!method.getName().startsWith("set")) continue; - if (method.getParameterCount() != 1) continue; - list.add(create(clazz, (java.lang.reflect.Method) null, method)); - } - return list.toArray(new Attribute[list.size()]); - } - - /** - * 根据Class、字段别名、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param getter getter方法 - * @param setter setter方法 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter) { - return create(clazz, fieldalias, (Class) null, (java.lang.reflect.Field) null, getter, setter, null); - } - - /** - * 根据Class、字段别名、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter, Object attach) { - return create(clazz, fieldalias, (Class) null, (java.lang.reflect.Field) null, getter, setter, attach); - } - - /** - * 根据Class、字段别名、Field、getter和setter方法生成 Attribute 对象。 Field、tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param field 字段 - * @param getter getter方法 - * @param setter setter方法 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, String fieldalias, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter) { - return create(clazz, fieldalias, (Class) null, field, getter, setter, null); - } - - /** - * 根据Class、字段别名、Field、getter和setter方法生成 Attribute 对象。 Field、tgetter、setter不能同时为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param field 字段 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, String fieldalias, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter, Object attach) { - return create(clazz, fieldalias, (Class) null, field, getter, setter, attach); - } - - /** - * 根据Class、字段别名、字段类型生成虚构的 Attribute 对象,get、set方法为空方法。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param fieldtype 字段的类 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype) { - return create(clazz, fieldalias, fieldtype, (java.lang.reflect.Type) null, (Function) null, (BiConsumer) null, null); - } - - /** - * 根据Class、字段别名、字段类型生成虚构的 Attribute 对象,get、set方法为空方法。 - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param fieldtype 字段的类 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype, Object attach) { - return create(clazz, fieldalias, fieldtype, (java.lang.reflect.Type) null, (Function) null, (BiConsumer) null, attach); - } - - /** - * 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias/fieldtype、Field、tgetter、setter不能同时为null. - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param fieldtype 字段类型 - * @param field 字段 - * @param getter getter方法 - * @param setter setter方法 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter) { - return create(clazz, fieldalias, fieldtype, field, getter, setter, null); - } - - /** - * 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias/fieldtype、Field、tgetter、setter不能同时为null. - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param fieldtype 字段类型 - * @param field 字段 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter, Object attach) { - return create(null, clazz, fieldalias, fieldtype, field, getter, setter, attach); - } - - /** - * 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias/fieldtype、Field、tgetter、setter不能同时为null. - * - * @param 依附类的类型 - * @param 字段类型 - * @param subclass 指定依附的子类 - * @param clazz 指定依附的类 - * @param fieldalias 字段别名 - * @param fieldtype 字段类型 - * @param field 字段 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - @SuppressWarnings("unchecked") - public static Attribute create(java.lang.reflect.Type subclass, final Class clazz, String fieldalias, final Class fieldtype, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter, Object attach) { - if (subclass == null) subclass = clazz; - if (fieldalias != null && fieldalias.isEmpty()) fieldalias = null; - int mod = field == null ? java.lang.reflect.Modifier.STATIC : field.getModifiers(); - if (field != null && !java.lang.reflect.Modifier.isStatic(mod) && !java.lang.reflect.Modifier.isPublic(mod)) { - Class t = field.getType(); - char[] fs = field.getName().toCharArray(); - fs[0] = Character.toUpperCase(fs[0]); - String mn = new String(fs); - if (getter == null) { - String prefix = t == boolean.class || t == Boolean.class ? "is" : "get"; - try { - getter = clazz.getMethod(prefix + mn); - } catch (Exception ex) { - } - } - if (setter == null) { - try { - setter = clazz.getMethod("set" + mn, field.getType()); - } catch (Exception ex) { - } - } - } - final java.lang.reflect.Field tfield = field == null ? null : (!java.lang.reflect.Modifier.isPublic(mod) || java.lang.reflect.Modifier.isStatic(mod) ? null : field); - final java.lang.reflect.Method tgetter = getter; - final java.lang.reflect.Method tsetter = setter; - if (fieldalias == null) { - if (field != null) { - fieldalias = field.getName(); - } else { - String s = null; - if (getter != null) { - s = getter.getName().substring(getter.getName().startsWith("is") ? 2 : 3); - } else if (setter != null) { - s = setter.getName().substring(3); - } - if (s != null) { - char[] d = s.toCharArray(); - if (d.length < 2 || Character.isLowerCase(d[1])) { - d[0] = Character.toLowerCase(d[0]); - } - fieldalias = new String(d); - } - } - } - if (fieldalias == null && fieldtype == null && tgetter == null && tsetter == null && tfield == null) { - throw new RuntimeException("[" + clazz + "]have no public field or setter or getter"); - } - final String fieldname = fieldalias; - Class column = fieldtype; - java.lang.reflect.Type generictype = fieldtype; - - if (tfield != null) { // public tfield - column = tfield.getType(); - generictype = tfield.getGenericType(); - } else if (tgetter != null) { - column = tgetter.getReturnType(); - generictype = tgetter.getGenericReturnType(); - } else if (tsetter != null) { - column = tsetter.getParameterTypes()[0]; - generictype = tsetter.getGenericParameterTypes()[0]; - } else if (fieldtype == null) { - throw new RuntimeException("[" + clazz + "]have no public field or setter or getter"); - } else if (column == null) { - throw new RuntimeException("[" + clazz + "]have no field type"); - } - boolean checkCast = false; - if (generictype instanceof java.lang.reflect.TypeVariable) { - checkCast = true; - generictype = TypeToken.getGenericType(generictype, subclass); - if (generictype instanceof Class) column = (Class) generictype; - } - final Class pcolumn = column; - if (column.isPrimitive()) column = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(column, 1), 0).getClass(); - final String supDynName = Attribute.class.getName().replace('.', '/'); - final String interName = TypeToken.typeToClass(subclass).getName().replace('.', '/'); - final String columnName = column.getName().replace('.', '/'); - final String interDesc = Type.getDescriptor(TypeToken.typeToClass(subclass)); - final String columnDesc = Type.getDescriptor(column); - - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - String newDynName = supDynName + "_Dyn_" + TypeToken.typeToClass(subclass).getSimpleName() + "_" - + fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array"); - if (String.class.getClassLoader() != TypeToken.typeToClass(subclass).getClassLoader()) { - loader = TypeToken.typeToClass(subclass).getClassLoader(); - newDynName = interName + "_Dyn" + Attribute.class.getSimpleName() + "_" - + fieldname.substring(fieldname.indexOf('.') + 1) + "_" + pcolumn.getSimpleName().replace("[]", "Array"); - } - try { - Attribute rs = (Attribute) loader.loadClass(newDynName.replace('/', '.')).getDeclaredConstructor().newInstance(); - java.lang.reflect.Field _gtype = rs.getClass().getDeclaredField("_gtype"); - _gtype.setAccessible(true); - _gtype.set(rs, generictype); - return rs; - } catch (Throwable ex) { - } - //--------------------------------------------------- - final ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - MethodVisitor mv; - - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + columnDesc + ">;", "java/lang/Object", new String[]{supDynName}); - { //_gtype - FieldVisitor fv = cw.visitField(ACC_PRIVATE, "_gtype", "Ljava/lang/reflect/Type;", null, null); - fv.visitEnd(); - } - { //_attach - FieldVisitor fv = cw.visitField(ACC_PRIVATE, "_attach", "Ljava/lang/Object;", null, null); - fv.visitEnd(); - } - { //构造方法 - mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - - { //field 方法 - mv = cw.visitMethod(ACC_PUBLIC, "field", "()Ljava/lang/String;", null, null); - mv.visitLdcInsn(fieldname); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //type 方法 - mv = cw.visitMethod(ACC_PUBLIC, "type", "()Ljava/lang/Class;", null, null); - if (pcolumn == boolean.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); - } else if (pcolumn == byte.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); - } else if (pcolumn == char.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); - } else if (pcolumn == short.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); - } else if (pcolumn == int.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); - } else if (pcolumn == float.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); - } else if (pcolumn == long.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); - } else if (pcolumn == double.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); - } else { - mv.visitLdcInsn(Type.getType(pcolumn)); - } - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //genericType - mv = cw.visitMethod(ACC_PUBLIC, "genericType", "()Ljava/lang/reflect/Type;", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "_gtype", "Ljava/lang/reflect/Type;"); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //attach - mv = cw.visitMethod(ACC_PUBLIC, "attach", "()Ljava/lang/Object;", "()TE;", null); - mv.visitVarInsn(ALOAD, 0); - mv.visitFieldInsn(GETFIELD, newDynName, "_attach", "Ljava/lang/Object;"); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //declaringClass 方法 - mv = cw.visitMethod(ACC_PUBLIC, "declaringClass", "()Ljava/lang/Class;", null, null); - mv.visitLdcInsn(Type.getType(TypeToken.typeToClass(subclass))); - mv.visitInsn(ARETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { //get 方法 - mv = cw.visitMethod(ACC_PUBLIC, "get", "(" + interDesc + ")" + columnDesc, null, null); - int m = 1; - if (tgetter == null) { - if (tfield == null) { - mv.visitInsn(ACONST_NULL); - } else { //public tfield - mv.visitVarInsn(ALOAD, 1); - mv.visitFieldInsn(GETFIELD, interName, tfield.getName(), Type.getDescriptor(pcolumn)); - if (pcolumn != column) { - mv.visitMethodInsn(INVOKESTATIC, columnName, "valueOf", "(" + Type.getDescriptor(pcolumn) + ")" + columnDesc, false); - m = 2; - } else { - if (checkCast) mv.visitTypeInsn(CHECKCAST, columnName); - } - } - } else { - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, interName, tgetter.getName(), Type.getMethodDescriptor(tgetter), false); - if (pcolumn != column) { - mv.visitMethodInsn(INVOKESTATIC, columnName, "valueOf", "(" + Type.getDescriptor(pcolumn) + ")" + columnDesc, false); - m = 2; - } else { - if (checkCast) mv.visitTypeInsn(CHECKCAST, columnName); - } - } - mv.visitInsn(ARETURN); - mv.visitMaxs(m, 2); - mv.visitEnd(); - } - { //set 方法 - mv = cw.visitMethod(ACC_PUBLIC, "set", "(" + interDesc + columnDesc + ")V", null, null); - int m = 2; - if (tsetter == null) { - if (tfield == null || java.lang.reflect.Modifier.isFinal(tfield.getModifiers())) { - m = 0; - } else { //public tfield - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - if (pcolumn != column) { - try { - java.lang.reflect.Method pm = column.getMethod(pcolumn.getSimpleName() + "Value"); - mv.visitMethodInsn(INVOKEVIRTUAL, columnName, pm.getName(), Type.getMethodDescriptor(pm), false); - m = 3; - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - } - if (!tfield.getType().isPrimitive() && tfield.getGenericType() instanceof TypeVariable) { - mv.visitFieldInsn(PUTFIELD, interName, tfield.getName(), "Ljava/lang/Object;"); - } else { - mv.visitFieldInsn(PUTFIELD, interName, tfield.getName(), Type.getDescriptor(pcolumn)); - } - } - } else { - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - if (pcolumn != column) { - try { - java.lang.reflect.Method pm = column.getMethod(pcolumn.getSimpleName() + "Value"); - mv.visitMethodInsn(INVOKEVIRTUAL, columnName, pm.getName(), Type.getMethodDescriptor(pm), false); - m = 3; - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - } - mv.visitMethodInsn(INVOKEVIRTUAL, interName, tsetter.getName(), Type.getMethodDescriptor(tsetter), false); - } - mv.visitInsn(RETURN); - mv.visitMaxs(m, 3); - mv.visitEnd(); - } - { //虚拟get - mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitTypeInsn(CHECKCAST, interName); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "get", "(" + interDesc + ")" + columnDesc, false); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - {//虚拟set - mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitTypeInsn(CHECKCAST, interName); - mv.visitVarInsn(ALOAD, 2); - mv.visitTypeInsn(CHECKCAST, columnName); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "set", "(" + interDesc + columnDesc + ")V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - cw.visitEnd(); - - byte[] bytes = cw.toByteArray(); - Class creatorClazz = (Class) new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - Attribute rs = creatorClazz.getDeclaredConstructor().newInstance(); - java.lang.reflect.Field _gtype = rs.getClass().getDeclaredField("_gtype"); - _gtype.setAccessible(true); - _gtype.set(rs, generictype); - java.lang.reflect.Field _attach = rs.getClass().getDeclaredField("_attach"); - _attach.setAccessible(true); - _attach.set(rs, attach); - return rs; - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - /** - * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldname 字段名 - * @param fieldtype 字段类型 - * @param getter getter方法 - * @param setter setter方法 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, final Function getter, final BiConsumer setter) { - return create(clazz, fieldname, fieldtype, fieldtype, getter, setter); - } - - /** - * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldname 字段名 - * @param fieldtype 字段类型 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, final Function getter, final BiConsumer setter, Object attach) { - return create(clazz, fieldname, fieldtype, fieldtype, getter, setter, attach); - } - - /** - * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldname 字段名 - * @param fieldtype 字段类型 - * @param fieldGenericType 字段泛型 - * @param getter getter方法 - * @param setter setter方法 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, - final java.lang.reflect.Type fieldGenericType, final Function getter, final BiConsumer setter) { - return create(clazz, fieldname, fieldtype, fieldGenericType, getter, setter, null); - } - - /** - * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null - * - * @param 依附类的类型 - * @param 字段类型 - * @param clazz 指定依附的类 - * @param fieldname 字段名 - * @param fieldtype 字段类型 - * @param fieldGenericType 字段泛型 - * @param getter getter方法 - * @param setter setter方法 - * @param attach 附加对象 - * - * @return Attribute对象 - */ - public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, - final java.lang.reflect.Type fieldGenericType, final Function getter, final BiConsumer setter, final Object attach) { - Objects.requireNonNull(clazz); - Objects.requireNonNull(fieldname); - Objects.requireNonNull(fieldtype); - return new Attribute() { - @Override - public Class type() { - return fieldtype; - } - - @Override - public java.lang.reflect.Type genericType() { - return fieldGenericType; - } - - @Override - public E attach() { - return (E) attach; - } - - @Override - public Class declaringClass() { - return clazz; - } - - @Override - public String field() { - return fieldname; - } - - @Override - public F get(T obj) { - return getter == null ? null : getter.apply(obj); - } - - @Override - public void set(T obj, F value) { - if (setter != null) setter.accept(obj, value); - } - }; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.reflect.TypeVariable; +import java.util.*; +import java.util.function.*; +import org.redkale.asm.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import static org.redkale.asm.Opcodes.*; + +/** + * 该类实现动态映射一个JavaBean类中成员对应的getter、setter方法; 代替低效的反射实现方式。 + *

    + *  public class Record {
    + *
    + *      private String name;
    + *
    + *      public String getName() {
    + *          return name;
    + *      }
    + *
    + *      public void setName(String name) {
    + *          this.name = name;
    + *      }
    + *  }
    + * 
    + * 获取name的 Attribute : + *
    + *  Attribute<Record, String> nameAction = Attribute.create(Record.class.getDeclaredField("name"));
    + * 
    + * 等价于: + *
    + *  Attribute<Record, String> nameAction = new Attribute<Record, String>() {
    + *
    + *      private java.lang.reflect.Type _gtype = String.class;
    + *
    + *      private java.lang.Object _attach;
    + *
    + *      @Override
    + *      public String field() {
    + *          return "name";
    + *      }
    + *
    + *      @Override
    + *      public String get(Record obj) {
    + *          return obj.getName();
    + *      }
    + *
    + *      @Override
    + *      public void set(Record obj, String value) {
    + *          obj.setName(value);
    + *      }
    + *
    + *      @Override
    + *      public Class type() {
    + *          return String.class;
    + *      }
    + *
    + *      @Override
    + *      public java.lang.reflect.Type genericType() {
    + *          return _gtype;
    + *      }
    + *
    + *      @Override
    + *      public Object attach() {
    + *          return _attach;
    + *      }
    + *
    + *      @Override
    + *      public Class declaringClass() {
    + *          return Record.class;
    + *      }
    + *  };
    + * 
    + *

    + * 映射Field时,field必须满足以下条件之一:
    + * 1、field属性是public且非final
    + * 2、至少存在对应的getter、setter方法中的一个
    + * 当不存在getter方法时,get操作固定返回null
    + * 当不存在setter方法时,set操作为空方法
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 字段依附的类 + * @param 字段的数据类型 + */ +@SuppressWarnings("unchecked") +public interface Attribute { + + /** + * 返回字段的数据类型 + * + * @return 字段的数据类型 + */ + public Class type(); + + /** + * 返回字段的数据泛型 + * + * @return 字段的数据泛型 + */ + default java.lang.reflect.Type genericType() { + return type(); + } + + /** + * 返回字段依附的类名 + * + * @return 依附的类名 + */ + public Class declaringClass(); + + /** + * 返回字段名 + * + * @return 字段名 + */ + public String field(); + + /** + * 获取指定对象的该字段的值 + * + * @param obj 指定对象 + * + * @return 字段的值 + */ + public F get(T obj); + + /** + * 给指定对象的该字段赋值 + * + * @param obj 指定对象 + * @param value 字段新值 + */ + public void set(T obj, F value); + + /** + * 附加对象 + * + * @param 泛型 + * + * @return 附加对象 + */ + default E attach() { + return null; + } + + /** + * 根据一个Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param field 字段,如果该字段不存在则抛异常 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(final java.lang.reflect.Field field) { + return create((Class) field.getDeclaringClass(), field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); + } + + /** + * 根据一个Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param field 字段,如果该字段不存在则抛异常 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(final java.lang.reflect.Field field, Object attach) { + return create((Class) field.getDeclaringClass(), field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); + } + + /** + * 根据一个Field和field的别名生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param fieldalias 别名 + * @param field 字段,如果该字段不存在则抛异常 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(String fieldalias, final java.lang.reflect.Field field) { + return create((Class) field.getDeclaringClass(), fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); + } + + /** + * 根据一个Field和field的别名生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param fieldalias 别名 + * @param field 字段,如果该字段不存在则抛异常 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(String fieldalias, final java.lang.reflect.Field field, Object attach) { + return create((Class) field.getDeclaringClass(), fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); + } + + /** + * 根据一个Class和field真实名称生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldname 字段名,如果该字段不存在则抛异常 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final String fieldname) { + try { + return create(clazz, fieldname, (Class) null, clazz.getDeclaredField(fieldname), (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); + } catch (NoSuchFieldException | SecurityException ex) { + throw new RuntimeException(ex); + } + } + + /** + * 根据一个Class和field真实名称生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldname 字段名,如果该字段不存在则抛异常 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final String fieldname, Object attach) { + try { + return create(clazz, fieldname, (Class) null, clazz.getDeclaredField(fieldname), (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); + } catch (NoSuchFieldException | SecurityException ex) { + throw new RuntimeException(ex); + } + } + + /** + * 根据一个Class和Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param field 字段,如果该字段不存在则抛异常 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final java.lang.reflect.Field field) { + return create(clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); + } + + /** + * 根据一个Class和Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param subclass 指定依附的子类 + * @param clazz 指定依附的类 + * @param field 字段,如果该字段不存在则抛异常 + * + * @return Attribute对象 + */ + public static Attribute create(Class subclass, Class clazz, final java.lang.reflect.Field field) { + return create(subclass, clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); + } + + /** + * 根据一个Class和Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param field 字段,如果该字段不存在则抛异常 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final java.lang.reflect.Field field, Object attach) { + return create(clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); + } + + /** + * 根据一个Class和Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param subclass 指定依附的子类 + * @param clazz 指定依附的类 + * @param field 字段,如果该字段不存在则抛异常 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(Class subclass, Class clazz, final java.lang.reflect.Field field, Object attach) { + return create(subclass, clazz, field.getName(), (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); + } + + /** + * 根据一个Class、field别名和Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param field 字段,如果该字段不存在则抛异常 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Field field) { + return create(clazz, fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, null); + } + + /** + * 根据一个Class、field别名和Field生成 Attribute 对象。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param field 字段,如果该字段不存在则抛异常 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Field field, Object attach) { + return create(clazz, fieldalias, (Class) null, field, (java.lang.reflect.Method) null, (java.lang.reflect.Method) null, attach); + } + + /** + * 根据一个getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param getter getter方法 + * @param setter setter方法 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(final java.lang.reflect.Method getter, final java.lang.reflect.Method setter) { + return create((Class) (getter == null ? setter.getDeclaringClass() : getter.getDeclaringClass()), (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, null); + } + + /** + * 根据一个getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(final java.lang.reflect.Method getter, final java.lang.reflect.Method setter, Object attach) { + return create((Class) (getter == null ? setter.getDeclaringClass() : getter.getDeclaringClass()), (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, attach); + } + + /** + * 根据Class、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param getter getter方法 + * @param setter setter方法 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter) { + return create(clazz, (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, null); + } + + /** + * 根据Class、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter, Object attach) { + return create(clazz, (String) null, (Class) null, (java.lang.reflect.Field) null, getter, setter, attach); + } + + /** + * 根据Class生成getter、setter方法都存在的字段对应的 Attribute 对象数组。 + * + * @param 依附类的类型 + * @param clazz 指定依附的类 + * + * @return Attribute对象数组 + */ + public static Attribute[] create(Class clazz) { + List> list = new ArrayList<>(); + RedkaleClassLoader.putReflectionPublicFields(clazz.getName()); + for (java.lang.reflect.Field field : clazz.getFields()) { + if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue; + if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) continue; + list.add(create(clazz, field)); + } + RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName()); + for (java.lang.reflect.Method setter : clazz.getDeclaredMethods()) { + if (java.lang.reflect.Modifier.isStatic(setter.getModifiers())) continue; + if (!setter.getName().startsWith("set")) continue; + if (setter.getReturnType() != void.class) continue; + if (setter.getParameterCount() != 1) continue; + Class t = setter.getParameterTypes()[0]; + String prefix = t == boolean.class || t == Boolean.class ? "is" : "get"; + java.lang.reflect.Method getter = null; + try { + getter = clazz.getMethod(setter.getName().replaceFirst("set", prefix)); + } catch (Exception e) { + continue; + } + list.add(create(clazz, getter, setter)); + } + return list.toArray(new Attribute[list.size()]); + } + + /** + * 根据Class生成getter方法对应的 Attribute 对象数组。 + * + * @param 依附类的类型 + * @param clazz 指定依附的类 + * + * @return Attribute对象数组 + */ + public static Attribute[] createGetters(Class clazz) { + List> list = new ArrayList<>(); + RedkaleClassLoader.putReflectionPublicFields(clazz.getName()); + for (java.lang.reflect.Field field : clazz.getFields()) { + if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue; + if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) continue; + list.add(create(clazz, field)); + } + RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName()); + for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) { + if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) continue; + if (method.getReturnType() == void.class) continue; + if (method.getParameterCount() != 0) continue; + if (method.getName().equals("getClass")) continue; + if (method.getName().startsWith("get") || method.getName().startsWith("is") + || Utility.isRecordGetter(clazz, method)) { + list.add(create(clazz, method, null)); + } + } + return list.toArray(new Attribute[list.size()]); + } + + /** + * 根据Class生成setter方法对应的 Attribute 对象数组。 + * + * @param 依附类的类型 + * @param clazz 指定依附的类 + * + * @return Attribute对象数组 + */ + public static Attribute[] createSetters(Class clazz) { + List> list = new ArrayList<>(); + RedkaleClassLoader.putReflectionPublicFields(clazz.getName()); + for (java.lang.reflect.Field field : clazz.getFields()) { + if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) continue; + if (java.lang.reflect.Modifier.isFinal(field.getModifiers())) continue; + list.add(create(clazz, field)); + } + RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName()); + for (java.lang.reflect.Method method : clazz.getDeclaredMethods()) { + if (java.lang.reflect.Modifier.isStatic(method.getModifiers())) continue; + if (!method.getName().startsWith("set")) continue; + if (method.getParameterCount() != 1) continue; + list.add(create(clazz, (java.lang.reflect.Method) null, method)); + } + return list.toArray(new Attribute[list.size()]); + } + + /** + * 根据Class、字段别名、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param getter getter方法 + * @param setter setter方法 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter) { + return create(clazz, fieldalias, (Class) null, (java.lang.reflect.Field) null, getter, setter, null); + } + + /** + * 根据Class、字段别名、getter和setter方法生成 Attribute 对象。 tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(Class clazz, final String fieldalias, final java.lang.reflect.Method getter, final java.lang.reflect.Method setter, Object attach) { + return create(clazz, fieldalias, (Class) null, (java.lang.reflect.Field) null, getter, setter, attach); + } + + /** + * 根据Class、字段别名、Field、getter和setter方法生成 Attribute 对象。 Field、tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param field 字段 + * @param getter getter方法 + * @param setter setter方法 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, String fieldalias, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter) { + return create(clazz, fieldalias, (Class) null, field, getter, setter, null); + } + + /** + * 根据Class、字段别名、Field、getter和setter方法生成 Attribute 对象。 Field、tgetter、setter不能同时为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param field 字段 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, String fieldalias, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter, Object attach) { + return create(clazz, fieldalias, (Class) null, field, getter, setter, attach); + } + + /** + * 根据Class、字段别名、字段类型生成虚构的 Attribute 对象,get、set方法为空方法。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param fieldtype 字段的类 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype) { + return create(clazz, fieldalias, fieldtype, (java.lang.reflect.Type) null, (Function) null, (BiConsumer) null, null); + } + + /** + * 根据Class、字段别名、字段类型生成虚构的 Attribute 对象,get、set方法为空方法。 + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param fieldtype 字段的类 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype, Object attach) { + return create(clazz, fieldalias, fieldtype, (java.lang.reflect.Type) null, (Function) null, (BiConsumer) null, attach); + } + + /** + * 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias/fieldtype、Field、tgetter、setter不能同时为null. + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param fieldtype 字段类型 + * @param field 字段 + * @param getter getter方法 + * @param setter setter方法 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter) { + return create(clazz, fieldalias, fieldtype, field, getter, setter, null); + } + + /** + * 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias/fieldtype、Field、tgetter、setter不能同时为null. + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param fieldtype 字段类型 + * @param field 字段 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(final Class clazz, String fieldalias, final Class fieldtype, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter, Object attach) { + return create(null, clazz, fieldalias, fieldtype, field, getter, setter, attach); + } + + /** + * 根据Class、字段别名、字段类型、Field、getter和setter方法生成 Attribute 对象。 fieldalias/fieldtype、Field、tgetter、setter不能同时为null. + * + * @param 依附类的类型 + * @param 字段类型 + * @param subclass 指定依附的子类 + * @param clazz 指定依附的类 + * @param fieldalias 字段别名 + * @param fieldtype 字段类型 + * @param field 字段 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + @SuppressWarnings("unchecked") + public static Attribute create(java.lang.reflect.Type subclass, final Class clazz, String fieldalias, final Class fieldtype, final java.lang.reflect.Field field, java.lang.reflect.Method getter, java.lang.reflect.Method setter, Object attach) { + if (subclass == null) subclass = clazz; + if (fieldalias != null && fieldalias.isEmpty()) fieldalias = null; + int mod = field == null ? java.lang.reflect.Modifier.STATIC : field.getModifiers(); + if (field != null && !java.lang.reflect.Modifier.isStatic(mod) && !java.lang.reflect.Modifier.isPublic(mod)) { + Class t = field.getType(); + char[] fs = field.getName().toCharArray(); + fs[0] = Character.toUpperCase(fs[0]); + String mn = new String(fs); + if (getter == null) { + String prefix = t == boolean.class || t == Boolean.class ? "is" : "get"; + try { + getter = clazz.getMethod(prefix + mn); + } catch (Exception ex) { + try { + java.lang.reflect.Method m = clazz.getMethod(field.getName()); + if (Utility.isRecordGetter(clazz, m) && field.getType() == m.getReturnType()) getter = m; + } catch (Exception ex2) { + } + } + } + if (setter == null) { + try { + setter = clazz.getMethod("set" + mn, field.getType()); + } catch (Exception ex) { + } + } + } + final java.lang.reflect.Field tfield = field == null ? null : (!java.lang.reflect.Modifier.isPublic(mod) || java.lang.reflect.Modifier.isStatic(mod) ? null : field); + final java.lang.reflect.Method tgetter = getter; + final java.lang.reflect.Method tsetter = setter; + String fieldkey = fieldalias; + if (fieldalias == null) { + if (field != null) { + fieldalias = field.getName(); + fieldkey = fieldalias; + } else { + String s = null; + if (getter != null) { + s = Utility.isRecordGetter(getter) ? getter.getName() : getter.getName().substring(getter.getName().startsWith("is") ? 2 : 3); + fieldkey = getter.getName(); + } else if (setter != null) { + s = setter.getName().substring(3); + fieldkey = setter.getName(); + } + if (s != null) { + char[] d = s.toCharArray(); + if (d.length < 2 || Character.isLowerCase(d[1])) { + d[0] = Character.toLowerCase(d[0]); + } + fieldalias = new String(d); + fieldkey = fieldalias; + } + } + } + if (getter != null) { //防止fieldname/getter/setter名字相同,所以加上1/2/3 + if (setter == null) { + fieldkey = (fieldalias == null ? "" : ("0_" + fieldalias + "_")) + "1_" + getter.getName(); + } else { + fieldkey = (fieldalias == null ? "" : ("0_" + fieldalias + "_")) + "3_" + getter.getName() + "_" + setter.getName(); + } + } else if (setter != null) { + fieldkey = (fieldalias == null ? "" : ("0_" + fieldalias + "_")) + "2_" + setter.getName(); + } + if (fieldalias == null && fieldtype == null && tgetter == null && tsetter == null && tfield == null) { + throw new RuntimeException("[" + clazz + "]have no public field or setter or getter"); + } + final String fieldname = fieldalias; + Class column = fieldtype; + java.lang.reflect.Type generictype = fieldtype; + if (tfield != null) { // public tfield + column = tfield.getType(); + generictype = tfield.getGenericType(); + } else if (tgetter != null) { + column = tgetter.getReturnType(); + generictype = tgetter.getGenericReturnType(); + } else if (tsetter != null) { + column = tsetter.getParameterTypes()[0]; + generictype = tsetter.getGenericParameterTypes()[0]; + } else if (fieldtype == null) { + throw new RuntimeException("[" + clazz + "]have no public field or setter or getter"); + } else if (column == null) { + throw new RuntimeException("[" + clazz + "]have no field type"); + } + boolean checkCast = false; + if (generictype instanceof java.lang.reflect.TypeVariable) { + checkCast = true; + generictype = TypeToken.getGenericType(generictype, subclass); + if (generictype instanceof Class) column = (Class) generictype; + } + StringBuilder newsubname = new StringBuilder(); + for (char ch : subclass.toString().replace("class ", "").toCharArray()) { //RetResult与RetResult>是不一样的 + if (ch >= '0' && ch <= '9') { + newsubname.append(ch); + } else if (ch >= 'a' && ch <= 'z') { + newsubname.append(ch); + } else if (ch >= 'A' && ch <= 'Z') { + newsubname.append(ch); + } else { + newsubname.append('_'); + } + } + String tostr = "Dyn" + Attribute.class.getSimpleName() + "_" + fieldname + "_" + column.getSimpleName(); + if (fieldkey.contains("1_")) { + tostr += "_getter"; + } else if (fieldkey.contains("2_")) { + tostr += "_setter"; + } else if (fieldkey.contains("3_")) { + tostr += "_getter_setter"; + } + final Class pcolumn = column; + if (column.isPrimitive()) column = java.lang.reflect.Array.get(java.lang.reflect.Array.newInstance(column, 1), 0).getClass(); + final String supDynName = Attribute.class.getName().replace('.', '/'); + final String interName = TypeToken.typeToClass(subclass).getName().replace('.', '/'); + final String columnName = column.getName().replace('.', '/'); + final String interDesc = Type.getDescriptor(TypeToken.typeToClass(subclass)); + final String columnDesc = Type.getDescriptor(column); + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + Class realclz = TypeToken.typeToClass(subclass); + String pkgname = ""; + String clzname = newsubname.toString(); + if (realclz != null) { + pkgname = realclz.getName(); + int pos = pkgname.lastIndexOf('.'); + if (pos > 0) pkgname = pkgname.substring(0, pos + 1); + pkgname = pkgname.replace('.', '/'); + } + final String newDynName = "org/redkaledyn/attribute/" + pkgname + "_Dyn" + Attribute.class.getSimpleName() + + "__" + clzname + "__" + fieldkey.substring(fieldkey.indexOf('.') + 1); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + Attribute rs = (Attribute) (clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz).getDeclaredConstructor().newInstance(); + java.lang.reflect.Field _gtype = rs.getClass().getDeclaredField("_gtype"); + _gtype.setAccessible(true); + _gtype.set(rs, generictype); + java.lang.reflect.Field _attach = rs.getClass().getDeclaredField("_attach"); + _attach.setAccessible(true); + _attach.set(rs, attach); + return rs; + } catch (Throwable ex) { + } + //--------------------------------------------------- + final ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + MethodVisitor mv; + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + columnDesc + ">;", "java/lang/Object", new String[]{supDynName}); + { //_gtype + FieldVisitor fv = cw.visitField(ACC_PRIVATE, "_gtype", "Ljava/lang/reflect/Type;", null, null); + fv.visitEnd(); + } + { //_attach + FieldVisitor fv = cw.visitField(ACC_PRIVATE, "_attach", "Ljava/lang/Object;", null, null); + fv.visitEnd(); + } + { //构造方法 + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + + { //field 方法 + mv = cw.visitMethod(ACC_PUBLIC, "field", "()Ljava/lang/String;", null, null); + mv.visitLdcInsn(fieldname); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //type 方法 + mv = cw.visitMethod(ACC_PUBLIC, "type", "()Ljava/lang/Class;", null, null); + if (pcolumn == boolean.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == byte.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == char.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == short.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == int.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == float.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == long.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); + } else if (pcolumn == double.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); + } else { + mv.visitLdcInsn(Type.getType(pcolumn)); + } + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //genericType + mv = cw.visitMethod(ACC_PUBLIC, "genericType", "()Ljava/lang/reflect/Type;", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "_gtype", "Ljava/lang/reflect/Type;"); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //attach + mv = cw.visitMethod(ACC_PUBLIC, "attach", "()Ljava/lang/Object;", "()TE;", null); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "_attach", "Ljava/lang/Object;"); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //declaringClass 方法 + mv = cw.visitMethod(ACC_PUBLIC, "declaringClass", "()Ljava/lang/Class;", null, null); + mv.visitLdcInsn(Type.getType(TypeToken.typeToClass(subclass))); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //get 方法 + mv = cw.visitMethod(ACC_PUBLIC, "get", "(" + interDesc + ")" + columnDesc, null, null); + int m = 1; + if (tgetter == null) { + if (tfield == null) { + mv.visitInsn(ACONST_NULL); + } else { //public tfield + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(GETFIELD, interName, tfield.getName(), Type.getDescriptor(pcolumn)); + if (pcolumn != column) { + mv.visitMethodInsn(INVOKESTATIC, columnName, "valueOf", "(" + Type.getDescriptor(pcolumn) + ")" + columnDesc, false); + m = 2; + } else { + if (checkCast) mv.visitTypeInsn(CHECKCAST, columnName); + } + } + } else { + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, interName, tgetter.getName(), Type.getMethodDescriptor(tgetter), false); + if (pcolumn != column) { + mv.visitMethodInsn(INVOKESTATIC, columnName, "valueOf", "(" + Type.getDescriptor(pcolumn) + ")" + columnDesc, false); + m = 2; + } else { + if (checkCast) mv.visitTypeInsn(CHECKCAST, columnName); + } + } + mv.visitInsn(ARETURN); + mv.visitMaxs(m, 2); + mv.visitEnd(); + } + { //set 方法 + mv = cw.visitMethod(ACC_PUBLIC, "set", "(" + interDesc + columnDesc + ")V", null, null); + int m = 2; + if (tsetter == null) { + if (tfield == null || java.lang.reflect.Modifier.isFinal(tfield.getModifiers())) { + m = 0; + } else { //public tfield + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + if (pcolumn != column) { + try { + java.lang.reflect.Method pm = column.getMethod(pcolumn.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, columnName, pm.getName(), Type.getMethodDescriptor(pm), false); + m = 3; + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + if (!tfield.getType().isPrimitive() && tfield.getGenericType() instanceof TypeVariable) { + mv.visitFieldInsn(PUTFIELD, interName, tfield.getName(), "Ljava/lang/Object;"); + } else { + mv.visitFieldInsn(PUTFIELD, interName, tfield.getName(), Type.getDescriptor(pcolumn)); + } + } + } else { + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + if (pcolumn != column) { + try { + java.lang.reflect.Method pm = column.getMethod(pcolumn.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, columnName, pm.getName(), Type.getMethodDescriptor(pm), false); + m = 3; + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } + mv.visitMethodInsn(INVOKEVIRTUAL, interName, tsetter.getName(), Type.getMethodDescriptor(tsetter), false); + } + mv.visitInsn(RETURN); + mv.visitMaxs(m, 3); + mv.visitEnd(); + } + { //虚拟get + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "get", "(Ljava/lang/Object;)Ljava/lang/Object;", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, interName); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "get", "(" + interDesc + ")" + columnDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + {//虚拟set + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, interName); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, columnName); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "set", "(" + interDesc + columnDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + { //toString函数 + mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null); + //mv.setDebug(true); + mv.visitLdcInsn(tostr); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + + byte[] bytes = cw.toByteArray(); + Class newClazz = (Class) new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + try { + Attribute rs = newClazz.getDeclaredConstructor().newInstance(); + java.lang.reflect.Field _gtype = rs.getClass().getDeclaredField("_gtype"); + _gtype.setAccessible(true); + _gtype.set(rs, generictype); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), _gtype); + java.lang.reflect.Field _attach = rs.getClass().getDeclaredField("_attach"); + _attach.setAccessible(true); + _attach.set(rs, attach); + RedkaleClassLoader.putReflectionField(newDynName.replace('/', '.'), _attach); + return rs; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + /** + * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldname 字段名 + * @param fieldtype 字段类型 + * @param getter getter方法 + * @param setter setter方法 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, final Function getter, final BiConsumer setter) { + return create(clazz, fieldname, fieldtype, fieldtype, getter, setter); + } + + /** + * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldname 字段名 + * @param fieldtype 字段类型 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, final Function getter, final BiConsumer setter, Object attach) { + return create(clazz, fieldname, fieldtype, fieldtype, getter, setter, attach); + } + + /** + * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldname 字段名 + * @param fieldtype 字段类型 + * @param fieldGenericType 字段泛型 + * @param getter getter方法 + * @param setter setter方法 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, + final java.lang.reflect.Type fieldGenericType, final Function getter, final BiConsumer setter) { + return create(clazz, fieldname, fieldtype, fieldGenericType, getter, setter, null); + } + + /** + * 根据Class、字段名、字段类型、getter和setter方法生成 Attribute 对象。 clazz、fieldname、fieldtype都不能为null + * + * @param 依附类的类型 + * @param 字段类型 + * @param clazz 指定依附的类 + * @param fieldname 字段名 + * @param fieldtype 字段类型 + * @param fieldGenericType 字段泛型 + * @param getter getter方法 + * @param setter setter方法 + * @param attach 附加对象 + * + * @return Attribute对象 + */ + public static Attribute create(final Class clazz, final String fieldname, final Class fieldtype, + final java.lang.reflect.Type fieldGenericType, final Function getter, final BiConsumer setter, final Object attach) { + Objects.requireNonNull(clazz); + Objects.requireNonNull(fieldname); + Objects.requireNonNull(fieldtype); + String str = Attribute.class.getSimpleName() + "_" + fieldname + "_" + fieldtype.getSimpleName(); + if (getter != null) str += "_getter"; + if (setter != null) str += "_setter"; + final String tostr = str; + return new Attribute() { + @Override + public Class type() { + return fieldtype; + } + + @Override + public java.lang.reflect.Type genericType() { + return fieldGenericType; + } + + @Override + public E attach() { + return (E) attach; + } + + @Override + public Class declaringClass() { + return clazz; + } + + @Override + public String field() { + return fieldname; + } + + @Override + public F get(T obj) { + return getter == null ? null : getter.apply(obj); + } + + @Override + public void set(T obj, F value) { + if (setter != null) setter.accept(obj, value); + } + + @Override + public String toString() { + return tostr; + } + }; + } +} diff --git a/src/org/redkale/util/AutoLoad.java b/src/main/java/org/redkale/util/AutoLoad.java similarity index 96% rename from src/org/redkale/util/AutoLoad.java rename to src/main/java/org/redkale/util/AutoLoad.java index ef0fadf4a..c42284976 100644 --- a/src/org/redkale/util/AutoLoad.java +++ b/src/main/java/org/redkale/util/AutoLoad.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - -/** - * 自动加载。 使用场景: - * 1、被标记为@AutoLoad(false)的Service类不会被自动加载, 当被依赖时才会被加载 - * 2、被标记为@AutoLoad(false)的Servlet类不会被自动加载 - * - *

    详情见: https://redkale.org - * @author zhangjx - */ -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface AutoLoad { - - boolean value() default true; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; + +/** + * 自动加载。 使用场景: + * 1、被标记为@AutoLoad(false)的Service类不会被自动加载, 当被依赖时才会被加载 + * 2、被标记为@AutoLoad(false)的Servlet类不会被自动加载 + * + *

    详情见: https://redkale.org + * @author zhangjx + */ +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface AutoLoad { + + boolean value() default true; +} diff --git a/src/main/java/org/redkale/util/Bean.java b/src/main/java/org/redkale/util/Bean.java new file mode 100644 index 000000000..c34c0ba64 --- /dev/null +++ b/src/main/java/org/redkale/util/Bean.java @@ -0,0 +1,23 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 标记参数bean + * + * @since 2.5.0 + */ +@Inherited +@Documented +@Target(TYPE) +@Retention(RUNTIME) +public @interface Bean { + +} diff --git a/src/org/redkale/util/ByteArray.java b/src/main/java/org/redkale/util/ByteArray.java similarity index 61% rename from src/org/redkale/util/ByteArray.java rename to src/main/java/org/redkale/util/ByteArray.java index ff13a769e..c1a20e1df 100644 --- a/src/org/redkale/util/ByteArray.java +++ b/src/main/java/org/redkale/util/ByteArray.java @@ -1,759 +1,965 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.io.*; -import java.nio.ByteBuffer; -import java.nio.charset.*; -import java.util.Arrays; - -/** - * 简单的byte[]操作类。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class ByteArray implements ByteTuple { - - private byte[] content; - - private int count; - - public ByteArray() { - this(1024); - } - - public ByteArray(int size) { - content = new byte[Math.max(128, size)]; - } - - public ByteArray(ByteTuple tuple) { - content = tuple.content(); - count = tuple.length(); - } - - /** - * 清空数据,将count置为0,并不清掉byte[]的内容 - * - * @return ByteArray 是否相同 - */ - public ByteArray clear() { - this.count = 0; - return this; - } - - /** - * 生成一个OutputStream - * - * @return OutputStream - */ - public OutputStream newOutputStream() { - return new OutputStream() { - @Override - public void write(int b) throws IOException { - put((byte) b); - } - }; - } - - /** - * 比较内容是否相同 - * - * @param bytes 待比较内容 - * - * @return 是否相同 - */ - public boolean equal(final byte[] bytes) { - if (bytes == null) return false; - int len = count; - if (len != bytes.length) return false; - byte[] ba = content; - for (int i = 0; i < len; i++) { - if (ba[i] != bytes[i]) return false; - } - return true; - } - - /** - * 判断内容是否为空 - * - * @return 是否为空 - */ - public boolean isEmpty() { - return count == 0; - } - - /** - * 获取字节长度 - * - * @return 长度 - */ - @Override - public int length() { - return count; - } - - @Override - public int offset() { - return 0; - } - - /** - * 获取指定位置的byte值,须确保0 <= index < length - * - * @param index 位置 - * - * @return byte值 - */ - public byte get(int index) { - return content[index]; - } - - /** - * 获取指定位置的char值,须确保0 <= offset+2 < length - * - * @param offset 位置 - * - * @return short值 - */ - public char getChar(int offset) { - return (char) ((0xff00 & (content[offset] << 8)) | (0xff & content[offset + 1])); - } - - /** - * 获取指定位置的short值,须确保0 <= offset+2 < length - * - * @param offset 位置 - * - * @return short值 - */ - public int getShort(int offset) { - return (short) ((0xff00 & (content[offset] << 8)) | (0xff & content[offset + 1])); - } - - /** - * 获取指定位置的int值,须确保0 <= offset+4 < length - * - * @param offset 位置 - * - * @return int值 - */ - public int getInt(int offset) { - return ((content[offset] & 0xff) << 24) | ((content[offset + 1] & 0xff) << 16) | ((content[offset + 2] & 0xff) << 8) | (content[offset + 3] & 0xff); - } - - /** - * 获取指定位置的long值,须确保0 <= offset+8 < length - * - * @param offset 位置 - * - * @return long值 - */ - public long getLong(int offset) { - return (((long) content[offset] & 0xff) << 56) | (((long) content[offset + 1] & 0xff) << 48) | (((long) content[offset + 2] & 0xff) << 40) | (((long) content[offset + 3] & 0xff) << 32) - | (((long) content[offset + 4] & 0xff) << 24) | (((long) content[offset + 5] & 0xff) << 16) | (((long) content[offset + 6] & 0xff) << 8) | ((long) content[offset + 7] & 0xff); - } - - /** - * 获取最后一个字节值,调用前须保证count大于0 - * - * @return byte值 - */ - public byte getLastByte() { - return content[count - 1]; - } - - /** - * count减一,调用前须保证count大于0 - * - */ - public void backCount() { - count--; - } - - /** - * 将buf内容覆盖到本对象内容中 - * - * @param buf 目标容器 - */ - public void copyTo(byte[] buf) { - System.arraycopy(this.content, 0, buf, 0, count); - } - - /** - * 将ByteBuffer的内容读取到本对象中 - * - * @param buffer ByteBuffer - */ - public void put(ByteBuffer buffer) { - if (buffer == null) return; - int remain = buffer.remaining(); - if (remain == 0) return; - int l = this.content.length - count; - if (remain > l) { - byte[] ns = new byte[this.content.length + remain]; - if (count > 0) System.arraycopy(content, 0, ns, 0, count); - this.content = ns; - } - buffer.get(content, count, remain); - count += remain; - } - - /** - * 将array的内容引用给本对象 - * - * @param array ByteArray - */ - public void directFrom(ByteArray array) { - if (array != null) { - this.content = array.content; - this.count = array.count; - } - } - - /** - * 将content的内容直接给本对象 - * - * @param content 内容 - * @param count 长度 - */ - public void directFrom(byte[] content, int count) { - this.content = content; - this.count = count; - } - - /** - * 将本对象的内容引用复制给array - * - * @param array ByteArray - */ - public void directTo(ByteArray array) { - if (array != null) { - array.content = this.content; - array.count = this.count; - } - } - - /** - * 直接获取全部数据, 实际数据需要根据length长度来截取 - * - * @return byte[] - */ - @Override - public byte[] content() { - return content; - } - - /** - * 获取byte[] - * - * @return byte[] - */ - public byte[] getBytes() { - return Arrays.copyOf(content, count); - } - - /** - * 获取byte[] - * - * @param offset 偏移位 - * @param length 长度 - * - * @return byte[] - */ - public byte[] getBytes(int offset, int length) { - if (length < 1) return new byte[0]; - byte[] bs = new byte[length]; - System.arraycopy(this.content, offset, bs, 0, length); - return bs; - } - - /** - * 获取byte[]并清空 - * - * @return byte[] - */ - public byte[] getBytesAndClear() { - byte[] bs = Arrays.copyOf(content, count); - clear(); - return bs; - } - - /** - * 查找指定值第一次出现的位置,没有返回-1 - * - * @param value 查询值 - * - * @return 所在位置 - */ - public int find(byte value) { - return find(0, value); - } - - /** - * 从指定的起始位置查询value值出现的位置,没有返回-1 - * - * @param offset 起始位置 - * @param value 查询值 - * - * @return 所在位置 - */ - public int find(int offset, char value) { - return find(offset, (byte) value); - } - - /** - * 从指定的起始位置查询value值出现的位置,没有返回-1 - * - * @param offset 起始位置 - * @param value 查询值 - * - * @return 所在位置 - */ - public int find(int offset, byte value) { - return find(offset, -1, value); - } - - /** - * 从指定的起始位置和长度查询value值出现的位置,没有返回-1 - * - * @param offset 起始位置 - * @param limit 长度限制 - * @param value 查询值 - * - * @return 所在位置 - */ - public int find(int offset, int limit, char value) { - return find(offset, limit, (byte) value); - } - - /** - * 从指定的起始位置和长度查询value值出现的位置,没有返回-1 - * - * @param offset 起始位置 - * @param limit 长度限制 - * @param value 查询值 - * - * @return 所在位置 - */ - public int find(int offset, int limit, byte value) { - byte[] bytes = this.content; - int end = limit > 0 ? limit : count; - for (int i = offset; i < end; i++) { - if (bytes[i] == value) return i; - } - return -1; - } - - /** - * 移除最后一个字节 - */ - public void removeLastByte() { - if (count > 0) count--; - } - - /** - * 写入一个char值 - * - * @param value int值 - */ - public void putChar(char value) { - this.put((byte) (value >> 8 & 0xFF), - (byte) (value & 0xFF)); - } - - /** - * 写入一个char值, content.length 必须不能小于offset+2 - * - * @param offset 偏移量 - * @param value char值 - */ - public void putChar(int offset, char value) { - this.put(offset, (byte) (value >> 8 & 0xFF), - (byte) (value & 0xFF)); - } - - /** - * 写入一个short值 - * - * @param value short值 - */ - public void putShort(short value) { - this.put((byte) (value >> 8 & 0xFF), - (byte) (value & 0xFF)); - } - - /** - * 写入一个short值, content.length 必须不能小于offset+2 - * - * @param offset 偏移量 - * @param value short值 - */ - public void putShort(int offset, short value) { - this.put(offset, (byte) (value >> 8 & 0xFF), - (byte) (value & 0xFF)); - } - - /** - * 写入一个int值 - * - * @param value int值 - */ - public void putInt(int value) { - this.put((byte) (value >> 24 & 0xFF), - (byte) (value >> 16 & 0xFF), - (byte) (value >> 8 & 0xFF), - (byte) (value & 0xFF)); - } - - /** - * 指定位置写入一个int值, content.length 必须不能小于offset+4 - * - * @param offset 偏移量 - * @param value int值 - */ - public void putInt(int offset, int value) { - content[offset] = (byte) (value >> 24 & 0xFF); - content[offset + 1] = (byte) (value >> 16 & 0xFF); - content[offset + 2] = (byte) (value >> 8 & 0xFF); - content[offset + 3] = (byte) (value & 0xFF); - } - - /** - * 写入一个float值 - * - * @param value float值 - */ - public void putFloat(float value) { - this.putInt(Float.floatToIntBits(value)); - } - - /** - * 指定位置写入一个float值, content.length 必须不能小于offset+4 - * - * @param offset 偏移量 - * @param value float值 - */ - public void putFloat(int offset, float value) { - this.putInt(offset, Float.floatToIntBits(value)); - } - - /** - * 写入一个long值 - * - * @param value long值 - */ - public void putLong(long value) { - this.put((byte) (value >> 56 & 0xFF), - (byte) (value >> 48 & 0xFF), - (byte) (value >> 40 & 0xFF), - (byte) (value >> 32 & 0xFF), - (byte) (value >> 24 & 0xFF), - (byte) (value >> 16 & 0xFF), - (byte) (value >> 8 & 0xFF), - (byte) (value & 0xFF)); - } - - /** - * 指定位置写入一个long值, content.length 必须不能小于offset+8 - * - * @param offset 偏移量 - * @param value long值 - */ - public void putLong(int offset, long value) { - this.put(offset, (byte) (value >> 56 & 0xFF), - (byte) (value >> 48 & 0xFF), - (byte) (value >> 40 & 0xFF), - (byte) (value >> 32 & 0xFF), - (byte) (value >> 24 & 0xFF), - (byte) (value >> 16 & 0xFF), - (byte) (value >> 8 & 0xFF), - (byte) (value & 0xFF)); - } - - /** - * 写入一个double值 - * - * @param value double值 - */ - public void putDouble(double value) { - this.putLong(Double.doubleToLongBits(value)); - } - - /** - * 指定位置写入一个double值, content.length 必须不能小于offset+8 - * - * @param offset 偏移量 - * @param value double值 - */ - public void putDouble(int offset, double value) { - this.putLong(offset, Double.doubleToLongBits(value)); - } - - public void putByte(short value) { - put((byte) value); - } - - public void putByte(char value) { - put((byte) value); - } - - public void putByte(int value) { - put((byte) value); - } - - /** - * 写入一个byte值 - * - * @param value byte值 - */ - public void put(byte value) { - if (count >= content.length - 1) { - byte[] ns = new byte[content.length + 8]; - System.arraycopy(content, 0, ns, 0, count); - this.content = ns; - } - content[count++] = value; - } - - /** - * 写入一个byte值, content.length 必须不能小于offset+1 - * - * @param offset 偏移量 - * @param value byte值 - */ - public void putByte(int offset, byte value) { - content[offset] = value; - } - - /** - * 写入一个byte值, content.length 必须不能小于offset+1 - * - * @param offset 偏移量 - * @param value byte值 - */ - public void putByte(int offset, int value) { - content[offset] = (byte) value; - } - - /** - * 写入一组byte值 - * - * @param values 一组byte值 - */ - public void put(byte... values) { - if (count >= content.length - values.length) { - byte[] ns = new byte[content.length + values.length]; - System.arraycopy(content, 0, ns, 0, count); - this.content = ns; - } - System.arraycopy(values, 0, content, count, values.length); - count += values.length; - } - - /** - * 指定位置写入一组byte值, content.length 必须不能小于offset+values.length - * - * @param offset 偏移量 - * @param values 一组byte值 - */ - public void put(int offset, byte... values) { - System.arraycopy(values, 0, content, offset, values.length); - } - - /** - * 写入一组byte值 - * - * @param values 一组byte值 - * @param offset 偏移量 - * @param length 长度 - */ - public void put(byte[] values, int offset, int length) { - if (count >= content.length - length) { - byte[] ns = new byte[content.length + Math.max(16, length)]; - System.arraycopy(content, 0, ns, 0, count); - this.content = ns; - } - System.arraycopy(values, offset, content, count, length); - count += length; - } - - /** - * 写入一组byte值, content.length 必须不能小于poffset+length - * - * @param poffset 偏移量 - * @param values 一组byte值 - * @param offset 偏移量 - * @param length 长度 - */ - public void put(int poffset, byte[] values, int offset, int length) { - System.arraycopy(values, offset, content, poffset, length); - } - - /** - * 写入ByteArray中的一部分 - * - * @param array ByteArray - * @param offset 偏移量 - * @param length 长度 - */ - public void put(ByteArray array, int offset, int length) { - if (count >= content.length - length) { - byte[] ns = new byte[content.length + Math.max(16, length)]; - System.arraycopy(content, 0, ns, 0, count); - this.content = ns; - } - System.arraycopy(array.content, offset, content, count, length); - count += length; - } - - /** - * 写入ByteBuffer指定长度的数据 - * - * @param buffer 数据 - * @param len 指定长度 - */ - public void put(ByteBuffer buffer, int len) { - if (len < 1) return; - if (count >= content.length - len) { - byte[] ns = new byte[content.length + len]; - System.arraycopy(content, 0, ns, 0, count); - this.content = ns; - } - buffer.get(content, count, len); - count += len; - } - - @Override - public String toString() { - return new String(content, 0, count); - } - - /** - * 按指定字符集转成字符串 - * - * @param charset 字符集 - * - * @return 字符串 - */ - public String toString(final Charset charset) { - return toString(0, count, charset); - } - - /** - * 按指定字符集转成字符串并清空数据 - * - * @param charset 字符集 - * - * @return 字符串 - */ - public String toStringAndClear(final Charset charset) { - String str = toString(0, count, charset); - clear(); - return str; - } - - /** - * 将指定的起始位置和长度按指定字符集转成字符串 - * - * @param offset 起始位置 - * @param len 长度 - * @param charset 字符集 - * - * @return 字符串 - */ - public String toString(final int offset, int len, final Charset charset) { - if (charset == null) return new String(content, offset, len, StandardCharsets.UTF_8); - return new String(content, offset, len, charset); - } - - /** - * 将指定的起始位置和长度按指定字符集转成字符串,并trim - * - * @param offset 起始位置 - * @param len 长度 - * @param charset 字符集 - * - * @return 字符串 - */ - public String toTrimString(int offset, int len, final Charset charset) { - if (len == 0) return ""; - int st = 0; - while (st < len && (content[offset] & 0xff) <= ' ') { - offset++; - st++; - } - while (len > 0 && (content[len - 1] & 0xff) <= ' ') len--; - if (len == 0) return ""; - if (charset == null) return new String(content, offset, len - st, StandardCharsets.UTF_8); - return new String(content, offset, len - st, charset); - } - - /** - * 将指定的起始位置和长度按指定字符集并转义后转成字符串 - * - * @param charset 字符集 - * - * @return 字符串 - */ - public String toDecodeString(final Charset charset) { - return toDecodeString(0, count, charset); - } - - /** - * 将指定的起始位置和长度按指定字符集并转义后转成字符串 - * - * @param offset 起始位置 - * @param len 长度 - * @param charset 字符集 - * - * @return 字符串 - */ - public String toDecodeString(final int offset, int len, final Charset charset) { - int start = offset; - final int end = offset + len; - boolean flag = false; //是否需要转义 - byte[] bs = content; - for (int i = offset; i < end; i++) { - if (content[i] == '+' || content[i] == '%') { - flag = true; - break; - } - } - if (flag) { - int index = 0; - bs = new byte[len]; - for (int i = offset; i < end; i++) { - switch (content[i]) { - case '+': - bs[index] = ' '; - break; - case '%': - bs[index] = (byte) ((hexBit(content[++i]) * 16 + hexBit(content[++i]))); - break; - default: - bs[index] = content[i]; - break; - } - index++; - } - start = 0; - len = index; - } - if (charset == null) return new String(bs, start, len, StandardCharsets.UTF_8); - return new String(bs, start, len, charset); - } - - private static int hexBit(byte b) { - if ('0' <= b && '9' >= b) return b - '0'; - if ('a' <= b && 'z' >= b) return b - 'a' + 10; - if ('A' <= b && 'Z' >= b) return b - 'A' + 10; - return b; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.charset.*; +import java.util.Arrays; + +/** + * 简单的byte[]操作类。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class ByteArray implements ByteTuple { + + private byte[] content; + + private int count; + + public ByteArray() { + this(1024); + } + + public ByteArray(int size) { + content = new byte[Math.max(128, size)]; + } + + public ByteArray(ByteTuple tuple) { + content = tuple.content(); + count = tuple.length(); + } + + public ByteArray(byte[] bs) { + content = bs; + count = 0; + } + + /** + * 清空数据,将count置为0,并不清掉byte[]的内容 + * + * @return ByteArray 是否相同 + */ + public ByteArray clear() { + this.count = 0; + return this; + } + + /** + * 生成一个OutputStream + * + * @return OutputStream + */ + public OutputStream newOutputStream() { + return new OutputStream() { + @Override + public void write(int b) throws IOException { + ByteArray.this.put((byte) b); + } + }; + } + + /** + * 比较内容是否相同 + * + * @param bytes 待比较内容 + * + * @return 是否相同 + */ + public boolean equal(final byte[] bytes) { + if (bytes == null) return false; + int len = count; + if (len != bytes.length) return false; + byte[] ba = content; + for (int i = 0; i < len; i++) { + if (ba[i] != bytes[i]) return false; + } + return true; + } + + /** + * 判断内容是否为空 + * + * @return 是否为空 + */ + public boolean isEmpty() { + return count == 0; + } + + /** + * 获取字节长度 + * + * @return 长度 + */ + @Override + public int length() { + return count; + } + + @Override + public int offset() { + return 0; + } + + /** + * 获取指定位置的byte值,须确保0 <= index < length + * + * @param index 位置 + * + * @return byte值 + */ + public byte get(int index) { + return content[index]; + } + + /** + * 获取指定位置的char值,须确保0 <= offset+2 < length + * + * @param offset 位置 + * + * @return short值 + */ + public char getChar(int offset) { + return (char) ((0xff00 & (content[offset] << 8)) | (0xff & content[offset + 1])); + } + + /** + * 获取指定位置的short值,须确保0 <= offset+2 < length + * + * @param offset 位置 + * + * @return short值 + */ + public int getShort(int offset) { + return (short) ((0xff00 & (content[offset] << 8)) | (0xff & content[offset + 1])); + } + + /** + * 获取指定位置的int值,须确保0 <= offset+4 < length + * + * @param offset 位置 + * + * @return int值 + */ + public int getInt(int offset) { + return ((content[offset] & 0xff) << 24) | ((content[offset + 1] & 0xff) << 16) | ((content[offset + 2] & 0xff) << 8) | (content[offset + 3] & 0xff); + } + + /** + * 获取指定位置的float值,须确保0 <= offset+4 < length + * + * @param offset 位置 + * + * @return float值 + */ + public float getFloat(int offset) { + return Float.intBitsToFloat(getInt(offset)); + } + + /** + * 获取指定位置的long值,须确保0 <= offset+8 < length + * + * @param offset 位置 + * + * @return long值 + */ + public long getLong(int offset) { + return (((long) content[offset] & 0xff) << 56) | (((long) content[offset + 1] & 0xff) << 48) | (((long) content[offset + 2] & 0xff) << 40) | (((long) content[offset + 3] & 0xff) << 32) + | (((long) content[offset + 4] & 0xff) << 24) | (((long) content[offset + 5] & 0xff) << 16) | (((long) content[offset + 6] & 0xff) << 8) | ((long) content[offset + 7] & 0xff); + } + + /** + * 获取指定位置的double值,须确保0 <= offset+8 < length + * + * @param offset 位置 + * + * @return double值 + */ + public double getDouble(int offset) { + return Double.longBitsToDouble(getLong(offset)); + } + + /** + * 获取最后一个字节值,调用前须保证count大于0 + * + * @return byte值 + */ + public byte getLastByte() { + return content[count - 1]; + } + + /** + * count减一,调用前须保证count大于0 + * + */ + public void backCount() { + count--; + } + + /** + * 将buf内容覆盖到本对象内容中 + * + * @param buf 目标容器 + */ + public void copyTo(byte[] buf) { + System.arraycopy(this.content, 0, buf, 0, count); + } + + /** + * 将ByteBuffer的内容读取到本对象中 + * + * @param buffer ByteBuffer + */ + public void put(ByteBuffer buffer) { + if (buffer == null) return; + int remain = buffer.remaining(); + if (remain == 0) return; + int l = this.content.length - count; + if (remain > l) { + byte[] ns = new byte[this.content.length + remain]; + if (count > 0) System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + buffer.get(content, count, remain); + count += remain; + } + + /** + * 将array的内容引用给本对象 + * + * @param array ByteArray + */ + public void directFrom(ByteArray array) { + if (array != null) { + this.content = array.content; + this.count = array.count; + } + } + + /** + * 将content的内容直接给本对象 + * + * @param content 内容 + * @param count 长度 + */ + public void directFrom(byte[] content, int count) { + this.content = content; + this.count = count; + } + + /** + * 将本对象的内容引用复制给array + * + * @param array ByteArray + */ + public void directTo(ByteArray array) { + if (array != null) { + array.content = this.content; + array.count = this.count; + } + } + + /** + * 直接获取全部数据, 实际数据需要根据length长度来截取 + * + * @return byte[] + */ + @Override + public byte[] content() { + return content; + } + + /** + * 获取byte[] + * + * @return byte[] + */ + public byte[] getBytes() { + return Arrays.copyOf(content, count); + } + + /** + * 获取byte[] + * + * @param offset 偏移位 + * @param length 长度 + * + * @return byte[] + */ + public byte[] getBytes(int offset, int length) { + if (length < 1) return new byte[0]; + byte[] bs = new byte[length]; + System.arraycopy(this.content, offset, bs, 0, length); + return bs; + } + + /** + * 查找指定值第一次出现的位置,没有返回-1 + * + * @param value 查询值 + * + * @return 所在位置 + */ + public int find(byte value) { + return find(0, value); + } + + /** + * 从指定的起始位置查询value值出现的位置,没有返回-1 + * + * @param offset 起始位置 + * @param value 查询值 + * + * @return 所在位置 + */ + public int find(int offset, char value) { + return find(offset, (byte) value); + } + + /** + * 从指定的起始位置查询value值出现的位置,没有返回-1 + * + * @param offset 起始位置 + * @param value 查询值 + * + * @return 所在位置 + */ + public int find(int offset, byte value) { + return find(offset, -1, value); + } + + /** + * 从指定的起始位置和长度查询value值出现的位置,没有返回-1 + * + * @param offset 起始位置 + * @param limit 长度限制 + * @param value 查询值 + * + * @return 所在位置 + */ + public int find(int offset, int limit, char value) { + return find(offset, limit, (byte) value); + } + + /** + * 从指定的起始位置和长度查询value值出现的位置,没有返回-1 + * + * @param offset 起始位置 + * @param limit 长度限制 + * @param value 查询值 + * + * @return 所在位置 + */ + public int find(int offset, int limit, byte value) { + byte[] bytes = this.content; + int end = limit > 0 ? limit : count; + for (int i = offset; i < end; i++) { + if (bytes[i] == value) return i; + } + return -1; + } + + /** + * 移除最后一个字节 + * + * @return ByteArray + */ + public ByteArray removeLastByte() { + if (count > 0) count--; + return this; + } + + /** + * 写入一个char值 + * + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putChar(char value) { + return this.put((byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个char值, content.length 必须不能小于offset+2 + * + * @param offset 偏移量 + * @param value char值 + * + * @return ByteArray + */ + public ByteArray putChar(int offset, char value) { + return this.put(offset, (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个short值 + * + * @param value short值 + * + * @return ByteArray + */ + public ByteArray putShort(short value) { + return this.put((byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个short值 + * + * @param value short值 + * + * @return ByteArray + */ + public ByteArray putShort(char value) { + int v = value; + return this.put((byte) (v >> 8 & 0xFF), + (byte) (v & 0xFF)); + } + + /** + * 写入一个short值 + * + * @param value short值 + * + * @return ByteArray + */ + public ByteArray putShort(int value) { + return this.put((byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个short值 + * + * @param value short值 + * + * @return ByteArray + */ + public ByteArray putUnsignedShort(int value) { + return this.put((byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个无符号short值, content.length 必须不能小于offset+2 + * + * @param offset 偏移量 + * @param value short值 + * + * @return ByteArray + */ + public ByteArray putUnsignedShort(int offset, int value) { + return this.put(offset, (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个short值, content.length 必须不能小于offset+2 + * + * @param offset 偏移量 + * @param value short值 + * + * @return ByteArray + */ + public ByteArray putShort(int offset, short value) { + return this.put(offset, (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个3字节的int值 + * + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putMedium(int value) { + return this.put((byte) (value >> 16 & 0xFF), + (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 指定位置写入一个3字节的int值, content.length 必须不能小于offset+3 + * + * @param offset 偏移量 + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putMedium(int offset, int value) { + content[offset] = (byte) (value >> 16 & 0xFF); + content[offset + 1] = (byte) (value >> 8 & 0xFF); + content[offset + 2] = (byte) (value & 0xFF); + return this; + } + + /** + * 写入一个3字节的int值 + * + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putUnsignedMedium(int value) { + return this.put((byte) (value >> 16 & 0xFF), + (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 指定位置写入一个3字节的int值, content.length 必须不能小于offset+3 + * + * @param offset 偏移量 + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putUnsignedMedium(int offset, int value) { + content[offset] = (byte) (value >> 16 & 0xFF); + content[offset + 1] = (byte) (value >> 8 & 0xFF); + content[offset + 2] = (byte) (value & 0xFF); + return this; + } + + /** + * 写入一个int值 + * + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putInt(int value) { + return this.put((byte) (value >> 24 & 0xFF), + (byte) (value >> 16 & 0xFF), + (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个int值 + * + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putUnsignedInt(long value) { + return this.put((byte) (value >> 24 & 0xFF), + (byte) (value >> 16 & 0xFF), + (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 指定位置写入一个int值, content.length 必须不能小于offset+4 + * + * @param offset 偏移量 + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putInt(int offset, int value) { + content[offset] = (byte) (value >> 24 & 0xFF); + content[offset + 1] = (byte) (value >> 16 & 0xFF); + content[offset + 2] = (byte) (value >> 8 & 0xFF); + content[offset + 3] = (byte) (value & 0xFF); + return this; + } + + /** + * 指定位置写入一个 无符号int值, content.length 必须不能小于offset+4 + * + * @param offset 偏移量 + * @param value int值 + * + * @return ByteArray + */ + public ByteArray putUnsignedInt(int offset, long value) { + content[offset] = (byte) (value >> 24 & 0xFF); + content[offset + 1] = (byte) (value >> 16 & 0xFF); + content[offset + 2] = (byte) (value >> 8 & 0xFF); + content[offset + 3] = (byte) (value & 0xFF); + return this; + } + + /** + * 写入一个float值 + * + * @param value float值 + * + * @return ByteArray + */ + public ByteArray putFloat(float value) { + return this.putInt(Float.floatToIntBits(value)); + } + + /** + * 指定位置写入一个float值, content.length 必须不能小于offset+4 + * + * @param offset 偏移量 + * @param value float值 + * + * @return ByteArray + */ + public ByteArray putFloat(int offset, float value) { + return this.putInt(offset, Float.floatToIntBits(value)); + } + + /** + * 写入一个long值 + * + * @param value long值 + * + * @return ByteArray + */ + public ByteArray putLong(long value) { + return this.put((byte) (value >> 56 & 0xFF), + (byte) (value >> 48 & 0xFF), + (byte) (value >> 40 & 0xFF), + (byte) (value >> 32 & 0xFF), + (byte) (value >> 24 & 0xFF), + (byte) (value >> 16 & 0xFF), + (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 指定位置写入一个long值, content.length 必须不能小于offset+8 + * + * @param offset 偏移量 + * @param value long值 + * + * @return ByteArray + */ + public ByteArray putLong(int offset, long value) { + return this.put(offset, (byte) (value >> 56 & 0xFF), + (byte) (value >> 48 & 0xFF), + (byte) (value >> 40 & 0xFF), + (byte) (value >> 32 & 0xFF), + (byte) (value >> 24 & 0xFF), + (byte) (value >> 16 & 0xFF), + (byte) (value >> 8 & 0xFF), + (byte) (value & 0xFF)); + } + + /** + * 写入一个double值 + * + * @param value double值 + * + * @return ByteArray + */ + public ByteArray putDouble(double value) { + return this.putLong(Double.doubleToLongBits(value)); + } + + /** + * 指定位置写入一个double值, content.length 必须不能小于offset+8 + * + * @param offset 偏移量 + * @param value double值 + * + * @return ByteArray + */ + public ByteArray putDouble(int offset, double value) { + return this.putLong(offset, Double.doubleToLongBits(value)); + } + + public ByteArray putByte(short value) { + return put((byte) value); + } + + public ByteArray putByte(char value) { + return put((byte) value); + } + + public ByteArray putByte(int value) { + return put((byte) value); + } + + /** + * 写入一个byte值 + * + * @param value byte值 + * + * @return ByteArray + */ + public ByteArray put(byte value) { + if (count >= content.length - 1) { + byte[] ns = new byte[content.length + 8]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + content[count++] = value; + return this; + } + + /** + * 写入一个byte值, content.length 必须不能小于offset+1 + * + * @param offset 偏移量 + * @param value byte值 + * + * @return ByteArray + */ + public ByteArray putByte(int offset, byte value) { + content[offset] = value; + return this; + } + + /** + * 写入一个byte值, content.length 必须不能小于offset+1 + * + * @param offset 偏移量 + * @param value byte值 + * + * @return ByteArray + */ + public ByteArray putByte(int offset, int value) { + content[offset] = (byte) value; + return this; + } + + /** + * 写入一组byte值 + * + * @param values 一组byte值 + * + * @return ByteArray + */ + public ByteArray put(byte... values) { + if (values.length < 1) return this; + if (count >= content.length - values.length) { + byte[] ns = new byte[content.length + values.length]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + System.arraycopy(values, 0, content, count, values.length); + count += values.length; + return this; + } + + /** + * 指定位置写入一组byte值, content.length 必须不能小于offset+values.length + * + * @param offset 偏移量 + * @param values 一组byte值 + * + * @return ByteArray + */ + public ByteArray put(int offset, byte... values) { + if (values.length < 1) throw new IllegalArgumentException(); + System.arraycopy(values, 0, content, offset, values.length); + return this; + } + + /** + * 写入一组byte值 + * + * @param values 一组byte值 + * @param offset 偏移量 + * @param length 长度 + * + * @return ByteArray + */ + public ByteArray put(byte[] values, int offset, int length) { + if (count >= content.length - length) { + byte[] ns = new byte[content.length + Math.max(16, length)]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + System.arraycopy(values, offset, content, count, length); + count += length; + return this; + } + + /** + * 写入一组byte值, content.length 必须不能小于poffset+length + * + * @param poffset 偏移量 + * @param values 一组byte值 + * @param offset 偏移量 + * @param length 长度 + * + * @return ByteArray + */ + public ByteArray put(int poffset, byte[] values, int offset, int length) { + System.arraycopy(values, offset, content, poffset, length); + return this; + } + + /** + * 写入ByteArray中的一部分 + * + * @param array ByteArray + * @param offset 偏移量 + * @param length 长度 + * + * @return ByteArray + */ + public ByteArray put(ByteArray array, int offset, int length) { + if (count >= content.length - length) { + byte[] ns = new byte[content.length + Math.max(16, length)]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + System.arraycopy(array.content, offset, content, count, length); + count += length; + return this; + } + + /** + * 写入ByteBuffer指定长度的数据 + * + * @param buffer 数据 + * @param len 指定长度 + * + * @return ByteArray + */ + public ByteArray put(ByteBuffer buffer, int len) { + if (len < 1) return this; + if (count >= content.length - len) { + byte[] ns = new byte[content.length + len]; + System.arraycopy(content, 0, ns, 0, count); + this.content = ns; + } + buffer.get(content, count, len); + count += len; + return this; + } + + @Override + public String toString() { + return new String(content, 0, count); + } + + /** + * 按指定字符集转成字符串 + * + * @param charset 字符集 + * + * @return 字符串 + */ + public String toString(final Charset charset) { + return toString(0, count, charset); + } + + /** + * 将指定的起始位置和长度按指定字符集转成字符串 + * + * @param offset 起始位置 + * @param len 长度 + * @param charset 字符集 + * + * @return 字符串 + */ + public String toString(final int offset, int len, final Charset charset) { + if (charset == null) return new String(content, offset, len, StandardCharsets.UTF_8); + return new String(content, offset, len, charset); + } + + /** + * 将指定的起始位置按指定字符集转成字符串 + * + * @param offset 起始位置 + * @param charset 字符集 + * + * @return 字符串 + */ + public String toString(final int offset, final Charset charset) { + if (charset == null) return new String(content, offset, count - offset, StandardCharsets.UTF_8); + return new String(content, offset, count - offset, charset); + } + + /** + * 将指定的起始位置和长度按指定字符集转成字符串,并trim + * + * @param offset 起始位置 + * @param len 长度 + * @param charset 字符集 + * + * @return 字符串 + */ +// public String toTrimString(int offset, int len, final Charset charset) { +// if (len == 0) return ""; +// int st = 0; +// while (st < len && (content[offset] & 0xff) <= ' ') { +// offset++; +// st++; +// } +// while (len > 0 && (content[len - 1] & 0xff) <= ' ') len--; +// if (len == 0) return ""; +// if (charset == null) return new String(content, offset, len - st, StandardCharsets.UTF_8); +// return new String(content, offset, len - st, charset); +// } + /** + * 将指定的起始位置和长度按指定字符集并转义后转成字符串 + * + * @param charset 字符集 + * + * @return 字符串 + */ +// public String toDecodeString(final Charset charset) { +// return toDecodeString(0, count, charset); +// } + /** + * 将指定的起始位置和长度按指定字符集并转义后转成字符串 + * + * @param offset 起始位置 + * @param len 长度 + * @param charset 字符集 + * + * @return 字符串 + */ +// public String toDecodeString(final int offset, int len, final Charset charset) { +// int start = offset; +// final int end = offset + len; +// boolean flag = false; //是否需要转义 +// byte[] bs = content; +// for (int i = offset; i < end; i++) { +// if (content[i] == '+' || content[i] == '%') { +// flag = true; +// break; +// } +// } +// if (flag) { +// int index = 0; +// bs = new byte[len]; +// for (int i = offset; i < end; i++) { +// switch (content[i]) { +// case '+': +// bs[index] = ' '; +// break; +// case '%': +// bs[index] = (byte) ((hexBit(content[++i]) * 16 + hexBit(content[++i]))); +// break; +// default: +// bs[index] = content[i]; +// break; +// } +// index++; +// } +// start = 0; +// len = index; +// } +// if (charset == null) return new String(bs, start, len, StandardCharsets.UTF_8); +// return new String(bs, start, len, charset); +// } +// +// private static int hexBit(byte b) { +// if ('0' <= b && '9' >= b) return b - '0'; +// if ('a' <= b && 'z' >= b) return b - 'a' + 10; +// if ('A' <= b && 'Z' >= b) return b - 'A' + 10; +// return b; +// } +} diff --git a/src/org/redkale/util/ByteBufferReader.java b/src/main/java/org/redkale/util/ByteBufferReader.java similarity index 95% rename from src/org/redkale/util/ByteBufferReader.java rename to src/main/java/org/redkale/util/ByteBufferReader.java index edd84daf9..09729f098 100644 --- a/src/org/redkale/util/ByteBufferReader.java +++ b/src/main/java/org/redkale/util/ByteBufferReader.java @@ -1,372 +1,396 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.nio.*; -import java.util.*; - -/** - * 以ByteBuffer为数据载体的Reader
    - * 注意:最小可读空间至少是8 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class ByteBufferReader { - - private ByteBuffer[] buffers; - - private int currIndex; - - private ByteBuffer currBuffer; - - private final boolean bigEndian; - - public ByteBufferReader(Collection buffers) { - Objects.requireNonNull(buffers); - this.buffers = buffers.toArray(new ByteBuffer[buffers.size()]); - this.currBuffer = this.buffers[0]; - this.currIndex = 0; - this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN; - } - - public ByteBufferReader(ByteBuffer[] buffers) { - Objects.requireNonNull(buffers); - this.buffers = buffers; - this.currBuffer = this.buffers[0]; - this.currIndex = 0; - this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN; - } - - public ByteBufferReader(ByteBuffer buffer) { - Objects.requireNonNull(buffer); - this.buffers = new ByteBuffer[]{buffer}; - this.currBuffer = this.buffers[0]; - this.currIndex = 0; - this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN; - } - - public ByteBufferReader append(ByteBuffer... buffs) { - for (ByteBuffer buf : buffs) { - Objects.requireNonNull(buf); - } - this.buffers = Utility.append(this.buffers, buffs); - return this; - } - - public static ByteBufferReader create(ByteBuffer buffer) { - return new ByteBufferReader(buffer); - } - - public static ByteBufferReader create(Collection buffers) { - return new ByteBufferReader(buffers); - } - - public static ByteBufferReader create(ByteBuffer[] buffers) { - return new ByteBufferReader(buffers); - } - - public static byte[] toBytes(ByteBuffer[] buffers) { - if (buffers == null) return null; - int size = 0; - for (ByteBuffer buffer : buffers) { - size += buffer.remaining(); - } - byte[] bs = new byte[size]; - int index = 0; - for (ByteBuffer buffer : buffers) { - int remain = buffer.remaining(); - buffer.get(bs, index, remain); - index += remain; - } - return bs; - } - - public boolean hasRemaining() { - boolean v = this.currBuffer.hasRemaining(); - if (v) return v; - if (this.currIndex == this.buffers.length - 1) return false; - for (int i = this.currIndex + 1; i < this.buffers.length; i++) { - if (this.buffers[i].hasRemaining()) return true; - } - return false; - } - - public int remaining() { - int v = this.currBuffer.remaining(); - for (int i = this.currIndex + 1; i < this.buffers.length; i++) { - v += this.buffers[i].remaining(); - } - return v; - } - - //提前预读一个字节 - public byte preget() { - ByteBuffer buf = this.currBuffer; - if (!buf.hasRemaining()) { - buf = this.buffers[this.currIndex + 1]; - } - return buf.get(buf.position()); - } - - public byte get() { - ByteBuffer buf = this.currBuffer; - if (!buf.hasRemaining()) { - buf = this.buffers[++this.currIndex]; - this.currBuffer = buf; - } - return this.currBuffer.get(); - } - - public short getShort() { - ByteBuffer buf = this.currBuffer; - int remain = buf.remaining(); - if (remain >= 2) return buf.getShort(); - if (remain == 0) { - buf = this.buffers[++this.currIndex]; - this.currBuffer = buf; - return buf.getShort(); - } - if (bigEndian) return (short) ((buf.get() << 8) | (get() & 0xff)); - return (short) ((buf.get() & 0xff) | (get() << 8)); - } - - public int getInt() { - ByteBuffer buf = this.currBuffer; - int remain = buf.remaining(); - if (remain >= 4) return buf.getInt(); - if (remain == 0) { - buf = this.buffers[++this.currIndex]; - this.currBuffer = buf; - return buf.getInt(); - } - if (bigEndian) { - if (remain == 1) { - return ((buf.get() << 24) - | ((get() & 0xff) << 16) - | ((get() & 0xff) << 8) - | ((get() & 0xff))); - } - if (remain == 2) { - return ((buf.get() << 24) - | ((buf.get() & 0xff) << 16) - | ((get() & 0xff) << 8) - | ((get() & 0xff))); - } - //remain == 3 - return ((buf.get() << 24) - | ((buf.get() & 0xff) << 16) - | ((buf.get() & 0xff) << 8) - | ((get() & 0xff))); - } - if (remain == 1) { - return ((buf.get() & 0xff) - | ((get() & 0xff) << 8) - | ((get() & 0xff) << 16) - | ((get() << 24))); - } - if (remain == 2) { - return ((buf.get() & 0xff) - | ((buf.get() & 0xff) << 8) - | ((get() & 0xff) << 16) - | ((get() << 24))); - } - //remain == 3 - return ((buf.get()) & 0xff) - | ((buf.get() & 0xff) << 8) - | ((buf.get() & 0xff) << 16) - | ((get() << 24)); - } - - public float getFloat() { - return Float.intBitsToFloat(getInt()); - } - - public long getLong() { - ByteBuffer buf = this.currBuffer; - int remain = buf.remaining(); - if (remain >= 8) return buf.getLong(); - if (remain == 0) { - buf = this.buffers[++this.currIndex]; - this.currBuffer = buf; - return buf.getLong(); - } - if (bigEndian) { - if (remain == 1) { - return ((((long) buf.get()) << 56) - | (((long) get() & 0xff) << 48) - | (((long) get() & 0xff) << 40) - | (((long) get() & 0xff) << 32) - | (((long) get() & 0xff) << 24) - | (((long) get() & 0xff) << 16) - | (((long) get() & 0xff) << 8) - | (((long) get() & 0xff))); - } - if (remain == 2) { - return ((((long) buf.get()) << 56) - | (((long) buf.get() & 0xff) << 48) - | (((long) get() & 0xff) << 40) - | (((long) get() & 0xff) << 32) - | (((long) get() & 0xff) << 24) - | (((long) get() & 0xff) << 16) - | (((long) get() & 0xff) << 8) - | (((long) get() & 0xff))); - } - if (remain == 3) { - return ((((long) buf.get()) << 56) - | (((long) buf.get() & 0xff) << 48) - | (((long) buf.get() & 0xff) << 40) - | (((long) get() & 0xff) << 32) - | (((long) get() & 0xff) << 24) - | (((long) get() & 0xff) << 16) - | (((long) get() & 0xff) << 8) - | (((long) get() & 0xff))); - } - if (remain == 4) { - return ((((long) buf.get()) << 56) - | (((long) buf.get() & 0xff) << 48) - | (((long) buf.get() & 0xff) << 40) - | (((long) buf.get() & 0xff) << 32) - | (((long) get() & 0xff) << 24) - | (((long) get() & 0xff) << 16) - | (((long) get() & 0xff) << 8) - | (((long) get() & 0xff))); - } - if (remain == 5) { - return ((((long) buf.get()) << 56) - | (((long) buf.get() & 0xff) << 48) - | (((long) buf.get() & 0xff) << 40) - | (((long) buf.get() & 0xff) << 32) - | (((long) buf.get() & 0xff) << 24) - | (((long) get() & 0xff) << 16) - | (((long) get() & 0xff) << 8) - | (((long) get() & 0xff))); - } - if (remain == 6) { - return ((((long) buf.get()) << 56) - | (((long) buf.get() & 0xff) << 48) - | (((long) buf.get() & 0xff) << 40) - | (((long) buf.get() & 0xff) << 32) - | (((long) buf.get() & 0xff) << 24) - | (((long) buf.get() & 0xff) << 16) - | (((long) get() & 0xff) << 8) - | (((long) get() & 0xff))); - } - //remain == 7 - return ((((long) buf.get()) << 56) - | (((long) buf.get() & 0xff) << 48) - | (((long) buf.get() & 0xff) << 40) - | (((long) buf.get() & 0xff) << 32) - | (((long) buf.get() & 0xff) << 24) - | (((long) buf.get() & 0xff) << 16) - | (((long) buf.get() & 0xff) << 8) - | (((long) get() & 0xff))); - } - if (remain == 1) { - return ((((long) buf.get() & 0xff)) - | (((long) get() & 0xff) << 8) - | (((long) get() & 0xff) << 16) - | (((long) get() & 0xff) << 24) - | (((long) get() & 0xff) << 32) - | (((long) get() & 0xff) << 40) - | (((long) get() & 0xff) << 48) - | (((long) get()) << 56)); - } - if (remain == 2) { - return ((((long) buf.get() & 0xff)) - | (((long) buf.get() & 0xff) << 8) - | (((long) get() & 0xff) << 16) - | (((long) get() & 0xff) << 24) - | (((long) get() & 0xff) << 32) - | (((long) get() & 0xff) << 40) - | (((long) get() & 0xff) << 48) - | (((long) get()) << 56)); - } - if (remain == 3) { - return ((((long) buf.get() & 0xff)) - | (((long) buf.get() & 0xff) << 8) - | (((long) buf.get() & 0xff) << 16) - | (((long) get() & 0xff) << 24) - | (((long) get() & 0xff) << 32) - | (((long) get() & 0xff) << 40) - | (((long) get() & 0xff) << 48) - | (((long) get()) << 56)); - } - if (remain == 4) { - return ((((long) buf.get() & 0xff)) - | (((long) buf.get() & 0xff) << 8) - | (((long) buf.get() & 0xff) << 16) - | (((long) buf.get() & 0xff) << 24) - | (((long) get() & 0xff) << 32) - | (((long) get() & 0xff) << 40) - | (((long) get() & 0xff) << 48) - | (((long) get()) << 56)); - } - if (remain == 5) { - return ((((long) buf.get() & 0xff)) - | (((long) buf.get() & 0xff) << 8) - | (((long) buf.get() & 0xff) << 16) - | (((long) buf.get() & 0xff) << 24) - | (((long) buf.get() & 0xff) << 32) - | (((long) get() & 0xff) << 40) - | (((long) get() & 0xff) << 48) - | (((long) get()) << 56)); - } - if (remain == 6) { - return ((((long) buf.get() & 0xff)) - | (((long) buf.get() & 0xff) << 8) - | (((long) buf.get() & 0xff) << 16) - | (((long) buf.get() & 0xff) << 24) - | (((long) buf.get() & 0xff) << 32) - | (((long) buf.get() & 0xff) << 40) - | (((long) get() & 0xff) << 48) - | (((long) get()) << 56)); - } - //remain == 7 - return ((((long) buf.get() & 0xff)) - | (((long) buf.get() & 0xff) << 8) - | (((long) buf.get() & 0xff) << 16) - | (((long) buf.get() & 0xff) << 24) - | (((long) buf.get() & 0xff) << 32) - | (((long) buf.get() & 0xff) << 40) - | (((long) buf.get() & 0xff) << 48) - | (((long) get()) << 56)); - } - - public double getDouble() { - return Double.longBitsToDouble(getLong()); - } - - public ByteBufferReader get(byte[] dst) { - return get(dst, 0, dst.length); - } - - public ByteBufferReader get(byte[] dst, int offset, int length) { - ByteBuffer buf = this.currBuffer; - int remain = buf.remaining(); - if (remain >= length) { - buf.get(dst, offset, length); - return this; - } - buf.get(dst, offset, remain); - this.currBuffer = this.buffers[++this.currIndex]; - return get(dst, offset + remain, length - remain); - } - - public ByteBufferReader skip(int size) { - ByteBuffer buf = this.currBuffer; - int remain = buf.remaining(); - if (remain >= size) { - buf.position(buf.position() + size); - return this; - } - buf.position(buf.position() + remain); - this.currBuffer = this.buffers[++this.currIndex]; - return skip(size - remain); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.nio.*; +import java.util.*; + +/** + * 以ByteBuffer为数据载体的Reader
    + * 注意:最小可读空间至少是8 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class ByteBufferReader { + + private ByteBuffer[] buffers; + + private int currIndex; + + private ByteBuffer currBuffer; + + private final boolean bigEndian; + + public ByteBufferReader(Collection buffers) { + Objects.requireNonNull(buffers); + this.buffers = buffers.toArray(new ByteBuffer[buffers.size()]); + this.currBuffer = this.buffers[0]; + this.currIndex = 0; + this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN; + } + + public ByteBufferReader(ByteBuffer[] buffers) { + Objects.requireNonNull(buffers); + this.buffers = buffers; + this.currBuffer = this.buffers[0]; + this.currIndex = 0; + this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN; + } + + public ByteBufferReader(ByteBuffer buffer) { + Objects.requireNonNull(buffer); + this.buffers = new ByteBuffer[]{buffer}; + this.currBuffer = this.buffers[0]; + this.currIndex = 0; + this.bigEndian = this.currBuffer.order() == ByteOrder.BIG_ENDIAN; + } + + public ByteBufferReader append(ByteBuffer... buffs) { + for (ByteBuffer buf : buffs) { + Objects.requireNonNull(buf); + } + this.buffers = Utility.append(this.buffers, buffs); + return this; + } + + public static ByteBufferReader create(ByteBuffer buffer) { + return new ByteBufferReader(buffer); + } + + public static ByteBufferReader create(Collection buffers) { + return new ByteBufferReader(buffers); + } + + public static ByteBufferReader create(ByteBuffer[] buffers) { + return new ByteBufferReader(buffers); + } + + public static byte[] toBytes(ByteBuffer[] buffers) { + if (buffers == null) return null; + int size = 0; + for (ByteBuffer buffer : buffers) { + size += buffer.remaining(); + } + byte[] bs = new byte[size]; + int index = 0; + for (ByteBuffer buffer : buffers) { + int remain = buffer.remaining(); + buffer.get(bs, index, remain); + index += remain; + } + return bs; + } + + public static boolean hasRemaining(ByteBuffer... buffers) { + for (ByteBuffer buf : buffers) { + if (buf.hasRemaining()) return true; + } + return false; + } + + public static int remaining(ByteBuffer... buffers) { + int remains = 0; + for (ByteBuffer buf : buffers) { + remains += buf.remaining(); + } + return remains; + } + + public static int remaining(ByteBuffer[] buffers, int offset, int length) { + int remains = 0; + int end = offset + length; + for (int i = offset; i < end; i++) { + remains += buffers[i].remaining(); + } + return remains; + } + + public boolean hasRemaining() { + boolean v = this.currBuffer.hasRemaining(); + if (v) return v; + if (this.currIndex == this.buffers.length - 1) return false; + for (int i = this.currIndex + 1; i < this.buffers.length; i++) { + if (this.buffers[i].hasRemaining()) return true; + } + return false; + } + + public int remaining() { + int v = this.currBuffer.remaining(); + for (int i = this.currIndex + 1; i < this.buffers.length; i++) { + v += this.buffers[i].remaining(); + } + return v; + } + + //提前预读一个字节 + public byte preget() { + ByteBuffer buf = this.currBuffer; + if (!buf.hasRemaining()) { + buf = this.buffers[this.currIndex + 1]; + } + return buf.get(buf.position()); + } + + public byte get() { + ByteBuffer buf = this.currBuffer; + if (!buf.hasRemaining()) { + buf = this.buffers[++this.currIndex]; + this.currBuffer = buf; + } + return this.currBuffer.get(); + } + + public short getShort() { + ByteBuffer buf = this.currBuffer; + int remain = buf.remaining(); + if (remain >= 2) return buf.getShort(); + if (remain == 0) { + buf = this.buffers[++this.currIndex]; + this.currBuffer = buf; + return buf.getShort(); + } + if (bigEndian) return (short) ((buf.get() << 8) | (get() & 0xff)); + return (short) ((buf.get() & 0xff) | (get() << 8)); + } + + public int getInt() { + ByteBuffer buf = this.currBuffer; + int remain = buf.remaining(); + if (remain >= 4) return buf.getInt(); + if (remain == 0) { + buf = this.buffers[++this.currIndex]; + this.currBuffer = buf; + return buf.getInt(); + } + if (bigEndian) { + if (remain == 1) { + return ((buf.get() << 24) + | ((get() & 0xff) << 16) + | ((get() & 0xff) << 8) + | ((get() & 0xff))); + } + if (remain == 2) { + return ((buf.get() << 24) + | ((buf.get() & 0xff) << 16) + | ((get() & 0xff) << 8) + | ((get() & 0xff))); + } + //remain == 3 + return ((buf.get() << 24) + | ((buf.get() & 0xff) << 16) + | ((buf.get() & 0xff) << 8) + | ((get() & 0xff))); + } + if (remain == 1) { + return ((buf.get() & 0xff) + | ((get() & 0xff) << 8) + | ((get() & 0xff) << 16) + | ((get() << 24))); + } + if (remain == 2) { + return ((buf.get() & 0xff) + | ((buf.get() & 0xff) << 8) + | ((get() & 0xff) << 16) + | ((get() << 24))); + } + //remain == 3 + return ((buf.get()) & 0xff) + | ((buf.get() & 0xff) << 8) + | ((buf.get() & 0xff) << 16) + | ((get() << 24)); + } + + public float getFloat() { + return Float.intBitsToFloat(getInt()); + } + + public long getLong() { + ByteBuffer buf = this.currBuffer; + int remain = buf.remaining(); + if (remain >= 8) return buf.getLong(); + if (remain == 0) { + buf = this.buffers[++this.currIndex]; + this.currBuffer = buf; + return buf.getLong(); + } + if (bigEndian) { + if (remain == 1) { + return ((((long) buf.get()) << 56) + | (((long) get() & 0xff) << 48) + | (((long) get() & 0xff) << 40) + | (((long) get() & 0xff) << 32) + | (((long) get() & 0xff) << 24) + | (((long) get() & 0xff) << 16) + | (((long) get() & 0xff) << 8) + | (((long) get() & 0xff))); + } + if (remain == 2) { + return ((((long) buf.get()) << 56) + | (((long) buf.get() & 0xff) << 48) + | (((long) get() & 0xff) << 40) + | (((long) get() & 0xff) << 32) + | (((long) get() & 0xff) << 24) + | (((long) get() & 0xff) << 16) + | (((long) get() & 0xff) << 8) + | (((long) get() & 0xff))); + } + if (remain == 3) { + return ((((long) buf.get()) << 56) + | (((long) buf.get() & 0xff) << 48) + | (((long) buf.get() & 0xff) << 40) + | (((long) get() & 0xff) << 32) + | (((long) get() & 0xff) << 24) + | (((long) get() & 0xff) << 16) + | (((long) get() & 0xff) << 8) + | (((long) get() & 0xff))); + } + if (remain == 4) { + return ((((long) buf.get()) << 56) + | (((long) buf.get() & 0xff) << 48) + | (((long) buf.get() & 0xff) << 40) + | (((long) buf.get() & 0xff) << 32) + | (((long) get() & 0xff) << 24) + | (((long) get() & 0xff) << 16) + | (((long) get() & 0xff) << 8) + | (((long) get() & 0xff))); + } + if (remain == 5) { + return ((((long) buf.get()) << 56) + | (((long) buf.get() & 0xff) << 48) + | (((long) buf.get() & 0xff) << 40) + | (((long) buf.get() & 0xff) << 32) + | (((long) buf.get() & 0xff) << 24) + | (((long) get() & 0xff) << 16) + | (((long) get() & 0xff) << 8) + | (((long) get() & 0xff))); + } + if (remain == 6) { + return ((((long) buf.get()) << 56) + | (((long) buf.get() & 0xff) << 48) + | (((long) buf.get() & 0xff) << 40) + | (((long) buf.get() & 0xff) << 32) + | (((long) buf.get() & 0xff) << 24) + | (((long) buf.get() & 0xff) << 16) + | (((long) get() & 0xff) << 8) + | (((long) get() & 0xff))); + } + //remain == 7 + return ((((long) buf.get()) << 56) + | (((long) buf.get() & 0xff) << 48) + | (((long) buf.get() & 0xff) << 40) + | (((long) buf.get() & 0xff) << 32) + | (((long) buf.get() & 0xff) << 24) + | (((long) buf.get() & 0xff) << 16) + | (((long) buf.get() & 0xff) << 8) + | (((long) get() & 0xff))); + } + if (remain == 1) { + return ((((long) buf.get() & 0xff)) + | (((long) get() & 0xff) << 8) + | (((long) get() & 0xff) << 16) + | (((long) get() & 0xff) << 24) + | (((long) get() & 0xff) << 32) + | (((long) get() & 0xff) << 40) + | (((long) get() & 0xff) << 48) + | (((long) get()) << 56)); + } + if (remain == 2) { + return ((((long) buf.get() & 0xff)) + | (((long) buf.get() & 0xff) << 8) + | (((long) get() & 0xff) << 16) + | (((long) get() & 0xff) << 24) + | (((long) get() & 0xff) << 32) + | (((long) get() & 0xff) << 40) + | (((long) get() & 0xff) << 48) + | (((long) get()) << 56)); + } + if (remain == 3) { + return ((((long) buf.get() & 0xff)) + | (((long) buf.get() & 0xff) << 8) + | (((long) buf.get() & 0xff) << 16) + | (((long) get() & 0xff) << 24) + | (((long) get() & 0xff) << 32) + | (((long) get() & 0xff) << 40) + | (((long) get() & 0xff) << 48) + | (((long) get()) << 56)); + } + if (remain == 4) { + return ((((long) buf.get() & 0xff)) + | (((long) buf.get() & 0xff) << 8) + | (((long) buf.get() & 0xff) << 16) + | (((long) buf.get() & 0xff) << 24) + | (((long) get() & 0xff) << 32) + | (((long) get() & 0xff) << 40) + | (((long) get() & 0xff) << 48) + | (((long) get()) << 56)); + } + if (remain == 5) { + return ((((long) buf.get() & 0xff)) + | (((long) buf.get() & 0xff) << 8) + | (((long) buf.get() & 0xff) << 16) + | (((long) buf.get() & 0xff) << 24) + | (((long) buf.get() & 0xff) << 32) + | (((long) get() & 0xff) << 40) + | (((long) get() & 0xff) << 48) + | (((long) get()) << 56)); + } + if (remain == 6) { + return ((((long) buf.get() & 0xff)) + | (((long) buf.get() & 0xff) << 8) + | (((long) buf.get() & 0xff) << 16) + | (((long) buf.get() & 0xff) << 24) + | (((long) buf.get() & 0xff) << 32) + | (((long) buf.get() & 0xff) << 40) + | (((long) get() & 0xff) << 48) + | (((long) get()) << 56)); + } + //remain == 7 + return ((((long) buf.get() & 0xff)) + | (((long) buf.get() & 0xff) << 8) + | (((long) buf.get() & 0xff) << 16) + | (((long) buf.get() & 0xff) << 24) + | (((long) buf.get() & 0xff) << 32) + | (((long) buf.get() & 0xff) << 40) + | (((long) buf.get() & 0xff) << 48) + | (((long) get()) << 56)); + } + + public double getDouble() { + return Double.longBitsToDouble(getLong()); + } + + public ByteBufferReader get(byte[] dst) { + return get(dst, 0, dst.length); + } + + public ByteBufferReader get(byte[] dst, int offset, int length) { + ByteBuffer buf = this.currBuffer; + int remain = buf.remaining(); + if (remain >= length) { + buf.get(dst, offset, length); + return this; + } + buf.get(dst, offset, remain); + this.currBuffer = this.buffers[++this.currIndex]; + return get(dst, offset + remain, length - remain); + } + + public ByteBufferReader skip(int size) { + ByteBuffer buf = this.currBuffer; + int remain = buf.remaining(); + if (remain >= size) { + buf.position(buf.position() + size); + return this; + } + buf.position(buf.position() + remain); + this.currBuffer = this.buffers[++this.currIndex]; + return skip(size - remain); + } +} diff --git a/src/org/redkale/util/ByteBufferWriter.java b/src/main/java/org/redkale/util/ByteBufferWriter.java similarity index 97% rename from src/org/redkale/util/ByteBufferWriter.java rename to src/main/java/org/redkale/util/ByteBufferWriter.java index 5938a05e7..d89720ee4 100644 --- a/src/org/redkale/util/ByteBufferWriter.java +++ b/src/main/java/org/redkale/util/ByteBufferWriter.java @@ -1,220 +1,220 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.nio.*; -import java.util.function.Supplier; - -/** - * 以ByteBuffer为数据载体的Writer - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class ByteBufferWriter { - - private final Supplier supplier; - - private ByteBuffer[] buffers; - - private int position; - - private int writeBytesCounter = 0; //put(byte[] src, int offset, int length) 调用的次数 - - private boolean bigEndian = true; - - protected ByteBufferWriter(Supplier supplier) { - this.supplier = supplier; - } - - public static ByteBufferWriter create(Supplier supplier) { - return new ByteBufferWriter(supplier); - } - - public static ByteBufferWriter create(Supplier supplier, ByteBuffer one) { - ByteBufferWriter writer = new ByteBufferWriter(supplier); - writer.bigEndian = one.order() == ByteOrder.BIG_ENDIAN; - writer.buffers = new ByteBuffer[]{one}; - return writer; - } - - public static ByteBuffer[] toBuffers(Supplier supplier, byte[] content) { - ByteBufferWriter writer = new ByteBufferWriter(supplier); - writer.put(false, content, 0, content.length); - return writer.toBuffers(); - } - - public static ByteBuffer[] toBuffers(Supplier supplier, byte[] content, int offset, int length) { - ByteBufferWriter writer = new ByteBufferWriter(supplier); - writer.put(false, content, offset, length); - return writer.toBuffers(); - } - - private ByteBuffer getLastBuffer(int size) { - if (this.buffers == null) { - ByteBuffer buf = supplier.get(); - this.bigEndian = buf.order() == ByteOrder.BIG_ENDIAN; - this.buffers = Utility.append(this.buffers, buf); - return buf; - } else if (this.buffers[this.buffers.length - 1].remaining() < size) { - ByteBuffer buf = supplier.get(); - this.buffers = Utility.append(this.buffers, buf); - return buf; - } - return this.buffers[this.buffers.length - 1]; - } - - public ByteBuffer[] toBuffers() { - if (buffers == null) return new ByteBuffer[0]; - for (ByteBuffer buf : this.buffers) { - buf.flip(); - } - return this.buffers; - } - - public int position() { - return position; - } - - public ByteBufferWriter put(byte b) { - getLastBuffer(1).put(b); - position++; - return this; - } - - public ByteBufferWriter putShort(short value) { - getLastBuffer(2).putShort(value); - position += 2; - return this; - } - - public ByteBufferWriter putInt(int value) { - getLastBuffer(4).putInt(value); - position += 4; - return this; - } - - //重新设置指定位置的值 - public ByteBufferWriter putInt(final int index, int value) { - int start = 0; - ByteBuffer[] buffs = this.buffers; - for (int i = 0; i < buffs.length; i++) { - int pos = buffs[i].position(); - if (pos + start > index) { - int r = pos + start - index; - if (r >= 4) { - buffs[i].putInt(index - start, value); - return this; - } else { - byte b1 = bigEndian ? (byte) ((value >> 24) & 0xFF) : (byte) (value & 0xFF); - byte b2 = bigEndian ? (byte) ((value >> 16) & 0xFF) : (byte) ((value >> 8) & 0xFF); - byte b3 = bigEndian ? (byte) ((value >> 8) & 0xFF) : (byte) ((value >> 16) & 0xFF); - byte b4 = bigEndian ? (byte) (value & 0xFF) : (byte) ((value >> 24) & 0xFF); - if (r == 3) { - buffs[i].put(index - start, b1); - buffs[i].put(index - start + 1, b2); - buffs[i].put(index - start + 2, b3); - buffs[i + 1].put(0, b4); - } else if (r == 2) { - buffs[i].put(index - start, b1); - buffs[i].put(index - start + 1, b2); - buffs[i + 1].put(0, b3); - buffs[i + 1].put(1, b4); - } else if (r == 1) { - buffs[i].put(index - start, b1); - buffs[i + 1].put(0, b2); - buffs[i + 1].put(1, b3); - buffs[i + 1].put(2, b4); - } - return this; - } - } else { - start += pos; - } - } - throw new ArrayIndexOutOfBoundsException(index); - } - -// public static void main(String[] args) throws Throwable { -// ObjectPool pool = new ObjectPool<>(20, (p) -> ByteBuffer.allocate(10), (ByteBuffer t) -> t.clear(), (ByteBuffer t) -> false); -// ByteBufferWriter writer = ByteBufferWriter.create(pool); -// for (int i = 1; i <= 18; i++) { -// writer.put((byte) i); -// } -// System.out.println(Arrays.toString(toBytes(writer.toBuffers()))); -// -// writer = ByteBufferWriter.create(pool); -// for (int i = 1; i <= 18; i++) { -// writer.put((byte) i); -// } -// int value = 0x223344; -// byte[] b4 = new byte[]{(byte) ((value >> 24) & 0xFF), (byte) ((value >> 16) & 0xFF), (byte) ((value >> 8) & 0xFF), (byte) (value & 0xFF)}; -// writer.putInt(9, value); -// System.out.println(Arrays.toString(b4)); -// System.out.println(Arrays.toString(toBytes(writer.toBuffers()))); -// } - public ByteBufferWriter putFloat(float value) { - getLastBuffer(4).putFloat(value); - position += 4; - return this; - } - - public ByteBufferWriter putLong(long value) { - getLastBuffer(8).putLong(value); - position += 8; - return this; - } - - public ByteBufferWriter putDouble(double value) { - getLastBuffer(8).putDouble(value); - position += 8; - return this; - } - - public int put(byte[] src) { - return put(true, src, 0, src.length); - } - - public int put(byte[] src, int offset, int length) { - return put(true, src, offset, length); - } - - public int put(byte[] src, int offset, int length, byte[] src2, int offset2, int length2) { - ByteBuffer buf = getLastBuffer(1); - int remain = buf.remaining(); - if (remain >= length + length2) { - buf.put(src, offset, length); - if (src2 != null) buf.put(src2, offset2, length2); - position += length + length2; - this.writeBytesCounter++; - } else { - put(true, src, offset, length); - if (src2 != null) put(false, src2, offset2, length2); - } - return writeBytesCounter; - } - - private int put(boolean outside, byte[] src, int offset, int length) { - ByteBuffer buf = getLastBuffer(1); - int remain = buf.remaining(); - if (remain >= length) { - buf.put(src, offset, length); - position += length; - } else { - buf.put(src, offset, remain); - position += remain; - put(false, src, offset + remain, length - remain); - } - if (outside) this.writeBytesCounter++; - return writeBytesCounter; - } - - public int getWriteBytesCounter() { - return this.writeBytesCounter; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.nio.*; +import java.util.function.Supplier; + +/** + * 以ByteBuffer为数据载体的Writer + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class ByteBufferWriter { + + private final Supplier supplier; + + private ByteBuffer[] buffers; + + private int position; + + private int writeBytesCounter = 0; //put(byte[] src, int offset, int length) 调用的次数 + + private boolean bigEndian = true; + + protected ByteBufferWriter(Supplier supplier) { + this.supplier = supplier; + } + + public static ByteBufferWriter create(Supplier supplier) { + return new ByteBufferWriter(supplier); + } + + public static ByteBufferWriter create(Supplier supplier, ByteBuffer one) { + ByteBufferWriter writer = new ByteBufferWriter(supplier); + writer.bigEndian = one.order() == ByteOrder.BIG_ENDIAN; + writer.buffers = new ByteBuffer[]{one}; + return writer; + } + + public static ByteBuffer[] toBuffers(Supplier supplier, byte[] content) { + ByteBufferWriter writer = new ByteBufferWriter(supplier); + writer.put(false, content, 0, content.length); + return writer.toBuffers(); + } + + public static ByteBuffer[] toBuffers(Supplier supplier, byte[] content, int offset, int length) { + ByteBufferWriter writer = new ByteBufferWriter(supplier); + writer.put(false, content, offset, length); + return writer.toBuffers(); + } + + private ByteBuffer getLastBuffer(int size) { + if (this.buffers == null) { + ByteBuffer buf = supplier.get(); + this.bigEndian = buf.order() == ByteOrder.BIG_ENDIAN; + this.buffers = Utility.append(this.buffers, buf); + return buf; + } else if (this.buffers[this.buffers.length - 1].remaining() < size) { + ByteBuffer buf = supplier.get(); + this.buffers = Utility.append(this.buffers, buf); + return buf; + } + return this.buffers[this.buffers.length - 1]; + } + + public ByteBuffer[] toBuffers() { + if (buffers == null) return new ByteBuffer[0]; + for (ByteBuffer buf : this.buffers) { + buf.flip(); + } + return this.buffers; + } + + public int position() { + return position; + } + + public ByteBufferWriter put(byte b) { + getLastBuffer(1).put(b); + position++; + return this; + } + + public ByteBufferWriter putShort(short value) { + getLastBuffer(2).putShort(value); + position += 2; + return this; + } + + public ByteBufferWriter putInt(int value) { + getLastBuffer(4).putInt(value); + position += 4; + return this; + } + + //重新设置指定位置的值 + public ByteBufferWriter putInt(final int index, int value) { + int start = 0; + ByteBuffer[] buffs = this.buffers; + for (int i = 0; i < buffs.length; i++) { + int pos = buffs[i].position(); + if (pos + start > index) { + int r = pos + start - index; + if (r >= 4) { + buffs[i].putInt(index - start, value); + return this; + } else { + byte b1 = bigEndian ? (byte) ((value >> 24) & 0xFF) : (byte) (value & 0xFF); + byte b2 = bigEndian ? (byte) ((value >> 16) & 0xFF) : (byte) ((value >> 8) & 0xFF); + byte b3 = bigEndian ? (byte) ((value >> 8) & 0xFF) : (byte) ((value >> 16) & 0xFF); + byte b4 = bigEndian ? (byte) (value & 0xFF) : (byte) ((value >> 24) & 0xFF); + if (r == 3) { + buffs[i].put(index - start, b1); + buffs[i].put(index - start + 1, b2); + buffs[i].put(index - start + 2, b3); + buffs[i + 1].put(0, b4); + } else if (r == 2) { + buffs[i].put(index - start, b1); + buffs[i].put(index - start + 1, b2); + buffs[i + 1].put(0, b3); + buffs[i + 1].put(1, b4); + } else if (r == 1) { + buffs[i].put(index - start, b1); + buffs[i + 1].put(0, b2); + buffs[i + 1].put(1, b3); + buffs[i + 1].put(2, b4); + } + return this; + } + } else { + start += pos; + } + } + throw new ArrayIndexOutOfBoundsException(index); + } + +// public static void main(String[] args) throws Throwable { +// ObjectPool pool = new ObjectPool<>(20, (p) -> ByteBuffer.allocate(10), (ByteBuffer t) -> t.clear(), (ByteBuffer t) -> false); +// ByteBufferWriter writer = ByteBufferWriter.create(pool); +// for (int i = 1; i <= 18; i++) { +// writer.put((byte) i); +// } +// System.out.println(Arrays.toString(toBytes(writer.toBuffers()))); +// +// writer = ByteBufferWriter.create(pool); +// for (int i = 1; i <= 18; i++) { +// writer.put((byte) i); +// } +// int value = 0x223344; +// byte[] b4 = new byte[]{(byte) ((value >> 24) & 0xFF), (byte) ((value >> 16) & 0xFF), (byte) ((value >> 8) & 0xFF), (byte) (value & 0xFF)}; +// writer.putInt(9, value); +// System.out.println(Arrays.toString(b4)); +// System.out.println(Arrays.toString(toBytes(writer.toBuffers()))); +// } + public ByteBufferWriter putFloat(float value) { + getLastBuffer(4).putFloat(value); + position += 4; + return this; + } + + public ByteBufferWriter putLong(long value) { + getLastBuffer(8).putLong(value); + position += 8; + return this; + } + + public ByteBufferWriter putDouble(double value) { + getLastBuffer(8).putDouble(value); + position += 8; + return this; + } + + public int put(byte[] src) { + return put(true, src, 0, src.length); + } + + public int put(byte[] src, int offset, int length) { + return put(true, src, offset, length); + } + + public int put(byte[] src, int offset, int length, byte[] src2, int offset2, int length2) { + ByteBuffer buf = getLastBuffer(1); + int remain = buf.remaining(); + if (remain >= length + length2) { + buf.put(src, offset, length); + if (src2 != null) buf.put(src2, offset2, length2); + position += length + length2; + this.writeBytesCounter++; + } else { + put(true, src, offset, length); + if (src2 != null) put(false, src2, offset2, length2); + } + return writeBytesCounter; + } + + private int put(boolean outside, byte[] src, int offset, int length) { + ByteBuffer buf = getLastBuffer(1); + int remain = buf.remaining(); + if (remain >= length) { + buf.put(src, offset, length); + position += length; + } else { + buf.put(src, offset, remain); + position += remain; + put(false, src, offset + remain, length - remain); + } + if (outside) this.writeBytesCounter++; + return writeBytesCounter; + } + + public int getWriteBytesCounter() { + return this.writeBytesCounter; + } +} diff --git a/src/org/redkale/util/ByteTuple.java b/src/main/java/org/redkale/util/ByteTuple.java similarity index 94% rename from src/org/redkale/util/ByteTuple.java rename to src/main/java/org/redkale/util/ByteTuple.java index 70d43d966..c738a24a0 100644 --- a/src/org/redkale/util/ByteTuple.java +++ b/src/main/java/org/redkale/util/ByteTuple.java @@ -1,24 +1,24 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -/** - * 简单的byte[]数据接口。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.3.0 - */ -public interface ByteTuple { - - public byte[] content(); - - public int offset(); - - public int length(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +/** + * 简单的byte[]数据接口。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.3.0 + */ +public interface ByteTuple { + + public byte[] content(); + + public int offset(); + + public int length(); +} diff --git a/src/org/redkale/util/Command.java b/src/main/java/org/redkale/util/Command.java similarity index 95% rename from src/org/redkale/util/Command.java rename to src/main/java/org/redkale/util/Command.java index 80397eb27..282f8e469 100644 --- a/src/org/redkale/util/Command.java +++ b/src/main/java/org/redkale/util/Command.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 接收命令的标记, 只能标记在本地模式下Service里参数为String且返回类型为void的public方法上 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -@Inherited -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -public @interface Command { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 接收命令的标记, 只能标记在本地模式下Service里参数为String且返回类型为void的public方法上 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +@Inherited +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface Command { + +} diff --git a/src/org/redkale/util/Comment.java b/src/main/java/org/redkale/util/Comment.java similarity index 95% rename from src/org/redkale/util/Comment.java rename to src/main/java/org/redkale/util/Comment.java index 0f5f779ee..fa11d8031 100644 --- a/src/org/redkale/util/Comment.java +++ b/src/main/java/org/redkale/util/Comment.java @@ -1,29 +1,29 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 标记注释,备注 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, TYPE_PARAMETER}) -@Retention(RUNTIME) -public @interface Comment { - - String name() default ""; - - String value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 标记注释,备注 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE, METHOD, FIELD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, TYPE_PARAMETER}) +@Retention(RUNTIME) +public @interface Comment { + + String name() default ""; + + String value(); +} diff --git a/src/org/redkale/util/ConstructorParameters.java b/src/main/java/org/redkale/util/ConstructorParameters.java similarity index 96% rename from src/org/redkale/util/ConstructorParameters.java rename to src/main/java/org/redkale/util/ConstructorParameters.java index ab456a138..b7adcaa41 100644 --- a/src/org/redkale/util/ConstructorParameters.java +++ b/src/main/java/org/redkale/util/ConstructorParameters.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 类似java.beans.ConstructorProperties, 必须配合Creator使用 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target({METHOD, CONSTRUCTOR}) -@Retention(RUNTIME) -public @interface ConstructorParameters { - - String[] value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 类似java.beans.ConstructorProperties, 必须配合Creator使用 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({METHOD, CONSTRUCTOR}) +@Retention(RUNTIME) +public @interface ConstructorParameters { + + String[] value(); +} diff --git a/src/org/redkale/util/Creator.java b/src/main/java/org/redkale/util/Creator.java similarity index 76% rename from src/org/redkale/util/Creator.java rename to src/main/java/org/redkale/util/Creator.java index 7ec56e2e5..abcebc80e 100644 --- a/src/org/redkale/util/Creator.java +++ b/src/main/java/org/redkale/util/Creator.java @@ -1,528 +1,688 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.util.*; -import java.util.AbstractMap.SimpleEntry; -import java.util.concurrent.*; -import java.util.logging.*; -import java.util.stream.Stream; -import org.redkale.asm.*; -import org.redkale.asm.Type; -import static org.redkale.asm.Opcodes.*; - -/** - *

    - * 实现一个类的构造方法。 代替低效的反射实现方式。 不支持数组类。 - * 常见的无参数的构造函数类都可以自动生成Creator, 对应自定义的类可以提供一个静态构建Creator方法。 - * 例如: - *

    - * public class Record {
    - *
    - *    private final int id;
    - *
    - *    private String name;
    - *
    - *    Record(int id, String name) {
    - *        this.id = id;
    - *        this.name = name;
    - *    }
    - *
    - *    private static Creator createCreator() {
    - *        return new Creator<Record>() {
    - *            @Override
    - *            @ConstructorParameters({"id", "name"})
    - *            public Record create(Object... params) {
    - *                if(params[0] == null) params[0] = 0;
    - *                return new Record((Integer) params[0], (String) params[1]);
    - *            }
    - *         };
    - *    }
    - * }
    - * 
    - * - * 或者: - *
    - * public class Record {
    - *
    - *    private final int id;
    - *
    - *    private String name;
    - *
    - *    @ConstructorParameters({"id", "name"})
    - *    public Record(int id, String name) {
    - *        this.id = id;
    - *        this.name = name;
    - *    }
    - * }
    - * 
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 构建对象的数据类型 - */ -public interface Creator { - - @SuppressWarnings("unchecked") - static class CreatorInner { - - static final Logger logger = Logger.getLogger(Creator.class.getSimpleName()); - - static final Map creatorCacheMap = new HashMap<>(); - - static { - creatorCacheMap.put(Object.class, p -> new Object()); - creatorCacheMap.put(ArrayList.class, p -> new ArrayList<>()); - creatorCacheMap.put(HashMap.class, p -> new HashMap<>()); - creatorCacheMap.put(HashSet.class, p -> new HashSet<>()); - creatorCacheMap.put(Stream.class, p -> new ArrayList<>().stream()); - creatorCacheMap.put(ConcurrentHashMap.class, p -> new ConcurrentHashMap<>()); - creatorCacheMap.put(CompletableFuture.class, p -> new CompletableFuture<>()); - creatorCacheMap.put(Map.Entry.class, new Creator() { - @Override - @ConstructorParameters({"key", "value"}) - public Map.Entry create(Object... params) { - return new AbstractMap.SimpleEntry(params[0], params[1]); - } - }); - creatorCacheMap.put(AbstractMap.SimpleEntry.class, new Creator() { - @Override - @ConstructorParameters({"key", "value"}) - public AbstractMap.SimpleEntry create(Object... params) { - return new AbstractMap.SimpleEntry(params[0], params[1]); - } - }); - creatorCacheMap.put(AnyValue.DefaultAnyValue.class, p -> new AnyValue.DefaultAnyValue()); - creatorCacheMap.put(AnyValue.class, p -> new AnyValue.DefaultAnyValue()); - } - - static class SimpleClassVisitor extends ClassVisitor { - - private final String constructorDesc; - - private final List fieldnames; - - private boolean started; - - public SimpleClassVisitor(int api, List fieldnames, String constructorDesc) { - super(api); - this.fieldnames = fieldnames; - this.constructorDesc = constructorDesc; - } - - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - if (java.lang.reflect.Modifier.isStatic(access) || !"".equals(name)) return null; - if (constructorDesc != null && !constructorDesc.equals(desc)) return null; - if (this.started) return null; - this.started = true; - //返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多 - return new MethodVisitor(Opcodes.ASM6) { - @Override - public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { - if (index < 1) return; - int size = fieldnames.size(); - //index不会按顺序执行的 - if (index > size) { - for (int i = size; i < index; i++) { - fieldnames.add(" "); - } - fieldnames.set(index - 1, name); - } - fieldnames.set(index - 1, name); - } - }; - } - } - - public static SimpleEntry[] getConstructorField(Class clazz, int paramcount, String constructorDesc) { - String n = clazz.getName(); - InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class"); - if (in == null) return null; - ByteArrayOutputStream out = new ByteArrayOutputStream(1024); - byte[] bytes = new byte[1024]; - int pos; - try { - while ((pos = in.read(bytes)) != -1) { - out.write(bytes, 0, pos); - } - in.close(); - } catch (IOException io) { - return null; - } - final List fieldnames = new ArrayList<>(); - new ClassReader(out.toByteArray()).accept(new SimpleClassVisitor(Opcodes.ASM6, fieldnames, constructorDesc), 0); - while (fieldnames.remove(" ")); //删掉空元素 - if (fieldnames.isEmpty()) return null; - if (paramcount == fieldnames.size()) { - return getConstructorField(clazz, paramcount, fieldnames.toArray(new String[fieldnames.size()])); - } else { - String[] fs = new String[paramcount]; - for (int i = 0; i < fs.length; i++) { - fs[i] = fieldnames.get(i); - } - return getConstructorField(clazz, paramcount, fs); - } - } - - public static SimpleEntry[] getConstructorField(Class clazz, int paramcount, String[] names) { - SimpleEntry[] se = new SimpleEntry[names.length]; - for (int i = 0; i < names.length; i++) { //查询参数名对应的Field - try { - Field field = clazz.getDeclaredField(names[i]); - se[i] = new SimpleEntry<>(field.getName(), field.getType()); - } catch (NoSuchFieldException fe) { - Class cz = clazz; - Field field = null; - while ((cz = cz.getSuperclass()) != Object.class) { - try { - field = cz.getDeclaredField(names[i]); - break; - } catch (NoSuchFieldException nsfe) { - } - } - if (field == null) return null; - se[i] = new SimpleEntry<>(field.getName(), field.getType()); - } catch (Exception e) { - if (logger.isLoggable(Level.FINE)) logger.log(Level.FINE, clazz + " getConstructorField error", e); - return null; - } - } - return se; - } - - public static SimpleEntry[] getConstructorField(Class clazz, int paramcount, Parameter[] params) { - SimpleEntry[] se = new SimpleEntry[params.length]; - for (int i = 0; i < params.length; i++) { //查询参数名对应的Field - try { - Field field = clazz.getDeclaredField(params[i].getName()); - se[i] = new SimpleEntry<>(field.getName(), field.getType()); - } catch (Exception e) { - return null; - } - } - return se; - } - } - - /** - * 创建对象 - * - * @param params 构造函数的参数 - * - * @return 构建的对象 - */ - public T create(Object... params); - - /** - * 根据指定的class采用ASM技术生产Creator。 - * - * @param 构建类的数据类型 - * @param clazz 构建类 - * - * @return Creator对象 - */ - @SuppressWarnings("unchecked") - public static Creator create(Class clazz) { - if (List.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayList.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections") || clazz.getName().startsWith("java.util.Arrays"))) { - clazz = (Class) ArrayList.class; - } else if (Map.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashMap.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { - clazz = (Class) HashMap.class; - } else if (Set.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashSet.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { - clazz = (Class) HashSet.class; - } else if (Map.class.isAssignableFrom(clazz) && clazz.isAssignableFrom(ConcurrentHashMap.class)) { - clazz = (Class) ConcurrentHashMap.class; - } else if (Deque.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayDeque.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { - clazz = (Class) ArrayDeque.class; - } else if (Collection.class.isAssignableFrom(clazz) && clazz.isAssignableFrom(ArrayList.class)) { - clazz = (Class) ArrayList.class; - } else if (Map.Entry.class.isAssignableFrom(clazz) && (Modifier.isInterface(clazz.getModifiers()) || Modifier.isAbstract(clazz.getModifiers()) || !Modifier.isPublic(clazz.getModifiers()))) { - clazz = (Class) AbstractMap.SimpleEntry.class; - } - Creator creator = CreatorInner.creatorCacheMap.get(clazz); - if (creator != null) return creator; - if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { - throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator."); - } - for (final Method method : clazz.getDeclaredMethods()) { //查找类中是否存在提供创建Creator实例的静态方法 - if (!Modifier.isStatic(method.getModifiers())) continue; - if (method.getParameterTypes().length != 0) continue; - if (method.getReturnType() != Creator.class) continue; - try { - method.setAccessible(true); - return (Creator) method.invoke(null); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - final String supDynName = Creator.class.getName().replace('.', '/'); - final String interName = clazz.getName().replace('.', '/'); - final String interDesc = Type.getDescriptor(clazz); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - String newDynName = supDynName + "_" + clazz.getSimpleName() + "_" + (System.currentTimeMillis() % 10000); - if (String.class.getClassLoader() != clazz.getClassLoader()) { - loader = clazz.getClassLoader(); - newDynName = interName + "_Dyn" + Creator.class.getSimpleName(); - } - try { - return (Creator) loader.loadClass(newDynName.replace('/', '.')).getDeclaredConstructor().newInstance(); - } catch (Throwable ex) { - } - - Constructor constructor0 = null; - SimpleEntry[] constructorParameters0 = null; //构造函数的参数 - - if (constructor0 == null) { // 1、查找public的空参数构造函数 - for (Constructor c : clazz.getConstructors()) { - if (c.getParameterCount() == 0) { - constructor0 = c; - constructorParameters0 = new SimpleEntry[0]; - break; - } - } - } - if (constructor0 == null) { // 2、查找public带ConstructorParameters注解的构造函数 - for (Constructor c : clazz.getConstructors()) { - ConstructorParameters cp = (ConstructorParameters) c.getAnnotation(ConstructorParameters.class); - if (cp == null) continue; - SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), cp.value()); - if (fields != null) { - constructor0 = c; - constructorParameters0 = fields; - break; - } - } - } - if (constructor0 == null) { // 3、查找public且不带ConstructorParameters注解的构造函数 - List cs = new ArrayList<>(); - for (Constructor c : clazz.getConstructors()) { - if (c.getAnnotation(ConstructorParameters.class) != null) continue; - if (c.getParameterCount() < 1) continue; - cs.add(c); - } - //优先参数最多的构造函数 - cs.sort((o1, o2) -> o2.getParameterCount() - o1.getParameterCount()); - for (Constructor c : cs) { - SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), Type.getConstructorDescriptor(c)); - if (fields != null) { - constructor0 = c; - constructorParameters0 = fields; - break; - } - } - } - if (constructor0 == null) { // 4、查找非private带ConstructorParameters的构造函数 - for (Constructor c : clazz.getDeclaredConstructors()) { - if (Modifier.isPublic(c.getModifiers()) || Modifier.isPrivate(c.getModifiers())) continue; - ConstructorParameters cp = (ConstructorParameters) c.getAnnotation(ConstructorParameters.class); - if (cp == null) continue; - SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), cp.value()); - if (fields != null) { - constructor0 = c; - constructorParameters0 = fields; - break; - } - } - } - if (constructor0 == null) { // 5、查找非private且不带ConstructorParameters的构造函数 - List cs = new ArrayList<>(); - for (Constructor c : clazz.getDeclaredConstructors()) { - if (Modifier.isPublic(c.getModifiers()) || Modifier.isPrivate(c.getModifiers())) continue; - if (c.getAnnotation(ConstructorParameters.class) != null) continue; - if (c.getParameterCount() < 1) continue; - cs.add(c); - } - //优先参数最多的构造函数 - cs.sort((o1, o2) -> o2.getParameterCount() - o1.getParameterCount()); - for (Constructor c : cs) { - SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), Type.getConstructorDescriptor(c)); - if (fields != null) { - constructor0 = c; - constructorParameters0 = fields; - break; - } - } - } - final Constructor constructor = constructor0; - final SimpleEntry[] constructorParameters = constructorParameters0; - if (constructor == null || constructorParameters == null) { - throw new RuntimeException("[" + clazz + "] have no public or ConstructorParameters-Annotation constructor."); - } - //------------------------------------------------------------- - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); - FieldVisitor fv; - MethodVisitor mv; - AnnotationVisitor av0; - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + ">;", "java/lang/Object", new String[]{supDynName}); - - {//Creator自身的构造方法 - mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - {//create 方法 - mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null); - if (constructorParameters.length > 0) { - av0 = mv.visitAnnotation(Type.getDescriptor(ConstructorParameters.class), true); - AnnotationVisitor av1 = av0.visitArray("value"); - for (SimpleEntry n : constructorParameters) { - av1.visit(null, n.getKey()); - } - av1.visitEnd(); - av0.visitEnd(); - } - final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5}; - { //有Primitive数据类型且值为null的参数需要赋默认值 - for (int i = 0; i < constructorParameters.length; i++) { - final Class pt = constructorParameters[i].getValue(); - if (!pt.isPrimitive()) continue; - mv.visitVarInsn(ALOAD, 1); - if (i < 6) { - mv.visitInsn(iconsts[i]); - } else if (i <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, i); - } else if (i <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, i); - } else { - mv.visitLdcInsn(i); - } - mv.visitInsn(AALOAD); - Label lab = new Label(); - mv.visitJumpInsn(IFNONNULL, lab); - mv.visitVarInsn(ALOAD, 1); - if (i < 6) { - mv.visitInsn(iconsts[i]); - } else if (i <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, i); - } else if (i <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, i); - } else { - mv.visitLdcInsn(i); - } - if (pt == int.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); - } else if (pt == long.class) { - mv.visitInsn(LCONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); - } else if (pt == boolean.class) { - mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); - } else if (pt == short.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); - } else if (pt == float.class) { - mv.visitInsn(FCONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); - } else if (pt == byte.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); - } else if (pt == double.class) { - mv.visitInsn(DCONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); - } else if (pt == char.class) { - mv.visitInsn(ICONST_0); - mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); - } - mv.visitInsn(AASTORE); - mv.visitLabel(lab); - } - } - mv.visitTypeInsn(NEW, interName); - mv.visitInsn(DUP); - //--------------------------------------- - { - for (int i = 0; i < constructorParameters.length; i++) { - mv.visitVarInsn(ALOAD, 1); - if (i < 6) { - mv.visitInsn(iconsts[i]); - } else if (i <= Byte.MAX_VALUE) { - mv.visitIntInsn(BIPUSH, i); - } else if (i <= Short.MAX_VALUE) { - mv.visitIntInsn(SIPUSH, i); - } else { - mv.visitLdcInsn(i); - } - mv.visitInsn(AALOAD); - final Class ct = constructorParameters[i].getValue(); - if (ct.isPrimitive()) { - final Class bigct = Array.get(Array.newInstance(ct, 1), 0).getClass(); - mv.visitTypeInsn(CHECKCAST, bigct.getName().replace('.', '/')); - try { - Method pm = bigct.getMethod(ct.getSimpleName() + "Value"); - mv.visitMethodInsn(INVOKEVIRTUAL, bigct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false); - } catch (Exception ex) { - throw new RuntimeException(ex); //不可能会发生 - } - } else { - mv.visitTypeInsn(CHECKCAST, ct.getName().replace('.', '/')); - } - } - } - //--------------------------------------- - mv.visitMethodInsn(INVOKESPECIAL, interName, "", Type.getConstructorDescriptor(constructor), false); - mv.visitInsn(ARETURN); - mv.visitMaxs((constructorParameters.length > 0 ? (constructorParameters.length + 3) : 2), 2); - mv.visitEnd(); - } - { //虚拟 create 方法 - mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false); - mv.visitInsn(ARETURN); - mv.visitMaxs(2, 2); - mv.visitEnd(); - } - cw.visitEnd(); - final byte[] bytes = cw.toByteArray(); - final boolean ispub = Modifier.isPublic(constructor.getModifiers()); - Class resultClazz = null; - if (loader instanceof URLClassLoader && !ispub) { - try { - final URLClassLoader urlLoader = (URLClassLoader) loader; - final URL url = new URL("memclass", "localhost", -1, "/" + newDynName.replace('/', '.') + "/", new URLStreamHandler() { - @Override - protected URLConnection openConnection(URL u) throws IOException { - return new URLConnection(u) { - @Override - public void connect() throws IOException { - } - - @Override - public InputStream getInputStream() throws IOException { - return new ByteArrayInputStream(bytes); - } - }; - } - }); - Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - addURLMethod.setAccessible(true); - addURLMethod.invoke(urlLoader, url); - resultClazz = urlLoader.loadClass(newDynName.replace('/', '.')); - } catch (Throwable t) { //异常无需理会, 使用下一种loader方式 - t.printStackTrace(); - } - } - if (!ispub && resultClazz == null) throw new RuntimeException("[" + clazz + "] have no public or ConstructorParameters-Annotation constructor."); - try { - if (resultClazz == null) resultClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - return (Creator) resultClazz.getDeclaredConstructor().newInstance(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } -} +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.util.AbstractMap.SimpleEntry; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.logging.*; +import java.util.stream.Stream; +import org.redkale.asm.*; +import org.redkale.asm.Type; +import static org.redkale.asm.Opcodes.*; + +/** + *

    + * 实现一个类的构造方法。 代替低效的反射实现方式。 不支持数组类。 + * 常见的无参数的构造函数类都可以自动生成Creator, 对应自定义的类可以提供一个静态构建Creator方法。 + * 例如: + *

    + * public class Record {
    + *
    + *    private final int id;
    + *
    + *    private String name;
    + *
    + *    Record(int id, String name) {
    + *        this.id = id;
    + *        this.name = name;
    + *    }
    + *
    + *    private static Creator createCreator() {
    + *        return new Creator<Record>() {
    + *            @Override
    + *            @ConstructorParameters({"id", "name"})
    + *            public Record create(Object... params) {
    + *                if(params[0] == null) params[0] = 0;
    + *                return new Record((Integer) params[0], (String) params[1]);
    + *            }
    + *         };
    + *    }
    + * }
    + * 
    + * + * 或者: + *
    + * public class Record {
    + *
    + *    private final int id;
    + *
    + *    private String name;
    + *
    + *    @ConstructorParameters({"id", "name"})
    + *    public Record(int id, String name) {
    + *        this.id = id;
    + *        this.name = name;
    + *    }
    + * }
    + * 
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 构建对象的数据类型 + */ +public interface Creator { + + @SuppressWarnings("unchecked") + static class CreatorInner { + + static final Logger logger = Logger.getLogger(Creator.class.getSimpleName()); + + static final Map creatorCacheMap = new HashMap<>(); + + static final Map arrayCacheMap = new ConcurrentHashMap<>(); + + static { + creatorCacheMap.put(Object.class, p -> new Object()); + creatorCacheMap.put(ArrayList.class, p -> new ArrayList<>()); + creatorCacheMap.put(HashMap.class, p -> new HashMap<>()); + creatorCacheMap.put(HashSet.class, p -> new HashSet<>()); + creatorCacheMap.put(Stream.class, p -> new ArrayList<>().stream()); + creatorCacheMap.put(ConcurrentHashMap.class, p -> new ConcurrentHashMap<>()); + creatorCacheMap.put(CompletableFuture.class, p -> new CompletableFuture<>()); + creatorCacheMap.put(CompletionStage.class, p -> new CompletableFuture<>()); + creatorCacheMap.put(Map.Entry.class, new Creator() { + @Override + @ConstructorParameters({"key", "value"}) + public Map.Entry create(Object... params) { + return new AbstractMap.SimpleEntry(params[0], params[1]); + } + }); + creatorCacheMap.put(AbstractMap.SimpleEntry.class, new Creator() { + @Override + @ConstructorParameters({"key", "value"}) + public AbstractMap.SimpleEntry create(Object... params) { + return new AbstractMap.SimpleEntry(params[0], params[1]); + } + }); + creatorCacheMap.put(AnyValue.DefaultAnyValue.class, p -> new AnyValue.DefaultAnyValue()); + creatorCacheMap.put(AnyValue.class, p -> new AnyValue.DefaultAnyValue()); + + arrayCacheMap.put(int.class, t -> new int[t]); + arrayCacheMap.put(byte.class, t -> new byte[t]); + arrayCacheMap.put(long.class, t -> new long[t]); + arrayCacheMap.put(String.class, t -> new String[t]); + arrayCacheMap.put(Object.class, t -> new Object[t]); + arrayCacheMap.put(boolean.class, t -> new boolean[t]); + arrayCacheMap.put(short.class, t -> new short[t]); + arrayCacheMap.put(char.class, t -> new char[t]); + arrayCacheMap.put(float.class, t -> new float[t]); + arrayCacheMap.put(double.class, t -> new double[t]); + } + + static class SimpleClassVisitor extends ClassVisitor { + + private final String constructorDesc; + + private final List fieldnames; + + private boolean started; + + public SimpleClassVisitor(int api, List fieldnames, String constructorDesc) { + super(api); + this.fieldnames = fieldnames; + this.constructorDesc = constructorDesc; + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (java.lang.reflect.Modifier.isStatic(access) || !"".equals(name)) return null; + if (constructorDesc != null && !constructorDesc.equals(desc)) return null; + if (this.started) return null; + this.started = true; + //返回的List中参数列表可能会比方法参数量多,因为方法内的临时变量也会存入list中, 所以需要list的元素集合比方法的参数多 + return new MethodVisitor(Opcodes.ASM6) { + @Override + public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) { + if (index < 1) return; + int size = fieldnames.size(); + //index不会按顺序执行的 + if (index > size) { + for (int i = size; i < index; i++) { + fieldnames.add(" "); + } + fieldnames.set(index - 1, name); + } + fieldnames.set(index - 1, name); + } + }; + } + } + + public static SimpleEntry[] getConstructorField(Class clazz, int paramcount, String constructorDesc) { + String n = clazz.getName(); + InputStream in = clazz.getResourceAsStream(n.substring(n.lastIndexOf('.') + 1) + ".class"); + if (in == null) return null; + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte[] bytes = new byte[1024]; + int pos; + try { + while ((pos = in.read(bytes)) != -1) { + out.write(bytes, 0, pos); + } + in.close(); + } catch (IOException io) { + return null; + } + final List fieldnames = new ArrayList<>(); + new ClassReader(out.toByteArray()).accept(new SimpleClassVisitor(Opcodes.ASM6, fieldnames, constructorDesc), 0); + while (fieldnames.remove(" ")); //删掉空元素 + if (fieldnames.isEmpty()) return null; + if (paramcount == fieldnames.size()) { + return getConstructorField(clazz, paramcount, fieldnames.toArray(new String[fieldnames.size()])); + } else { + String[] fs = new String[paramcount]; + for (int i = 0; i < fs.length; i++) { + fs[i] = fieldnames.get(i); + } + return getConstructorField(clazz, paramcount, fs); + } + } + + public static SimpleEntry[] getConstructorField(Class clazz, int paramcount, String[] names) { + SimpleEntry[] se = new SimpleEntry[names.length]; + for (int i = 0; i < names.length; i++) { //查询参数名对应的Field + try { + Field field = clazz.getDeclaredField(names[i]); + se[i] = new SimpleEntry<>(field.getName(), field.getType()); + } catch (NoSuchFieldException fe) { + Class cz = clazz; + Field field = null; + while ((cz = cz.getSuperclass()) != Object.class) { + try { + field = cz.getDeclaredField(names[i]); + break; + } catch (NoSuchFieldException nsfe) { + } + } + if (field == null) return null; + se[i] = new SimpleEntry<>(field.getName(), field.getType()); + } catch (Exception e) { + if (logger.isLoggable(Level.FINE)) logger.log(Level.FINE, clazz + " getConstructorField error", e); + return null; + } + } + return se; + } + + public static SimpleEntry[] getConstructorField(Class clazz, int paramcount, Parameter[] params) { + SimpleEntry[] se = new SimpleEntry[params.length]; + for (int i = 0; i < params.length; i++) { //查询参数名对应的Field + try { + Field field = clazz.getDeclaredField(params[i].getName()); + se[i] = new SimpleEntry<>(field.getName(), field.getType()); + } catch (Exception e) { + return null; + } + } + return se; + } + + static class TypeArrayIntFunction implements IntFunction { + + private final Class type; + + public TypeArrayIntFunction(Class type) { + this.type = type; + } + + @Override + public T[] apply(int value) { + return (T[]) Array.newInstance(type, value); + } + + } + + public static IntFunction createArrayFunction(final Class clazz) { + final String interName = clazz.getName().replace('.', '/'); + final String interDesc = Type.getDescriptor(clazz); + final ClassLoader loader = clazz.getClassLoader(); + final String newDynName = "org/redkaledyn/creator/_DynArrayFunction__" + clazz.getName().replace('.', '_').replace('$', '_'); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + return (IntFunction) (clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz).getDeclaredConstructor().newInstance(); + } catch (Throwable ex) { + } + + //------------------------------------------------------------- + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + MethodVisitor mv; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;Ljava/util/function/IntFunction<[" + interDesc + ">;", "java/lang/Object", new String[]{"java/util/function/IntFunction"}); + + {//IntFunction自身的构造方法 + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + {//apply 方法 + mv = cw.visitMethod(ACC_PUBLIC, "apply", "(I)[" + interDesc, null, null); + mv.visitVarInsn(ILOAD, 1); + mv.visitTypeInsn(ANEWARRAY, interName); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 2); + mv.visitEnd(); + } + { //虚拟 apply 方法 + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "apply", "(I)Ljava/lang/Object;", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ILOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "apply", "(I)[" + interDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + final byte[] bytes = cw.toByteArray(); + try { + Class resultClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, resultClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(resultClazz, newDynName.replace('/', '.')); + return (IntFunction) resultClazz.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + ex.printStackTrace(); //一般不会发生 + return t -> (T[]) Array.newInstance(clazz, t); + //throw new RuntimeException(ex); + } + } + } + + /** + * 创建对象 + * + * @param params 构造函数的参数 + * + * @return 构建的对象 + */ + public T create(Object... params); + + /** + * 创建指定类型对象数组的IntFunction + * + * @param 泛型 + * @param type 类型 + * + * @return IntFunction + */ + public static IntFunction arrayFunction(final Class type) { + return CreatorInner.arrayCacheMap.computeIfAbsent(type, t -> CreatorInner.createArrayFunction(t)); + } + + /** + * 创建指定大小的对象数组 + * + * @param 泛型 + * @param type 类型 + * @param size 数组大小 + * + * @return 数组 + */ + public static T[] newArray(final Class type, final int size) { + if (type == int.class) return (T[]) (Object) new int[size]; + if (type == byte.class) return (T[]) (Object) new byte[size]; + if (type == long.class) return (T[]) (Object) new long[size]; + if (type == String.class) return (T[]) new String[size]; + if (type == Object.class) return (T[]) new Object[size]; + if (type == boolean.class) return (T[]) (Object) new boolean[size]; + if (type == short.class) return (T[]) (Object) new short[size]; + if (type == char.class) return (T[]) (Object) new char[size]; + if (type == float.class) return (T[]) (Object) new float[size]; + if (type == double.class) return (T[]) (Object) new double[size]; + //return (T[]) Array.newInstance(type, size); + return arrayFunction(type).apply(size); + } + + /** + * 根据Supplier生产Creator + * + * @param 构建类的数据类型 + * @param supplier Supplier + * + * @return Creator对象 + */ + public static Creator create(final Supplier supplier) { + Objects.requireNonNull(supplier); + return (Object... params) -> supplier.get(); + } + + /** + * 根据Function生产Creator + * + * @param 构建类的数据类型 + * @param func Function + * + * @return Creator对象 + */ + public static Creator create(final Function func) { + Objects.requireNonNull(func); + return (Object... params) -> func.apply(params); + } + + /** + * 根据指定的class采用ASM技术生产Creator。 + * + * @param 构建类的数据类型 + * @param clazz 构建类 + * + * @return Creator对象 + */ + @SuppressWarnings("unchecked") + public static Creator create(Class clazz) { + if (List.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayList.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections") || clazz.getName().startsWith("java.util.Arrays"))) { + clazz = (Class) ArrayList.class; + } else if (Map.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashMap.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { + clazz = (Class) HashMap.class; + } else if (Set.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(HashSet.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { + clazz = (Class) HashSet.class; + } else if (Map.class.isAssignableFrom(clazz) && clazz.isAssignableFrom(ConcurrentHashMap.class)) { + clazz = (Class) ConcurrentHashMap.class; + } else if (Deque.class.isAssignableFrom(clazz) && (clazz.isAssignableFrom(ArrayDeque.class) || clazz.getName().startsWith("java.util.Collections") || clazz.getName().startsWith("java.util.ImmutableCollections"))) { + clazz = (Class) ArrayDeque.class; + } else if (Collection.class.isAssignableFrom(clazz) && clazz.isAssignableFrom(ArrayList.class)) { + clazz = (Class) ArrayList.class; + } else if (Map.Entry.class.isAssignableFrom(clazz) && (Modifier.isInterface(clazz.getModifiers()) || Modifier.isAbstract(clazz.getModifiers()) || !Modifier.isPublic(clazz.getModifiers()))) { + clazz = (Class) AbstractMap.SimpleEntry.class; + } else if (Iterable.class == clazz) { + clazz = (Class) ArrayList.class; + } + Creator creator = CreatorInner.creatorCacheMap.get(clazz); + if (creator != null) return creator; + if (clazz.isInterface() || Modifier.isAbstract(clazz.getModifiers())) { + throw new RuntimeException("[" + clazz + "] is a interface or abstract class, cannot create it's Creator."); + } + for (final Method method : clazz.getDeclaredMethods()) { //查找类中是否存在提供创建Creator实例的静态方法 + if (!Modifier.isStatic(method.getModifiers())) continue; + if (method.getParameterTypes().length != 0) continue; + if (method.getReturnType() != Creator.class) continue; + try { + method.setAccessible(true); + Creator c = (Creator) method.invoke(null); + if (c != null) { + RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName()); + RedkaleClassLoader.putReflectionMethod(clazz.getName(), method); + return c; + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + final String supDynName = Creator.class.getName().replace('.', '/'); + final String interName = clazz.getName().replace('.', '/'); + final String interDesc = Type.getDescriptor(clazz); + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + if (String.class.getClassLoader() != clazz.getClassLoader()) { + loader = clazz.getClassLoader(); + } + final String newDynName = "org/redkaledyn/creator/_Dyn" + Creator.class.getSimpleName() + "__" + clazz.getName().replace('.', '_').replace('$', '_'); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + return (Creator) (clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz).getDeclaredConstructor().newInstance(); + } catch (Throwable ex) { + } + + Constructor constructor0 = null; + SimpleEntry[] constructorParameters0 = null; //构造函数的参数 + + if (constructor0 == null) { // 1、查找public的空参数构造函数 + for (Constructor c : clazz.getConstructors()) { + if (c.getParameterCount() == 0) { + constructor0 = c; + constructorParameters0 = new SimpleEntry[0]; + break; + } + } + } + if (constructor0 == null) { // 2、查找public带ConstructorParameters注解的构造函数 + for (Constructor c : clazz.getConstructors()) { + ConstructorParameters cp = (ConstructorParameters) c.getAnnotation(ConstructorParameters.class); + if (cp == null) continue; + SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), cp.value()); + if (fields != null) { + constructor0 = c; + constructorParameters0 = fields; + break; + } + } + } + if (constructor0 == null) { // 3、查找public且不带ConstructorParameters注解的构造函数 + List cs = new ArrayList<>(); + for (Constructor c : clazz.getConstructors()) { + if (c.getAnnotation(ConstructorParameters.class) != null) continue; + if (c.getParameterCount() < 1) continue; + cs.add(c); + } + //优先参数最多的构造函数 + cs.sort((o1, o2) -> o2.getParameterCount() - o1.getParameterCount()); + for (Constructor c : cs) { + SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), Type.getConstructorDescriptor(c)); + if (fields != null) { + constructor0 = c; + constructorParameters0 = fields; + break; + } + } + } + if (constructor0 == null) { // 4、查找非private带ConstructorParameters的构造函数 + for (Constructor c : clazz.getDeclaredConstructors()) { + if (Modifier.isPublic(c.getModifiers()) || Modifier.isPrivate(c.getModifiers())) continue; + ConstructorParameters cp = (ConstructorParameters) c.getAnnotation(ConstructorParameters.class); + if (cp == null) continue; + SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), cp.value()); + if (fields != null) { + constructor0 = c; + constructorParameters0 = fields; + break; + } + } + } + if (constructor0 == null) { // 5、查找非private且不带ConstructorParameters的构造函数 + List cs = new ArrayList<>(); + for (Constructor c : clazz.getDeclaredConstructors()) { + if (Modifier.isPublic(c.getModifiers()) || Modifier.isPrivate(c.getModifiers())) continue; + if (c.getAnnotation(ConstructorParameters.class) != null) continue; + if (c.getParameterCount() < 1) continue; + cs.add(c); + } + //优先参数最多的构造函数 + cs.sort((o1, o2) -> o2.getParameterCount() - o1.getParameterCount()); + for (Constructor c : cs) { + SimpleEntry[] fields = CreatorInner.getConstructorField(clazz, c.getParameterCount(), Type.getConstructorDescriptor(c)); + if (fields != null) { + constructor0 = c; + constructorParameters0 = fields; + break; + } + } + } + final Constructor constructor = constructor0; + final SimpleEntry[] constructorParameters = constructorParameters0; + if (constructor == null || constructorParameters == null) { + throw new RuntimeException("[" + clazz + "] have no public or ConstructorParameters-Annotation constructor."); + } + //------------------------------------------------------------- + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + ">;", "java/lang/Object", new String[]{supDynName}); + + {//Creator自身的构造方法 + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + {//create 方法 + mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "create", "([Ljava/lang/Object;)L" + interName + ";", null, null); + if (constructorParameters.length > 0) { + av0 = mv.visitAnnotation(Type.getDescriptor(ConstructorParameters.class), true); + AnnotationVisitor av1 = av0.visitArray("value"); + for (SimpleEntry n : constructorParameters) { + av1.visit(null, n.getKey()); + } + av1.visitEnd(); + av0.visitEnd(); + } + final int[] iconsts = {ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5}; + { //有Primitive数据类型且值为null的参数需要赋默认值 + for (int i = 0; i < constructorParameters.length; i++) { + final Class pt = constructorParameters[i].getValue(); + if (!pt.isPrimitive()) continue; + mv.visitVarInsn(ALOAD, 1); + if (i < 6) { + mv.visitInsn(iconsts[i]); + } else if (i <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, i); + } else if (i <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, i); + } else { + mv.visitLdcInsn(i); + } + mv.visitInsn(AALOAD); + Label lab = new Label(); + mv.visitJumpInsn(IFNONNULL, lab); + mv.visitVarInsn(ALOAD, 1); + if (i < 6) { + mv.visitInsn(iconsts[i]); + } else if (i <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, i); + } else if (i <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, i); + } else { + mv.visitLdcInsn(i); + } + if (pt == int.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + } else if (pt == long.class) { + mv.visitInsn(LCONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + } else if (pt == boolean.class) { + mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;"); + } else if (pt == short.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (pt == float.class) { + mv.visitInsn(FCONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + } else if (pt == byte.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + } else if (pt == double.class) { + mv.visitInsn(DCONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + } else if (pt == char.class) { + mv.visitInsn(ICONST_0); + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); + } + mv.visitInsn(AASTORE); + mv.visitLabel(lab); + } + } + mv.visitTypeInsn(NEW, interName); + mv.visitInsn(DUP); + //--------------------------------------- + { + for (int i = 0; i < constructorParameters.length; i++) { + mv.visitVarInsn(ALOAD, 1); + if (i < 6) { + mv.visitInsn(iconsts[i]); + } else if (i <= Byte.MAX_VALUE) { + mv.visitIntInsn(BIPUSH, i); + } else if (i <= Short.MAX_VALUE) { + mv.visitIntInsn(SIPUSH, i); + } else { + mv.visitLdcInsn(i); + } + mv.visitInsn(AALOAD); + final Class ct = constructorParameters[i].getValue(); + if (ct.isPrimitive()) { + final Class bigct = Array.get(Array.newInstance(ct, 1), 0).getClass(); + mv.visitTypeInsn(CHECKCAST, bigct.getName().replace('.', '/')); + try { + Method pm = bigct.getMethod(ct.getSimpleName() + "Value"); + mv.visitMethodInsn(INVOKEVIRTUAL, bigct.getName().replace('.', '/'), pm.getName(), Type.getMethodDescriptor(pm), false); + } catch (Exception ex) { + throw new RuntimeException(ex); //不可能会发生 + } + } else { + mv.visitTypeInsn(CHECKCAST, ct.getName().replace('.', '/')); + } + } + } + //--------------------------------------- + mv.visitMethodInsn(INVOKESPECIAL, interName, "", Type.getConstructorDescriptor(constructor), false); + mv.visitInsn(ARETURN); + mv.visitMaxs((constructorParameters.length > 0 ? (constructorParameters.length + 3) : 2), 2); + mv.visitEnd(); + } + { //虚拟 create 方法 + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "create", "([Ljava/lang/Object;)Ljava/lang/Object;", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "create", "([Ljava/lang/Object;)" + interDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + cw.visitEnd(); + final byte[] bytes = cw.toByteArray(); + final boolean ispub = Modifier.isPublic(constructor.getModifiers()); + Class resultClazz = null; + if (loader instanceof URLClassLoader && !ispub) { + try { + final URLClassLoader urlLoader = (URLClassLoader) loader; + final URL url = new URL("memclass", "localhost", -1, "/" + newDynName.replace('/', '.') + "/", new URLStreamHandler() { + @Override + protected URLConnection openConnection(URL u) throws IOException { + return new URLConnection(u) { + @Override + public void connect() throws IOException { + } + + @Override + public InputStream getInputStream() throws IOException { + return new ByteArrayInputStream(bytes); + } + }; + } + }); + Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + addURLMethod.setAccessible(true); + addURLMethod.invoke(urlLoader, url); + resultClazz = urlLoader.loadClass(newDynName.replace('/', '.')); + } catch (Throwable t) { //异常无需理会, 使用下一种loader方式 + t.printStackTrace(); + } + } + if (!ispub && resultClazz == null) throw new RuntimeException("[" + clazz + "] have no public or ConstructorParameters-Annotation constructor."); + try { + if (resultClazz == null) resultClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, resultClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(resultClazz, newDynName.replace('/', '.')); + return (Creator) resultClazz.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/org/redkale/util/DLong.java b/src/main/java/org/redkale/util/DLong.java similarity index 96% rename from src/org/redkale/util/DLong.java rename to src/main/java/org/redkale/util/DLong.java index 7481f6895..fb79d9b9f 100644 --- a/src/org/redkale/util/DLong.java +++ b/src/main/java/org/redkale/util/DLong.java @@ -1,129 +1,129 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.nio.*; -import java.util.*; - -/** - * 16bytes数据结构 - * 注意: 为了提高性能, DLong中的bytes是直接返回, 不得对bytes的内容进行修改。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class DLong extends Number implements Comparable { - - public static final DLong ZERO = new DLong(new byte[16]); - - protected final byte[] value; - - protected DLong(long v1, long v2) { //暂时不用 - this.value = new byte[]{(byte) (v1 >> 56), (byte) (v1 >> 48), (byte) (v1 >> 40), (byte) (v1 >> 32), - (byte) (v1 >> 24), (byte) (v1 >> 16), (byte) (v1 >> 8), (byte) v1, (byte) (v2 >> 56), (byte) (v2 >> 48), (byte) (v2 >> 40), (byte) (v2 >> 32), - (byte) (v2 >> 24), (byte) (v2 >> 16), (byte) (v2 >> 8), (byte) v2}; - } - - protected DLong(byte[] bytes) { - if (bytes == null || bytes.length != 16) throw new NumberFormatException("Not 16 length bytes"); - this.value = bytes; - } - - public byte[] getBytes() { - return Arrays.copyOf(value, value.length); - } - - public byte[] directBytes() { - return value; - } - - public static DLong create(byte[] bytes) { - return new DLong(bytes); - } - - public static DLong read(ByteBuffer buffer) { - byte[] bs = new byte[16]; - buffer.get(bs); - return new DLong(bs); - } - - public static ByteBuffer write(ByteBuffer buffer, DLong dlong) { - buffer.put(dlong.value); - return buffer; - } - - public static ByteArray write(ByteArray array, DLong dlong) { - array.put(dlong.value); - return array; - } - - public static ByteArray write(ByteArray array, int offset, DLong dlong) { - array.put(offset, dlong.value); - return array; - } - - public boolean equals(byte[] bytes) { - return Arrays.equals(this.value, bytes); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - final DLong other = (DLong) obj; - return Arrays.equals(this.value, other.value); - } - - @Override - public int hashCode() { - return Arrays.hashCode(value); - } - - @Override - public String toString() { - if (this == ZERO) return "0"; - return new String(Utility.binToHex(value)); - } - - @Override - public int intValue() { - return ((value[12] & 0xff) << 24) | ((value[13] & 0xff) << 16) | ((value[14] & 0xff) << 8) | (value[15] & 0xff); - } - - @Override - public long longValue() { - return ((((long) value[8] & 0xff) << 56) - | (((long) value[9] & 0xff) << 48) - | (((long) value[10] & 0xff) << 40) - | (((long) value[11] & 0xff) << 32) - | (((long) value[12] & 0xff) << 24) - | (((long) value[13] & 0xff) << 16) - | (((long) value[14] & 0xff) << 8) - | (((long) value[15] & 0xff))); - } - - @Override - public float floatValue() { - return (float) longValue(); - } - - @Override - public double doubleValue() { - return (double) longValue(); - } - - @Override - public int compareTo(DLong o) { - if (o == null) return 1; - for (int i = 0; i < value.length; i++) { - if (this.value[i] != o.value[i]) return this.value[i] - o.value[i]; - } - return 0; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.nio.*; +import java.util.*; + +/** + * 16bytes数据结构 + * 注意: 为了提高性能, DLong中的bytes是直接返回, 不得对bytes的内容进行修改。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class DLong extends Number implements Comparable { + + public static final DLong ZERO = new DLong(new byte[16]); + + protected final byte[] value; + + protected DLong(long v1, long v2) { //暂时不用 + this.value = new byte[]{(byte) (v1 >> 56), (byte) (v1 >> 48), (byte) (v1 >> 40), (byte) (v1 >> 32), + (byte) (v1 >> 24), (byte) (v1 >> 16), (byte) (v1 >> 8), (byte) v1, (byte) (v2 >> 56), (byte) (v2 >> 48), (byte) (v2 >> 40), (byte) (v2 >> 32), + (byte) (v2 >> 24), (byte) (v2 >> 16), (byte) (v2 >> 8), (byte) v2}; + } + + protected DLong(byte[] bytes) { + if (bytes == null || bytes.length != 16) throw new NumberFormatException("Not 16 length bytes"); + this.value = bytes; + } + + public byte[] getBytes() { + return Arrays.copyOf(value, value.length); + } + + public byte[] directBytes() { + return value; + } + + public static DLong create(byte[] bytes) { + return new DLong(bytes); + } + + public static DLong read(ByteBuffer buffer) { + byte[] bs = new byte[16]; + buffer.get(bs); + return new DLong(bs); + } + + public static ByteBuffer write(ByteBuffer buffer, DLong dlong) { + buffer.put(dlong.value); + return buffer; + } + + public static ByteArray write(ByteArray array, DLong dlong) { + array.put(dlong.value); + return array; + } + + public static ByteArray write(ByteArray array, int offset, DLong dlong) { + array.put(offset, dlong.value); + return array; + } + + public boolean equals(byte[] bytes) { + return Arrays.equals(this.value, bytes); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final DLong other = (DLong) obj; + return Arrays.equals(this.value, other.value); + } + + @Override + public int hashCode() { + return Arrays.hashCode(value); + } + + @Override + public String toString() { + if (this == ZERO) return "0"; + return new String(Utility.binToHex(value)); + } + + @Override + public int intValue() { + return ((value[12] & 0xff) << 24) | ((value[13] & 0xff) << 16) | ((value[14] & 0xff) << 8) | (value[15] & 0xff); + } + + @Override + public long longValue() { + return ((((long) value[8] & 0xff) << 56) + | (((long) value[9] & 0xff) << 48) + | (((long) value[10] & 0xff) << 40) + | (((long) value[11] & 0xff) << 32) + | (((long) value[12] & 0xff) << 24) + | (((long) value[13] & 0xff) << 16) + | (((long) value[14] & 0xff) << 8) + | (((long) value[15] & 0xff))); + } + + @Override + public float floatValue() { + return (float) longValue(); + } + + @Override + public double doubleValue() { + return (double) longValue(); + } + + @Override + public int compareTo(DLong o) { + if (o == null) return 1; + for (int i = 0; i < value.length; i++) { + if (this.value[i] != o.value[i]) return this.value[i] - o.value[i]; + } + return 0; + } + +} diff --git a/src/main/java/org/redkale/util/Flows.java b/src/main/java/org/redkale/util/Flows.java new file mode 100644 index 000000000..d17478a16 --- /dev/null +++ b/src/main/java/org/redkale/util/Flows.java @@ -0,0 +1,272 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.Function; +import static org.redkale.util.Utility.hexToBin; + +/** + * Flow简单的操作 + * + *

    + * 详情见: https://redkale.org + * + * @since 2.5.0 + */ +public abstract class Flows { + + /** + *

    +     * public class AnonymousMonoFutureFunction implements java.util.function.Function<Object, java.util.concurrent.CompletableFuture> {
    +     *
    +     *      @Override
    +     *      public java.util.concurrent.CompletableFuture apply(Object t) {
    +     *          return ((reactor.core.publisher.Mono) t).toFuture();
    +     *      }
    +     *
    +     * }
    +     * 
    + */ + private static final String functionMonoFutureBinary = "cafebabe0000003700220a000200030700040c0005000" + + "60100106a6176612f6c616e672f4f626a6563740100063c696e69743e01000328295607000801001b72656163746f722f636f72652" + + "f7075626c69736865722f4d6f6e6f0a0007000a0c000b000c010008746f46757475726501002a28294c6a6176612f7574696c2f636" + + "f6e63757272656e742f436f6d706c657461626c654675747572653b0a000e000f0700100c0011001201002c6f72672f7265646b616" + + "c652f7574696c2f416e6f6e796d6f75734d6f6e6f46757475726546756e6374696f6e0100056170706c7901003c284c6a6176612f6" + + "c616e672f4f626a6563743b294c6a6176612f7574696c2f636f6e63757272656e742f436f6d706c657461626c654675747572653b0" + + "7001401001b6a6176612f7574696c2f66756e6374696f6e2f46756e6374696f6e010004436f646501000f4c696e654e756d6265725" + + "461626c650100124c6f63616c5661726961626c655461626c650100047468697301002e4c6f72672f7265646b616c652f7574696c2" + + "f416e6f6e796d6f75734d6f6e6f46757475726546756e6374696f6e3b010001740100124c6a6176612f6c616e672f4f626a6563743" + + "b0100104d6574686f64506172616d6574657273010026284c6a6176612f6c616e672f4f626a6563743b294c6a6176612f6c616e672" + + "f4f626a6563743b0100095369676e617475726501006b4c6a6176612f6c616e672f4f626a6563743b4c6a6176612f7574696c2f667" + + "56e6374696f6e2f46756e6374696f6e3c4c6a6176612f6c616e672f4f626a6563743b4c6a6176612f7574696c2f636f6e637572726" + + "56e742f436f6d706c657461626c654675747572653b3e3b01000a536f7572636546696c65010020416e6f6e796d6f75734d6f6e6f4" + + "6757475726546756e6374696f6e2e6a6176610021000e00020001001300000003000100050006000100150000002f0001000100000" + + "0052ab70001b10000000200160000000600010000000c00170000000c0001000000050018001900000001001100120002001500000" + + "03c00010002000000082bc00007b60009b000000002001600000006000100000010001700000016000200000008001800190000000" + + "00008001a001b0001001c0000000501001a000010410011001d000200150000003000020002000000062a2bb6000db000000002001" + + "60000000600010000000c00170000000c000100000006001800190000001c0000000501001a10000002001e00000002001f0020000" + + "000020021"; + + /** + *
    +     * public class AnonymousFluxFutureFunction implements java.util.function.Function<Object, java.util.concurrent.CompletableFuture> {
    +     *
    +     *      @Override
    +     *      public java.util.concurrent.CompletableFuture apply(Object t) {
    +     *          return ((reactor.core.publisher.Flux) t).collectList().toFuture();
    +     *      }
    +     *
    +     * }
    +     * 
    + */ + private static final String functionFluxFutureBinary = "cafebabe0000003700280a000200030700040c0005000" + + "60100106a6176612f6c616e672f4f626a6563740100063c696e69743e01000328295607000801001b72656163746f722f636f72" + + "652f7075626c69736865722f466c75780a0007000a0c000b000c01000b636f6c6c6563744c69737401001f28294c72656163746" + + "f722f636f72652f7075626c69736865722f4d6f6e6f3b0a000e000f0700100c0011001201001b72656163746f722f636f72652f" + + "7075626c69736865722f4d6f6e6f010008746f46757475726501002a28294c6a6176612f7574696c2f636f6e63757272656e742" + + "f436f6d706c657461626c654675747572653b0a001400150700160c0017001801002c6f72672f7265646b616c652f7574696c2f" + + "416e6f6e796d6f7573466c757846757475726546756e6374696f6e0100056170706c7901003c284c6a6176612f6c616e672f4f6" + + "26a6563743b294c6a6176612f7574696c2f636f6e63757272656e742f436f6d706c657461626c654675747572653b07001a0100" + + "1b6a6176612f7574696c2f66756e6374696f6e2f46756e6374696f6e010004436f646501000f4c696e654e756d6265725461626" + + "c650100124c6f63616c5661726961626c655461626c650100047468697301002e4c6f72672f7265646b616c652f7574696c2f41" + + "6e6f6e796d6f7573466c757846757475726546756e6374696f6e3b010001740100124c6a6176612f6c616e672f4f626a6563743" + + "b0100104d6574686f64506172616d6574657273010026284c6a6176612f6c616e672f4f626a6563743b294c6a6176612f6c616e" + + "672f4f626a6563743b0100095369676e617475726501006b4c6a6176612f6c616e672f4f626a6563743b4c6a6176612f7574696" + + "c2f66756e6374696f6e2f46756e6374696f6e3c4c6a6176612f6c616e672f4f626a6563743b4c6a6176612f7574696c2f636f6e" + + "63757272656e742f436f6d706c657461626c654675747572653b3e3b01000a536f7572636546696c65010020416e6f6e796d6f7" + + "573466c757846757475726546756e6374696f6e2e6a61766100210014000200010019000000030001000500060001001b000000" + + "2f00010001000000052ab70001b100000002001c0000000600010000000c001d0000000c000100000005001e001f00000001001" + + "700180002001b0000003f000100020000000b2bc00007b60009b6000db000000002001c00000006000100000010001d00000016" + + "00020000000b001e001f00000000000b00200021000100220000000501002000001041001700230002001b00000030000200020" + + "00000062a2bb60013b000000002001c0000000600010000000c001d0000000c000100000006001e001f00000022000000050100" + + "201000000200240000000200250026000000020027"; + + private static final Class reactorMonoClass; + + private static final Class reactorFluxClass; + + private static final Function reactorMonoFunction; + + private static final Function reactorFluxFunction; + + static { + + Class reactorMonoClass0 = null; + Class reactorFluxClass0 = null; + Function reactorMonoFunction0 = null; + Function reactorFluxFunction0 = null; + + if (!"executable".equals(System.getProperty("org.graalvm.nativeimage.kind"))) { //not native-image + try { + // + reactorMonoClass0 = Thread.currentThread().getContextClassLoader().loadClass("reactor.core.publisher.Mono"); + Class> monoFuncClass = null; + try { + monoFuncClass = (Class) Thread.currentThread().getContextClassLoader().loadClass("org.re" + "dkale.util.AnonymousMonoFutureFunction"); + } catch (Throwable t) { + } + if (monoFuncClass == null) { + byte[] classBytes = hexToBin(functionMonoFutureBinary); + monoFuncClass = (Class>) new ClassLoader() { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass("org.re" + "dkale.util.AnonymousMonoFutureFunction", classBytes); + RedkaleClassLoader.putDynClass(monoFuncClass.getName(), classBytes, monoFuncClass); + } + RedkaleClassLoader.putReflectionDeclaredConstructors(monoFuncClass, monoFuncClass.getName()); + reactorMonoFunction0 = (Function) monoFuncClass.getDeclaredConstructor().newInstance(); + // + reactorFluxClass0 = Thread.currentThread().getContextClassLoader().loadClass("reactor.core.publisher.Flux"); + Class> fluxFuncClass = null; + try { + fluxFuncClass = (Class) Thread.currentThread().getContextClassLoader().loadClass("org.re" + "dkale.util.AnonymousFluxFutureFunction"); + } catch (Throwable t) { + } + if (fluxFuncClass == null) { + byte[] classBytes = hexToBin(functionFluxFutureBinary); + fluxFuncClass = (Class>) new ClassLoader() { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass("org.re" + "dkale.util.AnonymousFluxFutureFunction", classBytes); + RedkaleClassLoader.putDynClass(fluxFuncClass.getName(), classBytes, fluxFuncClass); + } + RedkaleClassLoader.putReflectionDeclaredConstructors(fluxFuncClass, fluxFuncClass.getName()); + reactorFluxFunction0 = (Function) fluxFuncClass.getDeclaredConstructor().newInstance(); + } catch (Throwable t) { + } + } + + reactorMonoClass = reactorMonoClass0; + reactorFluxClass = reactorFluxClass0; + reactorMonoFunction = reactorMonoFunction0; + reactorFluxFunction = reactorFluxFunction0; + } + + Flows() { + } + + public static boolean maybePublisherClass(Class value) { + if (value == null) return false; + if (reactorFluxFunction != null) { + if (reactorMonoClass.isAssignableFrom(value)) return true; + if (reactorFluxClass.isAssignableFrom(value)) return true; + } + return Flow.Publisher.class.isAssignableFrom(value); + } + + public static Type maybePublisherSubType(Type value) { + if (value == null) return null; + if (!(value instanceof ParameterizedType)) return null; + ParameterizedType pt = (ParameterizedType) value; + Type parent = pt.getRawType() == null ? pt.getOwnerType() : pt.getRawType(); + if (!(parent instanceof Class)) return null; + if (pt.getActualTypeArguments().length != 1) return null; + if (reactorFluxFunction != null) { + if (reactorMonoClass.isAssignableFrom((Class) parent)) return pt.getActualTypeArguments()[0]; + if (reactorFluxClass.isAssignableFrom((Class) parent)) return pt.getActualTypeArguments()[0]; + } + if (Flow.Publisher.class.isAssignableFrom((Class) parent)) return pt.getActualTypeArguments()[0]; + return null; + } + + public static Object maybePublisherToFuture(Object value) { + if (value == null) return value; + if (reactorFluxFunction != null) { + Class clazz = value.getClass(); + if (reactorMonoClass.isAssignableFrom(clazz)) return reactorMonoFunction.apply(value); + if (reactorFluxClass.isAssignableFrom(clazz)) return reactorFluxFunction.apply(value); + if (Flow.Publisher.class.isAssignableFrom(clazz)) return createMonoFuture((Flow.Publisher) value); + } + return value; + } + + public static final CompletableFuture> createFluxFuture(Flow.Publisher publisher) { + SubscriberListFuture future = new SubscriberListFuture<>(); + publisher.subscribe(future); + return future; + } + + public static final CompletableFuture createMonoFuture(Flow.Publisher publisher) { + SubscriberFuture future = new SubscriberFuture<>(); + publisher.subscribe(future); + return future; + } + + /** + * 简单的CompletableFuture与Flow.Subscriber的结合类。 + * + *

    + * 详情见: https://redkale.org + * + * @since 2.5.0 + * @param T + */ + public static class SubscriberFuture extends CompletableFuture implements Flow.Subscriber { + + protected T rs; + + @Override + public void onSubscribe(Flow.Subscription s) { + s.request(Integer.MAX_VALUE); + } + + @Override + public void onNext(T item) { + rs = item; + } + + @Override + public void onError(Throwable t) { + completeExceptionally(t); + } + + @Override + public void onComplete() { + complete(rs); + } + } + + /** + * 简单的CompletableFuture与Flow.Subscriber的结合类。 + * + *

    + * 详情见: https://redkale.org + * + * @since 2.5.0 + * @param T + */ + public static class SubscriberListFuture extends CompletableFuture> implements Flow.Subscriber { + + protected List rs; + + @Override + public void onSubscribe(Flow.Subscription s) { + s.request(Integer.MAX_VALUE); + } + + @Override + public void onNext(T item) { + if (rs == null) rs = new ArrayList<>(); + rs.add(item); + } + + @Override + public void onError(Throwable t) { + completeExceptionally(t); + } + + @Override + public void onComplete() { + complete(rs == null ? new ArrayList<>() : rs); + } + + } +} diff --git a/src/main/java/org/redkale/util/Invoker.java b/src/main/java/org/redkale/util/Invoker.java new file mode 100644 index 000000000..562b74473 --- /dev/null +++ b/src/main/java/org/redkale/util/Invoker.java @@ -0,0 +1,224 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.reflect.*; +import org.redkale.asm.*; +import org.redkale.asm.Type; +import static org.redkale.asm.Opcodes.*; + +/** + * 动态生成指定方法的调用对象, 替代Method.invoke的反射方式 + * + *

    + * 详情见: https://redkale.org + * + * @param 泛型 + * @param 泛型 + * + * @author zhangjx + * @since 2.5.0 + */ +public interface Invoker { + + /** + * 调用方法放回值, 调用静态方法obj=null + * + * @param obj 操作对象 + * @param params 方法的参数 + * + * @return 方法返回的结果 + */ + public RETURN_TYPE invoke(OBJECT_TYPE obj, Object... params); + + public static Invoker create(final Class clazz, final String methodName, final Class... paramTypes) { + java.lang.reflect.Method method = null; + try { + method = clazz.getMethod(methodName, paramTypes); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + return create(clazz, method); + } + + public static Invoker create(final Class clazz, final Method method) { + RedkaleClassLoader.putReflectionDeclaredMethods(clazz.getName()); + RedkaleClassLoader.putReflectionMethod(clazz.getName(), method); + boolean throwflag = Utility.contains(method.getExceptionTypes(), e -> !RuntimeException.class.isAssignableFrom(e)); //方法是否会抛出非RuntimeException异常 + boolean staticflag = Modifier.isStatic(method.getModifiers()); + final Class returnType = (Class) method.getReturnType(); + final String supDynName = Invoker.class.getName().replace('.', '/'); + final String interName = clazz.getName().replace('.', '/'); + final String interDesc = Type.getDescriptor(clazz); + final String returnPrimiveDesc = Type.getDescriptor(returnType); + String returnDesc = Type.getDescriptor(returnType); + if (returnType == boolean.class) { + returnDesc = Type.getDescriptor(Boolean.class); + } else if (returnType == byte.class) { + returnDesc = Type.getDescriptor(Byte.class); + } else if (returnType == short.class) { + returnDesc = Type.getDescriptor(Short.class); + } else if (returnType == char.class) { + returnDesc = Type.getDescriptor(Character.class); + } else if (returnType == int.class) { + returnDesc = Type.getDescriptor(Integer.class); + } else if (returnType == float.class) { + returnDesc = Type.getDescriptor(Float.class); + } else if (returnType == long.class) { + returnDesc = Type.getDescriptor(Long.class); + } else if (returnType == double.class) { + returnDesc = Type.getDescriptor(Double.class); + } + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + StringBuilder sbpts = new StringBuilder(); + for (Class c : method.getParameterTypes()) { + sbpts.append('_').append(c.getName().replace('.', '_').replace('$', '_')); + } + final String newDynName = "org/redkaledyn/invoker/_Dyn" + Invoker.class.getSimpleName() + "_" + clazz.getName().replace('.', '_').replace('$', '_') + + "_" + method.getName() + sbpts; + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + return (Invoker) (clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz).getDeclaredConstructor().newInstance(); + } catch (Throwable ex) { + } + //------------------------------------------------------------- + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + FieldVisitor fv; + MethodDebugVisitor mv; + AnnotationVisitor av0; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + interDesc + returnDesc + ">;", "java/lang/Object", new String[]{supDynName}); + + {//Invoker自身的构造方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { //invoke 方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "invoke", "(" + interDesc + "[Ljava/lang/Object;)" + returnDesc, null, null)); + + Label label0 = new Label(); + Label label1 = new Label(); + Label label2 = new Label(); + if (throwflag) { + mv.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable"); + mv.visitLabel(label0); + } + if (!staticflag) { + mv.visitVarInsn(ALOAD, 1); + } + + StringBuilder paramDescs = new StringBuilder(); + int paramIndex = 0; + for (Class paramType : method.getParameterTypes()) { + //参数 + mv.visitVarInsn(ALOAD, 2); + MethodDebugVisitor.pushInt(mv, paramIndex); + mv.visitInsn(AALOAD); + if (paramType == boolean.class) { + paramDescs.append("Z"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z", false); + } else if (paramType == byte.class) { + paramDescs.append("B"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Byte"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B", false); + } else if (paramType == short.class) { + paramDescs.append("S"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Short"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S", false); + } else if (paramType == char.class) { + paramDescs.append("C"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Character"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C", false); + } else if (paramType == int.class) { + paramDescs.append("I"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Integer"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I", false); + } else if (paramType == float.class) { + paramDescs.append("F"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Float"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F", false); + } else if (paramType == long.class) { + paramDescs.append("J"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Long"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J", false); + } else if (paramType == double.class) { + paramDescs.append("D"); + mv.visitTypeInsn(CHECKCAST, "java/lang/Double"); + mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D", false); + } else { + paramDescs.append(Type.getDescriptor(paramType)); + if (paramType != Object.class) mv.visitTypeInsn(CHECKCAST, paramType.getName().replace('.', '/')); + } + paramIndex++; + } + + mv.visitMethodInsn(staticflag ? INVOKESTATIC : (clazz.isInterface() ? INVOKEINTERFACE : INVOKEVIRTUAL), interName, method.getName(), "(" + paramDescs + ")" + returnPrimiveDesc, !staticflag && clazz.isInterface()); + if (returnType == boolean.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;", false); + } else if (returnType == byte.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;", false); + } else if (returnType == short.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;", false); + } else if (returnType == char.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;", false); + } else if (returnType == int.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", false); + } else if (returnType == float.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;", false); + } else if (returnType == long.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;", false); + } else if (returnType == double.class) { + mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;", false); + } + mv.visitLabel(label1); + mv.visitInsn(ARETURN); + if (throwflag) { + mv.visitLabel(label2); + mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[]{"java/lang/Throwable"}); + mv.visitVarInsn(ASTORE, 3); + mv.visitTypeInsn(NEW, "java/lang/RuntimeException"); + mv.visitInsn(DUP); + mv.visitVarInsn(ALOAD, 3); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "", "(Ljava/lang/Throwable;)V", false); + mv.visitInsn(ATHROW); + } + mv.visitMaxs(3 + method.getParameterCount(), 4); + mv.visitEnd(); + } + + { //虚拟 invoke 方法 + mv = new MethodDebugVisitor(cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_VARARGS + ACC_SYNTHETIC, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;", null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, interName); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "invoke", "(" + interDesc + "[Ljava/lang/Object;)" + returnDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw.visitEnd(); + final byte[] bytes = cw.toByteArray(); + Class resultClazz = null; + try { + if (resultClazz == null) resultClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, resultClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(resultClazz, newDynName.replace('/', '.')); + return (Invoker) resultClazz.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/src/org/redkale/util/LogExcludeLevel.java b/src/main/java/org/redkale/util/LogExcludeLevel.java similarity index 96% rename from src/org/redkale/util/LogExcludeLevel.java rename to src/main/java/org/redkale/util/LogExcludeLevel.java index 25c022932..23fe8c107 100644 --- a/src/org/redkale/util/LogExcludeLevel.java +++ b/src/main/java/org/redkale/util/LogExcludeLevel.java @@ -1,46 +1,46 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 等于level日志级别且包含keys字符串的日志才会被排除
    - * - *

    - * @LogExcludeLevel(levels = {"FINEST"}, keys = {"SET username ="})
    - * public class UserRecord {
    - *      public int userid;
    - *      public String username = "";
    - * }
    - *
    - * 这样当调用DataSource对UserRecord对象进行操作时,拼接的SQL语句含"SET username ="字样的都会在FINEST日志级别过滤掉
    - * 
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -@Repeatable(LogExcludeLevel.LogExcludeLevels.class) -public @interface LogExcludeLevel { - - String[] levels(); - - String[] keys(); - - @Documented - @Target({TYPE}) - @Retention(RUNTIME) - @interface LogExcludeLevels { - - LogExcludeLevel[] value(); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 等于level日志级别且包含keys字符串的日志才会被排除
    + * + *

    + * @LogExcludeLevel(levels = {"FINEST"}, keys = {"SET username ="})
    + * public class UserRecord {
    + *      public int userid;
    + *      public String username = "";
    + * }
    + *
    + * 这样当调用DataSource对UserRecord对象进行操作时,拼接的SQL语句含"SET username ="字样的都会在FINEST日志级别过滤掉
    + * 
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +@Repeatable(LogExcludeLevel.LogExcludeLevels.class) +public @interface LogExcludeLevel { + + String[] levels(); + + String[] keys(); + + @Documented + @Target({TYPE}) + @Retention(RUNTIME) + @interface LogExcludeLevels { + + LogExcludeLevel[] value(); + } +} diff --git a/src/org/redkale/util/LogLevel.java b/src/main/java/org/redkale/util/LogLevel.java similarity index 95% rename from src/org/redkale/util/LogLevel.java rename to src/main/java/org/redkale/util/LogLevel.java index 050e073af..7eebba258 100644 --- a/src/org/redkale/util/LogLevel.java +++ b/src/main/java/org/redkale/util/LogLevel.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 被标记的日志级别以上的才会被记录 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface LogLevel { - - String value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 被标记的日志级别以上的才会被记录 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface LogLevel { + + String value(); +} diff --git a/src/main/java/org/redkale/util/MpscChunkedArrayQueue.java b/src/main/java/org/redkale/util/MpscChunkedArrayQueue.java new file mode 100644 index 000000000..0fa08dcfe --- /dev/null +++ b/src/main/java/org/redkale/util/MpscChunkedArrayQueue.java @@ -0,0 +1,730 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.util.*; +import java.util.function.Supplier; + +/** + * 参考 https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/MpscChunkedArrayQueue.java version: v3.3.0实现的MPSC队列 + * + *

    + * 详情见: https://redkale.org + * + * @param 泛型 + * + * @author zhangjx + * @since 2.5.0 + */ +public class MpscChunkedArrayQueue extends AbstractQueue { + + byte b000, b001, b002, b003, b004, b005, b006, b007;// 8b + + byte b010, b011, b012, b013, b014, b015, b016, b017;// 16b + + byte b020, b021, b022, b023, b024, b025, b026, b027;// 24b + + byte b030, b031, b032, b033, b034, b035, b036, b037;// 32b + + byte b040, b041, b042, b043, b044, b045, b046, b047;// 40b + + byte b050, b051, b052, b053, b054, b055, b056, b057;// 48b + + byte b060, b061, b062, b063, b064, b065, b066, b067;// 56b + + byte b070, b071, b072, b073, b074, b075, b076, b077;// 64b + + byte b100, b101, b102, b103, b104, b105, b106, b107;// 72b + + byte b110, b111, b112, b113, b114, b115, b116, b117;// 80b + + byte b120, b121, b122, b123, b124, b125, b126, b127;// 88b + + byte b130, b131, b132, b133, b134, b135, b136, b137;// 96b + + byte b140, b141, b142, b143, b144, b145, b146, b147;//104b + + byte b150, b151, b152, b153, b154, b155, b156, b157;//112b + + byte b160, b161, b162, b163, b164, b165, b166, b167;//120b + + byte b170, b171, b172, b173, b174, b175, b176, b177;//128b + + //---------------------------------------------- + private static final Unsafe UNSAFE = Utility.unsafe(); + + private static final long REF_ARRAY_BASE; + + private static final int REF_ELEMENT_SHIFT; + + static { + final int scale = UNSAFE == null ? 4 : UNSAFE.arrayIndexScale(Object[].class); + if (4 == scale) { + REF_ELEMENT_SHIFT = 2; + } else if (8 == scale) { + REF_ELEMENT_SHIFT = 3; + } else { + throw new IllegalStateException("Unknown pointer size: " + scale); + } + REF_ARRAY_BASE = UNSAFE == null ? 0L : UNSAFE.arrayBaseOffset(Object[].class); + } + + // No post padding here, subclasses must add + private static final Object JUMP = new Object(); + + private static final Object BUFFER_CONSUMED = new Object(); + + private static final int CONTINUE_TO_P_INDEX_CAS = 0; + + private static final int RETRY = 1; + + private static final int QUEUE_FULL = 2; + + private static final int QUEUE_RESIZE = 3; + + protected final long maxQueueCapacity; + + private final static long P_LIMIT_OFFSET = fieldOffset(MpscChunkedArrayQueue.class, "producerLimit"); + + private volatile long producerLimit; + + protected long producerMask; + + protected E[] producerBuffer; + + private final static long C_INDEX_OFFSET = fieldOffset(MpscChunkedArrayQueue.class, "consumerIndex"); + + private volatile long consumerIndex; + + protected long consumerMask; + + protected E[] consumerBuffer; + + private final static long P_INDEX_OFFSET = fieldOffset(MpscChunkedArrayQueue.class, "producerIndex"); + + private volatile long producerIndex; + + public MpscChunkedArrayQueue(int maxCapacity) { + this(Math.max(2, Math.min(1024, Utility.roundToPowerOfTwo(maxCapacity / 8))), maxCapacity); + } + + /** + * @param initialCapacity the queue initial capacity. If chunk size is fixed this will be the chunk size. + * Must be 2 or more. + * @param maxCapacity the maximum capacity will be rounded up to the closest power of 2 and will be the + * upper limit of number of elements in this queue. Must be 4 or more and round up to a larger + * power of 2 than initialCapacity. + */ + public MpscChunkedArrayQueue(int initialCapacity, int maxCapacity) { + if (initialCapacity < 2) throw new IllegalArgumentException("initialCapacity: " + initialCapacity + " (expected: >= 2)"); + int p2capacity = Utility.roundToPowerOfTwo(initialCapacity); + // leave lower bit of mask clear + long mask = (p2capacity - 1) << 1; + // need extra element to point at next array + E[] buffer = (E[]) new Object[p2capacity + 1]; + producerBuffer = buffer; + producerMask = mask; + consumerBuffer = buffer; + consumerMask = mask; + soProducerLimit(mask); // we know it's all empty to start with + + if (maxCapacity < 4) throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 4)"); + int p2max = Utility.roundToPowerOfTwo(maxCapacity); + if (Utility.roundToPowerOfTwo(initialCapacity) > p2max) { + throw new IllegalArgumentException("initialCapacity: " + Utility.roundToPowerOfTwo(initialCapacity) + " (expected: <= " + p2max + ")"); + } + maxQueueCapacity = ((long) Utility.roundToPowerOfTwo(maxCapacity)) << 1; + } + + static long fieldOffset(Class clz, String fieldName) throws RuntimeException { + if (UNSAFE == null) return 0L; + try { + return UNSAFE.objectFieldOffset(clz.getDeclaredField(fieldName)); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + static long modifiedCalcCircularRefElementOffset(long index, long mask) { + return REF_ARRAY_BASE + ((index & mask) << (REF_ELEMENT_SHIFT - 1)); + } + + static void spRefElement(E[] buffer, long offset, E e) { + UNSAFE.putObject(buffer, offset, e); + } + + @SuppressWarnings("unchecked") + static E lpRefElement(E[] buffer, long offset) { + return (E) UNSAFE.getObject(buffer, offset); + } + + /** + * A volatile load of an element from a given offset. + * + * @param buffer this.buffer + * @param offset computed via + * + * @return the element at the offset + */ + @SuppressWarnings("unchecked") + static E lvRefElement(E[] buffer, long offset) { + return (E) UNSAFE.getObjectVolatile(buffer, offset); + } + + /** + * An ordered store of an element to a given offset + * + * @param buffer this.buffer + * @param offset computed via + * @param e an orderly kitty + */ + public static void soRefElement(E[] buffer, long offset, E e) { + UNSAFE.putOrderedObject(buffer, offset, e); + } + + public static long calcRefElementOffset(long index) { + return REF_ARRAY_BASE + (index << REF_ELEMENT_SHIFT); + } + + /** + * Note: circular arrays are assumed a power of 2 in length and the `mask` is (length - 1). + * + * @param index desirable element index + * @param mask (length - 1) + * + * @return the offset in bytes within the circular array for a given index + */ + public static long calcCircularRefElementOffset(long index, long mask) { + return REF_ARRAY_BASE + ((index & mask) << REF_ELEMENT_SHIFT); + } + + public final long lvProducerIndex() { + return producerIndex; + } + + final void soProducerIndex(long newValue) { + UNSAFE.putOrderedLong(this, P_INDEX_OFFSET, newValue); + } + + final boolean casProducerIndex(long expect, long newValue) { + return UNSAFE.compareAndSwapLong(this, P_INDEX_OFFSET, expect, newValue); + } + + public final long lvConsumerIndex() { + return consumerIndex; + } + + final long lpConsumerIndex() { + return UNSAFE.getLong(this, C_INDEX_OFFSET); + } + + final void soConsumerIndex(long newValue) { + UNSAFE.putOrderedLong(this, C_INDEX_OFFSET, newValue); + } + + final long lvProducerLimit() { + return producerLimit; + } + + final boolean casProducerLimit(long expect, long newValue) { + return UNSAFE.compareAndSwapLong(this, P_LIMIT_OFFSET, expect, newValue); + } + + final void soProducerLimit(long newValue) { + UNSAFE.putOrderedLong(this, P_LIMIT_OFFSET, newValue); + } + + @Override + public int size() { + // NOTE: because indices are on even numbers we cannot use the size util. + + /* + * It is possible for a thread to be interrupted or reschedule between the read of the producer and + * consumer indices, therefore protection is required to ensure size is within valid range. In the + * event of concurrent polls/offers to this method the size is OVER estimated as we read consumer + * index BEFORE the producer index. + */ + long after = lvConsumerIndex(); + long size; + while (true) { + final long before = after; + final long currentProducerIndex = lvProducerIndex(); + after = lvConsumerIndex(); + if (before == after) { + size = ((currentProducerIndex - after) >> 1); + break; + } + } + // Long overflow is impossible, so size is always positive. Integer overflow is possible for the unbounded + // indexed queues. + if (size > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else { + return (int) size; + } + } + + @Override + public boolean isEmpty() { + // Order matters! + // Loading consumer before producer allows for producer increments after consumer index is read. + // This ensures this method is conservative in it's estimate. Note that as this is an MPMC there is + // nothing we can do to make this an exact method. + return (this.lvConsumerIndex() == this.lvProducerIndex()); + } + + @Override + public String toString() { + return this.getClass().getName(); + } + + @Override + public boolean offer(final E e) { + if (null == e) throw new NullPointerException(); + + long mask; + E[] buffer; + long pIndex; + + while (true) { + long producerLimit0 = lvProducerLimit(); + pIndex = lvProducerIndex(); + // lower bit is indicative of resize, if we see it we spin until it's cleared + if ((pIndex & 1) == 1) { + continue; + } + // pIndex is even (lower bit is 0) -> actual index is (pIndex >> 1) + + // mask/buffer may get changed by resizing -> only use for array access after successful CAS. + mask = this.producerMask; + buffer = this.producerBuffer; + // a successful CAS ties the ordering, lv(pIndex) - [mask/buffer] -> cas(pIndex) + + // assumption behind this optimization is that queue is almost always empty or near empty + if (producerLimit0 <= pIndex) { + int result = offerSlowPath(mask, pIndex, producerLimit0); + switch (result) { + case CONTINUE_TO_P_INDEX_CAS: + break; + case RETRY: + continue; + case QUEUE_FULL: + return false; + case QUEUE_RESIZE: + resize(mask, buffer, pIndex, e, null); + return true; + } + } + + if (casProducerIndex(pIndex, pIndex + 2)) { + break; + } + } + // INDEX visible before ELEMENT + final long offset = modifiedCalcCircularRefElementOffset(pIndex, mask); + soRefElement(buffer, offset, e); // release element e + return true; + } + + /** + * {@inheritDoc} + *

    + * This implementation is correct for single consumer thread use only. + */ + @SuppressWarnings("unchecked") + @Override + public E poll() { + final E[] buffer = consumerBuffer; + final long index = lpConsumerIndex(); + final long mask = consumerMask; + + final long offset = modifiedCalcCircularRefElementOffset(index, mask); + Object e = lvRefElement(buffer, offset); + if (e == null) { + if (index != lvProducerIndex()) { + // poll() == null iff queue is empty, null element is not strong enough indicator, so we must + // check the producer index. If the queue is indeed not empty we spin until element is + // visible. + do { + e = lvRefElement(buffer, offset); + } while (e == null); + } else { + return null; + } + } + + if (e == JUMP) { + final E[] nextBuffer = nextBuffer(buffer, mask); + return newBufferPoll(nextBuffer, index); + } + + soRefElement(buffer, offset, null); // release element null + soConsumerIndex(index + 2); // release cIndex + return (E) e; + } + + /** + * {@inheritDoc} + *

    + * This implementation is correct for single consumer thread use only. + */ + @SuppressWarnings("unchecked") + @Override + public E peek() { + final E[] buffer = consumerBuffer; + final long index = lpConsumerIndex(); + final long mask = consumerMask; + + final long offset = modifiedCalcCircularRefElementOffset(index, mask); + Object e = lvRefElement(buffer, offset); + if (e == null && index != lvProducerIndex()) { + // peek() == null iff queue is empty, null element is not strong enough indicator, so we must + // check the producer index. If the queue is indeed not empty we spin until element is visible. + do { + e = lvRefElement(buffer, offset); + } while (e == null); + } + if (e == JUMP) { + return newBufferPeek(nextBuffer(buffer, mask), index); + } + return (E) e; + } + + /** + * We do not inline resize into this method because we do not resize on fill. + */ + private int offerSlowPath(long mask, long pIndex, long producerLimit) { + final long cIndex = lvConsumerIndex(); + long bufferCapacity = getCurrentBufferCapacity(mask); + + if (cIndex + bufferCapacity > pIndex) { + if (!casProducerLimit(producerLimit, cIndex + bufferCapacity)) { + // retry from top + return RETRY; + } else { + // continue to pIndex CAS + return CONTINUE_TO_P_INDEX_CAS; + } + } // full and cannot grow + else if (availableInQueue(pIndex, cIndex) <= 0) { + // offer should return false; + return QUEUE_FULL; + } // grab index for resize -> set lower bit + else if (casProducerIndex(pIndex, pIndex + 1)) { + // trigger a resize + return QUEUE_RESIZE; + } else { + // failed resize attempt, retry from top + return RETRY; + } + } + + @SuppressWarnings("unchecked") + private E[] nextBuffer(final E[] buffer, final long mask) { + final long offset = nextArrayOffset(mask); + final E[] nextBuffer = (E[]) lvRefElement(buffer, offset); + consumerBuffer = nextBuffer; + consumerMask = (nextBuffer.length - 2) << 1; + soRefElement(buffer, offset, BUFFER_CONSUMED); + return nextBuffer; + } + + private static long nextArrayOffset(long mask) { + return modifiedCalcCircularRefElementOffset(mask + 2, Long.MAX_VALUE); + } + + private E newBufferPoll(E[] nextBuffer, long index) { + final long offset = modifiedCalcCircularRefElementOffset(index, consumerMask); + final E n = lvRefElement(nextBuffer, offset); + if (n == null) { + throw new IllegalStateException("new buffer must have at least one element"); + } + soRefElement(nextBuffer, offset, null); + soConsumerIndex(index + 2); + return n; + } + + private E newBufferPeek(E[] nextBuffer, long index) { + final long offset = modifiedCalcCircularRefElementOffset(index, consumerMask); + final E n = lvRefElement(nextBuffer, offset); + if (null == n) { + throw new IllegalStateException("new buffer must have at least one element"); + } + return n; + } + + public long currentProducerIndex() { + return lvProducerIndex() / 2; + } + + public long currentConsumerIndex() { + return lvConsumerIndex() / 2; + } + + public boolean relaxedOffer(E e) { + return offer(e); + } + + @SuppressWarnings("unchecked") + public E relaxedPoll() { + final E[] buffer = consumerBuffer; + final long index = lpConsumerIndex(); + final long mask = consumerMask; + + final long offset = modifiedCalcCircularRefElementOffset(index, mask); + Object e = lvRefElement(buffer, offset); + if (e == null) { + return null; + } + if (e == JUMP) { + final E[] nextBuffer = nextBuffer(buffer, mask); + return newBufferPoll(nextBuffer, index); + } + soRefElement(buffer, offset, null); + soConsumerIndex(index + 2); + return (E) e; + } + + @SuppressWarnings("unchecked") + public E relaxedPeek() { + final E[] buffer = consumerBuffer; + final long index = lpConsumerIndex(); + final long mask = consumerMask; + + final long offset = modifiedCalcCircularRefElementOffset(index, mask); + Object e = lvRefElement(buffer, offset); + if (e == JUMP) { + return newBufferPeek(nextBuffer(buffer, mask), index); + } + return (E) e; + } + + public int fill(Supplier s) { + long result = 0;// result is a long because we want to have a safepoint check at regular intervals + final int capacity = capacity(); + do { + final int filled = fill(s, Utility.cpus() * 4); + if (filled == 0) { + return (int) result; + } + result += filled; + } while (result <= capacity); + return (int) result; + } + + public int fill(Supplier s, int limit) { + if (null == s) + throw new IllegalArgumentException("supplier is null"); + if (limit < 0) + throw new IllegalArgumentException("limit is negative:" + limit); + if (limit == 0) + return 0; + + long mask; + E[] buffer; + long pIndex; + int claimedSlots; + while (true) { + long producerLimit0 = lvProducerLimit(); + pIndex = lvProducerIndex(); + // lower bit is indicative of resize, if we see it we spin until it's cleared + if ((pIndex & 1) == 1) { + continue; + } + // pIndex is even (lower bit is 0) -> actual index is (pIndex >> 1) + + // NOTE: mask/buffer may get changed by resizing -> only use for array access after successful CAS. + // Only by virtue offloading them between the lvProducerIndex and a successful casProducerIndex are they + // safe to use. + mask = this.producerMask; + buffer = this.producerBuffer; + // a successful CAS ties the ordering, lv(pIndex) -> [mask/buffer] -> cas(pIndex) + + // we want 'limit' slots, but will settle for whatever is visible to 'producerLimit' + long batchIndex = Math.min(producerLimit0, pIndex + 2l * limit); // -> producerLimit >= batchIndex + + if (pIndex >= producerLimit0) { + int result = offerSlowPath(mask, pIndex, producerLimit0); + switch (result) { + case CONTINUE_TO_P_INDEX_CAS: + // offer slow path verifies only one slot ahead, we cannot rely on indication here + case RETRY: + continue; + case QUEUE_FULL: + return 0; + case QUEUE_RESIZE: + resize(mask, buffer, pIndex, null, s); + return 1; + } + } + + // claim limit slots at once + if (casProducerIndex(pIndex, batchIndex)) { + claimedSlots = (int) ((batchIndex - pIndex) / 2); + break; + } + } + + for (int i = 0; i < claimedSlots; i++) { + final long offset = modifiedCalcCircularRefElementOffset(pIndex + 2l * i, mask); + soRefElement(buffer, offset, s.get()); + } + return claimedSlots; + } + + /** + * Get an iterator for this queue. This method is thread safe. + *

    + * The iterator provides a best-effort snapshot of the elements in the queue. + * The returned iterator is not guaranteed to return elements in queue order, + * and races with the consumer thread may cause gaps in the sequence of returned elements. + * Like {link #relaxedPoll}, the iterator may not immediately return newly inserted elements. + * + * @return The iterator. + */ + @Override + public Iterator iterator() { + return new WeakIterator(consumerBuffer, lvConsumerIndex(), lvProducerIndex()); + } + + private static class WeakIterator implements Iterator { + + private final long pIndex; + + private long nextIndex; + + private E nextElement; + + private E[] currentBuffer; + + private int mask; + + WeakIterator(E[] currentBuffer, long cIndex, long pIndex) { + this.pIndex = pIndex >> 1; + this.nextIndex = cIndex >> 1; + setBuffer(currentBuffer); + nextElement = getNext(); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove"); + } + + @Override + public boolean hasNext() { + return nextElement != null; + } + + @Override + public E next() { + final E e = nextElement; + if (e == null) { + throw new NoSuchElementException(); + } + nextElement = getNext(); + return e; + } + + private void setBuffer(E[] buffer) { + this.currentBuffer = buffer; + this.mask = buffer.length - 2; + } + + private E getNext() { + while (nextIndex < pIndex) { + long index = nextIndex++; + E e = lvRefElement(currentBuffer, calcCircularRefElementOffset(index, mask)); + // skip removed/not yet visible elements + if (e == null) { + continue; + } + + // not null && not JUMP -> found next element + if (e != JUMP) { + return e; + } + + // need to jump to the next buffer + int nextBufferIndex = mask + 1; + Object nextBuffer = lvRefElement(currentBuffer, + calcRefElementOffset(nextBufferIndex)); + + if (nextBuffer == BUFFER_CONSUMED || nextBuffer == null) { + // Consumer may have passed us, or the next buffer is not visible yet: drop out early + return null; + } + + setBuffer((E[]) nextBuffer); + // now with the new array retry the load, it can't be a JUMP, but we need to repeat same index + e = lvRefElement(currentBuffer, calcCircularRefElementOffset(index, mask)); + // skip removed/not yet visible elements + if (e == null) { + continue; + } else { + return e; + } + + } + return null; + } + } + + private void resize(long oldMask, E[] oldBuffer, long pIndex, E e, Supplier s) { + assert (e != null && s == null) || (e == null || s != null); + int newBufferLength = getNextBufferSize(oldBuffer); + final E[] newBuffer; + try { + newBuffer = (E[]) new Object[newBufferLength]; + } catch (OutOfMemoryError oom) { + assert lvProducerIndex() == pIndex + 1; + soProducerIndex(pIndex); + throw oom; + } + + producerBuffer = newBuffer; + final int newMask = (newBufferLength - 2) << 1; + producerMask = newMask; + + final long offsetInOld = modifiedCalcCircularRefElementOffset(pIndex, oldMask); + final long offsetInNew = modifiedCalcCircularRefElementOffset(pIndex, newMask); + + soRefElement(newBuffer, offsetInNew, e == null ? s.get() : e);// element in new array + soRefElement(oldBuffer, nextArrayOffset(oldMask), newBuffer);// buffer linked + + // ASSERT code + final long cIndex = lvConsumerIndex(); + final long availableInQueue = availableInQueue(pIndex, cIndex); + if (availableInQueue < 0) throw new IllegalArgumentException("availableInQueue: " + availableInQueue + " (expected: > 0)"); + + // Invalidate racing CASs + // We never set the limit beyond the bounds of a buffer + soProducerLimit(pIndex + Math.min(newMask, availableInQueue)); + + // make resize visible to the other producers + soProducerIndex(pIndex + 2); + + // INDEX visible before ELEMENT, consistent with consumer expectation + // make resize visible to consumer + soRefElement(oldBuffer, offsetInOld, JUMP); + } + + protected long availableInQueue(long pIndex, long cIndex) { + return maxQueueCapacity - (pIndex - cIndex); + } + + public int capacity() { + return (int) (maxQueueCapacity / 2); + } + + protected int getNextBufferSize(E[] buffer) { + return buffer.length; + } + + protected long getCurrentBufferCapacity(long mask) { + return mask; + } +} diff --git a/src/main/java/org/redkale/util/MpscGrowableArrayQueue.java b/src/main/java/org/redkale/util/MpscGrowableArrayQueue.java new file mode 100644 index 000000000..53fdb7fea --- /dev/null +++ b/src/main/java/org/redkale/util/MpscGrowableArrayQueue.java @@ -0,0 +1,43 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +/** + * 参考 https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/queues/MpscGrowableArrayQueue.java version: v3.3.0实现的MPSC队列
    + * 与基类的区别在于: 每次都会将连接块容量加倍,直到底层的数组可以完全容纳所有的元素。 + *

    + * 详情见: https://redkale.org + * + * @param 泛型 + * + * @author zhangjx + * @since 2.5.0 + */ +public class MpscGrowableArrayQueue extends MpscChunkedArrayQueue { + + public MpscGrowableArrayQueue(int maxCapacity) { + super(Math.max(2, Utility.roundToPowerOfTwo(maxCapacity / 8)), maxCapacity); + } + + public MpscGrowableArrayQueue(int initialCapacity, int maxCapacity) { + super(initialCapacity, maxCapacity); + } + + @Override + protected int getNextBufferSize(E[] buffer) { + final long maxSize = maxQueueCapacity / 2; + int len = buffer.length; + //checkLessThanOrEqual + if (len > maxSize) throw new IllegalArgumentException("buffer.length: " + len + " (expected: <= " + maxSize + ")"); + final int newSize = 2 * (len - 1); + return newSize + 1; + } + + @Override + protected long getCurrentBufferCapacity(long mask) { + return (mask + 2 == maxQueueCapacity) ? maxQueueCapacity : mask; + } +} diff --git a/src/main/java/org/redkale/util/NonBlockingHashMap.java b/src/main/java/org/redkale/util/NonBlockingHashMap.java new file mode 100644 index 000000000..a01fd147e --- /dev/null +++ b/src/main/java/org/redkale/util/NonBlockingHashMap.java @@ -0,0 +1,1915 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.io.*; +import java.util.*; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.*; + +/** + * 参考 https://github.com/JCTools/JCTools/blob/master/jctools-core/src/main/java/org/jctools/maps/NonBlockingHashMap.java version: v3.3.0实现的MPSC队列 + * + *

    + * 详情见: https://redkale.org + * + * @param 泛型 + * @param 泛型 + * + * @author zhangjx + * @since 2.5.0 + */ +public class NonBlockingHashMap extends AbstractMap implements ConcurrentMap, Cloneable, Serializable { + + private static final long serialVersionUID = 1234123412341234123L; + + private static final int REPROBE_LIMIT = 10; // Too many reprobes then force a table-resize + + private static final Unsafe UNSAFE = Utility.unsafe(); + // --- Bits to allow Unsafe access to arrays + + private static final int _Obase = UNSAFE == null ? 0 : UNSAFE.arrayBaseOffset(Object[].class); + + private static final int _Oscale = UNSAFE == null ? 0 : UNSAFE.arrayIndexScale(Object[].class); + + private static final int _Olog = _Oscale == 4 ? 2 : (_Oscale == 8 ? 3 : 9999); + + private static long rawIndex(final Object[] ary, final int idx) { + assert idx >= 0 && idx < ary.length; + // Note the long-math requirement, to handle arrays of more than 2^31 bytes + // - or 2^28 - or about 268M - 8-byte pointer elements. + return _Obase + ((long) idx << _Olog); + } + + // --- Setup to use Unsafe + private static final long _kvs_offset = fieldOffset(NonBlockingHashMap.class, "_kvs"); + + static long fieldOffset(Class clz, String fieldName) throws RuntimeException { + if (UNSAFE == null) return 0L; + try { + return UNSAFE.objectFieldOffset(clz.getDeclaredField(fieldName)); + } catch (NoSuchFieldException e) { + throw new RuntimeException(e); + } + } + + static void spRefElement(E[] buffer, long offset, E e) { + UNSAFE.putObject(buffer, offset, e); + } + + private boolean CAS_kvs(final Object[] oldkvs, final Object[] newkvs) { + return UNSAFE.compareAndSwapObject(this, _kvs_offset, oldkvs, newkvs); + } + + // --- Adding a 'prime' bit onto Values via wrapping with a junk wrapper class + private static final class Prime { + + final Object _V; + + Prime(Object V) { + _V = V; + } + + static Object unbox(Object V) { + return V instanceof Prime ? ((Prime) V)._V : V; + } + } + + // --- hash ---------------------------------------------------------------- + // Helper function to spread lousy hashCodes. Throws NPE for null Key, on + // purpose - as the first place to conveniently toss the required NPE for a + // null Key. + private static int hash(final Object key) { + int h = key.hashCode(); // The real hashCode call + h ^= (h >>> 20) ^ (h >>> 12); + h ^= (h >>> 7) ^ (h >>> 4); + h += h << 7; // smear low bits up high, for hashcodes that only differ by 1 + return h; + } + + // --- The Hash Table -------------------- + // Slot 0 is always used for a 'CHM' entry below to hold the interesting + // bits of the hash table. Slot 1 holds full hashes as an array of ints. + // Slots {2,3}, {4,5}, etc hold {Key,Value} pairs. The entire hash table + // can be atomically replaced by CASing the _kvs field. + // + // Why is CHM buried inside the _kvs Object array, instead of the other way + // around? The CHM info is used during resize events and updates, but not + // during standard 'get' operations. I assume 'get' is much more frequent + // than 'put'. 'get' can skip the extra indirection of skipping through the + // CHM to reach the _kvs array. + private transient Object[] _kvs; + + private static final CHM chm(Object[] kvs) { + return (CHM) kvs[0]; + } + + private static final int[] hashes(Object[] kvs) { + return (int[]) kvs[1]; + } + // Number of K,V pairs in the table + + private static final int len(Object[] kvs) { + return (kvs.length - 2) >> 1; + } + + // Time since last resize + private transient long _last_resize_milli; + + // --- Minimum table size ---------------- + // Pick size 8 K/V pairs, which turns into (8*2+2)*4+12 = 84 bytes on a + // standard 32-bit HotSpot, and (8*2+2)*8+12 = 156 bytes on 64-bit Azul. + private static final int MIN_SIZE_LOG = 3; // + + private static final int MIN_SIZE = (1 << MIN_SIZE_LOG); // Must be power of 2 + + // --- Sentinels ------------------------- + // No-Match-Old - putIfMatch does updates only if it matches the old value, + // and NO_MATCH_OLD basically counts as a wildcard match. + private static final Object NO_MATCH_OLD = new Object(); // Sentinel + // Match-Any-not-null - putIfMatch does updates only if it find a real old + // value. + + private static final Object MATCH_ANY = new Object(); // Sentinel + // This K/V pair has been deleted (but the Key slot is forever claimed). + // The same Key can be reinserted with a new value later. + + public static final Object TOMBSTONE = new Object(); + // Prime'd or box'd version of TOMBSTONE. This K/V pair was deleted, then a + // table resize started. The K/V pair has been marked so that no new + // updates can happen to the old table (and since the K/V pair was deleted + // nothing was copied to the new table). + + private static final Prime TOMBPRIME = new Prime(TOMBSTONE); + + // --- key,val ------------------------------------------------------------- + // Access K,V for a given idx + // + // Note that these are static, so that the caller is forced to read the _kvs + // field only once, and share that read across all key/val calls - lest the + // _kvs field move out from under us and back-to-back key & val calls refer + // to different _kvs arrays. + private static final Object key(Object[] kvs, int idx) { + return kvs[(idx << 1) + 2]; + } + + private static final Object val(Object[] kvs, int idx) { + return kvs[(idx << 1) + 3]; + } + + private static final boolean CAS_key(Object[] kvs, int idx, Object old, Object key) { + return UNSAFE.compareAndSwapObject(kvs, rawIndex(kvs, (idx << 1) + 2), old, key); + } + + private static final boolean CAS_val(Object[] kvs, int idx, Object old, Object val) { + return UNSAFE.compareAndSwapObject(kvs, rawIndex(kvs, (idx << 1) + 3), old, val); + } + + // --- dump ---------------------------------------------------------------- + // Verbose printout of table internals, useful for debugging. + public final void print() { + System.out.println("========="); + print2(_kvs); + System.out.println("========="); + } + // print the entire state of the table + + private final void print(Object[] kvs) { + for (int i = 0; i < len(kvs); i++) { + Object K = key(kvs, i); + if (K != null) { + String KS = (K == TOMBSTONE) ? "XXX" : K.toString(); + Object V = val(kvs, i); + Object U = Prime.unbox(V); + String p = (V == U) ? "" : "prime_"; + String US = (U == TOMBSTONE) ? "tombstone" : U.toString(); + System.out.println("" + i + " (" + KS + "," + p + US + ")"); + } + } + Object[] newkvs = chm(kvs)._newkvs; // New table, if any + if (newkvs != null) { + System.out.println("----"); + print(newkvs); + } + } + // print only the live values, broken down by the table they are in + + private final void print2(Object[] kvs) { + for (int i = 0; i < len(kvs); i++) { + Object key = key(kvs, i); + Object val = val(kvs, i); + Object U = Prime.unbox(val); + if (key != null && key != TOMBSTONE + && // key is sane + val != null && U != TOMBSTONE) { // val is sane + String p = (val == U) ? "" : "prime_"; + System.out.println("" + i + " (" + key + "," + p + val + ")"); + } + } + Object[] newkvs = chm(kvs)._newkvs; // New table, if any + if (newkvs != null) { + System.out.println("----"); + print2(newkvs); + } + } + + // Count of reprobes + private transient ConcurrentAutoTable _reprobes = new ConcurrentAutoTable(); + + /** Get and clear the current count of reprobes. Reprobes happen on key + * collisions, and a high reprobe rate may indicate a poor hash function or + * weaknesses in the table resizing function. + * + * @return the count of reprobes since the last call to {@link #reprobes} + * or since the table was created. */ + public long reprobes() { + long r = _reprobes.get(); + _reprobes = new ConcurrentAutoTable(); + return r; + } + + // --- reprobe_limit ----------------------------------------------------- + // Heuristic to decide if we have reprobed toooo many times. Running over + // the reprobe limit on a 'get' call acts as a 'miss'; on a 'put' call it + // can trigger a table resize. Several places must have exact agreement on + // what the reprobe_limit is, so we share it here. + private static int reprobe_limit(int len) { + return REPROBE_LIMIT + (len >> 4); + } + + // --- NonBlockingHashMap -------------------------------------------------- + // Constructors + /** Create a new NonBlockingHashMap with default minimum size (currently set + * to 8 K/V pairs or roughly 84 bytes on a standard 32-bit JVM). */ + public NonBlockingHashMap() { + this(MIN_SIZE); + } + + /** Create a new NonBlockingHashMap with initial room for the given number of + * elements, thus avoiding internal resizing operations to reach an + * appropriate size. Large numbers here when used with a small count of + * elements will sacrifice space for a small amount of time gained. The + * initial size will be rounded up internally to the next larger power of 2. */ + public NonBlockingHashMap(final int initial_sz) { + initialize(initial_sz); + } + + private final void initialize(int initial_sz) { + if (initial_sz < 0) { + throw new IllegalArgumentException("initial_sz: " + initial_sz + " (expected: >= 0)"); + } + int i; // Convert to next largest power-of-2 + if (initial_sz > 1024 * 1024) initial_sz = 1024 * 1024; + for (i = MIN_SIZE_LOG; (1 << i) < (initial_sz << 2); i++) ; + // Double size for K,V pairs, add 1 for CHM and 1 for hashes + _kvs = new Object[((1 << i) << 1) + 2]; + _kvs[0] = new CHM(new ConcurrentAutoTable()); // CHM in slot 0 + _kvs[1] = new int[1 << i]; // Matching hash entries + _last_resize_milli = System.currentTimeMillis(); + } + // Version for subclassed readObject calls, to be called after the defaultReadObject + + protected final void initialize() { + initialize(MIN_SIZE); + } + + // --- wrappers ------------------------------------------------------------ + /** Returns the number of key-value mappings in this map. + * + * @return the number of key-value mappings in this map */ + @Override + public int size() { + return chm(_kvs).size(); + } + + /** Returns <tt>size() == 0</tt>. + * + * @return <tt>size() == 0</tt> */ + @Override + public boolean isEmpty() { + return size() == 0; + } + + /** Tests if the key in the table using the <tt>equals</tt> method. + * + * @return <tt>true</tt> if the key is in the table using the <tt>equals</tt> method + * @throws NullPointerException if the specified key is null */ + @Override + public boolean containsKey(Object key) { + return get(key) != null; + } + + /** Legacy method testing if some key maps into the specified value in this + * table. This method is identical in functionality to {@link + * #containsValue}, and exists solely to ensure full compatibility with + * class {@link java.util.Hashtable}, which supported this method prior to + * introduction of the Java Collections framework. + * + * @param val a value to search for + * + * @return <tt>true</tt> if this map maps one or more keys to the specified value + * @throws NullPointerException if the specified value is null */ + public boolean contains(Object val) { + return containsValue(val); + } + + /** Maps the specified key to the specified value in the table. Neither key + * nor value can be null. + *

    + * The value can be retrieved by calling {@link #get} with a key that is + * equal to the original key. + * + * @param key key with which the specified value is to be associated + * @param val value to be associated with the specified key + * + * @return the previous value associated with <tt>key</tt>, or + * <tt>null</tt> if there was no mapping for <tt>key</tt> + * @throws NullPointerException if the specified key or value is null */ + @Override + public TypeV put(TypeK key, TypeV val) { + return putIfMatch(key, val, NO_MATCH_OLD); + } + + /** Atomically, do a {@link #put} if-and-only-if the key is not mapped. + * Useful to ensure that only a single mapping for the key exists, even if + * many threads are trying to create the mapping in parallel. + * + * @return the previous value associated with the specified key, + * or <tt>null</tt> if there was no mapping for the key + * @throws NullPointerException if the specified key or value is null */ + @Override + public TypeV putIfAbsent(TypeK key, TypeV val) { + return putIfMatch(key, val, TOMBSTONE); + } + + /** Removes the key (and its corresponding value) from this map. + * This method does nothing if the key is not in the map. + * + * @return the previous value associated with <tt>key</tt>, or + * <tt>null</tt> if there was no mapping for <tt>key</tt> + * @throws NullPointerException if the specified key is null */ + @Override + public TypeV remove(Object key) { + return putIfMatch(key, TOMBSTONE, NO_MATCH_OLD); + } + + /** Atomically do a {@link #remove(Object)} if-and-only-if the key is mapped + * to a value which is equals to the given value. + * + * @throws NullPointerException if the specified key or value is null */ + public boolean remove(Object key, Object val) { + return objectsEquals(putIfMatch(key, TOMBSTONE, val), val); + } + + /** Atomically do a put(key,val) if-and-only-if the key is + * mapped to some value already. + * + * @throws NullPointerException if the specified key or value is null */ + @Override + public TypeV replace(TypeK key, TypeV val) { + return putIfMatch(key, val, MATCH_ANY); + } + + /** Atomically do a put(key,newValue) if-and-only-if the key is + * mapped a value which is equals to oldValue. + * + * @throws NullPointerException if the specified key or value is null */ + @Override + public boolean replace(TypeK key, TypeV oldValue, TypeV newValue) { + return objectsEquals(putIfMatch(key, newValue, oldValue), oldValue); + } + + private static boolean objectsEquals(Object a, Object b) { + return (a == b) || (a != null && a.equals(b)); + } + + // Atomically replace newVal for oldVal, returning the value that existed + // there before. If the oldVal matches the returned value, then newVal was + // inserted, otherwise not. A null oldVal means the key does not exist (only + // insert if missing); a null newVal means to remove the key. + public final TypeV putIfMatchAllowNull(Object key, Object newVal, Object oldVal) { + if (oldVal == null) oldVal = TOMBSTONE; + if (newVal == null) newVal = TOMBSTONE; + final TypeV res = (TypeV) putIfMatch0(this, _kvs, key, newVal, oldVal); + assert !(res instanceof Prime); + //assert res != null; + return res == TOMBSTONE ? null : res; + } + + /** Atomically replace newVal for oldVal, returning the value that existed + * there before. If the oldVal matches the returned value, then newVal was + * inserted, otherwise not. + * + * @return the previous value associated with the specified key, + * or <tt>null</tt> if there was no mapping for the key + * @throws NullPointerException if the key or either value is null + */ + public final TypeV putIfMatch(Object key, Object newVal, Object oldVal) { + if (oldVal == null || newVal == null) throw new NullPointerException(); + final Object res = putIfMatch0(this, _kvs, key, newVal, oldVal); + assert !(res instanceof Prime); + assert res != null; + return res == TOMBSTONE ? null : (TypeV) res; + } + + /** Copies all of the mappings from the specified map to this one, replacing + * any existing mappings. + * + * @param m mappings to be stored in this map */ + @Override + public void putAll(Map m) { + for (Map.Entry e : m.entrySet()) + put(e.getKey(), e.getValue()); + } + + /** Removes all of the mappings from this map. */ + @Override + public void clear() { // Smack a new empty table down + Object[] newkvs = new NonBlockingHashMap(MIN_SIZE)._kvs; + while (!CAS_kvs(_kvs, newkvs)) // Spin until the clear works + ; + } + + /** Returns <tt>true</tt> if this Map maps one or more keys to the specified + * value. Note: This method requires a full internal traversal of the + * hash table and is much slower than {@link #containsKey}. + * + * @param val value whose presence in this map is to be tested + * + * @return <tt>true</tt> if this map maps one or more keys to the specified value + * @throws NullPointerException if the specified value is null */ + @Override + public boolean containsValue(final Object val) { + if (val == null) throw new NullPointerException(); + for (TypeV V : values()) + if (V == val || V.equals(val)) + return true; + return false; + } + + // This function is supposed to do something for Hashtable, and the JCK + // tests hang until it gets called... by somebody ... for some reason, + // any reason.... + protected void rehash() { + } + + /** + * Creates a shallow copy of this hashtable. All the structure of the + * hashtable itself is copied, but the keys and values are not cloned. + * This is a relatively expensive operation. + * + * @return a clone of the hashtable. + */ + @Override + public Object clone() { + try { + // Must clone, to get the class right; NBHM might have been + // extended so it would be wrong to just make a new NBHM. + NonBlockingHashMap t = (NonBlockingHashMap) super.clone(); + // But I don't have an atomic clone operation - the underlying _kvs + // structure is undergoing rapid change. If I just clone the _kvs + // field, the CHM in _kvs[0] won't be in sync. + // + // Wipe out the cloned array (it was shallow anyways). + t.clear(); + // Now copy sanely + for (TypeK K : keySet()) { + final TypeV V = get(K); // Do an official 'get' + t.put(K, V); + } + return t; + } catch (CloneNotSupportedException e) { + // this shouldn't happen, since we are Cloneable + throw new InternalError(); + } + } + + /** + * Returns a string representation of this map. The string representation + * consists of a list of key-value mappings in the order returned by the + * map's <tt>entrySet</tt> view's iterator, enclosed in braces + * (<tt>"{}"</tt>). Adjacent mappings are separated by the characters + * <tt>", "</tt> (comma and space). Each key-value mapping is rendered as + * the key followed by an equals sign (<tt>"="</tt>) followed by the + * associated value. Keys and values are converted to strings as by + * {@link String#valueOf(Object)}. + * + * @return a string representation of this map + */ + @Override + public String toString() { + Iterator> i = entrySet().iterator(); + if (!i.hasNext()) + return "{}"; + + StringBuilder sb = new StringBuilder(); + sb.append('{'); + for (;;) { + Entry e = i.next(); + TypeK key = e.getKey(); + TypeV value = e.getValue(); + sb.append(key == this ? "(this Map)" : key); + sb.append('='); + sb.append(value == this ? "(this Map)" : value); + if (!i.hasNext()) + return sb.append('}').toString(); + sb.append(", "); + } + } + + // --- keyeq --------------------------------------------------------------- + // Check for key equality. Try direct pointer compare first, then see if + // the hashes are unequal (fast negative test) and finally do the full-on + // 'equals' v-call. + private static boolean keyeq(Object K, Object key, int[] hashes, int hash, int fullhash) { + return K == key + || // Either keys match exactly OR + // hash exists and matches? hash can be zero during the install of a + // new key/value pair. + ((hashes[hash] == 0 || hashes[hash] == fullhash) + && // Do not call the users' "equals()" call with a Tombstone, as this can + // surprise poorly written "equals()" calls that throw exceptions + // instead of simply returning false. + K != TOMBSTONE + && // Do not call users' equals call with a Tombstone + // Do the match the hard way - with the users' key being the loop- + // invariant "this" pointer. I could have flipped the order of + // operands (since equals is commutative), but I'm making mega-morphic + // v-calls in a re-probing loop and nailing down the 'this' argument + // gives both the JIT and the hardware a chance to prefetch the call target. + key.equals(K)); // Finally do the hard match + } + + // --- get ----------------------------------------------------------------- + /** Returns the value to which the specified key is mapped, or {@code null} + * if this map contains no mapping for the key. + *

    + * More formally, if this map contains a mapping from a key {@code k} to + * a value {@code v} such that {@code key.equals(k)}, then this method + * returns {@code v}; otherwise it returns {@code null}. (There can be at + * most one such mapping.) + * + * @throws NullPointerException if the specified key is null */ + // Never returns a Prime nor a Tombstone. + @Override + public TypeV get(Object key) { + final Object V = get_impl(this, _kvs, key); + assert !(V instanceof Prime); // Never return a Prime + assert V != TOMBSTONE; + return (TypeV) V; + } + + private static final Object get_impl(final NonBlockingHashMap topmap, final Object[] kvs, final Object key) { + final int fullhash = hash(key); // throws NullPointerException if key is null + final int len = len(kvs); // Count of key/value pairs, reads kvs.length + final CHM chm = chm(kvs); // The CHM, for a volatile read below; reads slot 0 of kvs + final int[] hashes = hashes(kvs); // The memoized hashes; reads slot 1 of kvs + + int idx = fullhash & (len - 1); // First key hash + + // Main spin/reprobe loop, looking for a Key hit + int reprobe_cnt = 0; + while (true) { + // Probe table. Each read of 'val' probably misses in cache in a big + // table; hopefully the read of 'key' then hits in cache. + final Object K = key(kvs, idx); // Get key before volatile read, could be null + final Object V = val(kvs, idx); // Get value before volatile read, could be null or Tombstone or Prime + if (K == null) return null; // A clear miss + + // We need a volatile-read here to preserve happens-before semantics on + // newly inserted Keys. If the Key body was written just before inserting + // into the table a Key-compare here might read the uninitialized Key body. + // Annoyingly this means we have to volatile-read before EACH key compare. + // . + // We also need a volatile-read between reading a newly inserted Value + // and returning the Value (so the user might end up reading the stale + // Value contents). Same problem as with keys - and the one volatile + // read covers both. + final Object[] newkvs = chm._newkvs; // VOLATILE READ before key compare + + // Key-compare + if (keyeq(K, key, hashes, idx, fullhash)) { + // Key hit! Check for no table-copy-in-progress + if (!(V instanceof Prime)) // No copy? + return (V == TOMBSTONE) ? null : V; // Return the value + // Key hit - but slot is (possibly partially) copied to the new table. + // Finish the copy & retry in the new table. + return get_impl(topmap, chm.copy_slot_and_check(topmap, kvs, idx, key), key); // Retry in the new table + } + // get and put must have the same key lookup logic! But only 'put' + // needs to force a table-resize for a too-long key-reprobe sequence. + // Check for too-many-reprobes on get - and flip to the new table. + if (++reprobe_cnt >= reprobe_limit(len) + || // too many probes + K == TOMBSTONE) // found a TOMBSTONE key, means no more keys in this table + return newkvs == null ? null : get_impl(topmap, topmap.help_copy(newkvs), key); // Retry in the new table + + idx = (idx + 1) & (len - 1); // Reprobe by 1! (could now prefetch) + } + } + + // --- getk ----------------------------------------------------------------- + /** Returns the Key to which the specified key is mapped, or {@code null} + * if this map contains no mapping for the key. + * + * @throws NullPointerException if the specified key is null */ + // Never returns a Prime nor a Tombstone. + public TypeK getk(TypeK key) { + return (TypeK) getk_impl(this, _kvs, key); + } + + private static final Object getk_impl(final NonBlockingHashMap topmap, final Object[] kvs, final Object key) { + final int fullhash = hash(key); // throws NullPointerException if key is null + final int len = len(kvs); // Count of key/value pairs, reads kvs.length + final CHM chm = chm(kvs); // The CHM, for a volatile read below; reads slot 0 of kvs + final int[] hashes = hashes(kvs); // The memoized hashes; reads slot 1 of kvs + + int idx = fullhash & (len - 1); // First key hash + + // Main spin/reprobe loop, looking for a Key hit + int reprobe_cnt = 0; + while (true) { + // Probe table. + final Object K = key(kvs, idx); // Get key before volatile read, could be null + if (K == null) return null; // A clear miss + + // We need a volatile-read here to preserve happens-before semantics on + // newly inserted Keys. If the Key body was written just before inserting + // into the table a Key-compare here might read the uninitialized Key body. + // Annoyingly this means we have to volatile-read before EACH key compare. + // . + // We also need a volatile-read between reading a newly inserted Value + // and returning the Value (so the user might end up reading the stale + // Value contents). Same problem as with keys - and the one volatile + // read covers both. + final Object[] newkvs = chm._newkvs; // VOLATILE READ before key compare + + // Key-compare + if (keyeq(K, key, hashes, idx, fullhash)) + return K; // Return existing Key! + + // get and put must have the same key lookup logic! But only 'put' + // needs to force a table-resize for a too-long key-reprobe sequence. + // Check for too-many-reprobes on get - and flip to the new table. + if (++reprobe_cnt >= reprobe_limit(len) + || // too many probes + K == TOMBSTONE) { // found a TOMBSTONE key, means no more keys in this table + return newkvs == null ? null : getk_impl(topmap, topmap.help_copy(newkvs), key); // Retry in the new table + } + + idx = (idx + 1) & (len - 1); // Reprobe by 1! (could now prefetch) + } + } + + static volatile int DUMMY_VOLATILE; + + /** + * Put, Remove, PutIfAbsent, etc. Return the old value. If the returned value is equal to expVal (or expVal is + * {@link #NO_MATCH_OLD}) then the put can be assumed to work (although might have been immediately overwritten). + * Only the path through copy_slot passes in an expected value of null, and putIfMatch only returns a null if passed + * in an expected null. + * + * @param topmap the map to act on + * @param kvs the KV table snapshot we act on + * @param key not null (will result in {@link NullPointerException}) + * @param putval the new value to use. Not null. {@link #TOMBSTONE} will result in deleting the entry. + * @param expVal expected old value. Can be null. {@link #NO_MATCH_OLD} for an unconditional put/remove. + * {@link #TOMBSTONE} if we expect old entry to not exist(null/{@link #TOMBSTONE} value). + * {@link #MATCH_ANY} will ignore the current value, but only if an entry exists. A null expVal is used + * internally to perform a strict insert-if-never-been-seen-before operation. + * + * @return {@link #TOMBSTONE} if key does not exist or match has failed. null if expVal is + * null AND old value was null. Otherwise the old entry value (not null). + */ + private static final Object putIfMatch0( + final NonBlockingHashMap topmap, + final Object[] kvs, + final Object key, + final Object putval, + final Object expVal) { + assert putval != null; + assert !(putval instanceof Prime); + assert !(expVal instanceof Prime); + final int fullhash = hash(key); // throws NullPointerException if key null + final int len = len(kvs); // Count of key/value pairs, reads kvs.length + final CHM chm = chm(kvs); // Reads kvs[0] + final int[] hashes = hashes(kvs); // Reads kvs[1], read before kvs[0] + int idx = fullhash & (len - 1); + + // --- + // Key-Claim stanza: spin till we can claim a Key (or force a resizing). + int reprobe_cnt = 0; + Object K = null, V = null; + Object[] newkvs = null; + while (true) { // Spin till we get a Key slot + V = val(kvs, idx); // Get old value (before volatile read below!) + K = key(kvs, idx); // Get current key + if (K == null) { // Slot is free? + // Found an empty Key slot - which means this Key has never been in + // this table. No need to put a Tombstone - the Key is not here! + if (putval == TOMBSTONE) return TOMBSTONE; // Not-now & never-been in this table + if (expVal == MATCH_ANY) return TOMBSTONE; // Will not match, even after K inserts + // Claim the null key-slot + if (CAS_key(kvs, idx, null, key)) { // Claim slot for Key + chm._slots.add(1); // Raise key-slots-used count + hashes[idx] = fullhash; // Memoize fullhash + break; // Got it! + } + // CAS to claim the key-slot failed. + // + // This re-read of the Key points out an annoying short-coming of Java + // CAS. Most hardware CAS's report back the existing value - so that + // if you fail you have a *witness* - the value which caused the CAS to + // fail. The Java API turns this into a boolean destroying the + // witness. Re-reading does not recover the witness because another + // thread can write over the memory after the CAS. Hence we can be in + // the unfortunate situation of having a CAS fail *for cause* but + // having that cause removed by a later store. This turns a + // non-spurious-failure CAS (such as Azul has) into one that can + // apparently spuriously fail - and we avoid apparent spurious failure + // by not allowing Keys to ever change. + + // Volatile read, to force loads of K to retry despite JIT, otherwise + // it is legal to e.g. haul the load of "K = key(kvs,idx);" outside of + // this loop (since failed CAS ops have no memory ordering semantics). + int dummy = DUMMY_VOLATILE; + continue; + } + // Key slot was not null, there exists a Key here + + // We need a volatile-read here to preserve happens-before semantics on + // newly inserted Keys. If the Key body was written just before inserting + // into the table a Key-compare here might read the uninitialized Key body. + // Annoyingly this means we have to volatile-read before EACH key compare. + newkvs = chm._newkvs; // VOLATILE READ before key compare + + if (keyeq(K, key, hashes, idx, fullhash)) + break; // Got it! + + // get and put must have the same key lookup logic! Lest 'get' give + // up looking too soon. + //topmap._reprobes.add(1); + if (++reprobe_cnt >= reprobe_limit(len) + || // too many probes or + K == TOMBSTONE) { // found a TOMBSTONE key, means no more keys + // We simply must have a new table to do a 'put'. At this point a + // 'get' will also go to the new table (if any). We do not need + // to claim a key slot (indeed, we cannot find a free one to claim!). + newkvs = chm.resize(topmap, kvs); + if (expVal != null) topmap.help_copy(newkvs); // help along an existing copy + return putIfMatch0(topmap, newkvs, key, putval, expVal); + } + + idx = (idx + 1) & (len - 1); // Reprobe! + } // End of spinning till we get a Key slot + + while (true) { // Spin till we insert a value + // --- + // Found the proper Key slot, now update the matching Value slot. We + // never put a null, so Value slots monotonically move from null to + // not-null (deleted Values use Tombstone). Thus if 'V' is null we + // fail this fast cutout and fall into the check for table-full. + if (putval == V) return V; // Fast cutout for no-change + + // See if we want to move to a new table (to avoid high average re-probe + // counts). We only check on the initial set of a Value from null to + // not-null (i.e., once per key-insert). Of course we got a 'free' check + // of newkvs once per key-compare (not really free, but paid-for by the + // time we get here). + if (newkvs == null + && // New table-copy already spotted? + // Once per fresh key-insert check the hard way + ((V == null && chm.tableFull(reprobe_cnt, len)) + || // Or we found a Prime, but the JMM allowed reordering such that we + // did not spot the new table (very rare race here: the writing + // thread did a CAS of _newkvs then a store of a Prime. This thread + // reads the Prime, then reads _newkvs - but the read of Prime was so + // delayed (or the read of _newkvs was so accelerated) that they + // swapped and we still read a null _newkvs. The resize call below + // will do a CAS on _newkvs forcing the read. + V instanceof Prime)) { + newkvs = chm.resize(topmap, kvs); // Force the new table copy to start + } + // See if we are moving to a new table. + // If so, copy our slot and retry in the new table. + if (newkvs != null) { + return putIfMatch0(topmap, chm.copy_slot_and_check(topmap, kvs, idx, expVal), key, putval, expVal); + } + // --- + // We are finally prepared to update the existing table + assert !(V instanceof Prime); + + // Must match old, and we do not? Then bail out now. Note that either V + // or expVal might be TOMBSTONE. Also V can be null, if we've never + // inserted a value before. expVal can be null if we are called from + // copy_slot. + if (expVal != NO_MATCH_OLD + && // Do we care about expected-Value at all? + V != expVal + && // No instant match already? + (expVal != MATCH_ANY || V == TOMBSTONE || V == null) + && !(V == null && expVal == TOMBSTONE) + && // Match on null/TOMBSTONE combo + (expVal == null || !expVal.equals(V))) { // Expensive equals check at the last + return (V == null) ? TOMBSTONE : V; // Do not update! + } + + // Actually change the Value in the Key,Value pair + if (CAS_val(kvs, idx, V, putval)) break; + + // CAS failed + // Because we have no witness, we do not know why it failed. + // Indeed, by the time we look again the value under test might have flipped + // a thousand times and now be the expected value (despite the CAS failing). + // Check for the never-succeed condition of a Prime value and jump to any + // nested table, or else just re-run. + // We would not need this load at all if CAS returned the value on which + // the CAS failed (AKA witness). The new CAS semantics are supported via + // VarHandle in JDK9. + V = val(kvs, idx); // Get new value + + // If a Prime'd value got installed, we need to re-run the put on the + // new table. Otherwise we lost the CAS to another racing put. + if (V instanceof Prime) + return putIfMatch0(topmap, chm.copy_slot_and_check(topmap, kvs, idx, expVal), key, putval, expVal); + + // Simply retry from the start. + // NOTE: need the fence, since otherwise 'val(kvs,idx)' load could be hoisted + // out of loop. + int dummy = DUMMY_VOLATILE; + } + + // CAS succeeded - we did the update! + // Both normal put's and table-copy calls putIfMatch, but table-copy + // does not (effectively) increase the number of live k/v pairs. + if (expVal != null) { + // Adjust sizes - a striped counter + if ((V == null || V == TOMBSTONE) && putval != TOMBSTONE) chm._size.add(1); + if (!(V == null || V == TOMBSTONE) && putval == TOMBSTONE) chm._size.add(-1); + } + + // We won; we know the update happened as expected. + return (V == null && expVal != null) ? TOMBSTONE : V; + } + + // --- help_copy --------------------------------------------------------- + // Help along an existing resize operation. This is just a fast cut-out + // wrapper, to encourage inlining for the fast no-copy-in-progress case. We + // always help the top-most table copy, even if there are nested table + // copies in progress. + private final Object[] help_copy(Object[] helper) { + // Read the top-level KVS only once. We'll try to help this copy along, + // even if it gets promoted out from under us (i.e., the copy completes + // and another KVS becomes the top-level copy). + Object[] topkvs = _kvs; + CHM topchm = chm(topkvs); + if (topchm._newkvs == null) return helper; // No copy in-progress + topchm.help_copy_impl(this, topkvs, false); + return helper; + } + + // --- CHM ----------------------------------------------------------------- + // The control structure for the NonBlockingHashMap + private static final class CHM { + // Size in active K,V pairs + + private final ConcurrentAutoTable _size; + + public int size() { + return (int) _size.get(); + } + + // --- + // These next 2 fields are used in the resizing heuristics, to judge when + // it is time to resize or copy the table. Slots is a count of used-up + // key slots, and when it nears a large fraction of the table we probably + // end up reprobing too much. Last-resize-milli is the time since the + // last resize; if we are running back-to-back resizes without growing + // (because there are only a few live keys but many slots full of dead + // keys) then we need a larger table to cut down on the churn. + // Count of used slots, to tell when table is full of dead unusable slots + private final ConcurrentAutoTable _slots; + + public int slots() { + return (int) _slots.get(); + } + + // --- + // New mappings, used during resizing. + // The 'new KVs' array - created during a resize operation. This + // represents the new table being copied from the old one. It's the + // volatile variable that is read as we cross from one table to the next, + // to get the required memory orderings. It monotonically transits from + // null to set (once). + volatile Object[] _newkvs; + + private static final AtomicReferenceFieldUpdater _newkvsUpdater + = AtomicReferenceFieldUpdater.newUpdater(CHM.class, Object[].class, "_newkvs"); + // Set the _next field if we can. + + boolean CAS_newkvs(Object[] newkvs) { + while (_newkvs == null) + if (_newkvsUpdater.compareAndSet(this, null, newkvs)) + return true; + return false; + } + + // Sometimes many threads race to create a new very large table. Only 1 + // wins the race, but the losers all allocate a junk large table with + // hefty allocation costs. Attempt to control the overkill here by + // throttling attempts to create a new table. I cannot really block here + // (lest I lose the non-blocking property) but late-arriving threads can + // give the initial resizing thread a little time to allocate the initial + // new table. The Right Long Term Fix here is to use array-lets and + // incrementally create the new very large array. In C I'd make the array + // with malloc (which would mmap under the hood) which would only eat + // virtual-address and not real memory - and after Somebody wins then we + // could in parallel initialize the array. Java does not allow + // un-initialized array creation (especially of ref arrays!). + volatile long _resizers; // count of threads attempting an initial resize + + private static final AtomicLongFieldUpdater _resizerUpdater + = AtomicLongFieldUpdater.newUpdater(CHM.class, "_resizers"); + + // --- + // Simple constructor + CHM(ConcurrentAutoTable size) { + _size = size; + _slots = new ConcurrentAutoTable(); + } + + // --- tableFull --------------------------------------------------------- + // Heuristic to decide if this table is too full, and we should start a + // new table. Note that if a 'get' call has reprobed too many times and + // decided the table must be full, then always the estimate_sum must be + // high and we must report the table is full. If we do not, then we might + // end up deciding that the table is not full and inserting into the + // current table, while a 'get' has decided the same key cannot be in this + // table because of too many reprobes. The invariant is: + // slots.estimate_sum >= max_reprobe_cnt >= reprobe_limit(len) + private final boolean tableFull(int reprobe_cnt, int len) { + return // Do the cheap check first: we allow some number of reprobes always + reprobe_cnt >= REPROBE_LIMIT + && (reprobe_cnt >= reprobe_limit(len) + || // More expensive check: see if the table is > 1/2 full. + _slots.estimate_get() >= (len >> 1)); + } + + // --- resize ------------------------------------------------------------ + // Resizing after too many probes. "How Big???" heuristics are here. + // Callers will (not this routine) will 'help_copy' any in-progress copy. + // Since this routine has a fast cutout for copy-already-started, callers + // MUST 'help_copy' lest we have a path which forever runs through + // 'resize' only to discover a copy-in-progress which never progresses. + private final Object[] resize(NonBlockingHashMap topmap, Object[] kvs) { + assert chm(kvs) == this; + + // Check for resize already in progress, probably triggered by another thread + Object[] newkvs = _newkvs; // VOLATILE READ + if (newkvs != null) // See if resize is already in progress + return newkvs; // Use the new table already + + // No copy in-progress, so start one. First up: compute new table size. + int oldlen = len(kvs); // Old count of K,V pairs allowed + int sz = size(); // Get current table count of active K,V pairs + int newsz = sz; // First size estimate + + // Heuristic to determine new size. We expect plenty of dead-slots-with-keys + // and we need some decent padding to avoid endless reprobing. + if (sz >= (oldlen >> 2)) { // If we are >25% full of keys then... + newsz = oldlen << 1; // Double size, so new table will be between 12.5% and 25% full + // For tables less than 1M entries, if >50% full of keys then... + // For tables more than 1M entries, if >75% full of keys then... + if (4L * sz >= ((oldlen >> 20) != 0 ? 3L : 2L) * oldlen) + newsz = oldlen << 2; // Double double size, so new table will be between %12.5 (18.75%) and 25% (25%) + } + // This heuristic in the next 2 lines leads to a much denser table + // with a higher reprobe rate + //if( sz >= (oldlen>>1) ) // If we are >50% full of keys then... + // newsz = oldlen<<1; // Double size + + // Last (re)size operation was very recent? Then double again + // despite having few live keys; slows down resize operations + // for tables subject to a high key churn rate - but do not + // forever grow the table. If there is a high key churn rate + // the table needs a steady state of rare same-size resize + // operations to clean out the dead keys. + long tm = System.currentTimeMillis(); + if (newsz <= oldlen + && // New table would shrink or hold steady? + tm <= topmap._last_resize_milli + 10000) // Recent resize (less than 10 sec ago) + newsz = oldlen << 1; // Double the existing size + // Do not shrink, ever. If we hit this size once, assume we + // will again. + if (newsz < oldlen) newsz = oldlen; + // Convert to power-of-2 + int log2; + for (log2 = MIN_SIZE_LOG; (1 << log2) < newsz; log2++) ; // Compute log2 of size + long len = ((1L << log2) << 1) + 2; + // prevent integer overflow - limit of 2^31 elements in a Java array + // so here, 2^30 + 2 is the largest number of elements in the hash table + if ((int) len != len) { + log2 = 30; + len = (1L << log2) + 2; + if (sz > ((len >> 2) + (len >> 1))) throw new RuntimeException("Table is full."); + } + + // Now limit the number of threads actually allocating memory to a + // handful - lest we have 750 threads all trying to allocate a giant + // resized array. + long r = _resizers; + while (!_resizerUpdater.compareAndSet(this, r, r + 1)) + r = _resizers; + // Size calculation: 2 words (K+V) per table entry, plus a handful. We + // guess at 64-bit pointers; 32-bit pointers screws up the size calc by + // 2x but does not screw up the heuristic very much. + long megs = ((((1L << log2) << 1) + 8) << 3/* word to bytes */) >> 20/* megs */; + if (r >= 2 && megs > 0) { // Already 2 guys trying; wait and see + newkvs = _newkvs; // Between dorking around, another thread did it + if (newkvs != null) // See if resize is already in progress + return newkvs; // Use the new table already + // TODO - use a wait with timeout, so we'll wakeup as soon as the new table + // is ready, or after the timeout in any case. + //synchronized( this ) { wait(8*megs); } // Timeout - we always wakeup + // For now, sleep a tad and see if the 2 guys already trying to make + // the table actually get around to making it happen. + try { + Thread.sleep(megs); + } catch (Exception e) { + } + } + // Last check, since the 'new' below is expensive and there is a chance + // that another thread slipped in a new thread while we ran the heuristic. + newkvs = _newkvs; + if (newkvs != null) // See if resize is already in progress + return newkvs; // Use the new table already + + // Double size for K,V pairs, add 1 for CHM + newkvs = new Object[(int) len]; // This can get expensive for big arrays + newkvs[0] = new CHM(_size); // CHM in slot 0 + newkvs[1] = new int[1 << log2]; // hashes in slot 1 + + // Another check after the slow allocation + if (_newkvs != null) // See if resize is already in progress + return _newkvs; // Use the new table already + + // The new table must be CAS'd in so only 1 winner amongst duplicate + // racing resizing threads. Extra CHM's will be GC'd. + if (CAS_newkvs(newkvs)) { // NOW a resize-is-in-progress! + //notifyAll(); // Wake up any sleepers + //long nano = System.nanoTime(); + //System.out.println(" "+nano+" Resize from "+oldlen+" to "+(1< _copyIdxUpdater + = AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyIdx"); + + // Work-done reporting. Used to efficiently signal when we can move to + // the new table. From 0 to len(oldkvs) refers to copying from the old + // table to the new. + volatile long _copyDone = 0; + + static private final AtomicLongFieldUpdater _copyDoneUpdater + = AtomicLongFieldUpdater.newUpdater(CHM.class, "_copyDone"); + + // --- help_copy_impl ---------------------------------------------------- + // Help along an existing resize operation. We hope its the top-level + // copy (it was when we started) but this CHM might have been promoted out + // of the top position. + private final void help_copy_impl(NonBlockingHashMap topmap, Object[] oldkvs, boolean copy_all) { + assert chm(oldkvs) == this; + Object[] newkvs = _newkvs; + assert newkvs != null; // Already checked by caller + int oldlen = len(oldkvs); // Total amount to copy + final int MIN_COPY_WORK = Math.min(oldlen, 1024); // Limit per-thread work + + // --- + int panic_start = -1; + int copyidx = -9999; // Fool javac to think it's initialized + while (_copyDone < oldlen) { // Still needing to copy? + // Carve out a chunk of work. The counter wraps around so every + // thread eventually tries to copy every slot repeatedly. + + // We "panic" if we have tried TWICE to copy every slot - and it still + // has not happened. i.e., twice some thread somewhere claimed they + // would copy 'slot X' (by bumping _copyIdx) but they never claimed to + // have finished (by bumping _copyDone). Our choices become limited: + // we can wait for the work-claimers to finish (and become a blocking + // algorithm) or do the copy work ourselves. Tiny tables with huge + // thread counts trying to copy the table often 'panic'. + if (panic_start == -1) { // No panic? + copyidx = (int) _copyIdx; + while (!_copyIdxUpdater.compareAndSet(this, copyidx, copyidx + MIN_COPY_WORK)) + copyidx = (int) _copyIdx; // Re-read + if (!(copyidx < (oldlen << 1))) // Panic! + panic_start = copyidx; // Record where we started to panic-copy + } + + // We now know what to copy. Try to copy. + int workdone = 0; + for (int i = 0; i < MIN_COPY_WORK; i++) + if (copy_slot(topmap, (copyidx + i) & (oldlen - 1), oldkvs, newkvs)) // Made an oldtable slot go dead? + workdone++; // Yes! + if (workdone > 0) // Report work-done occasionally + copy_check_and_promote(topmap, oldkvs, workdone);// See if we can promote + //for( int i=0; i 0) { + while (!_copyDoneUpdater.compareAndSet(this, copyDone, copyDone + workdone)) { + copyDone = _copyDone; // Reload, retry + assert (copyDone + workdone) <= oldlen; + } + } + + // Check for copy being ALL done, and promote. Note that we might have + // nested in-progress copies and manage to finish a nested copy before + // finishing the top-level copy. We only promote top-level copies. + if (copyDone + workdone == oldlen + && // Ready to promote this table? + topmap._kvs == oldkvs + && // Looking at the top-level table? + // Attempt to promote + topmap.CAS_kvs(oldkvs, _newkvs)) { + topmap._last_resize_milli = System.currentTimeMillis(); // Record resize time for next check + } + } + + // --- copy_slot --------------------------------------------------------- + // Copy one K/V pair from oldkvs[i] to newkvs. Returns true if we can + // confirm that we set an old-table slot to TOMBPRIME, and only returns after + // updating the new table. We need an accurate confirmed-copy count so + // that we know when we can promote (if we promote the new table too soon, + // other threads may 'miss' on values not-yet-copied from the old table). + // We don't allow any direct updates on the new table, unless they first + // happened to the old table - so that any transition in the new table from + // null to not-null must have been from a copy_slot (or other old-table + // overwrite) and not from a thread directly writing in the new table. + private boolean copy_slot(NonBlockingHashMap topmap, int idx, Object[] oldkvs, Object[] newkvs) { + // Blindly set the key slot from null to TOMBSTONE, to eagerly stop + // fresh put's from inserting new values in the old table when the old + // table is mid-resize. We don't need to act on the results here, + // because our correctness stems from box'ing the Value field. Slamming + // the Key field is a minor speed optimization. + Object key; + while ((key = key(oldkvs, idx)) == null) + CAS_key(oldkvs, idx, null, TOMBSTONE); + + // --- + // Prevent new values from appearing in the old table. + // Box what we see in the old table, to prevent further updates. + Object oldval = val(oldkvs, idx); // Read OLD table + while (!(oldval instanceof Prime)) { + final Prime box = (oldval == null || oldval == TOMBSTONE) ? TOMBPRIME : new Prime(oldval); + if (CAS_val(oldkvs, idx, oldval, box)) { // CAS down a box'd version of oldval + // If we made the Value slot hold a TOMBPRIME, then we both + // prevented further updates here but also the (absent) + // oldval is vacuously available in the new table. We + // return with true here: any thread looking for a value for + // this key can correctly go straight to the new table and + // skip looking in the old table. + if (box == TOMBPRIME) + return true; + // Otherwise we boxed something, but it still needs to be + // copied into the new table. + oldval = box; // Record updated oldval + break; // Break loop; oldval is now boxed by us + } + oldval = val(oldkvs, idx); // Else try, try again + } + if (oldval == TOMBPRIME) return false; // Copy already complete here! + + // --- + // Copy the value into the new table, but only if we overwrite a null. + // If another value is already in the new table, then somebody else + // wrote something there and that write is happens-after any value that + // appears in the old table. + Object old_unboxed = ((Prime) oldval)._V; + assert old_unboxed != TOMBSTONE; + putIfMatch0(topmap, newkvs, key, old_unboxed, null); + + // --- + // Finally, now that any old value is exposed in the new table, we can + // forever hide the old-table value by slapping a TOMBPRIME down. This + // will stop other threads from uselessly attempting to copy this slot + // (i.e., it's a speed optimization not a correctness issue). + while (oldval != TOMBPRIME && !CAS_val(oldkvs, idx, oldval, TOMBPRIME)) + oldval = val(oldkvs, idx); + + return oldval != TOMBPRIME; // True if we slammed the TOMBPRIME down + } // end copy_slot + } // End of CHM + + // --- Snapshot ------------------------------------------------------------ + // The main class for iterating over the NBHM. It "snapshots" a clean + // view of the K/V array. + private class SnapshotV implements Iterator, Enumeration { + + final Object[] _sskvs; + + public SnapshotV() { + while (true) { // Verify no table-copy-in-progress + Object[] topkvs = _kvs; + CHM topchm = chm(topkvs); + if (topchm._newkvs == null) { // No table-copy-in-progress + // The "linearization point" for the iteration. Every key in this + // table will be visited, but keys added later might be skipped or + // even be added to a following table (also not iterated over). + _sskvs = topkvs; + break; + } + // Table copy in-progress - so we cannot get a clean iteration. We + // must help finish the table copy before we can start iterating. + topchm.help_copy_impl(NonBlockingHashMap.this, topkvs, true); + } + // Warm-up the iterator + next(); + } + + int length() { + return len(_sskvs); + } + + Object key(int idx) { + return NonBlockingHashMap.key(_sskvs, idx); + } + + private int _idx; // Varies from 0-keys.length + + private Object _nextK, _prevK; // Last 2 keys found + + private TypeV _nextV, _prevV; // Last 2 values found + + public boolean hasNext() { + return _nextV != null; + } + + public TypeV next() { + // 'next' actually knows what the next value will be - it had to + // figure that out last go-around lest 'hasNext' report true and + // some other thread deleted the last value. Instead, 'next' + // spends all its effort finding the key that comes after the + // 'next' key. + if (_idx != 0 && _nextV == null) throw new NoSuchElementException(); + _prevK = _nextK; // This will become the previous key + _prevV = _nextV; // This will become the previous value + _nextV = null; // We have no more next-key + // Attempt to set <_nextK,_nextV> to the next K,V pair. + // _nextV is the trigger: stop searching when it is != null + while (_idx < length()) { // Scan array + _nextK = key(_idx++); // Get a key that definitely is in the set (for the moment!) + if (_nextK != null + && // Found something? + _nextK != TOMBSTONE + && (_nextV = get(_nextK)) != null) + break; // Got it! _nextK is a valid Key + } // Else keep scanning + return _prevV; // Return current value. + } + + public void removeKey() { + if (_prevV == null) throw new IllegalStateException(); + NonBlockingHashMap.this.putIfMatch(_prevK, TOMBSTONE, NO_MATCH_OLD); + _prevV = null; + } + + @Override + public void remove() { + // NOTE: it would seem logical that value removal will semantically mean removing the matching value for the + // mapping , but the JDK always removes by key, even when the value has changed. + removeKey(); + } + + public TypeV nextElement() { + return next(); + } + + public boolean hasMoreElements() { + return hasNext(); + } + } + + public Object[] raw_array() { + return new SnapshotV()._sskvs; + } + + /** Returns an enumeration of the values in this table. + * + * @return an enumeration of the values in this table + * @see #values() */ + public Enumeration elements() { + return new SnapshotV(); + } + + // --- values -------------------------------------------------------------- + /** Returns a {@link Collection} view of the values contained in this map. + * The collection is backed by the map, so changes to the map are reflected + * in the collection, and vice-versa. The collection supports element + * removal, which removes the corresponding mapping from this map, via the + * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>, + * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations. + * It does not support the <tt>add</tt> or <tt>addAll</tt> operations. + * + *

    + * The view's <tt>iterator</tt> is a "weakly consistent" iterator that + * will never throw {@link ConcurrentModificationException}, and guarantees + * to traverse elements as they existed upon construction of the iterator, + * and may (but is not guaranteed to) reflect any modifications subsequent + * to construction. */ + @Override + public Collection values() { + return new AbstractCollection() { + @Override + public void clear() { + NonBlockingHashMap.this.clear(); + } + + @Override + public int size() { + return NonBlockingHashMap.this.size(); + } + + @Override + public boolean contains(Object v) { + return NonBlockingHashMap.this.containsValue(v); + } + + @Override + public Iterator iterator() { + return new SnapshotV(); + } + }; + } + + // --- keySet -------------------------------------------------------------- + private class SnapshotK implements Iterator, Enumeration { + + final SnapshotV _ss; + + public SnapshotK() { + _ss = new SnapshotV(); + } + + public void remove() { + _ss.removeKey(); + } + + public TypeK next() { + _ss.next(); + return (TypeK) _ss._prevK; + } + + public boolean hasNext() { + return _ss.hasNext(); + } + + public TypeK nextElement() { + return next(); + } + + public boolean hasMoreElements() { + return hasNext(); + } + } + + /** Returns an enumeration of the keys in this table. + * + * @return an enumeration of the keys in this table + * @see #keySet() */ + public Enumeration keys() { + return new SnapshotK(); + } + + /** Returns a {@link Set} view of the keys contained in this map. The set + * is backed by the map, so changes to the map are reflected in the set, + * and vice-versa. The set supports element removal, which removes the + * corresponding mapping from this map, via the <tt>Iterator.remove</tt>, + * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and + * <tt>clear</tt> operations. It does not support the <tt>add</tt> or + * <tt>addAll</tt> operations. + * + *

    + * The view's <tt>iterator</tt> is a "weakly consistent" iterator that + * will never throw {@link ConcurrentModificationException}, and guarantees + * to traverse elements as they existed upon construction of the iterator, + * and may (but is not guaranteed to) reflect any modifications subsequent + * to construction. */ + @Override + public Set keySet() { + return new AbstractSet() { + @Override + public void clear() { + NonBlockingHashMap.this.clear(); + } + + @Override + public int size() { + return NonBlockingHashMap.this.size(); + } + + @Override + public boolean contains(Object k) { + return NonBlockingHashMap.this.containsKey(k); + } + + @Override + public boolean remove(Object k) { + return NonBlockingHashMap.this.remove(k) != null; + } + + @Override + public Iterator iterator() { + return new SnapshotK(); + } + // This is an efficient implementation of toArray instead of the standard + // one. In particular it uses a smart iteration over the NBHM. + + @Override + public T[] toArray(T[] a) { + Object[] kvs = raw_array(); + // Estimate size of array; be prepared to see more or fewer elements + int sz = size(); + T[] r = a.length >= sz ? a + : (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), sz); + // Fast efficient element walk. + int j = 0; + for (int i = 0; i < len(kvs); i++) { + Object K = key(kvs, i); + Object V = Prime.unbox(val(kvs, i)); + if (K != null && K != TOMBSTONE && V != null && V != TOMBSTONE) { + if (j >= r.length) { + int sz2 = (int) Math.min(Integer.MAX_VALUE - 8, ((long) j) << 1); + if (sz2 <= r.length) throw new OutOfMemoryError("Required array size too large"); + r = Arrays.copyOf(r, sz2); + } + r[j++] = (T) K; + } + } + if (j <= a.length) { // Fit in the original array? + if (a != r) System.arraycopy(r, 0, a, 0, j); + if (j < a.length) r[j++] = null; // One final null not in the spec but in the default impl + return a; // Return the original + } + return Arrays.copyOf(r, j); + } + }; + } + + // --- entrySet ------------------------------------------------------------ + // Warning: Each call to 'next' in this iterator constructs a new NBHMEntry. + private class NBHMEntry extends AbstractEntry { + + NBHMEntry(final TypeK k, final TypeV v) { + super(k, v); + } + + public TypeV setValue(final TypeV val) { + if (val == null) throw new NullPointerException(); + _val = val; + return put(_key, val); + } + } + + private class SnapshotE implements Iterator> { + + final SnapshotV _ss; + + public SnapshotE() { + _ss = new SnapshotV(); + } + + public void remove() { + // NOTE: it would seem logical that entry removal will semantically mean removing the matching pair , but + // the JDK always removes by key, even when the value has changed. + _ss.removeKey(); + } + + public Map.Entry next() { + _ss.next(); + return new NBHMEntry((TypeK) _ss._prevK, _ss._prevV); + } + + public boolean hasNext() { + return _ss.hasNext(); + } + } + + /** Returns a {@link Set} view of the mappings contained in this map. The + * set is backed by the map, so changes to the map are reflected in the + * set, and vice-versa. The set supports element removal, which removes + * the corresponding mapping from the map, via the + * <tt>Iterator.remove</tt>, <tt>Set.remove</tt>, <tt>removeAll</tt>, + * <tt>retainAll</tt>, and <tt>clear</tt> operations. It does not support + * the <tt>add</tt> or <tt>addAll</tt> operations. + * + *

    + * The view's <tt>iterator</tt> is a "weakly consistent" iterator + * that will never throw {@link ConcurrentModificationException}, + * and guarantees to traverse elements as they existed upon + * construction of the iterator, and may (but is not guaranteed to) + * reflect any modifications subsequent to construction. + * + *

    + * Warning: the iterator associated with this Set + * requires the creation of {@link java.util.Map.Entry} objects with each + * iteration. The NonBlockingHashMap does not normally create or + * using {@link java.util.Map.Entry} objects so they will be created soley + * to support this iteration. Iterating using Map#keySet or Map##values will be more efficient. + */ + @Override + public Set> entrySet() { + return new AbstractSet>() { + @Override + public void clear() { + NonBlockingHashMap.this.clear(); + } + + @Override + public int size() { + return NonBlockingHashMap.this.size(); + } + + @Override + public boolean remove(final Object o) { + if (!(o instanceof Map.Entry)) return false; + final Map.Entry e = (Map.Entry) o; + return NonBlockingHashMap.this.remove(e.getKey(), e.getValue()); + } + + @Override + public boolean contains(final Object o) { + if (!(o instanceof Map.Entry)) return false; + final Map.Entry e = (Map.Entry) o; + TypeV v = get(e.getKey()); + return v != null && v.equals(e.getValue()); + } + + @Override + public Iterator> iterator() { + return new SnapshotE(); + } + }; + } + + // --- writeObject ------------------------------------------------------- + // Write a NBHM to a stream + private void writeObject(java.io.ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); // Nothing to write + for (Object K : keySet()) { + final Object V = get(K); // Do an official 'get' + s.writeObject(K); // Write the pair + s.writeObject(V); + } + s.writeObject(null); // Sentinel to indicate end-of-data + s.writeObject(null); + } + + // --- readObject -------------------------------------------------------- + // Read a CHM from a stream + private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { + s.defaultReadObject(); // Read nothing + initialize(MIN_SIZE); + for (;;) { + final TypeK K = (TypeK) s.readObject(); + final TypeV V = (TypeV) s.readObject(); + if (K == null) break; + put(K, V); // Insert with an offical put + } + } + + protected abstract class AbstractEntry implements Map.Entry { + + /** Strongly typed key */ + protected final TypeK _key; + + /** Strongly typed value */ + protected TypeV _val; + + public AbstractEntry(final TypeK key, final TypeV val) { + _key = key; + _val = val; + } + + public AbstractEntry(final Map.Entry e) { + _key = e.getKey(); + _val = e.getValue(); + } + + /** Return "key=val" string */ + public String toString() { + return _key + "=" + _val; + } + + /** Return key */ + public TypeK getKey() { + return _key; + } + + /** Return val */ + public TypeV getValue() { + return _val; + } + + /** Equal if the underlying key and value are equal */ + public boolean equals(final Object o) { + if (!(o instanceof Map.Entry)) return false; + final Map.Entry e = (Map.Entry) o; + return eq(_key, e.getKey()) && eq(_val, e.getValue()); + } + + /** Compute "key.hashCode() ^ val.hashCode()" */ + public int hashCode() { + return ((_key == null) ? 0 : _key.hashCode()) + ^ ((_val == null) ? 0 : _val.hashCode()); + } + + private boolean eq(final Object o1, final Object o2) { + return (o1 == null ? o2 == null : o1.equals(o2)); + } + } + + protected static class ConcurrentAutoTable implements Serializable { + + // --- public interface --- + /** + * Add the given value to current counter value. Concurrent updates will + * not be lost, but addAndGet or getAndAdd are not implemented because the + * total counter value (i.e., {@link #get}) is not atomically updated. + * Updates are striped across an array of counters to avoid cache contention + * and has been tested with performance scaling linearly up to 768 CPUs. + */ + public void add(long x) { + add_if(x); + } + + /** {@link #add} with -1 */ + public void decrement() { + add_if(-1L); + } + + /** {@link #add} with +1 */ + public void increment() { + add_if(1L); + } + + /** Atomically set the sum of the striped counters to specified value. + * Rather more expensive than a simple store, in order to remain atomic. + */ + public void set(long x) { + CAT newcat = new CAT(null, 4, x); + // Spin until CAS works + while (!CAS_cat(_cat, newcat)) {/* empty */ + } + } + + /** + * Current value of the counter. Since other threads are updating furiously + * the value is only approximate, but it includes all counts made by the + * current thread. Requires a pass over the internally striped counters. + */ + public long get() { + return _cat.sum(); + } + + /** Same as {@link #get}, included for completeness. */ + public int intValue() { + return (int) _cat.sum(); + } + + /** Same as {@link #get}, included for completeness. */ + public long longValue() { + return _cat.sum(); + } + + /** + * A cheaper {@link #get}. Updated only once/millisecond, but as fast as a + * simple load instruction when not updating. + */ + public long estimate_get() { + return _cat.estimate_sum(); + } + + /** + * Return the counter's {@code long} value converted to a string. + */ + public String toString() { + return _cat.toString(); + } + + /** + * A more verbose print than {@link #toString}, showing internal structure. + * Useful for debugging. + */ + public void print() { + _cat.print(); + } + + /** + * Return the internal counter striping factor. Useful for diagnosing + * performance problems. + */ + public int internal_size() { + return _cat._t.length; + } + + // Only add 'x' to some slot in table, hinted at by 'hash'. The sum can + // overflow. Value is CAS'd so no counts are lost. The CAS is retried until + // it succeeds. Returned value is the old value. + private long add_if(long x) { + return _cat.add_if(x, hash(), this); + } + + // The underlying array of concurrently updated long counters + private volatile CAT _cat = new CAT(null, 16/* Start Small, Think Big! */, 0L); + + private static AtomicReferenceFieldUpdater _catUpdater + = AtomicReferenceFieldUpdater.newUpdater(ConcurrentAutoTable.class, CAT.class, "_cat"); + + private boolean CAS_cat(CAT oldcat, CAT newcat) { + return _catUpdater.compareAndSet(this, oldcat, newcat); + } + + // Hash spreader + private static int hash() { + //int h = (int)Thread.currentThread().getId(); + int h = System.identityHashCode(Thread.currentThread()); + return h << 3; // Pad out cache lines. The goal is to avoid cache-line contention + } + + // --- CAT ----------------------------------------------------------------- + private static class CAT implements Serializable { + + // Unsafe crud: get a function which will CAS arrays + private static final int _Lbase = UNSAFE == null ? 0 : UNSAFE.arrayBaseOffset(long[].class); + + private static final int _Lscale = UNSAFE == null ? 0 : UNSAFE.arrayIndexScale(long[].class); + + private static long rawIndex(long[] ary, int i) { + assert i >= 0 && i < ary.length; + return _Lbase + (i * (long) _Lscale); + } + + private static boolean CAS(long[] A, int idx, long old, long nnn) { + return UNSAFE.compareAndSwapLong(A, rawIndex(A, idx), old, nnn); + } + + //volatile long _resizers; // count of threads attempting a resize + //static private final AtomicLongFieldUpdater _resizerUpdater = + // AtomicLongFieldUpdater.newUpdater(CAT.class, "_resizers"); + private final CAT _next; + + private volatile long _fuzzy_sum_cache; + + private volatile long _fuzzy_time; + + private static final int MAX_SPIN = 1; + + private final long[] _t; // Power-of-2 array of longs + + CAT(CAT next, int sz, long init) { + _next = next; + _t = new long[sz]; + _t[0] = init; + } + + // Only add 'x' to some slot in table, hinted at by 'hash'. The sum can + // overflow. Value is CAS'd so no counts are lost. The CAS is attempted + // ONCE. + public long add_if(long x, int hash, ConcurrentAutoTable master) { + final long[] t = _t; + final int idx = hash & (t.length - 1); + // Peel loop; try once fast + long old = t[idx]; + final boolean ok = CAS(t, idx, old, old + x); + if (ok) return old; // Got it + // Try harder + int cnt = 0; + while (true) { + old = t[idx]; + if (CAS(t, idx, old, old + x)) break; // Got it! + cnt++; + } + if (cnt < MAX_SPIN) return old; // Allowable spin loop count + if (t.length >= 1024 * 1024) return old; // too big already + + // Too much contention; double array size in an effort to reduce contention + //long r = _resizers; + //final int newbytes = (t.length<<1)<<3/*word to bytes*/; + //while( !_resizerUpdater.compareAndSet(this,r,r+newbytes) ) + // r = _resizers; + //r += newbytes; + if (master._cat != this) return old; // Already doubled, don't bother + //if( (r>>17) != 0 ) { // Already too much allocation attempts? + // // We could use a wait with timeout, so we'll wakeup as soon as the new + // // table is ready, or after the timeout in any case. Annoyingly, this + // // breaks the non-blocking property - so for now we just briefly sleep. + // //synchronized( this ) { wait(8*megs); } // Timeout - we always wakeup + // try { Thread.sleep(r>>17); } catch( InterruptedException e ) { } + // if( master._cat != this ) return old; + //} + + CAT newcat = new CAT(this, t.length * 2, 0); + // Take 1 stab at updating the CAT with the new larger size. If this + // fails, we assume some other thread already expanded the CAT - so we + // do not need to retry until it succeeds. + while (master._cat == this && !master.CAS_cat(this, newcat)) {/* empty */ + } + return old; + } + + // Return the current sum of all things in the table. Writers can be + // updating the table furiously, so the sum is only locally accurate. + public long sum() { + long sum = _next == null ? 0 : _next.sum(); // Recursively get cached sum + final long[] t = _t; + for (long cnt : t) sum += cnt; + return sum; + } + + // Fast fuzzy version. Used a cached value until it gets old, then re-up + // the cache. + public long estimate_sum() { + // For short tables, just do the work + if (_t.length <= 64) return sum(); + // For bigger tables, periodically freshen a cached value + long millis = System.currentTimeMillis(); + if (_fuzzy_time != millis) { // Time marches on? + _fuzzy_sum_cache = sum(); // Get sum the hard way + _fuzzy_time = millis; // Indicate freshness of cached value + } + return _fuzzy_sum_cache; // Return cached sum + } + + public String toString() { + return Long.toString(sum()); + } + + public void print() { + long[] t = _t; + System.out.print("[" + t[0]); + for (int i = 1; i < t.length; i++) + System.out.print("," + t[i]); + System.out.print("]"); + if (_next != null) _next.print(); + } + } + } +} diff --git a/src/org/redkale/util/ObjectPool.java b/src/main/java/org/redkale/util/ObjectPool.java similarity index 76% rename from src/org/redkale/util/ObjectPool.java rename to src/main/java/org/redkale/util/ObjectPool.java index e1a6abdfe..5286db3d3 100644 --- a/src/org/redkale/util/ObjectPool.java +++ b/src/main/java/org/redkale/util/ObjectPool.java @@ -1,234 +1,250 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.util.*; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.logging.*; - -/** - * 对象池 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 对象池元素的数据类型 - */ -public class ObjectPool implements Supplier, Consumer { - - protected static final Logger logger = Logger.getLogger(ObjectPool.class.getSimpleName()); - - protected final boolean debug; - - protected Creator creator; - - protected int max; - - protected final Consumer prepare; - - protected final Predicate recycler; - - protected final AtomicLong creatCounter; - - protected final AtomicLong cycleCounter; - - protected final Queue queue; - - protected final boolean unsafeDequeable; - - protected final ObjectPool parent; - - protected ObjectPool(ObjectPool parent, AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler, Queue queue) { - this.parent = parent; - this.creatCounter = creatCounter; - this.cycleCounter = cycleCounter; - this.creator = creator; - this.prepare = prepare; - this.recycler = recycler; - this.max = max; - this.debug = logger.isLoggable(Level.FINEST); - this.queue = queue; - this.unsafeDequeable = queue instanceof ArrayDeque; - } - - //非线程安全版 - public static ObjectPool createUnsafePool(Class clazz, Consumer prepare, Predicate recycler) { - return createUnsafePool(2, clazz, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(int max, Class clazz, Consumer prepare, Predicate recycler) { - return createUnsafePool(max, Creator.create(clazz), prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(Creator creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(2, creator, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(int max, Creator creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(null, null, max, creator, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(int max, Supplier creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(null, null, max, creator, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Supplier creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(creatCounter, cycleCounter, max, c -> creator.get(), prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(null, creatCounter, cycleCounter, max, creator, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(ObjectPool parent, Class clazz, Consumer prepare, Predicate recycler) { - return createUnsafePool(parent, 2, clazz, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(ObjectPool parent, int max, Class clazz, Consumer prepare, Predicate recycler) { - return createUnsafePool(parent, max, Creator.create(clazz), prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(ObjectPool parent, Creator creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(parent, 2, creator, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(ObjectPool parent, int max, Creator creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(parent, null, null, max, creator, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(ObjectPool parent, int max, Supplier creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(parent, null, null, max, creator, prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(ObjectPool parent, AtomicLong creatCounter, AtomicLong cycleCounter, int max, Supplier creator, Consumer prepare, Predicate recycler) { - return createUnsafePool(parent, creatCounter, cycleCounter, max, c -> creator.get(), prepare, recycler); - } - - //非线程安全版 - public static ObjectPool createUnsafePool(ObjectPool parent, AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler) { - return new ObjectPool(parent, creatCounter, cycleCounter, Math.max(Runtime.getRuntime().availableProcessors(), max), - creator, prepare, recycler, new ArrayDeque<>(Math.max(Runtime.getRuntime().availableProcessors(), max))); - } - - //线程安全版 - public static ObjectPool createSafePool(Class clazz, Consumer prepare, Predicate recycler) { - return createSafePool(2, clazz, prepare, recycler); - } - - //线程安全版 - public static ObjectPool createSafePool(int max, Class clazz, Consumer prepare, Predicate recycler) { - return createSafePool(max, Creator.create(clazz), prepare, recycler); - } - - //线程安全版 - public static ObjectPool createSafePool(Creator creator, Consumer prepare, Predicate recycler) { - return createSafePool(2, creator, prepare, recycler); - } - - //线程安全版 - public static ObjectPool createSafePool(int max, Creator creator, Consumer prepare, Predicate recycler) { - return createSafePool(null, null, max, creator, prepare, recycler); - } - - //线程安全版 - public static ObjectPool createSafePool(int max, Supplier creator, Consumer prepare, Predicate recycler) { - return createSafePool(null, null, max, creator, prepare, recycler); - } - - //线程安全版 - public static ObjectPool createSafePool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Supplier creator, Consumer prepare, Predicate recycler) { - return createSafePool(creatCounter, cycleCounter, max, c -> creator.get(), prepare, recycler); - } - - //线程安全版 - public static ObjectPool createSafePool(AtomicLong creatCounter, AtomicLong cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler) { - return new ObjectPool(null, creatCounter, cycleCounter, Math.max(Runtime.getRuntime().availableProcessors(), max), - creator, prepare, recycler, new LinkedBlockingQueue<>(Math.max(Runtime.getRuntime().availableProcessors(), max))); - } - - public void setCreator(Creator creator) { - this.creator = creator; - } - - public Creator getCreator() { - return this.creator; - } - - public int getMax() { - return max; - } - - public Consumer getPrepare() { - return prepare; - } - - public Predicate getRecycler() { - return recycler; - } - - public AtomicLong getCreatCounter() { - return creatCounter; - } - - public AtomicLong getCycleCounter() { - return cycleCounter; - } - - @Override - public T get() { - T result = queue.poll(); - if (result == null) { - if (parent != null) result = parent.queue.poll(); - if (result == null) { - if (creatCounter != null) creatCounter.incrementAndGet(); - result = this.creator.create(); - } - } - if (prepare != null) prepare.accept(result); - return result; - } - - @Override - public void accept(final T e) { - if (e == null) return; - if (recycler.test(e)) { - if (cycleCounter != null) cycleCounter.incrementAndGet(); -// if (debug) { -// for (T t : queue) { -// if (t == e) { -// logger.log(Level.WARNING, "[" + Thread.currentThread().getName() + "] repeat offer the same object(" + e + ")", new Exception()); -// return; -// } -// } -// } - boolean rs = unsafeDequeable ? queue.size() < max && queue.offer(e) : queue.offer(e); - if (!rs && parent != null) parent.accept(e); - } - } - - public long getCreatCount() { - return creatCounter.longValue(); - } - - public long getCycleCount() { - return cycleCounter.longValue(); - } - -} +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.*; +import java.util.function.*; +import java.util.logging.*; + +/** + * 对象池 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 对象池元素的数据类型 + */ +public class ObjectPool implements Supplier, Consumer { + + protected static final Logger logger = Logger.getLogger(ObjectPool.class.getSimpleName()); + + protected final boolean debug; + + protected Creator creator; + + protected int max; + + protected final Consumer prepare; + + protected final Predicate recycler; + + protected final LongAdder creatCounter; + + protected final LongAdder cycleCounter; + + protected final Queue queue; + + protected final boolean unsafeDequeable; + + protected final ObjectPool parent; + + protected Thread unsafeThread; + + protected ObjectPool(ObjectPool parent, LongAdder creatCounter, LongAdder cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler, Queue queue) { + this.parent = parent; + this.creatCounter = creatCounter; + this.cycleCounter = cycleCounter; + this.creator = creator; + this.prepare = prepare; + this.recycler = recycler; + this.max = max; + this.debug = logger.isLoggable(Level.FINEST); + this.queue = queue; + this.unsafeDequeable = queue instanceof ArrayDeque; + } + + //非线程安全版 + public static ObjectPool createUnsafePool(Class clazz, Consumer prepare, Predicate recycler) { + return createUnsafePool(2, clazz, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(int max, Class clazz, Consumer prepare, Predicate recycler) { + return createUnsafePool(max, Creator.create(clazz), prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(Creator creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(2, creator, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(int max, Creator creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(null, null, max, creator, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(int max, Supplier creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(null, null, max, creator, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(LongAdder creatCounter, LongAdder cycleCounter, int max, Supplier creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(creatCounter, cycleCounter, max, c -> creator.get(), prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(LongAdder creatCounter, LongAdder cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(null, creatCounter, cycleCounter, max, creator, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(ObjectPool parent, Class clazz, Consumer prepare, Predicate recycler) { + return createUnsafePool(parent, 2, clazz, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(ObjectPool parent, int max, Class clazz, Consumer prepare, Predicate recycler) { + return createUnsafePool(parent, max, Creator.create(clazz), prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(ObjectPool parent, Creator creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(parent, 2, creator, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(ObjectPool parent, int max, Creator creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(parent, null, null, max, creator, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(ObjectPool parent, int max, Supplier creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(parent, null, null, max, creator, prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(ObjectPool parent, LongAdder creatCounter, LongAdder cycleCounter, int max, Supplier creator, Consumer prepare, Predicate recycler) { + return createUnsafePool(parent, creatCounter, cycleCounter, max, c -> creator.get(), prepare, recycler); + } + + //非线程安全版 + public static ObjectPool createUnsafePool(ObjectPool parent, LongAdder creatCounter, LongAdder cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler) { + return new ObjectPool(parent, creatCounter, cycleCounter, Math.max(Utility.cpus(), max), + creator, prepare, recycler, new ArrayDeque<>(Math.max(Utility.cpus(), max))); + } + + //线程安全版 + public static ObjectPool createSafePool(Class clazz, Consumer prepare, Predicate recycler) { + return createSafePool(2, clazz, prepare, recycler); + } + + //线程安全版 + public static ObjectPool createSafePool(int max, Class clazz, Consumer prepare, Predicate recycler) { + return createSafePool(max, Creator.create(clazz), prepare, recycler); + } + + //线程安全版 + public static ObjectPool createSafePool(Creator creator, Consumer prepare, Predicate recycler) { + return createSafePool(2, creator, prepare, recycler); + } + + //线程安全版 + public static ObjectPool createSafePool(int max, Creator creator, Consumer prepare, Predicate recycler) { + return createSafePool(null, null, max, creator, prepare, recycler); + } + + //线程安全版 + public static ObjectPool createSafePool(int max, Supplier creator, Consumer prepare, Predicate recycler) { + return createSafePool(null, null, max, creator, prepare, recycler); + } + + //线程安全版 + public static ObjectPool createSafePool(LongAdder creatCounter, LongAdder cycleCounter, int max, Supplier creator, Consumer prepare, Predicate recycler) { + return createSafePool(creatCounter, cycleCounter, max, c -> creator.get(), prepare, recycler); + } + + //线程安全版 + public static ObjectPool createSafePool(LongAdder creatCounter, LongAdder cycleCounter, int max, Creator creator, Consumer prepare, Predicate recycler) { + return new ObjectPool(null, creatCounter, cycleCounter, Math.max(Utility.cpus(), max), + creator, prepare, recycler, new LinkedBlockingQueue<>(Math.max(Utility.cpus(), max))); + } + + public void setCreator(Creator creator) { + this.creator = creator; + } + + public Creator getCreator() { + return this.creator; + } + + public int getMax() { + return max; + } + + public Consumer getPrepare() { + return prepare; + } + + public Predicate getRecycler() { + return recycler; + } + + public LongAdder getCreatCounter() { + return creatCounter; + } + + public LongAdder getCycleCounter() { + return cycleCounter; + } + + @Override + public T get() { + if (unsafeDequeable) { + if (unsafeThread == null) { + unsafeThread = Thread.currentThread(); + } else if (unsafeThread != Thread.currentThread()) { + throw new IllegalCallerException("unsafeThread is " + unsafeThread + ", but currentThread is " + Thread.currentThread()); + } + } + T result = queue.poll(); + if (result == null) { + if (parent != null) result = parent.queue.poll(); + if (result == null) { + if (creatCounter != null) creatCounter.increment(); + result = this.creator.create(); + } + } + if (prepare != null) prepare.accept(result); + return result; + } + + @Override + public void accept(final T e) { + if (e == null) return; + if (unsafeDequeable) { + if (unsafeThread == null) { + unsafeThread = Thread.currentThread(); + } else if (unsafeThread != Thread.currentThread()) { + throw new IllegalCallerException("unsafeThread is " + unsafeThread + ", but currentThread is " + Thread.currentThread()); + } + } + if (recycler.test(e)) { + if (cycleCounter != null) cycleCounter.increment(); +// if (debug) { +// for (T t : queue) { +// if (t == e) { +// logger.log(Level.WARNING, "[" + Thread.currentThread().getName() + "] repeat offer the same object(" + e + ")", new Exception()); +// return; +// } +// } +// } + boolean rs = unsafeDequeable ? queue.size() < max && queue.offer(e) : queue.offer(e); + if (!rs && parent != null) parent.accept(e); + } + } + + public long getCreatCount() { + return creatCounter == null ? -1 : creatCounter.longValue(); + } + + public long getCycleCount() { + return cycleCounter == null ? -1 : cycleCounter.longValue(); + } + +} diff --git a/src/org/redkale/util/Redkale.java b/src/main/java/org/redkale/util/Redkale.java similarity index 67% rename from src/org/redkale/util/Redkale.java rename to src/main/java/org/redkale/util/Redkale.java index a07cb2783..bb7e1f48b 100644 --- a/src/org/redkale/util/Redkale.java +++ b/src/main/java/org/redkale/util/Redkale.java @@ -1,31 +1,37 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -/** - * 版本 - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class Redkale { - - private Redkale() { - } - - public static String getDotedVersion() { - return "2.4.0"; - } - - public static int getMajorVersion() { - return 2; - } - - public static int getMinorVersion() { - return 4; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +/** + * 版本 + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class Redkale { + + private static final String rootPackage = "org.redkale"; + + private Redkale() { + } + + public static String getRootPackage() { + return rootPackage; + } + + public static String getDotedVersion() { + return "2.5.0"; + } + + public static int getMajorVersion() { + return 2; + } + + public static int getMinorVersion() { + return 5; + } +} diff --git a/src/main/java/org/redkale/util/RedkaleClassLoader.java b/src/main/java/org/redkale/util/RedkaleClassLoader.java new file mode 100644 index 000000000..17900e9e9 --- /dev/null +++ b/src/main/java/org/redkale/util/RedkaleClassLoader.java @@ -0,0 +1,484 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.nio.file.Paths; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +/** + * Redkale内部ClassLoader + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class RedkaleClassLoader extends URLClassLoader { + + public static final String RESOURCE_CACHE_CLASSES_PATH = "/META-INF/redkale/redkale.load.classes"; + + public static final String RESOURCE_CACHE_CONF_PATH = "/META-INF/redkale/conf"; + + public static final URL URL_NONE; + + static { + URL url = null; + try { + url = URI.create("file://redkale/uri").toURL(); //不能是jar结尾,否则会视为jar文件url + } catch (MalformedURLException e) { + } + URL_NONE = url; + } + + private static final String[] buildClasses = {}; + + private static final String[] buildPackages = { + "org.redkaledyn", //所有动态生成类的根package + "org.redkale.boot", "org.redkale.boot.watch", + "org.redkale.cluster", "org.redkale.convert", + "org.redkale.convert.bson", "org.redkale.convert.ext", + "org.redkale.convert.json", "org.redkale.mq", + "org.redkale.net", "org.redkale.net.client", + "org.redkale.net.http", "org.redkale.net.sncp", + "org.redkale.service", "org.redkale.source", + "org.redkale.util", "org.redkale.watch" + }; + + //redkale里所有使用动态字节码生成的类都需要存于此处 + private static final ConcurrentHashMap dynClassBytesMap = new ConcurrentHashMap<>(); + + private static final ConcurrentHashMap dynClassTypeMap = new ConcurrentHashMap<>(); + + private static final CopyOnWriteArraySet resourcePathSet = new CopyOnWriteArraySet<>(); + + private static final ConcurrentHashMap serviceLoaderMap = new ConcurrentHashMap<>(); + + private static final ConcurrentHashMap> bundleResourcesMap = new ConcurrentHashMap<>(); + + private static final ConcurrentHashMap> reflectionMap = new ConcurrentHashMap<>(); + + public RedkaleClassLoader(ClassLoader parent) { + super(new URL[0], parent); + } + + public RedkaleClassLoader(URL[] urls, ClassLoader parent) { + super(urls, parent); + } + + public static URI getConfResourceAsURI(String confURI, String file) { + if (confURI != null && !confURI.contains("!")) { //带!的是 /usr/xxx.jar!/META-INF/conf/xxx + File f = new File(URI.create(confURI).getPath(), file); + if (f.isFile() && f.canRead()) { + return f.toURI(); + } + } + URL url = RedkaleClassLoader.class.getResource(RESOURCE_CACHE_CONF_PATH + (file.startsWith("/") ? file : ("/" + file))); + return url == null ? null : URI.create(url.toString()); + } + + public static InputStream getConfResourceAsStream(String confURI, String file) { + if (confURI != null && !confURI.contains("!")) { //带!的是 /usr/xxx.jar!/META-INF/conf/xxx + File f = new File(URI.create(confURI).getPath(), file); + if (f.isFile() && f.canRead()) { + try { + return new FileInputStream(f); + } catch (FileNotFoundException e) { //几乎不会发生 + throw new RuntimeException(e); + } + } + } + return RedkaleClassLoader.class.getResourceAsStream(RESOURCE_CACHE_CONF_PATH + (file.startsWith("/") ? file : ("/" + file))); + } + + public static void forEachBundleResource(BiConsumer> action) { + bundleResourcesMap.forEach(action); + } + + public static void putBundleResource(String name, String locale) { + bundleResourcesMap.computeIfAbsent(name, k -> new CopyOnWriteArraySet<>()).add(locale); + } + + public static void putResourcePath(String name) { + resourcePathSet.add(name); + } + + public static void forEachResourcePath(Consumer action) { + for (String name : resourcePathSet) { + action.accept(name); + } + } + + public static void forEachBuildClass(Consumer action) { + for (String name : buildClasses) { + action.accept(name); + } + } + + public static void forEachBuildPackage(Consumer action) { + for (String name : buildPackages) { + action.accept(name); + } + } + + public static byte[] putDynClass(String name, byte[] bs, Class clazz) { + Objects.requireNonNull(name); + Objects.requireNonNull(bs); + Objects.requireNonNull(clazz); + dynClassTypeMap.put(name, clazz); + return dynClassBytesMap.put(name, bs); + } + + public static Class findDynClass(String name) { + return dynClassTypeMap.get(name); + } + + public static void forEachDynClass(BiConsumer action) { + dynClassBytesMap.forEach(action); + } + + public static void putReflectionClass(String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap<>(); + map.put("name", name); + reflectionMap.put(name, map); + } + } + } + + public static void putServiceLoader(Class clazz) { + serviceLoaderMap.put(clazz.getName(), clazz); + putReflectionClass(clazz.getName()); + } + + public static void forEachServiceLoader(BiConsumer action) { + serviceLoaderMap.forEach(action); + } + + public static void putReflectionField(String name, Field field) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + List> list = (List) map.get("fields"); + if (list == null) { + list = new ArrayList<>(); + map.put("fields", list); + list.add((Map) Utility.ofMap("name", field.getName())); + } else { + boolean contains = false; + for (Map item : list) { + if (field.getName().equals(item.get("name"))) { + contains = true; + break; + } + } + if (!contains) list.add((Map) Utility.ofMap("name", field.getName())); + } + } + } + + public static void putReflectionMethod(String name, Method method) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + List> list = (List) map.get("methods"); + if (list == null) { + list = new ArrayList<>(); + map.put("methods", list); + list.add(createMap(method.getName(), method.getParameterTypes())); + } else { + Class[] cts = method.getParameterTypes(); + String[] types = new String[cts.length]; + for (int i = 0; i < types.length; i++) { + types[i] = cts[i].getName(); + } + boolean contains = false; + for (Map item : list) { + if (method.getName().equals(item.get("name")) + && Arrays.equals(types, (String[]) item.get("parameterTypes"))) { + contains = true; + break; + } + } + if (!contains) list.add(createMap(method.getName(), method.getParameterTypes())); + } + } + } + + public static void putReflectionDeclaredConstructors(Class clazz, String name, Class... cts) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allDeclaredConstructors", true); + + if (clazz != null) { + if (clazz.isInterface()) return; + if (Modifier.isAbstract(clazz.getModifiers())) return; + try { + clazz.getDeclaredConstructor(cts); + } catch (Throwable t) { + return; + } + } + String[] types = new String[cts.length]; + for (int i = 0; i < types.length; i++) { + types[i] = cts[i].getName(); + } + List> list = (List) map.get("methods"); + if (list == null) { + list = new ArrayList<>(); + map.put("methods", list); + list.add((Map) Utility.ofMap("name", "", "parameterTypes", types)); + } else { + boolean contains = false; + for (Map item : list) { + if ("".equals(item.get("name")) && Arrays.equals(types, (String[]) item.get("parameterTypes"))) { + contains = true; + break; + } + } + if (!contains) list.add((Map) Utility.ofMap("name", "", "parameterTypes", types)); + } + } + } + + public static void putReflectionPublicConstructors(Class clazz, String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allPublicConstructors", true); + + if (clazz != null) { + if (clazz.isInterface()) return; + if (Modifier.isAbstract(clazz.getModifiers())) return; + try { + clazz.getConstructor(); + } catch (Throwable t) { + return; + } + } + List> list = (List) map.get("methods"); + if (list == null) { + list = new ArrayList<>(); + map.put("methods", list); + list.add((Map) Utility.ofMap("name", "", "parameterTypes", new String[0])); + } else { + boolean contains = false; + for (Map item : list) { + if ("".equals(item.get("name")) && ((String[]) item.get("parameterTypes")).length == 0) { + contains = true; + break; + } + } + if (!contains) list.add((Map) Utility.ofMap("name", "", "parameterTypes", new String[0])); + } + } + } + + public static void putReflectionDeclaredMethods(String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allDeclaredMethods", true); + } + } + + public static void putReflectionPublicMethods(String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allPublicMethods", true); + } + } + + public static void putReflectionDeclaredFields(String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allDeclaredFields", true); + } + } + + public static void putReflectionPublicFields(String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allPublicFields", true); + } + } + + public static void putReflectionDeclaredClasses(String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allDeclaredClasses", true); + } + } + + public static void putReflectionPublicClasses(String name) { + synchronized (reflectionMap) { + Map map = reflectionMap.get(name); + if (map == null) { + map = new LinkedHashMap(); + map.put("name", name); + reflectionMap.put(name, map); + } + map.put("allPublicClasses", true); + } + } + + //https://www.graalvm.org/reference-manual/native-image/Reflection/#manual-configuration + private static Map createMap(String name, Class... cts) { + Map map = new LinkedHashMap<>(); + map.put("name", name); + String[] types = new String[cts.length]; + for (int i = 0; i < types.length; i++) { + types[i] = cts[i].getName(); + } + map.put("parameterTypes", types); + return map; + } + + public static void forEachReflection(BiConsumer> action) { + reflectionMap.forEach(action); + } + + public Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + + public void forEachCacheClass(Consumer action) { //getAllURLs返回URL_NONE时需要重载此方法 + if (this.getParent() instanceof RedkaleClassLoader) { + ((RedkaleClassLoader) getParent()).forEachCacheClass(action); + } + } + + @Override + public void addURL(URL url) { + super.addURL(url); + } + + @Override + public URL[] getURLs() { + return super.getURLs(); + } + + public URL[] getAllURLs() { + ClassLoader loader = this; + HashSet set = new HashSet<>(); + String appPath = System.getProperty("java.class.path"); + if (appPath != null && !appPath.isEmpty()) { + for (String path : appPath.replace("://", "&&").replace(":\\", "##").replace(':', ';').split(";")) { + try { + set.add(Paths.get(path.replace("&&", "://").replace("##", ":\\")).toRealPath().toFile().toURI().toURL()); + } catch (Exception e) { + } + } + } + do { + String loaderName = loader.getClass().getName(); + if (loaderName.startsWith("sun.") && loaderName.contains("ExtClassLoader")) continue; + if (loader instanceof URLClassLoader) { + for (URL url : ((URLClassLoader) loader).getURLs()) { + set.add(url); + } + } else { //可能JDK9及以上 + loader.getResource("org.redkale"); //必须要运行一次,确保URLClassPath的值被填充完毕 + Class loaderClazz = loader.getClass(); + Object ucp = null; + do { //读取 java.base/jdk.internal.loader.BuiltinClassLoader的URLClassPath ucp值 + try { + //需要在命令行里加入: --add-opens java.base/jdk.internal.loader=ALL-UNNAMED + Field field = loaderClazz.getDeclaredField("ucp"); + field.setAccessible(true); + ucp = field.get(loader); + break; + } catch (Throwable e) { + } + } while ((loaderClazz = loaderClazz.getSuperclass()) != Object.class); + if (ucp != null) { //URLClassPath + URL[] urls = null; + try { //读取 java.base/jdk.internal.loader.URLClassPath的urls值 + Method method = ucp.getClass().getMethod("getURLs"); + urls = (URL[]) method.invoke(ucp); + } catch (Exception e) { + } + if (urls != null) { + for (URL url : urls) { + set.add(url); + } + } + } + } + } while ((loader = loader.getParent()) != null); + return set.toArray(new URL[set.size()]); + } + + public static class RedkaleCacheClassLoader extends RedkaleClassLoader { + + protected final Set classes; + + public RedkaleCacheClassLoader(ClassLoader parent, Set classes) { + super(parent); + this.classes = classes; + } + + @Override + public URL[] getAllURLs() { + return new URL[]{URL_NONE}; + } + + @Override + public void forEachCacheClass(Consumer action) { + classes.forEach(action); + if (getParent() instanceof RedkaleClassLoader) { + ((RedkaleClassLoader) getParent()).forEachCacheClass(action); + } + } + } +} diff --git a/src/org/redkale/util/Reproduce.java b/src/main/java/org/redkale/util/Reproduce.java similarity index 88% rename from src/org/redkale/util/Reproduce.java rename to src/main/java/org/redkale/util/Reproduce.java index 9f422d83a..bd00ede2f 100644 --- a/src/org/redkale/util/Reproduce.java +++ b/src/main/java/org/redkale/util/Reproduce.java @@ -1,191 +1,192 @@ -package org.redkale.util; - -import java.lang.reflect.Modifier; -import java.util.Map; -import java.util.function.*; -import static org.redkale.asm.Opcodes.*; -import org.redkale.asm.*; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; - -/** - * JavaBean类对象的拷贝,相同的字段名会被拷贝
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 目标对象的数据类型 - * @param 源对象的数据类型 - */ -public interface Reproduce extends BiFunction { - - @Override - public D apply(D dest, S src); - - public static Reproduce create(final Class destClass, final Class srcClass) { - return create(destClass, srcClass, (BiPredicate) null, (Map) null); - } - - public static Reproduce create(final Class destClass, final Class srcClass, final Map names) { - return create(destClass, srcClass, (BiPredicate) null, names); - } - - @SuppressWarnings("unchecked") - public static Reproduce create(final Class destClass, final Class srcClass, final Predicate srcColumnPredicate) { - return create(destClass, srcClass, (sc, m) -> srcColumnPredicate.test(m), (Map) null); - } - - @SuppressWarnings("unchecked") - public static Reproduce create(final Class destClass, final Class srcClass, final Predicate srcColumnPredicate, final Map names) { - return create(destClass, srcClass, (sc, m) -> srcColumnPredicate.test(m), names); - } - - @SuppressWarnings("unchecked") - public static Reproduce create(final Class destClass, final Class srcClass, final BiPredicate srcColumnPredicate) { - return create(destClass, srcClass, srcColumnPredicate, (Map) null); - } - - @SuppressWarnings("unchecked") - public static Reproduce create(final Class destClass, final Class srcClass, final BiPredicate srcColumnPredicate, final Map names) { - // ------------------------------------------------------------------------------ - final String supDynName = Reproduce.class.getName().replace('.', '/'); - final String destClassName = destClass.getName().replace('.', '/'); - final String srcClassName = srcClass.getName().replace('.', '/'); - final String destDesc = Type.getDescriptor(destClass); - final String srcDesc = Type.getDescriptor(srcClass); - String newDynName = supDynName + "Dyn_" + destClass.getSimpleName() + "_" + srcClass.getSimpleName(); - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (String.class.getClassLoader() != destClass.getClassLoader()) { - loader = destClass.getClassLoader(); - newDynName = destClassName + "_Dyn" + Reproduce.class.getSimpleName() + "_" + srcClass.getSimpleName(); - } - try { - return (Reproduce) loader.loadClass(newDynName.replace('/', '.')).getDeclaredConstructor().newInstance(); - } catch (Throwable ex) { - } - // ------------------------------------------------------------------------------ - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodVisitor mv; - AnnotationVisitor av0; - - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + destDesc + srcDesc + ">;", "java/lang/Object", new String[]{supDynName}); - - { // 构造函数 - mv = (cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - { - mv = (cw.visitMethod(ACC_PUBLIC, "apply", "(" + destDesc + srcDesc + ")" + destDesc, null, null)); - //mv.setDebug(true); - - for (java.lang.reflect.Field field : srcClass.getFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - if (Modifier.isFinal(field.getModifiers())) continue; - if (!Modifier.isPublic(field.getModifiers())) continue; - final String sfname = field.getName(); - if (srcColumnPredicate != null && !srcColumnPredicate.test(field, sfname)) continue; - - final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); - java.lang.reflect.Method setter = null; - try { - if (!field.getType().equals(destClass.getField(dfname).getType())) continue; - } catch (Exception e) { - try { - char[] cs = dfname.toCharArray(); - cs[0] = Character.toUpperCase(cs[0]); - String dfname2 = new String(cs); - setter = destClass.getMethod("set" + dfname2, field.getType()); - } catch (Exception e2) { - continue; - } - } - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - String td = Type.getDescriptor(field.getType()); - mv.visitFieldInsn(GETFIELD, srcClassName, sfname, td); - if (setter == null) { - mv.visitFieldInsn(PUTFIELD, destClassName, dfname, td); - } else { - mv.visitMethodInsn(INVOKEVIRTUAL, destClassName, setter.getName(), Type.getMethodDescriptor(setter), false); - } - } - - for (java.lang.reflect.Method getter : srcClass.getMethods()) { - if (Modifier.isStatic(getter.getModifiers())) continue; - if (getter.getParameterTypes().length > 0) continue; - if ("getClass".equals(getter.getName())) continue; - if (!getter.getName().startsWith("get") && !getter.getName().startsWith("is")) continue; - final boolean is = getter.getName().startsWith("is"); - String sfname = getter.getName().substring(is ? 2 : 3); - if (sfname.length() < 2 || Character.isLowerCase(sfname.charAt(1))) { - char[] cs = sfname.toCharArray(); - cs[0] = Character.toLowerCase(cs[0]); - sfname = new String(cs); - } - if (srcColumnPredicate != null && !srcColumnPredicate.test(getter, sfname)) continue; - - final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); - java.lang.reflect.Method setter = null; - java.lang.reflect.Field srcField = null; - char[] cs = dfname.toCharArray(); - cs[0] = Character.toUpperCase(cs[0]); - String dfname2 = new String(cs); - try { - setter = destClass.getMethod("set" + dfname2, getter.getReturnType()); - } catch (Exception e) { - try { - srcField = destClass.getField(dfname); - if (!getter.getReturnType().equals(srcField.getType())) continue; - } catch (Exception e2) { - continue; - } - } - mv.visitVarInsn(ALOAD, 1); - mv.visitVarInsn(ALOAD, 2); - mv.visitMethodInsn(INVOKEVIRTUAL, srcClassName, getter.getName(), Type.getMethodDescriptor(getter), false); - if (srcField == null) { - mv.visitMethodInsn(INVOKEVIRTUAL, destClassName, setter.getName(), Type.getMethodDescriptor(setter), false); - } else { - mv.visitFieldInsn(PUTFIELD, destClassName, dfname, Type.getDescriptor(getter.getReturnType())); - } - } - mv.visitVarInsn(ALOAD, 1); - mv.visitInsn(ARETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - { - mv = (cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, null)); - //mv.setDebug(true); - mv.visitVarInsn(ALOAD, 0); - mv.visitVarInsn(ALOAD, 1); - mv.visitTypeInsn(CHECKCAST, destClassName); - mv.visitVarInsn(ALOAD, 2); - mv.visitTypeInsn(CHECKCAST, srcClassName); - mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "apply", "(" + destDesc + srcDesc + ")" + destDesc, false); - mv.visitInsn(ARETURN); - mv.visitMaxs(3, 3); - mv.visitEnd(); - } - cw.visitEnd(); - // ------------------------------------------------------------------------------ - byte[] bytes = cw.toByteArray(); - Class creatorClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - return (Reproduce) creatorClazz.getDeclaredConstructor().newInstance(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - -} +package org.redkale.util; + +import java.lang.reflect.Modifier; +import java.util.Map; +import java.util.function.*; +import static org.redkale.asm.Opcodes.*; +import org.redkale.asm.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; + +/** + * JavaBean类对象的拷贝,相同的字段名会被拷贝
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 目标对象的数据类型 + * @param 源对象的数据类型 + */ +public interface Reproduce extends BiFunction { + + @Override + public D apply(D dest, S src); + + public static Reproduce create(final Class destClass, final Class srcClass) { + return create(destClass, srcClass, (BiPredicate) null, (Map) null); + } + + public static Reproduce create(final Class destClass, final Class srcClass, final Map names) { + return create(destClass, srcClass, (BiPredicate) null, names); + } + + @SuppressWarnings("unchecked") + public static Reproduce create(final Class destClass, final Class srcClass, final Predicate srcColumnPredicate) { + return create(destClass, srcClass, (sc, m) -> srcColumnPredicate.test(m), (Map) null); + } + + @SuppressWarnings("unchecked") + public static Reproduce create(final Class destClass, final Class srcClass, final Predicate srcColumnPredicate, final Map names) { + return create(destClass, srcClass, (sc, m) -> srcColumnPredicate.test(m), names); + } + + @SuppressWarnings("unchecked") + public static Reproduce create(final Class destClass, final Class srcClass, final BiPredicate srcColumnPredicate) { + return create(destClass, srcClass, srcColumnPredicate, (Map) null); + } + + @SuppressWarnings("unchecked") + public static Reproduce create(final Class destClass, final Class srcClass, final BiPredicate srcColumnPredicate, final Map names) { + // ------------------------------------------------------------------------------ + final String supDynName = Reproduce.class.getName().replace('.', '/'); + final String destClassName = destClass.getName().replace('.', '/'); + final String srcClassName = srcClass.getName().replace('.', '/'); + final String destDesc = Type.getDescriptor(destClass); + final String srcDesc = Type.getDescriptor(srcClass); + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final String newDynName = "org/redkaledyn/reproduce/_Dyn" + Reproduce.class.getSimpleName() + + "__" + destClass.getName().replace('.', '_').replace('$', '_') + + "__" + srcClass.getName().replace('.', '_').replace('$', '_'); + try { + Class clz = RedkaleClassLoader.findDynClass(newDynName.replace('/', '.')); + return (Reproduce) (clz == null ? loader.loadClass(newDynName.replace('/', '.')) : clz).getDeclaredConstructor().newInstance(); + } catch (Throwable ex) { + } + // ------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, "Ljava/lang/Object;L" + supDynName + "<" + destDesc + srcDesc + ">;", "java/lang/Object", new String[]{supDynName}); + + { // 构造函数 + mv = (cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = (cw.visitMethod(ACC_PUBLIC, "apply", "(" + destDesc + srcDesc + ")" + destDesc, null, null)); + //mv.setDebug(true); + + for (java.lang.reflect.Field field : srcClass.getFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + if (Modifier.isFinal(field.getModifiers())) continue; + if (!Modifier.isPublic(field.getModifiers())) continue; + final String sfname = field.getName(); + if (srcColumnPredicate != null && !srcColumnPredicate.test(field, sfname)) continue; + + final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); + java.lang.reflect.Method setter = null; + try { + if (!field.getType().equals(destClass.getField(dfname).getType())) continue; + } catch (Exception e) { + try { + char[] cs = dfname.toCharArray(); + cs[0] = Character.toUpperCase(cs[0]); + String dfname2 = new String(cs); + setter = destClass.getMethod("set" + dfname2, field.getType()); + } catch (Exception e2) { + continue; + } + } + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + String td = Type.getDescriptor(field.getType()); + mv.visitFieldInsn(GETFIELD, srcClassName, sfname, td); + if (setter == null) { + mv.visitFieldInsn(PUTFIELD, destClassName, dfname, td); + } else { + mv.visitMethodInsn(INVOKEVIRTUAL, destClassName, setter.getName(), Type.getMethodDescriptor(setter), false); + } + } + + for (java.lang.reflect.Method getter : srcClass.getMethods()) { + if (Modifier.isStatic(getter.getModifiers())) continue; + if (getter.getParameterTypes().length > 0) continue; + if ("getClass".equals(getter.getName())) continue; + if (!getter.getName().startsWith("get") && !getter.getName().startsWith("is")) continue; + final boolean is = getter.getName().startsWith("is"); + String sfname = getter.getName().substring(is ? 2 : 3); + if (sfname.length() < 2 || Character.isLowerCase(sfname.charAt(1))) { + char[] cs = sfname.toCharArray(); + cs[0] = Character.toLowerCase(cs[0]); + sfname = new String(cs); + } + if (srcColumnPredicate != null && !srcColumnPredicate.test(getter, sfname)) continue; + + final String dfname = names == null ? sfname : names.getOrDefault(sfname, sfname); + java.lang.reflect.Method setter = null; + java.lang.reflect.Field srcField = null; + char[] cs = dfname.toCharArray(); + cs[0] = Character.toUpperCase(cs[0]); + String dfname2 = new String(cs); + try { + setter = destClass.getMethod("set" + dfname2, getter.getReturnType()); + } catch (Exception e) { + try { + srcField = destClass.getField(dfname); + if (!getter.getReturnType().equals(srcField.getType())) continue; + } catch (Exception e2) { + continue; + } + } + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, srcClassName, getter.getName(), Type.getMethodDescriptor(getter), false); + if (srcField == null) { + mv.visitMethodInsn(INVOKEVIRTUAL, destClassName, setter.getName(), Type.getMethodDescriptor(setter), false); + } else { + mv.visitFieldInsn(PUTFIELD, destClassName, dfname, Type.getDescriptor(getter.getReturnType())); + } + } + mv.visitVarInsn(ALOAD, 1); + mv.visitInsn(ARETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + { + mv = (cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "apply", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, destClassName); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, srcClassName); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "apply", "(" + destDesc + srcDesc + ")" + destDesc, false); + mv.visitInsn(ARETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw.visitEnd(); + // ------------------------------------------------------------------------------ + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionDeclaredConstructors(newClazz, newDynName.replace('/', '.')); + try { + return (Reproduce) newClazz.getDeclaredConstructor().newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/src/org/redkale/util/Resourcable.java b/src/main/java/org/redkale/util/Resourcable.java similarity index 96% rename from src/org/redkale/util/Resourcable.java rename to src/main/java/org/redkale/util/Resourcable.java index 03e64f35a..8553fbeb4 100644 --- a/src/org/redkale/util/Resourcable.java +++ b/src/main/java/org/redkale/util/Resourcable.java @@ -1,19 +1,19 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -/** - * 对象的类没有标记为@Resource, 可以通过实现Resourcable接口实现动态获取Resource.name - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface Resourcable { - - public String resourceName(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +/** + * 对象的类没有标记为@Resource, 可以通过实现Resourcable接口实现动态获取Resource.name + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface Resourcable { + + public String resourceName(); +} diff --git a/src/org/redkale/util/ResourceFactory.java b/src/main/java/org/redkale/util/ResourceFactory.java similarity index 95% rename from src/org/redkale/util/ResourceFactory.java rename to src/main/java/org/redkale/util/ResourceFactory.java index 07a52fa53..2d0d3c920 100644 --- a/src/org/redkale/util/ResourceFactory.java +++ b/src/main/java/org/redkale/util/ResourceFactory.java @@ -1,862 +1,877 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.Annotation; -import java.lang.ref.WeakReference; -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.logging.*; -import javax.annotation.Resource; - -/** - * - * 依赖注入功能主类
    - * - * 如果@Resource(name = "$") 表示资源name采用所属对象的name
    - * 如果没有@Resource且对象实现了Resourcable, 则会取对象的resourceName()方法值 - *

    - * name规则:
    - *    1: "$"有特殊含义, 不能表示"$"资源本身
    - *    2: 只能是字母、数字、(短横)-、(下划线)_、点(.)的组合
    - * 
    - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@SuppressWarnings("unchecked") -public final class ResourceFactory { - - public static final String RESOURCE_PARENT_NAME = "$"; - - private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName()); - - private final ResourceFactory parent; - - private static final ResourceFactory instance = new ResourceFactory(null); - - private final List> chidren = new CopyOnWriteArrayList<>(); - - private final ConcurrentHashMap injectLoaderMap = new ConcurrentHashMap(); - - private final ConcurrentHashMap resLoaderMap = new ConcurrentHashMap(); - - private final ConcurrentHashMap> store = new ConcurrentHashMap(); - - private ResourceFactory(ResourceFactory parent) { - this.parent = parent; - if (parent == null) { - ServiceLoader loaders = ServiceLoader.load(ResourceInjectLoader.class); - Iterator it = loaders.iterator(); - while (it.hasNext()) { - ResourceInjectLoader ril = it.next(); - this.injectLoaderMap.put(ril.annotationType(), ril); - } - } - } - - /** - * 获取根ResourceFactory - * - * @return ResourceFactory - */ - public static ResourceFactory root() { - return instance; - } - - /** - * 创建ResourceFactory子节点 - * - * @return ResourceFactory - */ - public ResourceFactory createChild() { - ResourceFactory child = new ResourceFactory(this); - this.chidren.add(new WeakReference<>(child)); - return child; - } - - /** - * 获取所有ResourceFactory子节点 - * - * @return List - */ - public List getChildren() { - List result = new ArrayList<>(); - for (WeakReference ref : chidren) { - ResourceFactory rf = ref.get(); - if (rf != null) result.add(rf); - } - return result; - } - - /** - * 清空当前ResourceFactory注入资源 - * - */ - public void release() { - this.store.clear(); - } - - /** - * 检查资源名是否合法 - *

    -     * name规则:
    -     *    1: "$"有特殊含义, 表示资源本身,"$"不能单独使用
    -     *    2: 只能是字母、数字、(短横)-、(下划线)_、点(.)的组合
    -     * 
    - * - * @param name String - */ - public static void checkResourceName(String name) { - if (name == null || (!name.isEmpty() && !name.matches("^[a-zA-Z0-9_;\\-\\.\\[\\]\\(\\)]+$"))) { - throw new IllegalArgumentException("name(" + name + ") contains illegal character, must be (a-z,A-Z,0-9,_,.,(,),-,[,])"); - } - } - - /** - * 将对象指定类型且name=""注入到资源池中,并同步已被注入的资源 - * - * @param 泛型 - * @param clazz 资源类型 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final Class clazz, final A rs) { - return register(true, clazz, rs); - } - - /** - * 将对象指定类型且name=""注入到资源池中 - * - * @param 泛型 - * @param autoSync 是否同步已被注入的资源 - * @param clazz 资源类型 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final boolean autoSync, final Class clazz, final A rs) { - return register(autoSync, "", clazz, rs); - } - - /** - * 将对象以name=""注入到资源池中,并同步已被注入的资源 - * - * @param 泛型 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final A rs) { - return register(true, rs); - } - - /** - * 将对象以name=""注入到资源池中,并同步已被注入的资源 - * - * @param 泛型 - * @param autoSync 是否同步已被注入的资源 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final boolean autoSync, final A rs) { - if (rs == null) return null; - return (A) register(autoSync, "", rs); - } - - /** - * 将boolean对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final String name, final boolean value) { - register(true, name, boolean.class, value); - } - - /** - * 将boolean对象以指定资源名注入到资源池中 - * - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final boolean autoSync, final String name, final boolean value) { - register(autoSync, name, boolean.class, value); - } - - /** - * 将byte对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final String name, final byte value) { - register(true, name, byte.class, value); - } - - /** - * 将byte对象以指定资源名注入到资源池中 - * - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final boolean autoSync, final String name, final byte value) { - register(autoSync, name, byte.class, value); - } - - /** - * 将short对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final String name, final short value) { - register(true, name, short.class, value); - } - - /** - * 将short对象以指定资源名注入到资源池中 - * - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final boolean autoSync, final String name, final short value) { - register(autoSync, name, short.class, value); - } - - /** - * 将int对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final String name, final int value) { - register(true, name, int.class, value); - } - - /** - * 将int对象以指定资源名注入到资源池中 - * - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final boolean autoSync, final String name, final int value) { - register(autoSync, name, int.class, value); - } - - /** - * 将float对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final String name, final float value) { - register(true, name, float.class, value); - } - - /** - * 将float对象以指定资源名注入到资源池中 - * - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final boolean autoSync, final String name, final float value) { - register(autoSync, name, float.class, value); - } - - /** - * 将long对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final String name, final long value) { - register(true, name, long.class, value); - } - - /** - * 将long对象以指定资源名注入到资源池中 - * - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final boolean autoSync, final String name, final long value) { - register(autoSync, name, long.class, value); - } - - /** - * 将double对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final String name, final double value) { - register(true, name, double.class, value); - } - - /** - * 将double对象以指定资源名注入到资源池中 - * - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param value 资源值 - * - */ - public void register(final boolean autoSync, final String name, final double value) { - register(autoSync, name, double.class, value); - } - - /** - * 将对象以指定资源名注入到资源池中,并同步已被注入的资源 - * - * @param 泛型 - * @param name 资源名 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final String name, final A rs) { - return register(true, name, rs); - } - - /** - * 将对象以指定资源名注入到资源池中 - * - * @param 泛型 - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final boolean autoSync, final String name, final A rs) { - checkResourceName(name); - final Class claz = rs.getClass(); - ResourceType rtype = claz.getAnnotation(ResourceType.class); - if (rtype == null) { - return (A) register(autoSync, name, claz, rs); - } else { - A old = null; - A t = (A) register(autoSync, name, rtype.value(), rs); - if (t != null) old = t; - return old; - } - } - - /** - * 将对象以指定资源名和类型注入到资源池中,并同步已被注入的资源 - * - * @param 泛型 - * @param name 资源名 - * @param clazz 资源类型 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final String name, final Class clazz, final A rs) { - return register(true, name, clazz, rs); - } - - /** - * 将对象以指定资源名和类型注入到资源池中,并同步已被注入的资源 - * - * @param 泛型 - * @param name 资源名 - * @param clazz 资源类型 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final String name, final Type clazz, final A rs) { - return register(true, name, clazz, rs); - } - - /** - * 将对象以指定资源名和类型注入到资源池中 - * - * @param 泛型 - * @param autoSync 是否同步已被注入的资源 - * @param name 资源名 - * @param clazz 资源类型 - * @param rs 资源对象 - * - * @return 旧资源对象 - */ - public A register(final boolean autoSync, final String name, final Type clazz, final A rs) { - checkResourceName(name); - ConcurrentHashMap map = this.store.get(clazz); - if (map == null) { - synchronized (clazz) { - map = this.store.get(clazz); - if (map == null) { - map = new ConcurrentHashMap(); - store.put(clazz, map); - } - } - } - ResourceEntry re = map.get(name); - if (re == null) { - map.put(name, new ResourceEntry(rs)); - } else { - map.put(name, new ResourceEntry(rs, name, re.elements, autoSync)); - } - return re == null ? null : (A) re.value; - } - - /** - * 判断是否包含指定资源名和资源类型的资源对象 - * - * @param 泛型 - * @param recursive 是否遍历父节点 - * @param name 资源名 - * @param clazz 资源类型 - * - * @return 是否存在 - */ - public boolean contains(boolean recursive, String name, Class clazz) { - Map map = this.store.get(clazz); - return map == null ? ((recursive && parent != null) ? parent.contains(recursive, name, clazz) : false) : map.containsKey(name); - } - - /** - * 查找指定资源名和资源类型的资源对象所在的ResourceFactory, 没有则返回null - * - * @param name 资源名 - * @param clazz 资源类型 - * - * @return ResourceFactory - */ - public ResourceFactory findResourceFactory(String name, Type clazz) { - Map map = this.store.get(clazz); - if (map != null && map.containsKey(name)) return this; - if (parent != null) return parent.findResourceFactory(name, clazz); - return null; - } - - public A find(Class clazz) { - return find("", clazz); - } - - public A find(String name, Type clazz) { - ResourceEntry re = findEntry(name, clazz); - return re == null ? null : (A) re.value; - } - - public A find(String name, Class clazz) { - ResourceEntry re = findEntry(name, clazz); - return re == null ? null : re.value; - } - - public A findChild(final String name, final Class clazz) { - A rs = find(name, clazz); - if (rs != null) return rs; - for (Map.Entry> en : this.store.entrySet()) { - if (!(en.getKey() instanceof Class)) continue; - if (!clazz.isAssignableFrom((Class) en.getKey())) continue; - ResourceEntry v = en.getValue().get(name); - if (v != null) return (A) v.value; - } - return null; - } - - private ResourceEntry findEntry(String name, Type clazz) { - Map map = this.store.get(clazz); - if (map != null) { - ResourceEntry re = map.get(name); - if (re != null) return re; - } - if (parent != null) return parent.findEntry(name, clazz); - return null; - } - - public List query(Class clazz) { - return query(new ArrayList<>(), clazz); - } - - public List query(Type clazz) { - return query(new ArrayList<>(), clazz); - } - - private List query(final List list, Type clazz) { - Map map = this.store.get(clazz); - if (map != null) { - for (ResourceEntry re : map.values()) { - if (re.value != null) list.add((A) re.value); - } - } - if (parent != null) parent.query(list, clazz); - return list; - } - - public List query(final BiPredicate predicate) { - return query(new ArrayList<>(), predicate); - } - - private List query(final List list, final BiPredicate predicate) { - if (predicate == null) return list; - for (ConcurrentHashMap map : this.store.values()) { - for (Map.Entry en : map.entrySet()) { - if (predicate.test(en.getKey(), en.getValue().value)) { - list.add((A) en.getValue().value); - } - } - } - if (parent != null) parent.query(list, predicate); - return list; - } - - private ResourceEntry findEntry(String name, Class clazz) { - Map map = this.store.get(clazz); - if (map != null) { - ResourceEntry rs = map.get(name); - if (rs != null) return rs; - } - if (parent != null) return parent.findEntry(name, clazz); - return null; - } - - public boolean inject(final Object src) { - return inject(src, null); - } - - public boolean inject(final Object src, final T attachment) { - return inject(src, attachment, null); - } - - public boolean inject(final Object src, final BiConsumer consumer) { - return inject(src, null, consumer); - } - - public boolean inject(final Object src, final T attachment, final BiConsumer consumer) { - return inject(src, attachment, consumer, new ArrayList()); - } - - public static String formatResourceName(String name) { - if (name == null) return null; - int pos = name.indexOf("{system.property."); - if (pos < 0) return name; - String prefix = name.substring(0, pos); - String subname = name.substring(pos + "{system.property.".length()); - pos = subname.lastIndexOf('}'); - if (pos < 0) return name; - String postfix = subname.substring(pos + 1); - String property = subname.substring(0, pos); - return formatResourceName(prefix + System.getProperty(property, "") + postfix); - } - - private boolean inject(final Object src, final T attachment, final BiConsumer consumer, final List list) { - if (src == null) return false; - try { - list.add(src); - Class clazz = src.getClass(); - final boolean diyloaderflag = !instance.injectLoaderMap.isEmpty(); - do { - if (java.lang.Enum.class.isAssignableFrom(clazz)) break; - final String cname = clazz.getName(); - if (cname.startsWith("java.") || cname.startsWith("javax.") - || cname.startsWith("jdk.") || cname.startsWith("sun.")) break; - for (Field field : clazz.getDeclaredFields()) { - if (Modifier.isStatic(field.getModifiers())) continue; - field.setAccessible(true); - final Class classtype = field.getType(); - final Type genctype = TypeToken.containsUnknownType(field.getGenericType()) - ? TypeToken.getGenericType(field.getGenericType(), src.getClass()) : field.getGenericType(); - Resource rc = field.getAnnotation(Resource.class); - if (rc == null) { //深度注入 - boolean flag = true; //是否没有重复 - Object ns = field.get(src); - for (Object o : list) { - if (o == ns) { - flag = false; - break; - } - } - if (flag && diyloaderflag) { - instance.injectLoaderMap.values().stream().forEach(iloader -> { - Annotation ann = field.getAnnotation(iloader.annotationType()); - if (ann == null) return; - iloader.load(this, src, ann, field, attachment); - }); - } - if (ns == null) continue; - final String nsname = ns.getClass().getName(); - if (ns.getClass().isPrimitive() || ns.getClass().isArray() - || nsname.startsWith("java.") || nsname.startsWith("javax.") - || nsname.startsWith("jdk.") || nsname.startsWith("sun.")) continue; - if (flag) this.inject(ns, attachment, consumer, list); - continue; - } - if (Modifier.isFinal(field.getModifiers())) continue; - if (consumer != null) consumer.accept(src, field); - String tname = rc.name(); - if (tname.contains(RESOURCE_PARENT_NAME)) { - Resource res = src.getClass().getAnnotation(Resource.class); - if (res == null) { - if (src instanceof Resourcable) { - tname = tname.replace(RESOURCE_PARENT_NAME, ((Resourcable) src).resourceName()); - } else { - logger.log(Level.SEVERE, src.getClass().getName() + " not found @Resource on Class or not implements Resourcable"); - } - } else { - tname = tname.replace(RESOURCE_PARENT_NAME, res.name()); - } - - } - boolean autoregnull = true; - final String rcname = formatResourceName(tname); - Object rs; - if (rcname.startsWith("system.property.")) { - rs = System.getProperty(rcname.substring("system.property.".length())); - } else { - ResourceEntry re = findEntry(rcname, genctype); - if (re == null) { - if (rcname.startsWith("property.")) { - re = findEntry(rcname, String.class); - } else { - re = findEntry(rcname, classtype); - } - } - if (re == null) { - ResourceLoader it = findLoader(genctype, field); - if (it != null) { - it.load(this, src, rcname, field, attachment); - autoregnull = it.autoNone(); - re = findEntry(rcname, genctype); - } - } - if (re == null && genctype != classtype) { - re = findEntry(rcname, classtype); - if (re == null) { - if (rcname.startsWith("property.")) { - re = findEntry(rcname, String.class); - } else { - re = findEntry(rcname, classtype); - } - } - if (re == null) { - ResourceLoader it = findLoader(classtype, field); - if (it != null) { - it.load(this, src, rcname, field, attachment); - autoregnull = it.autoNone(); - re = findEntry(rcname, classtype); - } - } - } - if (re == null && autoregnull) { - register(rcname, genctype, null); //自动注入null的值 - re = findEntry(rcname, genctype); - } - if (re == null) continue; - re.elements.add(new ResourceElement<>(src, field)); - rs = re.value; - } - if (rs != null && !rs.getClass().isPrimitive() && classtype.isPrimitive()) { - if (classtype == int.class) { - rs = Integer.decode(rs.toString()); - } else if (classtype == long.class) { - rs = Long.decode(rs.toString()); - } else if (classtype == short.class) { - rs = Short.decode(rs.toString()); - } else if (classtype == boolean.class) { - rs = "true".equalsIgnoreCase(rs.toString()); - } else if (classtype == byte.class) { - rs = Byte.decode(rs.toString()); - } else if (classtype == float.class) { - rs = Float.parseFloat(rs.toString()); - } else if (classtype == double.class) { - rs = Double.parseDouble(rs.toString()); - } - } - if (rs != null) field.set(src, rs); - } - } while ((clazz = clazz.getSuperclass()) != Object.class); - return true; - } catch (Exception ex) { - logger.log(Level.SEVERE, "inject " + src + " error", ex); - return false; - } - } - - public void register(final ResourceInjectLoader loader) { - if (loader == null) return; - instance.injectLoaderMap.put(loader.annotationType(), loader); - } - - public void register(final ResourceLoader rs, final Type... clazzs) { - if (clazzs == null || rs == null) return; - for (Type clazz : clazzs) { - resLoaderMap.put(clazz, rs); - } - } - - private ResourceLoader findMatchLoader(Type ft, Field field) { - ResourceLoader it = this.resLoaderMap.get(ft); - if (it == null && field != null) it = this.resLoaderMap.get(field.getType()); - if (it != null) return it; - return parent == null ? null : parent.findMatchLoader(ft, field); - } - - private ResourceLoader findRegxLoader(Type ft, Field field) { - if (field == null) return null; - Class c = field.getType(); - for (Map.Entry en : this.resLoaderMap.entrySet()) { - Type t = en.getKey(); - if (t == ft) return en.getValue(); - if (t instanceof Class && (((Class) t)).isAssignableFrom(c)) return en.getValue(); - } - return parent == null ? null : parent.findRegxLoader(ft, field); - } - - public ResourceLoader findLoader(Type ft, Field field) { - ResourceLoader it = this.findMatchLoader(ft, field); - return it == null ? findRegxLoader(ft, field) : it; - } - - private static class ResourceEntry { - - public final T value; - - public final List elements; - - public ResourceEntry(T value) { - this.value = value; - this.elements = new CopyOnWriteArrayList<>(); - } - - public ResourceEntry(T value, final String name, final List elements, boolean sync) { - this.value = value; - this.elements = elements == null ? new CopyOnWriteArrayList<>() : elements; - if (sync && elements != null && !elements.isEmpty()) { - - for (ResourceElement element : elements) { - Object dest = element.dest.get(); - if (dest == null) continue; - Object rs = value; - final Class classtype = element.fieldType; - if (rs != null && !rs.getClass().isPrimitive() && classtype.isPrimitive()) { - if (classtype == int.class) { - rs = Integer.decode(rs.toString()); - } else if (classtype == long.class) { - rs = Long.decode(rs.toString()); - } else if (classtype == short.class) { - rs = Short.decode(rs.toString()); - } else if (classtype == boolean.class) { - rs = "true".equalsIgnoreCase(rs.toString()); - } else if (classtype == byte.class) { - rs = Byte.decode(rs.toString()); - } else if (classtype == float.class) { - rs = Float.parseFloat(rs.toString()); - } else if (classtype == double.class) { - rs = Double.parseDouble(rs.toString()); - } - } - if (rs == null && classtype.isPrimitive()) rs = Array.get(Array.newInstance(classtype, 1), 0); - Object oldVal = null; - if (element.listener != null) { - try { - oldVal = element.field.get(dest); - } catch (Exception e) { - e.printStackTrace(); - } - } - try { - element.field.set(dest, rs); - } catch (Exception e) { - e.printStackTrace(); - } - if (element.listener != null) { - try { - element.listener.invoke(dest, name, rs, oldVal); - } catch (Exception e) { - logger.log(Level.SEVERE, dest + " resource change listener error", e); - } - } - } - } - } - } - - private static class ResourceElement { - - private static final HashMap listenerMethods = new HashMap<>(); //不使用ConcurrentHashMap是因为value不能存null - - public final WeakReference dest; - - public final Field field; //Resource 字段 - - public final Class fieldType; - - public final Method listener; - - public ResourceElement(T dest, Field field) { - this.dest = new WeakReference(dest); - this.field = field; - this.fieldType = field.getType(); - Class t = dest.getClass(); - String tn = t.getName(); - this.listener = tn.startsWith("java.") || tn.startsWith("javax.") ? null : findListener(t, field.getType()); - } - - private static Method findListener(Class clazz, Class fieldType) { - synchronized (listenerMethods) { - Class loop = clazz; - Method m = listenerMethods.get(clazz.getName() + "-" + fieldType.getName()); - if (m != null) return m; - do { - for (Method method : loop.getDeclaredMethods()) { - if (method.getAnnotation(ResourceListener.class) != null - && method.getParameterCount() == 3 - && String.class.isAssignableFrom(method.getParameterTypes()[0]) - && method.getParameterTypes()[1] == method.getParameterTypes()[2] - && method.getParameterTypes()[1].isAssignableFrom(fieldType)) { - m = method; - m.setAccessible(true); - break; - } - } - } while ((loop = loop.getSuperclass()) != Object.class); - listenerMethods.put(clazz.getName() + "-" + fieldType.getName(), m); - return m; - } - } - } - - @FunctionalInterface - public static interface ResourceLoader { - - public void load(ResourceFactory factory, Object src, String resourceName, Field field, Object attachment); - - // 返回true 表示调用ResourceLoader之后资源仍不存在,则会在ResourceFactory里注入默认值null,返回false表示资源不存在下次仍会调用ResourceLoader自行处理 - default boolean autoNone() { - return true; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.Annotation; +import java.lang.ref.WeakReference; +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.logging.*; +import javax.annotation.Resource; +import org.redkale.convert.*; + +/** + * + * 依赖注入功能主类
    + * + * 如果@Resource(name = "$") 表示资源name采用所属对象的name
    + * 如果没有@Resource且对象实现了Resourcable, 则会取对象的resourceName()方法值 + *
    + * name规则:
    + *    1: "$"有特殊含义, 不能表示"$"资源本身
    + *    2: 只能是字母、数字、(短横)-、(下划线)_、点(.)的组合
    + * 
    + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@SuppressWarnings("unchecked") +public final class ResourceFactory { + + public static final String RESOURCE_PARENT_NAME = "$"; + + private static final Logger logger = Logger.getLogger(ResourceFactory.class.getSimpleName()); + + private final ResourceFactory parent; + + private final List> chidren = new CopyOnWriteArrayList<>(); + + private final ConcurrentHashMap injectLoaderMap = new ConcurrentHashMap(); + + private final ConcurrentHashMap resLoaderMap = new ConcurrentHashMap(); + + private final ConcurrentHashMap> store = new ConcurrentHashMap(); + + private ResourceFactory(ResourceFactory parent) { + this.parent = parent; + if (parent == null) { + ServiceLoader loaders = ServiceLoader.load(ResourceInjectLoader.class); + RedkaleClassLoader.putServiceLoader(ResourceInjectLoader.class); + Iterator it = loaders.iterator(); + while (it.hasNext()) { + ResourceInjectLoader ril = it.next(); + RedkaleClassLoader.putReflectionPublicConstructors(ril.getClass(), ril.getClass().getName()); + this.injectLoaderMap.put(ril.annotationType(), ril); + } + } + } + + /** + * 创建一个根ResourceFactory + * + * @return ResourceFactory + */ + public static ResourceFactory create() { + return new ResourceFactory(null); + } + + /** + * 创建ResourceFactory子节点 + * + * @return ResourceFactory + */ + public ResourceFactory createChild() { + ResourceFactory child = new ResourceFactory(this); + this.chidren.add(new WeakReference<>(child)); + return child; + } + + /** + * 获取所有ResourceFactory子节点 + * + * @return List + */ + public List getChildren() { + List result = new ArrayList<>(); + for (WeakReference ref : chidren) { + ResourceFactory rf = ref.get(); + if (rf != null) result.add(rf); + } + return result; + } + + /** + * 清空当前ResourceFactory注入资源 + * + */ + public void release() { + this.store.clear(); + } + + /** + * 检查资源名是否合法 + *

    +     * name规则:
    +     *    1: "$"有特殊含义, 表示资源本身,"$"不能单独使用
    +     *    2: 只能是字母、数字、(短横)-、(下划线)_、点(.)的组合
    +     * 
    + * + * @param name String + */ + public static void checkResourceName(String name) { + if (name == null || (!name.isEmpty() && !name.matches("^[a-zA-Z0-9_;\\-\\.\\[\\]\\(\\)]+$"))) { + throw new IllegalArgumentException("name(" + name + ") contains illegal character, must be (a-z,A-Z,0-9,_,.,(,),-,[,])"); + } + } + + /** + * 将对象指定类型且name=""注入到资源池中,并同步已被注入的资源 + * + * @param 泛型 + * @param clazz 资源类型 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final Class clazz, final A rs) { + return register(true, clazz, rs); + } + + /** + * 将对象指定类型且name=""注入到资源池中 + * + * @param 泛型 + * @param autoSync 是否同步已被注入的资源 + * @param clazz 资源类型 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final boolean autoSync, final Class clazz, final A rs) { + return register(autoSync, "", clazz, rs); + } + + /** + * 将对象以name=""注入到资源池中,并同步已被注入的资源 + * + * @param 泛型 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final A rs) { + return register(true, rs); + } + + /** + * 将对象以name=""注入到资源池中,并同步已被注入的资源 + * + * @param 泛型 + * @param autoSync 是否同步已被注入的资源 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final boolean autoSync, final A rs) { + if (rs == null) return null; + return (A) register(autoSync, "", rs); + } + + /** + * 将boolean对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final String name, final boolean value) { + register(true, name, boolean.class, value); + } + + /** + * 将boolean对象以指定资源名注入到资源池中 + * + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final boolean autoSync, final String name, final boolean value) { + register(autoSync, name, boolean.class, value); + } + + /** + * 将byte对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final String name, final byte value) { + register(true, name, byte.class, value); + } + + /** + * 将byte对象以指定资源名注入到资源池中 + * + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final boolean autoSync, final String name, final byte value) { + register(autoSync, name, byte.class, value); + } + + /** + * 将short对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final String name, final short value) { + register(true, name, short.class, value); + } + + /** + * 将short对象以指定资源名注入到资源池中 + * + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final boolean autoSync, final String name, final short value) { + register(autoSync, name, short.class, value); + } + + /** + * 将int对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final String name, final int value) { + register(true, name, int.class, value); + } + + /** + * 将int对象以指定资源名注入到资源池中 + * + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final boolean autoSync, final String name, final int value) { + register(autoSync, name, int.class, value); + } + + /** + * 将float对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final String name, final float value) { + register(true, name, float.class, value); + } + + /** + * 将float对象以指定资源名注入到资源池中 + * + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final boolean autoSync, final String name, final float value) { + register(autoSync, name, float.class, value); + } + + /** + * 将long对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final String name, final long value) { + register(true, name, long.class, value); + } + + /** + * 将long对象以指定资源名注入到资源池中 + * + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final boolean autoSync, final String name, final long value) { + register(autoSync, name, long.class, value); + } + + /** + * 将double对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final String name, final double value) { + register(true, name, double.class, value); + } + + /** + * 将double对象以指定资源名注入到资源池中 + * + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param value 资源值 + * + */ + public void register(final boolean autoSync, final String name, final double value) { + register(autoSync, name, double.class, value); + } + + /** + * 将对象以指定资源名注入到资源池中,并同步已被注入的资源 + * + * @param 泛型 + * @param name 资源名 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final String name, final A rs) { + return register(true, name, rs); + } + + /** + * 将对象以指定资源名注入到资源池中 + * + * @param 泛型 + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final boolean autoSync, final String name, final A rs) { + checkResourceName(name); + final Class claz = rs.getClass(); + ResourceType rtype = claz.getAnnotation(ResourceType.class); + if (rtype == null) { + return (A) register(autoSync, name, claz, rs); + } else { + A old = null; + A t = (A) register(autoSync, name, rtype.value(), rs); + if (t != null) old = t; + return old; + } + } + + /** + * 将对象以指定资源名和类型注入到资源池中,并同步已被注入的资源 + * + * @param 泛型 + * @param name 资源名 + * @param clazz 资源类型 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final String name, final Class clazz, final A rs) { + return register(true, name, clazz, rs); + } + + /** + * 将对象以指定资源名和类型注入到资源池中,并同步已被注入的资源 + * + * @param 泛型 + * @param name 资源名 + * @param clazz 资源类型 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final String name, final Type clazz, final A rs) { + return register(true, name, clazz, rs); + } + + /** + * 将对象以指定资源名和类型注入到资源池中 + * + * @param 泛型 + * @param autoSync 是否同步已被注入的资源 + * @param name 资源名 + * @param clazz 资源类型 + * @param rs 资源对象 + * + * @return 旧资源对象 + */ + public A register(final boolean autoSync, final String name, final Type clazz, final A rs) { + checkResourceName(name); + ConcurrentHashMap map = this.store.get(clazz); + if (map == null) { + synchronized (clazz) { + map = this.store.get(clazz); + if (map == null) { + map = new ConcurrentHashMap(); + store.put(clazz, map); + } + } + } + ResourceEntry re = map.get(name); + if (re == null) { + map.put(name, new ResourceEntry(rs)); + } else { + map.put(name, new ResourceEntry(rs, name, re.elements, autoSync)); + } + return re == null ? null : (A) re.value; + } + + /** + * 判断是否包含指定资源名和资源类型的资源对象 + * + * @param 泛型 + * @param recursive 是否遍历父节点 + * @param name 资源名 + * @param clazz 资源类型 + * + * @return 是否存在 + */ + public boolean contains(boolean recursive, String name, Class clazz) { + Map map = this.store.get(clazz); + return map == null ? ((recursive && parent != null) ? parent.contains(recursive, name, clazz) : false) : map.containsKey(name); + } + + /** + * 查找指定资源名和资源类型的资源对象所在的ResourceFactory, 没有则返回null + * + * @param name 资源名 + * @param clazz 资源类型 + * + * @return ResourceFactory + */ + public ResourceFactory findResourceFactory(String name, Type clazz) { + Map map = this.store.get(clazz); + if (map != null && map.containsKey(name)) return this; + if (parent != null) return parent.findResourceFactory(name, clazz); + return null; + } + + public A find(Class clazz) { + return find("", clazz); + } + + public A find(String name, Type clazz) { + ResourceEntry re = findEntry(name, clazz); + return re == null ? null : (A) re.value; + } + + public A find(String name, Class clazz) { + ResourceEntry re = findEntry(name, clazz); + return re == null ? null : re.value; + } + + public A findChild(final String name, final Class clazz) { + A rs = find(name, clazz); + if (rs != null) return rs; + for (Map.Entry> en : this.store.entrySet()) { + if (!(en.getKey() instanceof Class)) continue; + if (!clazz.isAssignableFrom((Class) en.getKey())) continue; + ResourceEntry v = en.getValue().get(name); + if (v != null) return (A) v.value; + } + return null; + } + + private ResourceEntry findEntry(String name, Type clazz) { + Map map = this.store.get(clazz); + if (map != null) { + ResourceEntry re = map.get(name); + if (re != null) return re; + } + if (parent != null) return parent.findEntry(name, clazz); + return null; + } + + public List query(Class clazz) { + return query(new ArrayList<>(), clazz); + } + + public List query(Type clazz) { + return query(new ArrayList<>(), clazz); + } + + private List query(final List list, Type clazz) { + Map map = this.store.get(clazz); + if (map != null) { + for (ResourceEntry re : map.values()) { + if (re.value != null) list.add((A) re.value); + } + } + if (parent != null) parent.query(list, clazz); + return list; + } + + public List query(final BiPredicate predicate) { + return query(new ArrayList<>(), predicate); + } + + private List query(final List list, final BiPredicate predicate) { + if (predicate == null) return list; + for (ConcurrentHashMap map : this.store.values()) { + for (Map.Entry en : map.entrySet()) { + if (predicate.test(en.getKey(), en.getValue().value)) { + list.add((A) en.getValue().value); + } + } + } + if (parent != null) parent.query(list, predicate); + return list; + } + + private ResourceEntry findEntry(String name, Class clazz) { + Map map = this.store.get(clazz); + if (map != null) { + ResourceEntry rs = map.get(name); + if (rs != null) return rs; + } + if (parent != null) return parent.findEntry(name, clazz); + return null; + } + + public boolean inject(final Object src) { + return inject(src, null); + } + + public boolean inject(final Object src, final T attachment) { + return inject(src, attachment, null); + } + + public boolean inject(final Object src, final BiConsumer consumer) { + return inject(src, null, consumer); + } + + public boolean inject(final Object src, final T attachment, final BiConsumer consumer) { + return inject(src, attachment, consumer, new ArrayList()); + } + + public static String formatResourceName(String name) { + if (name == null) return null; + int pos = name.indexOf("{system.property."); + if (pos < 0) return name; + String prefix = name.substring(0, pos); + String subname = name.substring(pos + "{system.property.".length()); + pos = subname.lastIndexOf('}'); + if (pos < 0) return name; + String postfix = subname.substring(pos + 1); + String property = subname.substring(0, pos); + return formatResourceName(prefix + System.getProperty(property, "") + postfix); + } + + private boolean inject(final Object src, final T attachment, final BiConsumer consumer, final List list) { + if (src == null) return false; + try { + list.add(src); + Class clazz = src.getClass(); + final boolean diyloaderflag = !parentRoot().injectLoaderMap.isEmpty(); + do { + if (java.lang.Enum.class.isAssignableFrom(clazz)) break; + final String cname = clazz.getName(); + if (cname.startsWith("java.") || cname.startsWith("javax.") + || cname.startsWith("jdk.") || cname.startsWith("sun.")) break; + if (cname.indexOf('/') < 0) {//排除内部类, 如:JsonConvert$$Lambda$87/0x0000000100197440- + RedkaleClassLoader.putReflectionDeclaredFields(cname); + } + for (Field field : clazz.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) continue; + field.setAccessible(true); + final Class classtype = field.getType(); + Resource rc = field.getAnnotation(Resource.class); + if (rc == null) { //深度注入 + if (Convert.class.isAssignableFrom(classtype)) continue; + if (ConvertFactory.class.isAssignableFrom(classtype)) continue; + if (ResourceFactory.class.isAssignableFrom(classtype)) continue; + boolean flag = true; //是否没有重复 + Object ns = field.get(src); + for (Object o : list) { + if (o == ns) { + flag = false; + break; + } + } + if (flag && diyloaderflag) { + parentRoot().injectLoaderMap.values().stream().forEach(iloader -> { + Annotation ann = field.getAnnotation(iloader.annotationType()); + if (ann == null) return; + iloader.load(this, src, ann, field, attachment); + }); + } + if (ns == null) continue; + final String nsname = ns.getClass().getName(); + if (ns.getClass().isPrimitive() || ns.getClass().isArray() + || nsname.startsWith("java.") || nsname.startsWith("javax.") + || nsname.startsWith("jdk.") || nsname.startsWith("sun.")) continue; + if (flag) this.inject(ns, attachment, consumer, list); + continue; + } + if (Modifier.isFinal(field.getModifiers())) continue; + RedkaleClassLoader.putReflectionField(cname, field); + final Type genctype = TypeToken.containsUnknownType(field.getGenericType()) + ? TypeToken.getGenericType(field.getGenericType(), src.getClass()) : field.getGenericType(); + if (consumer != null) consumer.accept(src, field); + String tname = rc.name(); + if (tname.contains(RESOURCE_PARENT_NAME)) { + Resource res = src.getClass().getAnnotation(Resource.class); + if (res == null) { + if (src instanceof Resourcable) { + tname = tname.replace(RESOURCE_PARENT_NAME, ((Resourcable) src).resourceName()); + } else { + logger.log(Level.SEVERE, src.getClass().getName() + " not found @Resource on Class or not implements Resourcable"); + } + } else { + tname = tname.replace(RESOURCE_PARENT_NAME, res.name()); + } + + } + boolean autoregnull = true; + final String rcname = formatResourceName(tname); + Object rs; + if (rcname.startsWith("system.property.")) { + rs = System.getProperty(rcname.substring("system.property.".length())); + } else { + ResourceEntry re = findEntry(rcname, genctype); + if (re == null) { + if (rcname.startsWith("property.")) { + re = findEntry(rcname, String.class); + } else { + re = findEntry(rcname, classtype); + } + } + if (re == null) { + ResourceLoader it = findLoader(genctype, field); + if (it != null) { + it.load(this, src, rcname, field, attachment); + autoregnull = it.autoNone(); + re = findEntry(rcname, genctype); + } + } + if (re == null && genctype != classtype) { + re = findEntry(rcname, classtype); + if (re == null) { + if (rcname.startsWith("property.")) { + re = findEntry(rcname, String.class); + } else { + re = findEntry(rcname, classtype); + } + } + if (re == null) { + ResourceLoader it = findLoader(classtype, field); + if (it != null) { + it.load(this, src, rcname, field, attachment); + autoregnull = it.autoNone(); + re = findEntry(rcname, classtype); + } + } + } + if (re == null && autoregnull) { + register(rcname, genctype, null); //自动注入null的值 + re = findEntry(rcname, genctype); + } + if (re == null) continue; + re.elements.add(new ResourceElement<>(src, field)); + rs = re.value; + } + if (rs != null && !rs.getClass().isPrimitive() && classtype.isPrimitive()) { + if (classtype == int.class) { + rs = Integer.decode(rs.toString()); + } else if (classtype == long.class) { + rs = Long.decode(rs.toString()); + } else if (classtype == short.class) { + rs = Short.decode(rs.toString()); + } else if (classtype == boolean.class) { + rs = "true".equalsIgnoreCase(rs.toString()); + } else if (classtype == byte.class) { + rs = Byte.decode(rs.toString()); + } else if (classtype == float.class) { + rs = Float.parseFloat(rs.toString()); + } else if (classtype == double.class) { + rs = Double.parseDouble(rs.toString()); + } + } + if (rs != null) field.set(src, rs); + } + } while ((clazz = clazz.getSuperclass()) != Object.class); + return true; + } catch (Exception ex) { + logger.log(Level.SEVERE, "inject " + src + " error", ex); + return false; + } + } + + public void register(final ResourceInjectLoader loader) { + if (loader == null) return; + parentRoot().injectLoaderMap.put(loader.annotationType(), loader); + } + + public void register(final ResourceLoader rs, final Type... clazzs) { + if (clazzs == null || rs == null) return; + for (Type clazz : clazzs) { + resLoaderMap.put(clazz, rs); + } + } + + private ResourceFactory parentRoot() { + if (parent == null) return this; + return parent.parentRoot(); + } + + private ResourceLoader findMatchLoader(Type ft, Field field) { + ResourceLoader it = this.resLoaderMap.get(ft); + if (it == null && field != null) it = this.resLoaderMap.get(field.getType()); + if (it != null) return it; + return parent == null ? null : parent.findMatchLoader(ft, field); + } + + private ResourceLoader findRegxLoader(Type ft, Field field) { + if (field == null) return null; + Class c = field.getType(); + for (Map.Entry en : this.resLoaderMap.entrySet()) { + Type t = en.getKey(); + if (t == ft) return en.getValue(); + if (t instanceof Class && (((Class) t)).isAssignableFrom(c)) return en.getValue(); + } + return parent == null ? null : parent.findRegxLoader(ft, field); + } + + public ResourceLoader findLoader(Type ft, Field field) { + ResourceLoader it = this.findMatchLoader(ft, field); + return it == null ? findRegxLoader(ft, field) : it; + } + + private static class ResourceEntry { + + public final T value; + + public final List elements; + + public ResourceEntry(T value) { + this.value = value; + this.elements = new CopyOnWriteArrayList<>(); + } + + public ResourceEntry(T value, final String name, final List elements, boolean sync) { + this.value = value; + this.elements = elements == null ? new CopyOnWriteArrayList<>() : elements; + if (sync && elements != null && !elements.isEmpty()) { + + for (ResourceElement element : elements) { + Object dest = element.dest.get(); + if (dest == null) continue; + Object rs = value; + final Class classtype = element.fieldType; + if (rs != null && !rs.getClass().isPrimitive() && classtype.isPrimitive()) { + if (classtype == int.class) { + rs = Integer.decode(rs.toString()); + } else if (classtype == long.class) { + rs = Long.decode(rs.toString()); + } else if (classtype == short.class) { + rs = Short.decode(rs.toString()); + } else if (classtype == boolean.class) { + rs = "true".equalsIgnoreCase(rs.toString()); + } else if (classtype == byte.class) { + rs = Byte.decode(rs.toString()); + } else if (classtype == float.class) { + rs = Float.parseFloat(rs.toString()); + } else if (classtype == double.class) { + rs = Double.parseDouble(rs.toString()); + } + } + if (rs == null && classtype.isPrimitive()) rs = Array.get(Array.newInstance(classtype, 1), 0); + Object oldVal = null; + if (element.listener != null) { + try { + oldVal = element.field.get(dest); + } catch (Exception e) { + e.printStackTrace(); + } + } + try { + element.field.set(dest, rs); + } catch (Exception e) { + e.printStackTrace(); + } + if (element.listener != null) { + try { + element.listener.invoke(dest, name, rs, oldVal); + } catch (Exception e) { + logger.log(Level.SEVERE, dest + " resource change listener error", e); + } + } + } + } + } + } + + private static class ResourceElement { + + private static final HashMap listenerMethods = new HashMap<>(); //不使用ConcurrentHashMap是因为value不能存null + + public final WeakReference dest; + + public final Field field; //Resource 字段 + + public final Class fieldType; + + public final Method listener; + + public ResourceElement(T dest, Field field) { + this.dest = new WeakReference(dest); + this.field = field; + this.fieldType = field.getType(); + Class t = dest.getClass(); + String tn = t.getName(); + this.listener = tn.startsWith("java.") || tn.startsWith("javax.") ? null : findListener(t, field.getType()); + } + + private static Method findListener(Class clazz, Class fieldType) { + synchronized (listenerMethods) { + Class loop = clazz; + Method m = listenerMethods.get(clazz.getName() + "-" + fieldType.getName()); + if (m != null) return m; + do { + RedkaleClassLoader.putReflectionDeclaredMethods(loop.getName()); + for (Method method : loop.getDeclaredMethods()) { + if (method.getAnnotation(ResourceListener.class) != null + && method.getParameterCount() == 3 + && String.class.isAssignableFrom(method.getParameterTypes()[0]) + && method.getParameterTypes()[1] == method.getParameterTypes()[2] + && method.getParameterTypes()[1].isAssignableFrom(fieldType)) { + m = method; + m.setAccessible(true); + RedkaleClassLoader.putReflectionMethod(loop.getName(), method); + break; + } + } + } while ((loop = loop.getSuperclass()) != Object.class); + listenerMethods.put(clazz.getName() + "-" + fieldType.getName(), m); + return m; + } + } + } + + @FunctionalInterface + public static interface ResourceLoader { + + public void load(ResourceFactory factory, Object src, String resourceName, Field field, Object attachment); + + // 返回true 表示调用ResourceLoader之后资源仍不存在,则会在ResourceFactory里注入默认值null,返回false表示资源不存在下次仍会调用ResourceLoader自行处理 + default boolean autoNone() { + return true; + } + } + +} diff --git a/src/org/redkale/util/ResourceInjectLoader.java b/src/main/java/org/redkale/util/ResourceInjectLoader.java similarity index 96% rename from src/org/redkale/util/ResourceInjectLoader.java rename to src/main/java/org/redkale/util/ResourceInjectLoader.java index 26cd4ab54..d97f95ae8 100644 --- a/src/org/redkale/util/ResourceInjectLoader.java +++ b/src/main/java/org/redkale/util/ResourceInjectLoader.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.Annotation; -import java.lang.reflect.Field; - -/** - * 自定义注入加载器 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param Annotation - */ -public interface ResourceInjectLoader { - - public void load(ResourceFactory factory, Object src, T annotation, Field field, Object attachment); - - public Class annotationType(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; + +/** + * 自定义注入加载器 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param Annotation + */ +public interface ResourceInjectLoader { + + public void load(ResourceFactory factory, Object src, T annotation, Field field, Object attachment); + + public Class annotationType(); +} diff --git a/src/org/redkale/util/ResourceListener.java b/src/main/java/org/redkale/util/ResourceListener.java similarity index 96% rename from src/org/redkale/util/ResourceListener.java rename to src/main/java/org/redkale/util/ResourceListener.java index e559baf0a..dfdfaf654 100644 --- a/src/org/redkale/util/ResourceListener.java +++ b/src/main/java/org/redkale/util/ResourceListener.java @@ -1,52 +1,52 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * @Resource资源被更新时的监听事件。本注解只能标记在方法参数为(String name, T newVal, T oldVal)上。 - * 方法在资源被更新以后调用。 - * - *

    - * public class RecordService implements Service {
    - *
    - *    @Resource(name = "record.id")
    - *    private int id;
    - *
    - *    @Resource(name = "record.name")
    - *    private String name;
    - *
    - *    @ResourceListener
    - *    private void changeResource(String name, Object newVal, Object oldVal) {
    - *        System.out.println("@Resource = " + name + " 资源变更:  newVal = " + newVal + ", oldVal = " + oldVal);
    - *    }
    - *
    - *    public static void main(String[] args) throws Exception {
    - *        ResourceFactory factory = ResourceFactory.root();
    - *        factory.register("record.id", "2345"); 
    - *        factory.register("record.name", "my old name"); 
    - *        Record record = new Record();
    - *        factory.inject(record);
    - *        factory.register("record.name", "my new name"); 
    - *   }
    - * 
    - * }
    - * 
    - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -public @interface ResourceListener { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * @Resource资源被更新时的监听事件。本注解只能标记在方法参数为(String name, T newVal, T oldVal)上。 + * 方法在资源被更新以后调用。 + * + *

    + * public class RecordService implements Service {
    + *
    + *    @Resource(name = "record.id")
    + *    private int id;
    + *
    + *    @Resource(name = "record.name")
    + *    private String name;
    + *
    + *    @ResourceListener
    + *    private void changeResource(String name, Object newVal, Object oldVal) {
    + *        System.out.println("@Resource = " + name + " 资源变更:  newVal = " + newVal + ", oldVal = " + oldVal);
    + *    }
    + *
    + *    public static void main(String[] args) throws Exception {
    + *        ResourceFactory factory = ResourceFactory.root();
    + *        factory.register("record.id", "2345"); 
    + *        factory.register("record.name", "my old name"); 
    + *        Record record = new Record();
    + *        factory.inject(record);
    + *        factory.register("record.name", "my new name"); 
    + *   }
    + * 
    + * }
    + * 
    + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface ResourceListener { + +} diff --git a/src/org/redkale/util/ResourceType.java b/src/main/java/org/redkale/util/ResourceType.java similarity index 96% rename from src/org/redkale/util/ResourceType.java rename to src/main/java/org/redkale/util/ResourceType.java index 4c95150a2..7bc9f8a46 100644 --- a/src/org/redkale/util/ResourceType.java +++ b/src/main/java/org/redkale/util/ResourceType.java @@ -1,29 +1,29 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.annotation.*; - -/** - * 显式的指明资源类型。 - * 调用ResourceFactory.register(Object rs)时通常执行的是ResourceFactory.register(rs.getClass(), Object rs); - * 若rs.getClass()的类标记了@ResourceType, 则使用@ResourceType.value()的class值进行注入。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE}) -@Retention(RUNTIME) -public @interface ResourceType { - - Class value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.annotation.*; + +/** + * 显式的指明资源类型。 + * 调用ResourceFactory.register(Object rs)时通常执行的是ResourceFactory.register(rs.getClass(), Object rs); + * 若rs.getClass()的类标记了@ResourceType, 则使用@ResourceType.value()的class值进行注入。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE}) +@Retention(RUNTIME) +public @interface ResourceType { + + Class value(); +} diff --git a/src/org/redkale/util/SelectColumn.java b/src/main/java/org/redkale/util/SelectColumn.java similarity index 96% rename from src/org/redkale/util/SelectColumn.java rename to src/main/java/org/redkale/util/SelectColumn.java index c819e0449..ee6e4efef 100644 --- a/src/org/redkale/util/SelectColumn.java +++ b/src/main/java/org/redkale/util/SelectColumn.java @@ -1,222 +1,222 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.util.*; -import java.util.function.*; -import java.util.regex.*; - -/** - * 判断字符串数组是否包含或排除指定字符串的操作类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class SelectColumn implements Predicate { - - private Pattern[] patterns; - - private String[] columns; - - private boolean excludable; //是否排除 - - public SelectColumn() { - } - - protected SelectColumn(final String[] columns0, final boolean excludable) { - this.excludable = excludable; - final int len = columns0.length; - if (len < 1) return; - Pattern[] regs = null; - String[] cols = null; - int regcount = 0; - int colcount = 0; - for (String col : columns0) { - boolean reg = false; - for (int i = 0; i < col.length(); i++) { - char ch = col.charAt(i); - if (ch == '^' || ch == '$' || ch == '*' || ch == '?' || ch == '+' || ch == '[' || ch == '(') { - reg = true; - break; - } - } - if (reg) { - if (regs == null) regs = new Pattern[len]; - regs[regcount++] = Pattern.compile(col); - } else { - if (cols == null) cols = new String[len]; - cols[colcount++] = col; - } - } - if (regs != null) { - if (regcount == len) { - this.patterns = regs; - } else { - this.patterns = Arrays.copyOf(regs, regcount); - } - } - if (cols != null) { - if (colcount == len) { - this.columns = cols; - } else { - this.columns = Arrays.copyOf(cols, colcount); - } - } - } - - /** - * class中的字段名 - * - * @param columns 包含的字段名集合 - * - * @return SelectColumn - */ -// @Deprecated -// public static SelectColumn createIncludes(String... columns) { -// return new SelectColumn(columns, false); -// } - - /** - * class中的字段名 - * - * @param columns 包含的字段名集合 - * - * @return SelectColumn - */ - public static SelectColumn includes(String... columns) { - return new SelectColumn(columns, false); - } - - /** - * class中的字段名 - * - * @param cols 包含的字段名集合 - * @param columns 包含的字段名集合 - * - * @return SelectColumn - */ -// @Deprecated -// public static SelectColumn createIncludes(String[] cols, String... columns) { -// return new SelectColumn(Utility.append(cols, columns), false); -// } - - /** - * class中的字段名 - * - * @param cols 包含的字段名集合 - * @param columns 包含的字段名集合 - * - * @return SelectColumn - */ - public static SelectColumn includes(String[] cols, String... columns) { - return new SelectColumn(Utility.append(cols, columns), false); - } - - /** - * - * class中的字段名 - * - * @param columns 排除的字段名集合 - * - * @return SelectColumn - */ -// @Deprecated -// public static SelectColumn createExcludes(String... columns) { -// return new SelectColumn(columns, true); -// } - - /** - * class中的字段名 - * - * @param columns 排除的字段名集合 - * - * @return SelectColumn - */ - public static SelectColumn excludes(String... columns) { - return new SelectColumn(columns, true); - } - - /** - * class中的字段名 - * - * @param cols 排除的字段名集合 - * @param columns 排除的字段名集合 - * - * @return SelectColumn - */ -// @Deprecated -// public static SelectColumn createExcludes(String[] cols, String... columns) { -// return new SelectColumn(Utility.append(cols, columns), true); -// } - - /** - * - * class中的字段名 - * - * @param cols 排除的字段名集合 - * @param columns 排除的字段名集合 - * - * @return SelectColumn - */ - public static SelectColumn excludes(String[] cols, String... columns) { - return new SelectColumn(Utility.append(cols, columns), true); - } - - public boolean isOnlyOneColumn() { - return !excludable && columns != null && columns.length == 1; - } - - @Override - public boolean test(final String column) { - if (this.columns != null) { - for (String col : this.columns) { - if (col.equalsIgnoreCase(column)) return !excludable; - } - } - if (this.patterns != null) { - for (Pattern reg : this.patterns) { - if (reg.matcher(column).find()) return !excludable; - } - } - return excludable; - } - - public String[] getColumns() { - return columns; - } - - public void setColumns(String[] columns) { - this.columns = columns; - } - - public boolean isExcludable() { - return excludable; - } - - public void setExcludable(boolean excludable) { - this.excludable = excludable; - } - - public Pattern[] getPatterns() { - return patterns; - } - - public void setPatterns(Pattern[] patterns) { - this.patterns = patterns; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getClass().getSimpleName()).append("{excludable=").append(excludable); - if (columns != null) sb.append(", columns=").append(Arrays.toString(columns)); - if (patterns != null) sb.append(", patterns=").append(Arrays.toString(patterns)); - return sb.append('}').toString(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.util.*; +import java.util.function.*; +import java.util.regex.*; + +/** + * 判断字符串数组是否包含或排除指定字符串的操作类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class SelectColumn implements Predicate { + + private Pattern[] patterns; + + private String[] columns; + + private boolean excludable; //是否排除 + + public SelectColumn() { + } + + protected SelectColumn(final String[] columns0, final boolean excludable) { + this.excludable = excludable; + final int len = columns0.length; + if (len < 1) return; + Pattern[] regs = null; + String[] cols = null; + int regcount = 0; + int colcount = 0; + for (String col : columns0) { + boolean reg = false; + for (int i = 0; i < col.length(); i++) { + char ch = col.charAt(i); + if (ch == '^' || ch == '$' || ch == '*' || ch == '?' || ch == '+' || ch == '[' || ch == '(') { + reg = true; + break; + } + } + if (reg) { + if (regs == null) regs = new Pattern[len]; + regs[regcount++] = Pattern.compile(col); + } else { + if (cols == null) cols = new String[len]; + cols[colcount++] = col; + } + } + if (regs != null) { + if (regcount == len) { + this.patterns = regs; + } else { + this.patterns = Arrays.copyOf(regs, regcount); + } + } + if (cols != null) { + if (colcount == len) { + this.columns = cols; + } else { + this.columns = Arrays.copyOf(cols, colcount); + } + } + } + + /** + * class中的字段名 + * + * @param columns 包含的字段名集合 + * + * @return SelectColumn + */ +// @Deprecated +// public static SelectColumn createIncludes(String... columns) { +// return new SelectColumn(columns, false); +// } + + /** + * class中的字段名 + * + * @param columns 包含的字段名集合 + * + * @return SelectColumn + */ + public static SelectColumn includes(String... columns) { + return new SelectColumn(columns, false); + } + + /** + * class中的字段名 + * + * @param cols 包含的字段名集合 + * @param columns 包含的字段名集合 + * + * @return SelectColumn + */ +// @Deprecated +// public static SelectColumn createIncludes(String[] cols, String... columns) { +// return new SelectColumn(Utility.append(cols, columns), false); +// } + + /** + * class中的字段名 + * + * @param cols 包含的字段名集合 + * @param columns 包含的字段名集合 + * + * @return SelectColumn + */ + public static SelectColumn includes(String[] cols, String... columns) { + return new SelectColumn(Utility.append(cols, columns), false); + } + + /** + * + * class中的字段名 + * + * @param columns 排除的字段名集合 + * + * @return SelectColumn + */ +// @Deprecated +// public static SelectColumn createExcludes(String... columns) { +// return new SelectColumn(columns, true); +// } + + /** + * class中的字段名 + * + * @param columns 排除的字段名集合 + * + * @return SelectColumn + */ + public static SelectColumn excludes(String... columns) { + return new SelectColumn(columns, true); + } + + /** + * class中的字段名 + * + * @param cols 排除的字段名集合 + * @param columns 排除的字段名集合 + * + * @return SelectColumn + */ +// @Deprecated +// public static SelectColumn createExcludes(String[] cols, String... columns) { +// return new SelectColumn(Utility.append(cols, columns), true); +// } + + /** + * + * class中的字段名 + * + * @param cols 排除的字段名集合 + * @param columns 排除的字段名集合 + * + * @return SelectColumn + */ + public static SelectColumn excludes(String[] cols, String... columns) { + return new SelectColumn(Utility.append(cols, columns), true); + } + + public boolean isOnlyOneColumn() { + return !excludable && columns != null && columns.length == 1; + } + + @Override + public boolean test(final String column) { + if (this.columns != null) { + for (String col : this.columns) { + if (col.equalsIgnoreCase(column)) return !excludable; + } + } + if (this.patterns != null) { + for (Pattern reg : this.patterns) { + if (reg.matcher(column).find()) return !excludable; + } + } + return excludable; + } + + public String[] getColumns() { + return columns; + } + + public void setColumns(String[] columns) { + this.columns = columns; + } + + public boolean isExcludable() { + return excludable; + } + + public void setExcludable(boolean excludable) { + this.excludable = excludable; + } + + public Pattern[] getPatterns() { + return patterns; + } + + public void setPatterns(Pattern[] patterns) { + this.patterns = patterns; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getSimpleName()).append("{excludable=").append(excludable); + if (columns != null) sb.append(", columns=").append(Arrays.toString(columns)); + if (patterns != null) sb.append(", patterns=").append(Arrays.toString(patterns)); + return sb.append('}').toString(); + } + +} diff --git a/src/org/redkale/util/Sheet.java b/src/main/java/org/redkale/util/Sheet.java similarity index 96% rename from src/org/redkale/util/Sheet.java rename to src/main/java/org/redkale/util/Sheet.java index 484a6bff0..8c22d4d81 100644 --- a/src/org/redkale/util/Sheet.java +++ b/src/main/java/org/redkale/util/Sheet.java @@ -1,154 +1,154 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.util.*; -import java.util.function.*; -import java.util.stream.*; -import org.redkale.convert.ConvertColumn; - -/** - * 页集合。 结构由一个total总数和一个List列表组合而成。 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 集合元素的数据类型 - */ -@SuppressWarnings("unchecked") -public class Sheet implements java.io.Serializable, Iterable { - - @ConvertColumn(index = 1) - private long total = -1; - - @ConvertColumn(index = 2) - private Collection rows; - - public Sheet() { - super(); - } - - public Sheet(int total, Collection data) { - this((long) total, data); - } - - public Sheet(long total, Collection data) { - this.total = total; - this.rows = (Collection) data; - } - - public static Sheet asSheet(Collection data) { - return data == null ? new Sheet() : new Sheet(data.size(), data); - } - - public static Sheet empty() { - return new Sheet<>(); - } - - public Sheet copyTo(Sheet copy) { - if (copy == null) return copy; - copy.total = this.total; - if (this.getRows() != null) { - copy.setRows(new ArrayList(this.getRows())); - } else { - copy.rows = null; - } - return copy; - } - - /** - * 判断数据列表是否为空 - * - * @return 是否为空 - */ - @ConvertColumn(index = 3) - public boolean isEmpty() { - return this.rows == null || this.rows.isEmpty(); - } - - @Override - public String toString() { - return "{\"total\":" + this.total + ", \"rows\":" + this.rows + "}"; - } - - public long getTotal() { - return this.total; - } - - public void setTotal(long total) { - this.total = total; - } - - public Collection getRows() { - return this.rows; - } - - public List list() { - return list(false); - } - - public List list(boolean created) { - if (this.rows == null) return created ? new ArrayList() : null; - return (this.rows instanceof List) ? (List) this.rows : new ArrayList(this.rows); - } - - public void setRows(Collection data) { - this.rows = (Collection) data; - } - - @Override - public Iterator iterator() { - return (this.rows == null) ? new ArrayList().iterator() : this.rows.iterator(); - } - - @Override - public void forEach(final Consumer consumer) { - if (consumer != null && this.rows != null && !this.rows.isEmpty()) { - this.rows.forEach(consumer); - } - } - - public Sheet map(Function mapper) { - if (this.isEmpty()) return (Sheet) this; - final List list = new ArrayList<>(); - for (T item : this.rows) { - list.add(mapper.apply(item)); - } - return new Sheet<>(getTotal(), list); - } - - public void forEachParallel(final Consumer consumer) { - if (consumer != null && this.rows != null && !this.rows.isEmpty()) { - this.rows.parallelStream().forEach(consumer); - } - } - - @Override - public Spliterator spliterator() { - return (this.rows == null) ? new ArrayList().spliterator() : this.rows.spliterator(); - } - - public Stream stream() { - return (this.rows == null) ? new ArrayList().stream() : this.rows.stream(); - } - - public Stream parallelStream() { - return (this.rows == null) ? new ArrayList().parallelStream() : this.rows.parallelStream(); - } - - public Object[] toArray() { - return (this.rows == null) ? new ArrayList().toArray() : this.rows.toArray(); - } - - public E[] toArray(E[] a) { - return (this.rows == null) ? new ArrayList().toArray(a) : this.rows.toArray(a); - } - - public E[] toArray(IntFunction generator) { - return (this.rows == null) ? new ArrayList().toArray(generator.apply(0)) : this.rows.toArray(generator.apply(this.rows.size())); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.util.*; +import java.util.function.*; +import java.util.stream.*; +import org.redkale.convert.ConvertColumn; + +/** + * 页集合。 结构由一个total总数和一个List列表组合而成。 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 集合元素的数据类型 + */ +@SuppressWarnings("unchecked") +public class Sheet implements java.io.Serializable, Iterable { + + @ConvertColumn(index = 1) + private long total = -1; + + @ConvertColumn(index = 2) + private Collection rows; + + public Sheet() { + super(); + } + + public Sheet(int total, Collection data) { + this((long) total, data); + } + + public Sheet(long total, Collection data) { + this.total = total; + this.rows = (Collection) data; + } + + public static Sheet asSheet(Collection data) { + return data == null ? new Sheet() : new Sheet(data.size(), data); + } + + public static Sheet empty() { + return new Sheet<>(); + } + + public Sheet copyTo(Sheet copy) { + if (copy == null) return copy; + copy.total = this.total; + if (this.getRows() != null) { + copy.setRows(new ArrayList(this.getRows())); + } else { + copy.rows = null; + } + return copy; + } + + /** + * 判断数据列表是否为空 + * + * @return 是否为空 + */ + @ConvertColumn(index = 3) + public boolean isEmpty() { + return this.rows == null || this.rows.isEmpty(); + } + + @Override + public String toString() { + return "{\"total\":" + this.total + ", \"rows\":" + this.rows + "}"; + } + + public long getTotal() { + return this.total; + } + + public void setTotal(long total) { + this.total = total; + } + + public Collection getRows() { + return this.rows; + } + + public List list() { + return list(false); + } + + public List list(boolean created) { + if (this.rows == null) return created ? new ArrayList() : null; + return (this.rows instanceof List) ? (List) this.rows : new ArrayList(this.rows); + } + + public void setRows(Collection data) { + this.rows = (Collection) data; + } + + @Override + public Iterator iterator() { + return (this.rows == null) ? new ArrayList().iterator() : this.rows.iterator(); + } + + @Override + public void forEach(final Consumer consumer) { + if (consumer != null && this.rows != null && !this.rows.isEmpty()) { + this.rows.forEach(consumer); + } + } + + public Sheet map(Function mapper) { + if (this.isEmpty()) return (Sheet) this; + final List list = new ArrayList<>(); + for (T item : this.rows) { + list.add(mapper.apply(item)); + } + return new Sheet<>(getTotal(), list); + } + + public void forEachParallel(final Consumer consumer) { + if (consumer != null && this.rows != null && !this.rows.isEmpty()) { + this.rows.parallelStream().forEach(consumer); + } + } + + @Override + public Spliterator spliterator() { + return (this.rows == null) ? new ArrayList().spliterator() : this.rows.spliterator(); + } + + public Stream stream() { + return (this.rows == null) ? new ArrayList().stream() : this.rows.stream(); + } + + public Stream parallelStream() { + return (this.rows == null) ? new ArrayList().parallelStream() : this.rows.parallelStream(); + } + + public Object[] toArray() { + return (this.rows == null) ? new ArrayList().toArray() : this.rows.toArray(); + } + + public E[] toArray(E[] a) { + return (this.rows == null) ? new ArrayList().toArray(a) : this.rows.toArray(a); + } + + public E[] toArray(IntFunction generator) { + return (this.rows == null) ? new ArrayList().toArray(generator.apply(0)) : this.rows.toArray(generator.apply(this.rows.size())); + } +} diff --git a/src/org/redkale/util/StringWrapper.java b/src/main/java/org/redkale/util/StringWrapper.java similarity index 95% rename from src/org/redkale/util/StringWrapper.java rename to src/main/java/org/redkale/util/StringWrapper.java index 207877f59..adf4c9728 100644 --- a/src/org/redkale/util/StringWrapper.java +++ b/src/main/java/org/redkale/util/StringWrapper.java @@ -1,44 +1,44 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.io.Serializable; -import org.redkale.convert.ConvertColumn; - -/** - * 主要供 JsonConvert.writeWrapper 使用 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class StringWrapper implements Serializable { - - @ConvertColumn(index = 1) - protected String value; - - public StringWrapper() { - } - - public StringWrapper(String value) { - this.value = value; - } - - public String getValue() { - return value; - } - - public void setValue(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.io.Serializable; +import org.redkale.convert.ConvertColumn; + +/** + * 主要供 JsonConvert.writeWrapper 使用 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class StringWrapper implements Serializable { + + @ConvertColumn(index = 1) + protected String value; + + public StringWrapper() { + } + + public StringWrapper(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + +} diff --git a/src/org/redkale/util/ThreadHashExecutor.java b/src/main/java/org/redkale/util/ThreadHashExecutor.java similarity index 95% rename from src/org/redkale/util/ThreadHashExecutor.java rename to src/main/java/org/redkale/util/ThreadHashExecutor.java index 84c08ec66..257a13041 100644 --- a/src/org/redkale/util/ThreadHashExecutor.java +++ b/src/main/java/org/redkale/util/ThreadHashExecutor.java @@ -1,177 +1,177 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * 线程池 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.1.0 - */ -public class ThreadHashExecutor extends AbstractExecutorService { - - private final LinkedBlockingQueue[] queues; - - private final ThreadPoolExecutor[] executors; - - public ThreadHashExecutor() { - this(Runtime.getRuntime().availableProcessors(), null); - } - - public ThreadHashExecutor(int size) { - this(size, null); - } - - public ThreadHashExecutor(int size, ThreadFactory factory) { - ThreadPoolExecutor[] array = new ThreadPoolExecutor[size]; - LinkedBlockingQueue[] ques = new LinkedBlockingQueue[size]; - final AtomicInteger counter = new AtomicInteger(); - for (int i = 0; i < array.length; i++) { - LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); - ques[i] = queue; - array[i] = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue, - factory == null ? (Runnable r) -> { - Thread t = new Thread(r); - t.setDaemon(true); - int c = counter.incrementAndGet(); - t.setName("Redkale-HashThread-" + (c > 9 ? c : ("0" + c))); - return t; - } : factory); - } - this.queues = ques; - this.executors = array; - } - - private ExecutorService hashExecutor(int hash) { - if (hash == 0) { - int k = 0; - int minsize = queues[0].size(); - for (int i = 1; i < queues.length; i++) { - int size = queues[i].size(); - if (size < minsize) { - minsize = size; - k = i; - } - } - return this.executors[k]; - } else { - return this.executors[(hash < 0 ? -hash : hash) % this.executors.length]; - } - } - - public void setThreadFactory(ThreadFactory factory) { - for (ThreadPoolExecutor executor : this.executors) { - executor.setThreadFactory(factory); - } - } - - public int size() { - return executors.length; - } - - @Override - public void execute(Runnable command) { - hashExecutor(0).execute(command); - } - - public void execute(int hash, Runnable command) { - hashExecutor(hash).execute(command); - } - - @Override - public Future submit(Runnable task) { - return hashExecutor(0).submit(task); - } - - public Future submit(int hash, Runnable task) { - return hashExecutor(hash).submit(task); - } - - @Override - public Future submit(Runnable task, T result) { - return hashExecutor(0).submit(task, result); - } - - public Future submit(int hash, Runnable task, T result) { - return hashExecutor(hash).submit(task, result); - } - - @Override - public Future submit(Callable task) { - return hashExecutor(0).submit(task); - } - - public Future submit(int hash, Callable task) { - return hashExecutor(hash).submit(task); - } - - public int waitingSize() { - int wsize = queues[0].size(); - for (int i = 1; i < queues.length; i++) { - wsize += queues[i].size(); - } - return wsize; - } - - @Override - public void shutdown() { - for (ExecutorService executor : this.executors) { - executor.shutdown(); - } - } - - @Override - public List shutdownNow() { - List list = new ArrayList<>(); - for (ExecutorService executor : this.executors) { - list.addAll(executor.shutdownNow()); - } - return list; - } - - @Override - public boolean isShutdown() { - return this.executors[0].isShutdown(); - } - - @Override - public boolean isTerminated() { - return this.executors[0].isTerminated(); - } - - @Override - public boolean awaitTermination(long l, TimeUnit tu) throws InterruptedException { - return this.executors[0].awaitTermination(l, tu); - } - - @Override - public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { - return hashExecutor(0).invokeAny(tasks); - } - - @Override - public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { - return hashExecutor(0).invokeAny(tasks, timeout, unit); - } - - @Override - public List> invokeAll(Collection> tasks) throws InterruptedException { - return hashExecutor(0).invokeAll(tasks); - } - - @Override - public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { - return hashExecutor(0).invokeAll(tasks, timeout, unit); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 线程池 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * + * @since 2.1.0 + */ +public class ThreadHashExecutor extends AbstractExecutorService { + + private final LinkedBlockingQueue[] queues; + + private final ThreadPoolExecutor[] executors; + + public ThreadHashExecutor() { + this(Utility.cpus(), null); + } + + public ThreadHashExecutor(int size) { + this(size, null); + } + + public ThreadHashExecutor(int size, ThreadFactory factory) { + ThreadPoolExecutor[] array = new ThreadPoolExecutor[size]; + LinkedBlockingQueue[] ques = new LinkedBlockingQueue[size]; + final AtomicInteger counter = new AtomicInteger(); + for (int i = 0; i < array.length; i++) { + LinkedBlockingQueue queue = new LinkedBlockingQueue<>(); + ques[i] = queue; + array[i] = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, queue, + factory == null ? (Runnable r) -> { + Thread t = new Thread(r); + t.setDaemon(true); + int c = counter.incrementAndGet(); + t.setName("Redkale-HashThread-" + (c > 9 ? c : ("0" + c))); + return t; + } : factory); + } + this.queues = ques; + this.executors = array; + } + + private ExecutorService hashExecutor(int hash) { + if (hash == 0) { + int k = 0; + int minsize = queues[0].size(); + for (int i = 1; i < queues.length; i++) { + int size = queues[i].size(); + if (size < minsize) { + minsize = size; + k = i; + } + } + return this.executors[k]; + } else { + return this.executors[(hash < 0 ? -hash : hash) % this.executors.length]; + } + } + + public void setThreadFactory(ThreadFactory factory) { + for (ThreadPoolExecutor executor : this.executors) { + executor.setThreadFactory(factory); + } + } + + public int size() { + return executors.length; + } + + @Override + public void execute(Runnable command) { + hashExecutor(0).execute(command); + } + + public void execute(int hash, Runnable command) { + hashExecutor(hash).execute(command); + } + + @Override + public Future submit(Runnable task) { + return hashExecutor(0).submit(task); + } + + public Future submit(int hash, Runnable task) { + return hashExecutor(hash).submit(task); + } + + @Override + public Future submit(Runnable task, T result) { + return hashExecutor(0).submit(task, result); + } + + public Future submit(int hash, Runnable task, T result) { + return hashExecutor(hash).submit(task, result); + } + + @Override + public Future submit(Callable task) { + return hashExecutor(0).submit(task); + } + + public Future submit(int hash, Callable task) { + return hashExecutor(hash).submit(task); + } + + public int waitingSize() { + int wsize = queues[0].size(); + for (int i = 1; i < queues.length; i++) { + wsize += queues[i].size(); + } + return wsize; + } + + @Override + public void shutdown() { + for (ExecutorService executor : this.executors) { + executor.shutdown(); + } + } + + @Override + public List shutdownNow() { + List list = new ArrayList<>(); + for (ExecutorService executor : this.executors) { + list.addAll(executor.shutdownNow()); + } + return list; + } + + @Override + public boolean isShutdown() { + return this.executors[0].isShutdown(); + } + + @Override + public boolean isTerminated() { + return this.executors[0].isTerminated(); + } + + @Override + public boolean awaitTermination(long l, TimeUnit tu) throws InterruptedException { + return this.executors[0].awaitTermination(l, tu); + } + + @Override + public T invokeAny(Collection> tasks) throws InterruptedException, ExecutionException { + return hashExecutor(0).invokeAny(tasks); + } + + @Override + public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return hashExecutor(0).invokeAny(tasks, timeout, unit); + } + + @Override + public List> invokeAll(Collection> tasks) throws InterruptedException { + return hashExecutor(0).invokeAll(tasks); + } + + @Override + public List> invokeAll(Collection> tasks, long timeout, TimeUnit unit) throws InterruptedException { + return hashExecutor(0).invokeAll(tasks, timeout, unit); + } +} diff --git a/src/org/redkale/util/TypeToken.java b/src/main/java/org/redkale/util/TypeToken.java similarity index 92% rename from src/org/redkale/util/TypeToken.java rename to src/main/java/org/redkale/util/TypeToken.java index 57539d45e..d8d0670b1 100644 --- a/src/org/redkale/util/TypeToken.java +++ b/src/main/java/org/redkale/util/TypeToken.java @@ -1,521 +1,543 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.reflect.Type; -import java.lang.reflect.*; -import java.util.*; -import org.redkale.asm.*; -import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; -import static org.redkale.asm.Opcodes.*; - -/** - * - * 获取泛型的Type类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 泛型 - */ -public abstract class TypeToken { - - private final Type type; - - public TypeToken() { - type = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - } - - public final Type getType() { - return type; - } - - /** - * 判断Type是否能确定最终的class, 是则返回true,存在通配符或者不确定类型则返回false。 - * 例如: Map< String, String > 返回 ture; Map< ? extends Serializable, String > 返回false; - * - * @param type Type对象 - * - * @return 是否可反解析 - */ - public final static boolean isClassType(final Type type) { - if (type instanceof Class) return true; - if (type instanceof WildcardType) return false; - if (type instanceof TypeVariable) return false; - if (type instanceof GenericArrayType) return isClassType(((GenericArrayType) type).getGenericComponentType()); - if (!(type instanceof ParameterizedType)) return false; //只能是null了 - final ParameterizedType ptype = (ParameterizedType) type; - if (ptype.getOwnerType() != null && !isClassType(ptype.getOwnerType())) return false; - if (!isClassType(ptype.getRawType())) return false; - for (Type t : ptype.getActualTypeArguments()) { - if (!isClassType(t)) return false; - } - return true; - } - - public final static boolean containsUnknownType(final Type type) { - if (type == null) return false; - if (type instanceof Class) return false; - if (type instanceof WildcardType) return true; - if (type instanceof TypeVariable) return true; - if (type instanceof GenericArrayType) return containsUnknownType(((GenericArrayType) type).getGenericComponentType()); - if (type instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) type; - if (containsUnknownType(pt.getRawType())) return true; - if (containsUnknownType(pt.getOwnerType())) return true; - Type[] ts = pt.getActualTypeArguments(); - if (ts != null) { - for (Type t : ts) { - if (containsUnknownType(t)) return true; - } - } - return false; - } - return true; - } - - public final static Class typeToClass(final Type type) { - if (type instanceof Class) return (Class) type; - if (type instanceof WildcardType) return null; - if (type instanceof TypeVariable) return null; - if (type instanceof GenericArrayType) return Array.newInstance(typeToClass(((GenericArrayType) type).getGenericComponentType()), 0).getClass(); - if (!(type instanceof ParameterizedType)) return null; //只能是null了 - Type owner = ((ParameterizedType) type).getOwnerType(); - Type raw = ((ParameterizedType) type).getRawType(); - //A$B owner=A raw=A$B, 所以内部类情况下使用owner是错误的 - return typeToClass(raw != null ? raw : owner); - } - - public static Type[] getGenericType(final Type[] types, final Type declaringClass) { - Type[] newTypes = new Type[types.length]; - for (int i = 0; i < newTypes.length; i++) { - newTypes[i] = getGenericType(types[i], declaringClass); - } - return newTypes; - } -// -// public static void main(String[] args) throws Throwable { -// Method tt0 = C.class.getMethod("getValue"); -// System.out.println("tt0.type=" + tt0.getReturnType() + "======" + tt0.getGenericReturnType() + "======" + getGenericType(tt0.getGenericReturnType(), C.class)); -// -// Method tt3 = C.class.getMethod("getValue3"); -// System.out.println("tt3.type=" + tt3.getReturnType() + "======" + tt3.getGenericReturnType() + "======" + getGenericType(tt3.getGenericReturnType(), C.class)); -// -// Method ttr = AGameService.class.getMethod("getResult"); -// System.out.println("ttr.type=" + ttr.getReturnType() + "======" + ttr.getGenericReturnType() + "======" + getGenericType(ttr.getGenericReturnType(), AGameService.class)); -// System.out.println("ttr.应该是: List, 结果是: " + getGenericType(ttr.getGenericReturnType(), AGameService.class)); -// } -// -// public static class GamePlayer { -// } -// -// public static class GameTable

    { -// } -// -// public static class GameService { -// -// } -// -// public static class AbstractGamePlayer extends GamePlayer { -// } -// -// public static class AbstractGameTable

    extends GameTable

    { -// } -// -// public static class AbstractGameService, P extends AbstractGamePlayer> extends GameService { -// -// public List getResult() { -// return null; -// } -// } -// -// public static class IGamePlayer extends AbstractGamePlayer { -// } -// -// public static class IGameTable

    extends AbstractGameTable

    { -// } -// -// public static class IGameService, P extends IGamePlayer> extends AbstractGameService { -// -// } -// -// public static class AGamePlayer extends IGamePlayer { -// } -// -// public static class AGameTable extends IGameTable { -// } -// -// public static class AGameService extends IGameService { -// -// } -// -// public static class A { -// -// public List getValue() { -// return null; -// } -// -// public T getValue3() { -// return null; -// } -// } -// -// public static class B extends A { -// } -// -// public static class C extends B { -// } - - /** - * 获取TypeVariable对应的实际Type, 如果type不是TypeVariable 直接返回type。 - *

    -     *  public abstract class Key {
    -     *  }
    -     *  public abstract class Val {
    -     *  }
    -     *  public abstract class AService <K extends Key, V extends Val> {
    -     *       public abstract V findValue(K key);
    -     *       public abstract Sheet<V> queryValue(K key);
    -     *  }
    -     *  public class Key2 extends Key {
    -     *  }
    -     *  public class Val2 extends Val {
    -     *  }
    -     *  public class Service2 extends Service <Key2, Val2> {
    -     *       public Val2 findValue(Key2 key){
    -     *          return new Val2();
    -     *       }
    -     *       public Sheet<Val2> queryValue(Key2 key){
    -     *          return new Sheet();
    -     *       }
    -     *  }
    -     * 
    - * - * - * @param type 泛型 - * @param declaringClass 泛型依附类 - * - * @return Type - */ - public static Type getGenericType(final Type type, final Type declaringClass) { - if (type == null || declaringClass == null) return type; - if (type instanceof TypeVariable) { - Type superType = null; - Class declaringClass0 = null; - if (declaringClass instanceof Class) { - declaringClass0 = (Class) declaringClass; - superType = declaringClass0.getGenericSuperclass(); - if (superType instanceof ParameterizedType) { - Map map = new HashMap<>(); - parseType(map, declaringClass0); - Type rstype = getType(map, type); - if (rstype instanceof Class) return rstype; - } - while (superType instanceof Class && superType != Object.class) superType = ((Class) superType).getGenericSuperclass(); - } else if (declaringClass instanceof ParameterizedType) { - superType = declaringClass; - Type rawType = ((ParameterizedType) declaringClass).getRawType(); - if (rawType instanceof Class) declaringClass0 = (Class) rawType; - } - if (declaringClass0 != null && superType instanceof ParameterizedType) { - ParameterizedType superPT = (ParameterizedType) superType; - Type[] atas = superPT.getActualTypeArguments(); - Class ss = declaringClass0; - TypeVariable[] asts = ss.getTypeParameters(); - while (atas.length != asts.length && ss != Object.class) { - ss = ss.getSuperclass(); - asts = ss.getTypeParameters(); - } - - if (atas.length == asts.length) { - for (int i = 0; i < asts.length; i++) { - Type currt = asts[i]; - if (asts[i] != type && superPT.getRawType() instanceof Class) { - if (asts[i] instanceof TypeVariable) { - - Class raw = (Class) superPT.getRawType(); - do { - Type rawsuper = raw.getGenericSuperclass(); - if (!(rawsuper instanceof ParameterizedType)) break; - ParameterizedType rpt = (ParameterizedType) rawsuper; - Type supraw = rpt.getRawType(); - if (!(supraw instanceof Class)) break; - Type[] tps = ((Class) supraw).getTypeParameters(); - if (rpt.getActualTypeArguments().length == tps.length) { - for (int k = 0; k < rpt.getActualTypeArguments().length; k++) { - if (rpt.getActualTypeArguments()[k] == currt) { - currt = tps[k]; - break; - } - } - } - Type rtrt = rpt.getRawType(); - if (!(rtrt instanceof Class)) break; - raw = (Class) rtrt; - } while (raw != Object.class); - } - } - if (currt == type) { - if (atas[i] instanceof Class - && ((TypeVariable) type).getBounds().length == 1 - && ((TypeVariable) type).getBounds()[0] instanceof Class - && ((Class) ((TypeVariable) type).getBounds()[0]).isAssignableFrom((Class) atas[i])) - return atas[i]; - if (atas[i] instanceof Class - && ((TypeVariable) type).getBounds().length == 1 - && ((TypeVariable) type).getBounds()[0] instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) ((TypeVariable) type).getBounds()[0]; - if (pt.getRawType() instanceof Class && ((Class) pt.getRawType()).isAssignableFrom((Class) atas[i])) { - return atas[i]; - } - } - if (atas[i] instanceof ParameterizedType - && ((TypeVariable) type).getBounds().length == 1 - && ((TypeVariable) type).getBounds()[0] == Object.class) - return atas[i]; - } - } - ParameterizedType cycType = superPT; - if (cycType.getRawType() instanceof Class) { - TypeVariable[] argTypes = ((Class) cycType.getRawType()).getTypeParameters(); - if (argTypes.length == asts.length) { - for (int i = 0; i < argTypes.length; i++) { - if (argTypes[i] == type) { - if (atas[i] instanceof TypeVariable - && ((TypeVariable) atas[i]).getBounds().length == 1 - && ((TypeVariable) atas[i]).getBounds()[0] instanceof Class) - return ((Class) ((TypeVariable) atas[i]).getBounds()[0]); - } - } - } - } - } - Type moreType = ((ParameterizedType) superType).getRawType(); - if (moreType != Object.class) return getGenericType(type, moreType); - } - TypeVariable tv = (TypeVariable) type; - if (tv.getBounds().length == 1) return tv.getBounds()[0]; - } else if (type instanceof GenericArrayType) { - final Type rst = getGenericType(((GenericArrayType) type).getGenericComponentType(), declaringClass); - return (GenericArrayType) () -> rst; - } - if (type instanceof ParameterizedType) { - ParameterizedType pt = (ParameterizedType) type; - return createParameterizedType(getGenericType(pt.getOwnerType(), declaringClass), - getGenericType(pt.getRawType(), declaringClass), - getGenericType(pt.getActualTypeArguments(), declaringClass)); - } - return type; - } - - private static Type getType(Map map, Type type) { - Type one = map.get(type); - if (one == null) return type; - return getType(map, one); - } - - private static Map parseType(Map map, Class clzz) { - if (clzz == Object.class) return map; - Type superType = clzz.getGenericSuperclass(); - if (!(superType instanceof ParameterizedType)) return map; - ParameterizedType pt = (ParameterizedType) superType; - Type[] ptt = pt.getActualTypeArguments(); - Type superRaw = pt.getRawType(); - if (!(superRaw instanceof Class)) return map; - Class superClazz = (Class) superRaw; - TypeVariable[] scs = superClazz.getTypeParameters(); - if (scs.length != ptt.length) return map; - for (int i = 0; i < scs.length; i++) { - if (scs[i] == ptt[i]) continue; - map.put(scs[i], ptt[i]); - } - return parseType(map, clzz.getSuperclass()); - } - - /** - * 动态创建类型为ParameterizedType或Class的Type - * - * @param type 当前泛型 - * @param declaringType0 子类 - * - * @return Type - */ - public static Type createClassType(final Type type, final Type declaringType0) { - if (isClassType(type)) return type; - if (type instanceof ParameterizedType) { // e.g. Map - final ParameterizedType pt = (ParameterizedType) type; - final Type[] paramTypes = pt.getActualTypeArguments(); - for (int i = 0; i < paramTypes.length; i++) { - paramTypes[i] = createClassType(paramTypes[i], declaringType0); - } - return createParameterizedType(pt.getOwnerType(), pt.getRawType(), paramTypes); - } - Type declaringType = declaringType0; - if (declaringType instanceof Class) { - do { - declaringType = ((Class) declaringType).getGenericSuperclass(); - if (declaringType == Object.class) return Object.class; - } while (declaringType instanceof Class); - } - //存在通配符则declaringType 必须是 ParameterizedType - if (!(declaringType instanceof ParameterizedType)) return Object.class; - final ParameterizedType declaringPType = (ParameterizedType) declaringType; - final Type[] virTypes = ((Class) declaringPType.getRawType()).getTypeParameters(); - final Type[] desTypes = declaringPType.getActualTypeArguments(); - if (type instanceof WildcardType) { // e.g. - final WildcardType wt = (WildcardType) type; - for (Type f : wt.getUpperBounds()) { - for (int i = 0; i < virTypes.length; i++) { - if (virTypes[i].equals(f)) return desTypes.length <= i ? Object.class : desTypes[i]; - } - } - } else if (type instanceof TypeVariable) { // e.g. - for (int i = 0; i < virTypes.length; i++) { - if (virTypes[i].equals(type)) return desTypes.length <= i ? Object.class : desTypes[i]; - } - } - return type; - } - - /** - * 动态创建 ParameterizedType - * - * @param ownerType0 ParameterizedType 的 ownerType - * @param rawType0 ParameterizedType 的 rawType - * @param actualTypeArguments0 ParameterizedType 的 actualTypeArguments - * - * @return Type - */ - public static Type createParameterizedType(final Type ownerType0, final Type rawType0, final Type... actualTypeArguments0) { - if (ownerType0 == null && rawType0 instanceof Class) { - int count = 0; - for (Type t : actualTypeArguments0) { - if (isClassType(t)) count++; - } - if (count == actualTypeArguments0.length) return createParameterizedType((Class) rawType0, actualTypeArguments0); - } - return new ParameterizedType() { - private final Class rawType = (Class) rawType0; - - private final Type ownerType = ownerType0; - - private final Type[] actualTypeArguments = actualTypeArguments0; - - @Override - public Type[] getActualTypeArguments() { - return actualTypeArguments.clone(); - } - - @Override - public Type getRawType() { - return rawType; - } - - @Override - public Type getOwnerType() { - return ownerType; - } - - @Override - public int hashCode() { - return Arrays.hashCode(actualTypeArguments) ^ Objects.hashCode(rawType) ^ Objects.hashCode(ownerType); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof ParameterizedType)) return false; - final ParameterizedType that = (ParameterizedType) o; - if (this == that) return true; - return Objects.equals(ownerType, that.getOwnerType()) - && Objects.equals(rawType, that.getRawType()) - && Arrays.equals(actualTypeArguments, that.getActualTypeArguments()); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (ownerType != null) sb.append((ownerType instanceof Class) ? (((Class) ownerType).getName()) : ownerType.toString()).append("."); - sb.append(rawType.getName()); - - if (actualTypeArguments != null && actualTypeArguments.length > 0) { - sb.append("<"); - boolean first = true; - for (Type t : actualTypeArguments) { - if (!first) sb.append(", "); - sb.append(t); - first = false; - } - sb.append(">"); - } - return sb.toString(); - } - }; - } - - // 注意: RetResult[]> 这种泛型带[]的尚未实现支持 - private static Type createParameterizedType(final Class rawType, final Type... actualTypeArguments) { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - String newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + System.currentTimeMillis(); - for (;;) { - try { - loader.loadClass(newDynName.replace('/', '.')); - newDynName = TypeToken.class.getName().replace('.', '/') + "_Dyn" + Math.abs(System.nanoTime()); - } catch (Throwable ex) { //异常说明类不存在 - break; - } - } - ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); - FieldVisitor fv; - MethodVisitor mv; - cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, "java/lang/Object", null); - String rawTypeDesc = org.redkale.asm.Type.getDescriptor(rawType); - StringBuilder sb = new StringBuilder(); - sb.append(rawTypeDesc.substring(0, rawTypeDesc.length() - 1)).append('<'); - for (Type c : actualTypeArguments) { - sb.append(getClassTypeDescriptor(c)); - } - sb.append(">;"); - { - fv = cw.visitField(ACC_PUBLIC, "field", rawTypeDesc, sb.toString(), null); - fv.visitEnd(); - } - {//构造方法 - mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); - mv.visitVarInsn(ALOAD, 0); - mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); - mv.visitInsn(RETURN); - mv.visitMaxs(1, 1); - mv.visitEnd(); - } - cw.visitEnd(); - byte[] bytes = cw.toByteArray(); - Class newClazz = new ClassLoader(loader) { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass(newDynName.replace('/', '.'), bytes); - try { - return newClazz.getField("field").getGenericType(); - } catch (Exception ex) { - throw new RuntimeException(ex); - } - } - - private static CharSequence getClassTypeDescriptor(Type type) { - if (!isClassType(type)) throw new IllegalArgumentException(type + " not a class type"); - if (type instanceof Class) return org.redkale.asm.Type.getDescriptor((Class) type); - if (type instanceof GenericArrayType) return getClassTypeDescriptor(((GenericArrayType) type).getGenericComponentType()) + "[]"; - final ParameterizedType pt = (ParameterizedType) type; - CharSequence rawTypeDesc = getClassTypeDescriptor(pt.getRawType()); - StringBuilder sb = new StringBuilder(); - sb.append(rawTypeDesc.subSequence(0, rawTypeDesc.length() - 1)).append('<'); - for (Type c : pt.getActualTypeArguments()) { - sb.append(getClassTypeDescriptor(c)); - } - sb.append(">;"); - return sb; - } -} +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.reflect.Type; +import java.lang.reflect.*; +import java.util.*; +import org.redkale.asm.*; +import static org.redkale.asm.ClassWriter.COMPUTE_FRAMES; +import static org.redkale.asm.Opcodes.*; + +/** + * + * 获取泛型的Type类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @param 泛型 + */ +@SuppressWarnings("unchecked") +public abstract class TypeToken { + + private final Type type; + + public TypeToken() { + type = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]; + } + + public final Type getType() { + return type; + } + + /** + * 判断Type是否能确定最终的class, 是则返回true,存在通配符或者不确定类型则返回false。 + * 例如: Map< String, String > 返回 ture; Map< ? extends Serializable, String > 返回false; + * + * @param type Type对象 + * + * @return 是否可反解析 + */ + public final static boolean isClassType(final Type type) { + if (type instanceof Class) return true; + if (type instanceof WildcardType) return false; + if (type instanceof TypeVariable) return false; + if (type instanceof GenericArrayType) return isClassType(((GenericArrayType) type).getGenericComponentType()); + if (!(type instanceof ParameterizedType)) return false; //只能是null了 + final ParameterizedType ptype = (ParameterizedType) type; + if (ptype.getOwnerType() != null && !isClassType(ptype.getOwnerType())) return false; + if (!isClassType(ptype.getRawType())) return false; + for (Type t : ptype.getActualTypeArguments()) { + if (!isClassType(t)) return false; + } + return true; + } + + public final static boolean containsUnknownType(final Type type) { + if (type == null) return false; + if (type instanceof Class) return false; + if (type instanceof WildcardType) return true; + if (type instanceof TypeVariable) return true; + if (type instanceof GenericArrayType) return containsUnknownType(((GenericArrayType) type).getGenericComponentType()); + if (type instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) type; + if (containsUnknownType(pt.getRawType())) return true; + if (containsUnknownType(pt.getOwnerType())) return true; + Type[] ts = pt.getActualTypeArguments(); + if (ts != null) { + for (Type t : ts) { + if (containsUnknownType(t)) return true; + } + } + return false; + } + return true; + } + + public final static Class typeToClass(final Type type) { + if (type instanceof Class) return (Class) type; + if (type instanceof WildcardType) return null; + if (type instanceof TypeVariable) return null; + if (type instanceof GenericArrayType) return Array.newInstance(typeToClass(((GenericArrayType) type).getGenericComponentType()), 0).getClass(); + if (!(type instanceof ParameterizedType)) return null; //只能是null了 + Type owner = ((ParameterizedType) type).getOwnerType(); + Type raw = ((ParameterizedType) type).getRawType(); + //A$B owner=A raw=A$B, 所以内部类情况下使用owner是错误的 + return typeToClass(raw != null ? raw : owner); + } + + public final static Class typeToClassOrElse(final Type type, final Class defClass) { + Class clazz = typeToClass(type); + return clazz == null ? defClass : clazz; + } + + public static Type[] getGenericType(final Type[] types, final Type declaringClass) { + Type[] newTypes = new Type[types.length]; + for (int i = 0; i < newTypes.length; i++) { + newTypes[i] = getGenericType(types[i], declaringClass); + } + return newTypes; + } +// +// public static void main(String[] args) throws Throwable { +// Method tt0 = C.class.getMethod("getValue"); +// System.out.println("tt0.type=" + tt0.getReturnType() + "======" + tt0.getGenericReturnType() + "======" + getGenericType(tt0.getGenericReturnType(), C.class)); +// +// Method tt3 = C.class.getMethod("getValue3"); +// System.out.println("tt3.type=" + tt3.getReturnType() + "======" + tt3.getGenericReturnType() + "======" + getGenericType(tt3.getGenericReturnType(), C.class)); +// +// Method ttr = AGameService.class.getMethod("getResult"); +// System.out.println("ttr.type=" + ttr.getReturnType() + "======" + ttr.getGenericReturnType() + "======" + getGenericType(ttr.getGenericReturnType(), AGameService.class)); +// System.out.println("ttr.应该是: List, 结果是: " + getGenericType(ttr.getGenericReturnType(), AGameService.class)); +// } +// +// public static class GamePlayer { +// } +// +// public static class GameTable

    { +// } +// +// public static class GameService { +// +// } +// +// public static class AbstractGamePlayer extends GamePlayer { +// } +// +// public static class AbstractGameTable

    extends GameTable

    { +// } +// +// public static class AbstractGameService, P extends AbstractGamePlayer> extends GameService { +// +// public List getResult() { +// return null; +// } +// } +// +// public static class IGamePlayer extends AbstractGamePlayer { +// } +// +// public static class IGameTable

    extends AbstractGameTable

    { +// } +// +// public static class IGameService, P extends IGamePlayer> extends AbstractGameService { +// +// } +// +// public static class AGamePlayer extends IGamePlayer { +// } +// +// public static class AGameTable extends IGameTable { +// } +// +// public static class AGameService extends IGameService { +// +// } +// +// public static class A { +// +// public List getValue() { +// return null; +// } +// +// public T getValue3() { +// return null; +// } +// } +// +// public static class B extends A { +// } +// +// public static class C extends B { +// } + + /** + * 获取TypeVariable对应的实际Type, 如果type不是TypeVariable 直接返回type。 + *

    +     *  public abstract class Key {
    +     *  }
    +     *  public abstract class Val {
    +     *  }
    +     *  public abstract class AService <K extends Key, V extends Val> {
    +     *       public abstract V findValue(K key);
    +     *       public abstract Sheet<V> queryValue(K key);
    +     *  }
    +     *  public class Key2 extends Key {
    +     *  }
    +     *  public class Val2 extends Val {
    +     *  }
    +     *  public class Service2 extends Service <Key2, Val2> {
    +     *       public Val2 findValue(Key2 key){
    +     *          return new Val2();
    +     *       }
    +     *       public Sheet<Val2> queryValue(Key2 key){
    +     *          return new Sheet();
    +     *       }
    +     *  }
    +     * 
    + * + * + * @param type 泛型 + * @param declaringClass 泛型依附类 + * + * @return Type + */ + public static Type getGenericType(final Type type, final Type declaringClass) { + if (type == null || declaringClass == null) return type; + if (type instanceof TypeVariable) { + Type superType = null; + Class declaringClass0 = null; + if (declaringClass instanceof Class) { + declaringClass0 = (Class) declaringClass; + superType = declaringClass0.getGenericSuperclass(); + if (superType instanceof ParameterizedType) { + Map map = new HashMap<>(); + parseType(map, declaringClass0); + Type rstype = getType(map, type); + if (rstype instanceof Class) return rstype; + } + while (superType instanceof Class && superType != Object.class) superType = ((Class) superType).getGenericSuperclass(); + } else if (declaringClass instanceof ParameterizedType) { + superType = declaringClass; + Type rawType = ((ParameterizedType) declaringClass).getRawType(); + if (rawType instanceof Class) declaringClass0 = (Class) rawType; + } + if (declaringClass0 != null && superType instanceof ParameterizedType) { + ParameterizedType superPT = (ParameterizedType) superType; + Type[] atas = superPT.getActualTypeArguments(); + Class ss = declaringClass0; + TypeVariable[] asts = ss.getTypeParameters(); + while (atas.length != asts.length && ss != Object.class) { + ss = ss.getSuperclass(); + asts = ss.getTypeParameters(); + } + + if (atas.length == asts.length) { + for (int i = 0; i < asts.length; i++) { + Type currt = asts[i]; + if (asts[i] != type && superPT.getRawType() instanceof Class) { + if (asts[i] instanceof TypeVariable) { + + Class raw = (Class) superPT.getRawType(); + do { + Type rawsuper = raw.getGenericSuperclass(); + if (!(rawsuper instanceof ParameterizedType)) break; + ParameterizedType rpt = (ParameterizedType) rawsuper; + Type supraw = rpt.getRawType(); + if (!(supraw instanceof Class)) break; + Type[] tps = ((Class) supraw).getTypeParameters(); + if (rpt.getActualTypeArguments().length == tps.length) { + for (int k = 0; k < rpt.getActualTypeArguments().length; k++) { + if (rpt.getActualTypeArguments()[k] == currt) { + currt = tps[k]; + break; + } + } + } + Type rtrt = rpt.getRawType(); + if (!(rtrt instanceof Class)) break; + raw = (Class) rtrt; + } while (raw != Object.class); + } + } + if (currt == type) { + if (atas[i] instanceof Class + && ((TypeVariable) type).getBounds().length == 1 + && ((TypeVariable) type).getBounds()[0] instanceof Class + && ((Class) ((TypeVariable) type).getBounds()[0]).isAssignableFrom((Class) atas[i])) + return atas[i]; + if (atas[i] instanceof Class + && ((TypeVariable) type).getBounds().length == 1 + && ((TypeVariable) type).getBounds()[0] instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) ((TypeVariable) type).getBounds()[0]; + if (pt.getRawType() instanceof Class && ((Class) pt.getRawType()).isAssignableFrom((Class) atas[i])) { + return atas[i]; + } + } + if (atas[i] instanceof ParameterizedType + && ((TypeVariable) type).getBounds().length == 1 + && ((TypeVariable) type).getBounds()[0] == Object.class) + return atas[i]; + } + } + ParameterizedType cycType = superPT; + if (cycType.getRawType() instanceof Class) { + TypeVariable[] argTypes = ((Class) cycType.getRawType()).getTypeParameters(); + if (argTypes.length == asts.length) { + for (int i = 0; i < argTypes.length; i++) { + if (argTypes[i] == type) { + if (atas[i] instanceof TypeVariable + && ((TypeVariable) atas[i]).getBounds().length == 1 + && ((TypeVariable) atas[i]).getBounds()[0] instanceof Class) + return ((Class) ((TypeVariable) atas[i]).getBounds()[0]); + } + } + } + } + } + Type moreType = ((ParameterizedType) superType).getRawType(); + if (moreType != Object.class) return getGenericType(type, moreType); + } + TypeVariable tv = (TypeVariable) type; + if (tv.getBounds().length == 1) return tv.getBounds()[0]; + } else if (type instanceof GenericArrayType) { + final Type rst = getGenericType(((GenericArrayType) type).getGenericComponentType(), declaringClass); + return (GenericArrayType) () -> rst; + } + if (type instanceof ParameterizedType) { + ParameterizedType pt = (ParameterizedType) type; + return createParameterizedType(getGenericType(pt.getOwnerType(), declaringClass), + getGenericType(pt.getRawType(), declaringClass), + getGenericType(pt.getActualTypeArguments(), declaringClass)); + } + return type; + } + + private static Type getType(Map map, Type type) { + Type one = map.get(type); + if (one == null) return type; + return getType(map, one); + } + + private static Map parseType(Map map, Class clzz) { + if (clzz == Object.class) return map; + Type superType = clzz.getGenericSuperclass(); + if (!(superType instanceof ParameterizedType)) return map; + ParameterizedType pt = (ParameterizedType) superType; + Type[] ptt = pt.getActualTypeArguments(); + Type superRaw = pt.getRawType(); + if (!(superRaw instanceof Class)) return map; + Class superClazz = (Class) superRaw; + TypeVariable[] scs = superClazz.getTypeParameters(); + if (scs.length != ptt.length) return map; + for (int i = 0; i < scs.length; i++) { + if (scs[i] == ptt[i]) continue; + map.put(scs[i], ptt[i]); + } + return parseType(map, clzz.getSuperclass()); + } + + /** + * 动态创建类型为ParameterizedType或Class的Type + * + * @param type 当前泛型 + * @param declaringType0 子类 + * + * @return Type + */ + public static Type createClassType(final Type type, final Type declaringType0) { + if (isClassType(type)) return type; + if (type instanceof ParameterizedType) { // e.g. Map + final ParameterizedType pt = (ParameterizedType) type; + final Type[] paramTypes = pt.getActualTypeArguments(); + for (int i = 0; i < paramTypes.length; i++) { + paramTypes[i] = createClassType(paramTypes[i], declaringType0); + } + return createParameterizedType(pt.getOwnerType(), pt.getRawType(), paramTypes); + } + Type declaringType = declaringType0; + if (declaringType instanceof Class) { + do { + declaringType = ((Class) declaringType).getGenericSuperclass(); + if (declaringType == Object.class) return Object.class; + } while (declaringType instanceof Class); + } + //存在通配符则declaringType 必须是 ParameterizedType + if (!(declaringType instanceof ParameterizedType)) return Object.class; + final ParameterizedType declaringPType = (ParameterizedType) declaringType; + final Type[] virTypes = ((Class) declaringPType.getRawType()).getTypeParameters(); + final Type[] desTypes = declaringPType.getActualTypeArguments(); + if (type instanceof WildcardType) { // e.g. + final WildcardType wt = (WildcardType) type; + for (Type f : wt.getUpperBounds()) { + for (int i = 0; i < virTypes.length; i++) { + if (virTypes[i].equals(f)) return desTypes.length <= i ? Object.class : desTypes[i]; + } + } + } else if (type instanceof TypeVariable) { // e.g. + for (int i = 0; i < virTypes.length; i++) { + if (virTypes[i].equals(type)) return desTypes.length <= i ? Object.class : desTypes[i]; + } + } + return type; + } + + /** + * 动态创建 ParameterizedType + * + * @param ownerType0 ParameterizedType 的 ownerType + * @param rawType0 ParameterizedType 的 rawType + * @param actualTypeArguments0 ParameterizedType 的 actualTypeArguments + * + * @return Type + */ + public static Type createParameterizedType(final Type ownerType0, final Type rawType0, final Type... actualTypeArguments0) { + if (ownerType0 == null && rawType0 instanceof Class) { + int count = 0; + for (Type t : actualTypeArguments0) { + if (isClassType(t)) count++; + } + if (count == actualTypeArguments0.length) return createParameterizedType0((Class) rawType0, actualTypeArguments0); + } + return new ParameterizedType() { + private final Class rawType = (Class) rawType0; + + private final Type ownerType = ownerType0; + + private final Type[] actualTypeArguments = actualTypeArguments0; + + @Override + public Type[] getActualTypeArguments() { + return actualTypeArguments.clone(); + } + + @Override + public Type getRawType() { + return rawType; + } + + @Override + public Type getOwnerType() { + return ownerType; + } + + @Override + public int hashCode() { + return Arrays.hashCode(actualTypeArguments) ^ Objects.hashCode(rawType) ^ Objects.hashCode(ownerType); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ParameterizedType)) return false; + final ParameterizedType that = (ParameterizedType) o; + if (this == that) return true; + return Objects.equals(ownerType, that.getOwnerType()) + && Objects.equals(rawType, that.getRawType()) + && Arrays.equals(actualTypeArguments, that.getActualTypeArguments()); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + if (ownerType != null) sb.append((ownerType instanceof Class) ? (((Class) ownerType).getName()) : ownerType.toString()).append("."); + sb.append(rawType.getName()); + + if (actualTypeArguments != null && actualTypeArguments.length > 0) { + sb.append("<"); + boolean first = true; + for (Type t : actualTypeArguments) { + if (!first) sb.append(", "); + sb.append(t); + first = false; + } + sb.append(">"); + } + return sb.toString(); + } + }; + } + + // 注意: RetResult[]> 这种泛型带[]的尚未实现支持 + private static synchronized Type createParameterizedType0(final Class rawType, final Type... actualTypeArguments) { + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + StringBuilder tmpps = new StringBuilder(getClassTypeDescriptor(rawType)); + for (Type cz : actualTypeArguments) { + tmpps.append(" ").append(getClassTypeDescriptor(cz)); + } + StringBuilder nsb = new StringBuilder(); + for (char ch : tmpps.toString().toCharArray()) { + if (ch >= '0' && ch <= '9') { + nsb.append(ch); + } else if (ch >= 'a' && ch <= 'z') { + nsb.append(ch); + } else if (ch >= 'A' && ch <= 'Z') { + nsb.append(ch); + } else { + nsb.append('_'); + } + } + nsb.append("_GenericType"); + final String newDynName = "org/redkaledyn/typetoken/_Dyn" + TypeToken.class.getSimpleName() + "_" + nsb.toString(); + try { + return loader.loadClass(newDynName.replace('/', '.')).getField("field").getGenericType(); + } catch (Throwable ex) { + } + // ------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodVisitor mv; + cw.visit(V11, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, "java/lang/Object", null); + String rawTypeDesc = org.redkale.asm.Type.getDescriptor(rawType); + StringBuilder sb = new StringBuilder(); + sb.append(rawTypeDesc.substring(0, rawTypeDesc.length() - 1)).append('<'); + for (Type c : actualTypeArguments) { + sb.append(getClassTypeDescriptor(c)); + } + sb.append(">;"); + { + fv = cw.visitField(ACC_PUBLIC, "field", rawTypeDesc, sb.toString(), null); + fv.visitEnd(); + } + {//构造方法 + mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + RedkaleClassLoader.putDynClass(newDynName.replace('/', '.'), bytes, newClazz); + RedkaleClassLoader.putReflectionPublicFields(newDynName.replace('/', '.')); + try { + return newClazz.getField("field").getGenericType(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + private static CharSequence getClassTypeDescriptor(Type type) { + if (!isClassType(type)) throw new IllegalArgumentException(type + " not a class type"); + if (type instanceof Class) return org.redkale.asm.Type.getDescriptor((Class) type); + if (type instanceof GenericArrayType) return getClassTypeDescriptor(((GenericArrayType) type).getGenericComponentType()) + "[]"; + final ParameterizedType pt = (ParameterizedType) type; + CharSequence rawTypeDesc = getClassTypeDescriptor(pt.getRawType()); + StringBuilder sb = new StringBuilder(); + sb.append(rawTypeDesc.subSequence(0, rawTypeDesc.length() - 1)).append('<'); + for (Type c : pt.getActualTypeArguments()) { + sb.append(getClassTypeDescriptor(c)); + } + sb.append(">;"); + return sb; + } +} diff --git a/src/main/java/org/redkale/util/Unsafe.java b/src/main/java/org/redkale/util/Unsafe.java new file mode 100644 index 000000000..b77c9dcde --- /dev/null +++ b/src/main/java/org/redkale/util/Unsafe.java @@ -0,0 +1,192 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.reflect.Field; + +/** + * sun.misc.Unsafe 封装类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + * @since 2.5.0 + */ +public interface Unsafe { + + public int getInt(Object o, long offset); + + public void putInt(Object o, long offset, int x); + + public Object getObject(Object o, long offset); + + public void putObject(Object o, long offset, Object x); + + public boolean getBoolean(Object o, long offset); + + public void putBoolean(Object o, long offset, boolean x); + + public byte getByte(Object o, long offset); + + public void putByte(Object o, long offset, byte x); + + public short getShort(Object o, long offset); + + public void putShort(Object o, long offset, short x); + + public char getChar(Object o, long offset); + + public void putChar(Object o, long offset, char x); + + public long getLong(Object o, long offset); + + public void putLong(Object o, long offset, long x); + + public float getFloat(Object o, long offset); + + public void putFloat(Object o, long offset, float x); + + public double getDouble(Object o, long offset); + + public void putDouble(Object o, long offset, double x); + + public byte getByte(long address); + + public void putByte(long address, byte x); + + public short getShort(long address); + + public void putShort(long address, short x); + + public char getChar(long address); + + public void putChar(long address, char x); + + public int getInt(long address); + + public void putInt(long address, int x); + + public long getLong(long address); + + public void putLong(long address, long x); + + public float getFloat(long address); + + public void putFloat(long address, float x); + + public double getDouble(long address); + + public void putDouble(long address, double x); + + public long getAddress(long address); + + public void putAddress(long address, long x); + + public long allocateMemory(long bytes); + + public long reallocateMemory(long address, long bytes); + + public void setMemory(Object o, long offset, long bytes, byte value); + + public void setMemory(long address, long bytes, byte value); + + public void copyMemory(Object srcBase, long srcOffset, Object destBase, long destOffset, long bytes); + + public void copyMemory(long srcAddress, long destAddress, long bytes); + + public void freeMemory(long address); + + public long objectFieldOffset(Field f); + + public long staticFieldOffset(Field f); + + public Object staticFieldBase(Field f); + + public int arrayBaseOffset(Class arrayClass); + + public int arrayIndexScale(Class arrayClass); + + public int addressSize(); + + public int pageSize(); + + public Object allocateInstance(Class cls) throws InstantiationException; + + public void throwException(Throwable ee); + + public boolean compareAndSwapObject(Object o, long offset, Object expected, Object x); + + public boolean compareAndSwapInt(Object o, long offset, int expected, int x); + + public boolean compareAndSwapLong(Object o, long offset, long expected, long x); + + public Object getObjectVolatile(Object o, long offset); + + public void putObjectVolatile(Object o, long offset, Object x); + + public int getIntVolatile(Object o, long offset); + + public void putIntVolatile(Object o, long offset, int x); + + public boolean getBooleanVolatile(Object o, long offset); + + public void putBooleanVolatile(Object o, long offset, boolean x); + + public byte getByteVolatile(Object o, long offset); + + public void putByteVolatile(Object o, long offset, byte x); + + public short getShortVolatile(Object o, long offset); + + public void putShortVolatile(Object o, long offset, short x); + + public char getCharVolatile(Object o, long offset); + + public void putCharVolatile(Object o, long offset, char x); + + public long getLongVolatile(Object o, long offset); + + public void putLongVolatile(Object o, long offset, long x); + + public float getFloatVolatile(Object o, long offset); + + public void putFloatVolatile(Object o, long offset, float x); + + public double getDoubleVolatile(Object o, long offset); + + public void putDoubleVolatile(Object o, long offset, double x); + + public void putOrderedObject(Object o, long offset, Object x); + + public void putOrderedInt(Object o, long offset, int x); + + public void putOrderedLong(Object o, long offset, long x); + + public void unpark(Object thread); + + public void park(boolean isAbsolute, long time); + + public int getLoadAverage(double[] loadavg, int nelems); + + public int getAndAddInt(Object o, long offset, int delta); + + public long getAndAddLong(Object o, long offset, long delta); + + public int getAndSetInt(Object o, long offset, int newValue); + + public long getAndSetLong(Object o, long offset, long newValue); + + public Object getAndSetObject(Object o, long offset, Object newValue); + + public void loadFence(); + + public void storeFence(); + + public void fullFence(); + + public void invokeCleaner(java.nio.ByteBuffer directBuffer); +} diff --git a/src/org/redkale/util/Utility.java b/src/main/java/org/redkale/util/Utility.java similarity index 58% rename from src/org/redkale/util/Utility.java rename to src/main/java/org/redkale/util/Utility.java index c5ac4c1ed..c87127cdd 100644 --- a/src/org/redkale/util/Utility.java +++ b/src/main/java/org/redkale/util/Utility.java @@ -1,2801 +1,2880 @@ -/* - * To change this template, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.CompletionHandler; -import java.nio.charset.*; -import static java.nio.charset.StandardCharsets.UTF_8; -import java.security.*; -import java.time.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.function.*; -import java.util.zip.GZIPInputStream; -import javax.net.ssl.*; - -/** - * - * 常见操作的工具类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class Utility { - - private static final int zoneRawOffset = TimeZone.getDefault().getRawOffset(); - - static final String format1 = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"; //yyyy-MM-dd HH:mm:ss - - static final String format2 = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL"; //yyyy-MM-dd HH:mm:ss.fff - - private static final char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; - - private static final boolean greatejdk8; - - private static final ScheduledThreadPoolExecutor futureDelayer; - - //复制JDK9+ java.util.concurrent.CompletableFuture.Canceller - private static final class FutureCanceller implements BiConsumer { - - final Future f; - - FutureCanceller(Future f) { - this.f = f; - } - - public void accept(Object ignore, Throwable ex) { - if (ex == null && f != null && !f.isDone()) - f.cancel(false); - } - } - //复制JDK9+ java.util.concurrent.CompletableFuture.Timeout - - private static final class FutureTimeout implements Runnable { - - final CompletableFuture f; - - final boolean or; - - final Object u; - - FutureTimeout(CompletableFuture f, boolean or, Object u) { - this.f = f; - this.or = or; - this.u = u; - } - - @Override - public void run() { - if (or) { - if (f != null) f.complete(u); - } else { - if (f != null && !f.isDone()) f.completeExceptionally(new TimeoutException()); - } - } - } - - /** - *

    -     * public final class AnonymousUnsafeObjectFunction implements java.util.function.Function<Object, Object> {
    -     *
    -     *      private final sun.misc.Unsafe unsafe;
    -     *
    -     *      private final long fd;
    -     *
    -     *      public AnonymousUnsafeObjectFunction(Object obj, long fd) {
    -     *          this.unsafe = (sun.misc.Unsafe) obj;
    -     *          this.fd = fd;
    -     *      }
    -     *
    -     *      @Override
    -     *      public Object apply(Object t) {
    -     *          return unsafe.getObject(t, fd);
    -     *      }
    -     *
    -     * }
    -     * 
    - */ - private static final String functionUnsafeObjectBinary = "cafebabe0000003400280a0007001d07001e090006001f09000600" - + "200a00020021070022070023070024010006756e736166650100114c73756e2f6d6973632f556e736166653b01000266640" - + "100014a0100063c696e69743e010016284c6a6176612f6c616e672f4f626a6563743b4a2956010004436f646501000f4c69" - + "6e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c65010004746869730100304c6f72672f7" - + "265646b616c652f7574696c2f416e6f6e796d6f7573556e736166654f626a65637446756e6374696f6e3b0100036f626a01" - + "00124c6a6176612f6c616e672f4f626a6563743b0100056170706c79010026284c6a6176612f6c616e672f4f626a6563743" - + "b294c6a6176612f6c616e672f4f626a6563743b010001740100095369676e61747572650100554c6a6176612f6c616e672f" - + "4f626a6563743b4c6a6176612f7574696c2f66756e6374696f6e2f46756e6374696f6e3c4c6a6176612f6c616e672f4f626" - + "a6563743b4c6a6176612f6c616e672f4f626a6563743b3e3b01000a536f7572636546696c65010022416e6f6e796d6f7573" - + "556e736166654f626a65637446756e6374696f6e2e6a6176610c000d002501000f73756e2f6d6973632f556e736166650c0" - + "009000a0c000b000c0c0026002701002e6f72672f7265646b616c652f7574696c2f416e6f6e796d6f7573556e736166654f" - + "626a65637446756e6374696f6e0100106a6176612f6c616e672f4f626a65637401001b6a6176612f7574696c2f66756e637" - + "4696f6e2f46756e6374696f6e0100032829560100096765744f626a656374010027284c6a6176612f6c616e672f4f626a65" - + "63743b4a294c6a6176612f6c616e672f4f626a6563743b00210006000700010008000200120009000a00000012000b000c0" - + "00000020001000d000e0001000f0000005c00030004000000122ab700012a2bc00002b500032a20b50004b1000000020010" - + "0000001200040000001200040013000c0014001100150011000000200003000000120012001300000000001200140015000" - + "100000012000b000c00020001001600170001000f00000041000400020000000d2ab400032b2ab40004b60005b000000002" - + "00100000000600010000001900110000001600020000000d0012001300000000000d0018001500010002001900000002001" - + "a001b00000002001c"; - - /** - *
    -     * public final class AnonymousUnsafeLongFunction implements java.util.function.ToLongFunction<Object> {
    -     *
    -     *      private final sun.misc.Unsafe unsafe;
    -     *
    -     *      private final long fd;
    -     *
    -     *      public AnonymousUnsafeLongFunction(Object obj, long fd) {
    -     *          this.unsafe = (sun.misc.Unsafe) obj;
    -     *          this.fd = fd;
    -     *      }
    -     *
    -     *      @Override
    -     *      public long applyAsLong(Object t) {
    -     *          return unsafe.getLong(t, fd);
    -     *      }
    -     *
    -     * }
    -     * 
    - */ - private static final String functionUnsafeLongBinary = "cafebabe0000003400280a0007001d07001e090006001f0900" - + "0600200a00020021070022070023070024010006756e736166650100114c73756e2f6d6973632f556e736166653b0100026" - + "6640100014a0100063c696e69743e010016284c6a6176612f6c616e672f4f626a6563743b4a2956010004436f646501000f" - + "4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c650100047468697301002e4c6f726" - + "72f7265646b616c652f7574696c2f416e6f6e796d6f7573556e736166654c6f6e6746756e6374696f6e3b0100036f626a01" - + "00124c6a6176612f6c616e672f4f626a6563743b01000b6170706c7941734c6f6e67010015284c6a6176612f6c616e672f4" - + "f626a6563743b294a010001740100095369676e61747572650100494c6a6176612f6c616e672f4f626a6563743b4c6a6176" - + "612f7574696c2f66756e6374696f6e2f546f4c6f6e6746756e6374696f6e3c4c6a6176612f6c616e672f4f626a6563743b3" - + "e3b01000a536f7572636546696c65010020416e6f6e796d6f7573556e736166654c6f6e6746756e6374696f6e2e6a617661" - + "0c000d002501000f73756e2f6d6973632f556e736166650c0009000a0c000b000c0c0026002701002c6f72672f7265646b6" - + "16c652f7574696c2f416e6f6e796d6f7573556e736166654c6f6e6746756e6374696f6e0100106a6176612f6c616e672f4f" - + "626a6563740100216a6176612f7574696c2f66756e6374696f6e2f546f4c6f6e6746756e6374696f6e01000328295601000" - + "76765744c6f6e67010016284c6a6176612f6c616e672f4f626a6563743b4a294a0021000600070001000800020012000900" - + "0a00000012000b000c000000020001000d000e0001000f0000005c00030004000000122ab700012a2bc00002b500032a20b" - + "50004b10000000200100000001200040000001200040013000c001400110015001100000020000300000012001200130000" - + "0000001200140015000100000012000b000c00020001001600170001000f00000041000400020000000d2ab400032b2ab40" - + "004b60005ad0000000200100000000600010000001900110000001600020000000d0012001300000000000d001800150001" - + "0002001900000002001a001b00000002001c"; - - /** - *
    -     * public final class AnonymousUnsafeByteBooleanFunction implements java.util.function.Predicate<Object> {
    -     *
    -     *      private final sun.misc.Unsafe unsafe;
    -     *
    -     *      private final long fd;
    -     *
    -     *      public AnonymousUnsafeByteBooleanFunction(Object obj, long fd) {
    -     *          this.unsafe = (sun.misc.Unsafe) obj;
    -     *          this.fd = fd;
    -     *      }
    -     *
    -     *      @Override
    -     *      public boolean test(Object t) {
    -     *          return unsafe.getByte(t, fd) == 0; //LATIN1:0  UTF16:1
    -     *      }
    -     *
    -     * }
    -     * 
    - */ - private static final String functionUnsafeByteBooleanBinary = "cafebabe0000003400290a0007001e07001f09000600" - + "2009000600210a00020022070023070024070025010006756e736166650100114c73756e2f6d6973632f556e736166653b01" - + "000266640100014a0100063c696e69743e010016284c6a6176612f6c616e672f4f626a6563743b4a2956010004436f646501" - + "000f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c65010004746869730100354c6f" - + "72672f7265646b616c652f7574696c2f416e6f6e796d6f7573556e7361666542797465426f6f6c65616e46756e6374696f6e" - + "3b0100036f626a0100124c6a6176612f6c616e672f4f626a6563743b01000474657374010015284c6a6176612f6c616e672f" - + "4f626a6563743b295a0100017401000d537461636b4d61705461626c650100095369676e61747572650100444c6a6176612f" - + "6c616e672f4f626a6563743b4c6a6176612f7574696c2f66756e6374696f6e2f5072656469636174653c4c6a6176612f6c61" - + "6e672f4f626a6563743b3e3b01000a536f7572636546696c65010027416e6f6e796d6f7573556e7361666542797465426f6f" - + "6c65616e46756e6374696f6e2e6a6176610c000d002601000f73756e2f6d6973632f556e736166650c0009000a0c000b000c" - + "0c002700280100336f72672f7265646b616c652f7574696c2f416e6f6e796d6f7573556e7361666542797465426f6f6c6561" - + "6e46756e6374696f6e0100106a6176612f6c616e672f4f626a65637401001c6a6176612f7574696c2f66756e6374696f6e2f" - + "50726564696361746501000328295601000767657442797465010016284c6a6176612f6c616e672f4f626a6563743b4a2942" - + "00210006000700010008000200120009000a00000012000b000c000000020001000d000e0001000f0000005c000300040000" - + "00122ab700012a2bc00002b500032a20b50004b10000000200100000001200040000001200040013000c0014001100150011" - + "000000200003000000120012001300000000001200140015000100000012000b000c00020001001600170001000f00000054" - + "00040002000000152ab400032b2ab40004b600059a000704a7000403ac000000030010000000060001000000190011000000" - + "160002000000150012001300000000001500180015000100190000000500021340010002001a00000002001b001c00000002" - + "001d"; - - private static final Function strByteFunction; - - private static final Function sbByteFunction; - - private static final Function strCharFunction; - - private static final Function sbCharFunction; - - private static final Predicate strLatin1Function; - - private static final ToLongFunction bufferAddrFunction; - - private static final javax.net.ssl.SSLContext DEFAULTSSL_CONTEXT; - - private static final javax.net.ssl.HostnameVerifier defaultVerifier = (s, ss) -> true; - - static { - - (futureDelayer = new ScheduledThreadPoolExecutor(1, r -> { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("Redkale-CompletableFutureDelayScheduler"); - return t; - })).setRemoveOnCancelPolicy(true); - - Function strCharFunction0 = null; - Function sbCharFunction0 = null; - Function strByteFunction0 = null; - Function sbByteFunction0 = null; - Predicate strLatin1Function0 = null; - ToLongFunction bufferAddrFunction0 = null; - boolean big8 = true; - try { - Field f = String.class.getDeclaredField("value"); - final Class unsafeClass = Class.forName("sun.misc.Unsafe"); - final Field safeField = unsafeClass.getDeclaredField("theUnsafe"); - safeField.setAccessible(true); - final Object usafe = safeField.get(null); - final Method fm = usafe.getClass().getMethod("objectFieldOffset", Field.class); - if (f.getType() == char[].class) { //JDK8及以下还是char[] - big8 = false; - final long fd1 = (Long) fm.invoke(usafe, f); - final long fd2 = (Long) fm.invoke(usafe, StringBuilder.class.getSuperclass().getDeclaredField("value")); - Class creatorClazz = (Class) new ClassLoader() { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass("org.re" + "dkale.util.AnonymousUnsafeObjectFunction", hexToBin(functionUnsafeObjectBinary)); - strCharFunction0 = (Function) creatorClazz.getDeclaredConstructor(Object.class, long.class).newInstance(usafe, fd1); - sbCharFunction0 = (Function) creatorClazz.getDeclaredConstructor(Object.class, long.class).newInstance(usafe, fd2); - } else { - final long fd1 = (Long) fm.invoke(usafe, f); - final long fd2 = (Long) fm.invoke(usafe, StringBuilder.class.getSuperclass().getDeclaredField("value")); - final long fd3 = (Long) fm.invoke(usafe, String.class.getDeclaredField("coder")); - final long fd4 = (Long) fm.invoke(usafe, Buffer.class.getDeclaredField("address")); - - Class creatorClazz = (Class) new ClassLoader() { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass("org.re" + "dkale.util.AnonymousUnsafeObjectFunction", hexToBin(functionUnsafeObjectBinary)); - - Class creatorClazz2 = (Class) new ClassLoader() { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass("org.re" + "dkale.util.AnonymousUnsafeByteBooleanFunction", hexToBin(functionUnsafeByteBooleanBinary)); - - Class creatorClazz3 = (Class) new ClassLoader() { - public final Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - }.loadClass("org.re" + "dkale.util.AnonymousUnsafeLongFunction", hexToBin(functionUnsafeLongBinary)); - - strByteFunction0 = (Function) creatorClazz.getDeclaredConstructor(Object.class, long.class).newInstance(usafe, fd1); - sbByteFunction0 = (Function) creatorClazz.getDeclaredConstructor(Object.class, long.class).newInstance(usafe, fd2); - strLatin1Function0 = (Predicate) creatorClazz2.getDeclaredConstructor(Object.class, long.class).newInstance(usafe, fd3); - bufferAddrFunction0 = (ToLongFunction) creatorClazz3.getDeclaredConstructor(Object.class, long.class).newInstance(usafe, fd4); - } - } catch (Throwable e) { //不会发生 - e.printStackTrace(); - } - greatejdk8 = big8; - strCharFunction = strCharFunction0; - sbCharFunction = sbCharFunction0; - strByteFunction = strByteFunction0; - sbByteFunction = sbByteFunction0; - strLatin1Function = strLatin1Function0; - bufferAddrFunction = bufferAddrFunction0; - - try { - DEFAULTSSL_CONTEXT = javax.net.ssl.SSLContext.getInstance("SSL"); - DEFAULTSSL_CONTEXT.init(null, new javax.net.ssl.TrustManager[]{new javax.net.ssl.X509TrustManager() { - @Override - public java.security.cert.X509Certificate[] getAcceptedIssuers() { - return null; - } - - @Override - public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { - } - - @Override - public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { - } - }}, null); - } catch (Exception e) { - throw new RuntimeException(e); //不会发生 - } - } - - private Utility() { - } - - //是否JDK9+ - public static boolean greaterJDK8() { - return greatejdk8; - } - - public static CompletableFuture orTimeout(CompletableFuture future, long timeout, TimeUnit unit) { - //if (greatejdk8) return future.orTimeout(timeout, unit); - return future.whenComplete(new FutureCanceller(futureDelayer.schedule(new FutureTimeout(future, false, null), timeout, unit))); - } - - public static CompletableFuture completeOnTimeout(CompletableFuture future, T value, long timeout, TimeUnit unit) { - //if (greatejdk8) return future.completeOnTimeout(value, timeout, unit); - return future.whenComplete(new FutureCanceller(futureDelayer.schedule(new FutureTimeout(future, true, value), timeout, unit))); - } - - /** - * 将多个key:value的字符串键值对组合成一个Map,items长度必须是偶数, 参数个数若是奇数的话,最后一个会被忽略 - * 类似 JDK9中的 Map.of 方法 - * - * @param items 键值对 - * - * @return Map - */ - public static HashMap ofMap(String... items) { - HashMap map = new LinkedHashMap<>(Math.max(1, items.length / 2)); - int len = items.length / 2; - for (int i = 0; i < len; i++) { - map.put(items[i * 2], items[i * 2 + 1]); - } - return map; - } - - /** - * 将多个key:value对应值组合成一个Map,items长度必须是偶数, 参数个数若是奇数的话,最后一个会被忽略 - * 类似 JDK9中的 Map.of 方法 - * - * @param 泛型 - * @param 泛型 - * @param items 键值对 - * - * @return Map - */ - public static HashMap ofMap(Object... items) { - HashMap map = new LinkedHashMap<>(Math.max(1, items.length / 2)); - int len = items.length / 2; - for (int i = 0; i < len; i++) { - map.put((K) items[i * 2], (V) items[i * 2 + 1]); - } - return map; - } - - /** - * 将多个Map合并成一个Map - * - * @param 泛型 - * @param 泛型 - * @param maps Map - * - * @return Map - */ - public static Map merge(Map... maps) { - Map map = null; - for (Map m : maps) { - if (map == null) { - map = m; - } else if (m != null) { - map.putAll(m); - } - } - return map; - } - - /** - * 将多个元素组合成一个Set - * - * @param 泛型 - * @param items 元素 - * - * @return Set - */ - public static Set ofSet(T... items) { - Set set = new LinkedHashSet<>(items.length); - for (T item : items) set.add(item); - return set; - } - - /** - * 将多个元素组合成一个List
    - * 类似 JDK9中的 List.of 方法 - * - * @param 泛型 - * @param items 元素 - * - * @return List - */ - public static List ofList(T... items) { - List list = new ArrayList<>(items.length); - for (T item : items) list.add(item); - return list; - } - - /** - * 将多个元素组合成一个Array - * - * @param 泛型 - * @param items 元素 - * - * @return Array - */ - public static T[] ofArray(T... items) { - return items; - } - - /** - * 裁剪List,使其size不超过limit大小
    - * - * @param list 集合 - * @param limit 大小 - * - * @return List - */ - public static List limit(List list, int limit) { - if (list == null || list.isEmpty() || list.size() <= limit) return list; - return list.subList(0, limit); - } - - /** - * 获取不带"-"的UUID值 - * - * @return 不带"-"UUID值 - */ - public static String uuid() { - return UUID.randomUUID().toString().replace("-", ""); - } - - /** - * 将一个或多个新元素添加到数组开始,数组中的元素自动后移 - * - * @param 泛型 - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static T[] unshift(final T[] array, final T... objs) { - if (array == null || array.length == 0) return objs; - final T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.length); - System.arraycopy(objs, 0, news, 0, objs.length); - System.arraycopy(array, 0, news, objs.length, array.length); - return news; - } - - /** - * 将一个或多个新元素添加到数组开始,数组中的元素自动后移 - * - * @param 泛型 - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static T[] unshift(final T[] array, final Collection objs) { - if (objs == null || objs.isEmpty()) return array; - if (array == null) { - T one = null; - for (T t : objs) { - if (t != null) one = t; - break; - } - if (one == null) return array; - T[] news = (T[]) Array.newInstance(one.getClass(), objs.size()); - return objs.toArray(news); - } - T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.size()); - int index = -1; - for (T t : objs) { - news[(++index)] = t; - } - System.arraycopy(array, 0, news, objs.size(), array.length); - return news; - } - - /** - * 获取int数组之和, 空数组返回0 - * - * @param array 数组 - * - * @return int - */ - public static int sum(final int... array) { - return sum(false, array); - } - - /** - * 获取int数组之和 - * - * @param check 是否检测空 - * @param array 数组 - * - * @return int - */ - public static int sum(boolean check, final int... array) { - if (array == null || array.length == 0) { - if (!check) return 0; - throw new NullPointerException("array is null or empty"); - } - int sum = 0; - for (int i : array) { - sum += i; - } - return sum; - } - - /** - * 获取long数组之和, 空数组返回0 - * - * @param array 数组 - * - * @return long - */ - public static long sum(final long... array) { - return sum(false, array); - } - - /** - * 获取long数组之和 - * - * @param check 是否检测空 - * @param array 数组 - * - * @return long - */ - public static long sum(boolean check, final long... array) { - if (array == null || array.length == 0) { - if (!check) return 0; - throw new NullPointerException("array is null or empty"); - } - long sum = 0L; - for (long i : array) { - sum += i; - } - return sum; - } - - /** - * 获取int数组最大值 - * - * @param array 数组 - * - * @return int - */ - public static int max(final int... array) { - if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); - int max = array[0]; - for (int i : array) { - if (i > max) i = max; - } - return max; - } - - /** - * 获取long数组最大值 - * - * @param array 数组 - * - * @return long - */ - public static long max(final long... array) { - if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); - long max = array[0]; - for (long i : array) { - if (i > max) i = max; - } - return max; - } - - /** - * 获取int数组最小值 - * - * @param array 数组 - * - * @return int - */ - public static long min(final int... array) { - if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); - int min = array[0]; - for (int i : array) { - if (i < min) i = min; - } - return min; - } - - /** - * 获取long数组最小值 - * - * @param array 数组 - * - * @return long - */ - public static long min(final long... array) { - if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); - long min = array[0]; - for (long i : array) { - if (i < min) i = min; - } - return min; - } - - /** - * 将char数组用分隔符拼接成字符串 - * - * @param array 数组 - * @param delimiter 分隔符 - * - * @return String - */ - public static String joining(final char[] array, final String delimiter) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (char i : array) { - if (sb.length() > 0) sb.append(delimiter); - sb.append(i); - } - return sb.toString(); - } - - /** - * 将int数组用分隔符拼接成字符串 - * - * @param array 数组 - * @param delimiter 分隔符 - * - * @return String - */ - public static String joining(final int[] array, final String delimiter) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (int i : array) { - if (sb.length() > 0) sb.append(delimiter); - sb.append(i); - } - return sb.toString(); - } - - /** - * 将long数组用分隔符拼接成字符串 - * - * @param array 数组 - * @param delimiter 分隔符 - * - * @return String - */ - public static String joining(final long[] array, final String delimiter) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (long i : array) { - if (sb.length() > 0) sb.append(delimiter); - sb.append(i); - } - return sb.toString(); - } - - /** - * 将对象数组用分隔符拼接成字符串 - * - * @param 泛型 - * @param array 数组 - * @param delimiter 分隔符 - * - * @return String - */ - public static String joining(final T[] array, final String delimiter) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (T i : array) { - if (sb.length() > 0) sb.append(delimiter); - sb.append(i); - } - return sb.toString(); - } - - /** - * 将对象数组用分隔符拼接成字符串 - * - * @param 泛型 - * @param array 数组 - * @param delimiter 分隔符 - * - * @return String - */ - public static String joining(final String[] array, final char delimiter) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (String i : array) { - if (sb.length() > 0) sb.append(delimiter); - sb.append(i); - } - return sb.toString(); - } - - /** - * 将对象数组用分隔符拼接成字符串 - * - * @param 泛型 - * @param array 数组 - * @param delimiter 分隔符 - * - * @return String - */ - public static String joiningHex(final byte[] array, final char delimiter) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - for (byte i : array) { - if (sb.length() > 0) sb.append(delimiter); - String s = Integer.toHexString(i & 0xff); - sb.append(s.length() > 1 ? "0x" : "0x0").append(s); - } - return sb.toString(); - } - - /** - * 将对象数组用分隔符拼接成字符串 - * - * @param 泛型 - * @param array 数组 - * @param offset 偏移量 - * @param length 长度 - * @param delimiter 分隔符 - * - * @return String - */ - public static String joiningHex(final byte[] array, int offset, int length, final char delimiter) { - if (array == null || array.length == 0) return ""; - StringBuilder sb = new StringBuilder(); - int len = offset + length; - for (int i = offset; i < len; i++) { - if (sb.length() > 0) sb.append(delimiter); - String s = Integer.toHexString(array[i] & 0xff); - sb.append(s.length() > 1 ? "0x" : "0x0").append(s); - } - return sb.toString(); - } - - /** - * 将一个或多个byte新元素添加到byte数组结尾 - * - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static byte[] append(final byte[] array, final byte... objs) { - if (array == null || array.length == 0) return objs; - if (objs == null || objs.length == 0) return array; - final byte[] news = new byte[array.length + objs.length]; - System.arraycopy(array, 0, news, 0, array.length); - System.arraycopy(objs, 0, news, array.length, objs.length); - return news; - } - - /** - * 将一个或多个byte新元素添加到byte数组结尾 - * - * @param array 原数组 - * @param objs 待追加数据 - * @param offset 待追加数据偏移量 - * @param length 待追加数据的长度 - * - * @return 新数组 - */ - public static byte[] append(final byte[] array, final byte[] objs, int offset, int length) { - if (array == null || array.length == 0) { - if (objs != null && offset == 0 && objs.length == length) return objs; - final byte[] news = new byte[length]; - System.arraycopy(objs, 0, news, 0, length); - return news; - } - if (objs == null || length == 0) return array; - final byte[] news = new byte[array.length + length]; - System.arraycopy(array, 0, news, 0, array.length); - System.arraycopy(objs, offset, news, array.length, length); - return news; - } - - /** - * 将一个或多个short新元素添加到short数组结尾 - * - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static short[] append(final short[] array, final short... objs) { - if (array == null || array.length == 0) return objs; - if (objs == null || objs.length == 0) return array; - final short[] news = new short[array.length + objs.length]; - System.arraycopy(array, 0, news, 0, array.length); - System.arraycopy(objs, 0, news, array.length, objs.length); - return news; - } - - /** - * 将一个或多个char新元素添加到char数组结尾 - * - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static char[] append(final char[] array, final char... objs) { - if (array == null || array.length == 0) return objs; - if (objs == null || objs.length == 0) return array; - final char[] news = new char[array.length + objs.length]; - System.arraycopy(array, 0, news, 0, array.length); - System.arraycopy(objs, 0, news, array.length, objs.length); - return news; - } - - /** - * 将一个或多个int新元素添加到int数组结尾 - * - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static int[] append(final int[] array, final int... objs) { - if (array == null || array.length == 0) return objs; - if (objs == null || objs.length == 0) return array; - final int[] news = new int[array.length + objs.length]; - System.arraycopy(array, 0, news, 0, array.length); - System.arraycopy(objs, 0, news, array.length, objs.length); - return news; - } - - /** - * 将一个或多个long新元素添加到long数组结尾 - * - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static long[] append(final long[] array, final long... objs) { - if (array == null || array.length == 0) return objs; - if (objs == null || objs.length == 0) return array; - final long[] news = new long[array.length + objs.length]; - System.arraycopy(array, 0, news, 0, array.length); - System.arraycopy(objs, 0, news, array.length, objs.length); - return news; - } - - /** - * 将一个或多个新元素添加到数组结尾 - * - * @param 泛型 - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static T[] append(final T[] array, final T... objs) { - if (array == null || array.length == 0) return objs; - if (objs == null || objs.length == 0) return array; - final T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.length); - System.arraycopy(array, 0, news, 0, array.length); - System.arraycopy(objs, 0, news, array.length, objs.length); - return news; - } - - /** - * 将一个或多个新元素添加到数组结尾 - * - * @param 泛型 - * @param array 原数组 - * @param objs 待追加数据 - * - * @return 新数组 - */ - public static T[] append(final T[] array, final Collection objs) { - if (objs == null || objs.isEmpty()) return array; - if (array == null) { - T one = null; - for (T t : objs) { - if (t != null) one = t; - break; - } - if (one == null) return array; - T[] news = (T[]) Array.newInstance(one.getClass(), objs.size()); - return objs.toArray(news); - } - T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.size()); - System.arraycopy(array, 0, news, 0, array.length); - int index = -1; - for (T t : objs) { - news[array.length + (++index)] = t; - } - return news; - } - - /** - * 将int数组倒序 - * - * @param array 原数组 - * - * @return 新数组 - */ - public static int[] reverseSort(final int[] array) { - if (array == null || array.length == 0) return array; - return Arrays.stream(array).boxed().sorted(Collections.reverseOrder()).mapToInt(x -> x).toArray(); - } - - /** - * 将long数组倒序 - * - * @param array 原数组 - * - * @return 新数组 - */ - public static long[] reverseSort(final long[] array) { - if (array == null || array.length == 0) return array; - return Arrays.stream(array).boxed().sorted(Collections.reverseOrder()).mapToLong(x -> x).toArray(); - } - - /** - * 将元素从数组中删除 - * - * @param 泛型 - * @param array 原数组 - * @param item 元素 - * - * @return 新数组 - */ - public static T[] remove(final T[] array, final T item) { - return remove(array, (i) -> Objects.equals(i, item)); - } - - /** - * 将符合条件的元素从数组中删除 - * - * @param 泛型 - * @param array 原数组 - * @param filter Predicate - * - * @return 新数组 - */ - public static T[] remove(final T[] array, final Predicate filter) { - if (array == null || array.length == 0 || filter == null) return array; - final T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length); - int index = 0; - for (int i = 0; i < news.length; i++) { - if (!filter.test(array[i])) { - news[index++] = array[i]; - } - } - if (index == array.length) return array; - final T[] rs = (T[]) Array.newInstance(array.getClass().getComponentType(), index); - System.arraycopy(news, 0, rs, 0, index); - return rs; - } - - /** - * 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除
    - * 例如:
    - * remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    - * - * @param array 原数组 - * @param items short[] - * - * @return 新数组 - */ - public static short[] removeMatch(final short[] array, final short... items) { - return remove(array, false, items); - } - - /** - * 将指定的int元素从数组中删除, repeat=true时相同的元素会根据items里重复次数来执行删除
    - * 例如:
    - * remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = []
    - * remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3] - * - * @param array 原数组 - * @param repeat 是否重复删除相同的元素 - * @param items short[] - * - * @return 新数组 - */ - public static short[] remove(final short[] array, boolean repeat, final short... items) { - if (array == null || array.length == 0 || items == null || items.length == 0) return array; - final short[] news = new short[array.length]; - short[] subs = items; - int index = 0; - for (int i = 0; i < news.length; i++) { - if (subs.length > 0 && contains(subs, array[i])) { - if (!repeat) { - short[] newsubs = new short[subs.length - 1]; - int k = 0; - boolean done = false; - for (short v : subs) { - if (done) { - newsubs[k++] = v; - } else if (v == array[i]) { - done = true; - } else { - newsubs[k++] = v; - } - } - subs = newsubs; - } - } else { - news[index++] = array[i]; - } - } - if (index == array.length) return array; - final short[] rs = new short[index]; - System.arraycopy(news, 0, rs, 0, index); - return rs; - } - - /** - * 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除
    - * 例如:
    - * remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    - * - * @param array 原数组 - * @param items int[] - * - * @return 新数组 - */ - public static int[] removeMatch(final int[] array, final int... items) { - return remove(array, false, items); - } - - /** - * 将指定的int元素从数组中删除, repeat=false时相同的元素会根据items里重复次数来执行删除
    - * 例如:
    - * remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = []
    - * remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3] - * - * @param array 原数组 - * @param repeat 是否重复删除相同的元素 - * @param items int[] - * - * @return 新数组 - */ - public static int[] remove(final int[] array, boolean repeat, final int... items) { - if (array == null || array.length == 0 || items == null || items.length == 0) return array; - final int[] news = new int[array.length]; - int[] subs = items; - int index = 0; - for (int i = 0; i < news.length; i++) { - if (subs.length > 0 && contains(subs, array[i])) { - if (!repeat) { - int[] newsubs = new int[subs.length - 1]; - int k = 0; - boolean done = false; - for (int v : subs) { - if (done) { - newsubs[k++] = v; - } else if (v == array[i]) { - done = true; - } else { - newsubs[k++] = v; - } - } - subs = newsubs; - } - } else { - news[index++] = array[i]; - } - } - if (index == array.length) return array; - final int[] rs = new int[index]; - System.arraycopy(news, 0, rs, 0, index); - return rs; - } - - /** - * 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除
    - * 例如:
    - * remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    - * - * @param array 原数组 - * @param items long[] - * - * @return 新数组 - */ - public static long[] removeMatch(final long[] array, final long... items) { - return remove(array, false, items); - } - - /** - * 将指定的long元素从数组中删除, repeat=false时相同的元素会根据items里重复次数来执行删除
    - * 例如:
    - * remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = []
    - * remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    - * - * @param array 原数组 - * @param repeat 是否重复删除相同的元素 - * @param items long[] - * - * @return 新数组 - */ - public static long[] remove(final long[] array, boolean repeat, final long... items) { - if (array == null || array.length == 0 || items == null || items.length == 0) return array; - final long[] news = new long[array.length]; - long[] subs = items; - int index = 0; - for (int i = 0; i < news.length; i++) { - if (subs.length > 0 && contains(subs, array[i])) { - if (!repeat) { - long[] newsubs = new long[subs.length - 1]; - int k = 0; - boolean done = false; - for (long v : subs) { - if (done) { - newsubs[k++] = v; - } else if (v == array[i]) { - done = true; - } else { - newsubs[k++] = v; - } - } - subs = newsubs; - } - } else { - news[index++] = array[i]; - } - } - if (index == array.length) return array; - final long[] rs = new long[index]; - System.arraycopy(news, 0, rs, 0, index); - return rs; - } - - /** - * 判断字符串是否包含指定的字符,包含返回true - * - * @param string 字符串 - * @param values 字符集合 - * - * @return boolean - */ - public static boolean contains(String string, char... values) { - if (string == null) return false; - for (char ch : Utility.charArray(string)) { - for (char ch2 : values) { - if (ch == ch2) return true; - } - } - return false; - } - - /** - * 判断指定值是否包含指定的数组中,包含返回true - * - * @param values 集合 - * @param value 单值 - * - * @return boolean - */ - public static boolean contains(char[] values, char value) { - if (values == null) return false; - for (char v : values) { - if (v == value) return true; - } - return false; - } - - /** - * 判断指定值是否包含指定的数组中,包含返回true - * - * @param values 集合 - * @param value 单值 - * - * @return boolean - */ - public static boolean contains(short[] values, short value) { - if (values == null) return false; - for (short v : values) { - if (v == value) return true; - } - return false; - } - - /** - * 判断指定值(不要包含相同的元素)是否包含指定的数组中,包含返回true - * - * @param values 集合 - * @param items 多值 - * - * @return boolean - */ - public static boolean contains(short[] values, short... items) { - if (values == null) return false; - for (short item : items) { - if (!contains(values, item)) return false; - } - return true; - } - - /** - * 判断指定值是否包含指定的数组中,包含返回true - * - * @param values 集合 - * @param value 单值 - * - * @return boolean - */ - public static boolean contains(int[] values, int value) { - if (values == null) return false; - for (int v : values) { - if (v == value) return true; - } - return false; - } - - /** - * 判断指定值(不要包含相同的元素)是否包含指定的数组中,包含返回true - * - * @param values 集合 - * @param items 多值 - * - * @return boolean - */ - public static boolean contains(int[] values, int... items) { - if (values == null) return false; - for (int item : items) { - if (!contains(values, item)) return false; - } - return true; - } - - /** - * 判断指定值是否包含指定的数组中,包含返回true - * - * @param values 集合 - * @param value 单值 - * - * @return boolean - */ - public static boolean contains(long[] values, long value) { - if (values == null) return false; - for (long v : values) { - if (v == value) return true; - } - return false; - } - - /** - * 判断指定值(不要包含相同的元素)是否包含指定的数组中,包含返回true - * - * @param values 集合 - * @param items 多值 - * - * @return boolean - */ - public static boolean contains(long[] values, long... items) { - if (values == null) return false; - for (long item : items) { - if (!contains(values, item)) return false; - } - return true; - } - - /** - * 判断指定值是否包含指定的数组中,包含返回true - * - * @param 泛型 - * @param values 集合 - * @param value 单值 - * - * @return boolean - */ - public static boolean contains(T[] values, T value) { - if (values == null) return false; - for (T v : values) { - if (Objects.equals(v, value)) return true; - } - return false; - } - - /** - * 将指定的short元素是否数组中完全包含,重复元素的次数也要相同
    - * 例如:
    - * containsMatch(new short[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true
    - * containsMatch(new short[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false
    - * - * @param array 原数组 - * @param items short[] - * - * @return 是否完全包含 - */ - public static boolean containsMatch(final short[] array, final short... items) { - if (array == null) return false; - if (items == null || items.length == 0) return true; - if (array.length == 0 && items.length == 0) return true; - if (array.length < items.length) return false; - - short[] subs = array; - for (short item : items) { - if (!contains(subs, item)) return false; - short[] newsubs = new short[subs.length - 1]; - int k = 0; - boolean done = false; - for (short v : subs) { - if (done) { - newsubs[k++] = v; - } else if (v == item) { - done = true; - } else { - newsubs[k++] = v; - } - } - subs = newsubs; - } - return true; - } - - /** - * 将指定的int元素是否数组中完全包含,重复元素的次数也要相同
    - * 例如:
    - * containsMatch(new int[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true
    - * containsMatch(new int[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false
    - * - * @param array 原数组 - * @param items int[] - * - * @return 是否完全包含 - */ - public static boolean containsMatch(final int[] array, final int... items) { - if (array == null) return false; - if (items == null || items.length == 0) return true; - if (array.length == 0 && items.length == 0) return true; - if (array.length < items.length) return false; - - int[] subs = array; - for (int item : items) { - if (!contains(subs, item)) return false; - int[] newsubs = new int[subs.length - 1]; - int k = 0; - boolean done = false; - for (int v : subs) { - if (done) { - newsubs[k++] = v; - } else if (v == item) { - done = true; - } else { - newsubs[k++] = v; - } - } - subs = newsubs; - } - return true; - } - - /** - * 将指定的long元素是否数组中完全包含,重复元素的次数也要相同
    - * 例如:
    - * containsMatch(new long[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true
    - * containsMatch(new long[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false
    - * - * @param array 原数组 - * @param items long[] - * - * @return 是否完全包含 - */ - public static boolean containsMatch(final long[] array, final long... items) { - if (array == null) return false; - if (items == null || items.length == 0) return true; - if (array.length == 0 && items.length == 0) return true; - if (array.length < items.length) return false; - - long[] subs = array; - for (long item : items) { - if (!contains(subs, item)) return false; - long[] newsubs = new long[subs.length - 1]; - int k = 0; - boolean done = false; - for (long v : subs) { - if (done) { - newsubs[k++] = v; - } else if (v == item) { - done = true; - } else { - newsubs[k++] = v; - } - } - subs = newsubs; - } - return true; - } - - /** - * 删除掉字符串数组中包含指定的字符串 - * - * @param columns 待删除数组 - * @param cols 需排除的字符串 - * - * @return 新字符串数组 - */ - public static String[] exclude(final String[] columns, final String... cols) { - if (columns == null || columns.length == 0 || cols == null || cols.length == 0) return columns; - int count = 0; - for (String column : columns) { - boolean flag = false; - for (String col : cols) { - if (column != null && column.equals(col)) { - flag = true; - break; - } - } - if (flag) count++; - } - if (count == 0) return columns; - if (count == columns.length) return new String[0]; - final String[] newcols = new String[columns.length - count]; - count = 0; - for (String column : columns) { - boolean flag = false; - for (String col : cols) { - if (column != null && column.equals(col)) { - flag = true; - break; - } - } - if (!flag) newcols[count++] = column; - } - return newcols; - } - - /** - * 将buffer的内容转换成字符串, string参数不为空时会追加在buffer内容字符串之前 - * - * @param string 字符串前缀 - * @param buffer ByteBuffer - * - * @return 字符串 - */ - public static String toString(String string, ByteBuffer buffer) { - if (buffer == null || !buffer.hasRemaining()) return string; - int pos = buffer.position(); - int limit = buffer.limit(); - byte[] bytes = new byte[buffer.remaining()]; - buffer.get(bytes); - buffer.position(pos); - buffer.limit(limit); - if (string == null) return new String(bytes, UTF_8); - return string + new String(bytes, UTF_8); - } - - /** - * 将buffer的内容转换成字符串并打印到控制台, string参数不为空时会追加在buffer内容字符串之前 - * - * @param string 字符串前缀 - * @param buffer ByteBuffer - * - */ - public static void println(String string, ByteBuffer buffer) { - if (buffer == null || !buffer.hasRemaining()) return; - int pos = buffer.position(); - int limit = buffer.limit(); - byte[] bytes = new byte[buffer.remaining()]; - buffer.get(bytes); - buffer.position(pos); - buffer.limit(limit); - println(string, bytes); - } - - /** - * 将字节数组的内容转换成字符串并打印到控制台, string参数不为空时会追加在字节数组内容字符串之前 - * - * @param string 字符串前缀 - * @param bytes 字节数组 - * - */ - public static void println(String string, byte... bytes) { - if (bytes == null) return; - StringBuilder sb = new StringBuilder(); - if (string != null) sb.append(string); - sb.append(bytes.length).append(".["); - boolean last = false; - for (byte b : bytes) { - if (last) sb.append(','); - int v = b & 0xff; - sb.append("0x"); - if (v < 16) sb.append('0'); - sb.append(Integer.toHexString(v)); - last = true; - } - sb.append(']'); - (System.out).println(sb); - } - - /** - * 返回本机的第一个内网IPv4地址, 没有则返回null - * - * @return IPv4地址 - */ - public static InetAddress localInetAddress() { - InetAddress back = null; - try { - Enumeration nifs = NetworkInterface.getNetworkInterfaces(); - while (nifs.hasMoreElements()) { - NetworkInterface nif = nifs.nextElement(); - if (!nif.isUp()) continue; - Enumeration eis = nif.getInetAddresses(); - while (eis.hasMoreElements()) { - InetAddress ia = eis.nextElement(); - if (ia.isLoopbackAddress() && ia instanceof Inet4Address) back = ia; - if (ia.isSiteLocalAddress() && ia instanceof Inet4Address) return ia; - } - } - } catch (Exception e) { - e.printStackTrace(); - } - return back; - } - - /** - * 创建 CompletionHandler 对象 - * - * @param 结果对象的泛型 - * @param 附件对象的泛型 - * @param success 成功的回调函数 - * @param fail 失败的回调函数 - * - * @return CompletionHandler - */ - public static CompletionHandler createAsyncHandler(final BiConsumer success, final BiConsumer fail) { - return new CompletionHandler() { - @Override - public void completed(V result, A attachment) { - if (success != null) success.accept(result, attachment); - } - - @Override - public void failed(Throwable exc, A attachment) { - if (fail != null) fail.accept(exc, attachment); - } - }; - } - - /** - * 创建没有返回结果的 CompletionHandler 对象 - * - * @param 附件对象的泛型 - * @param success 成功的回调函数 - * @param fail 失败的回调函数 - * - * @return CompletionHandler - */ - public static CompletionHandler createAsyncHandler(final Consumer success, final BiConsumer fail) { - return new CompletionHandler() { - @Override - public void completed(Void result, A attachment) { - if (success != null) success.accept(attachment); - } - - @Override - public void failed(Throwable exc, A attachment) { - if (fail != null) fail.accept(exc, attachment); - } - }; - } - - /** - * 创建没有附件对象的 CompletionHandler 对象 - * - * @param 结果对象的泛型 - * @param success 成功的回调函数 - * @param fail 失败的回调函数 - * - * @return CompletionHandler - */ - public static CompletionHandler createAsyncHandler(final Consumer success, final Consumer fail) { - return new CompletionHandler() { - @Override - public void completed(V result, Void attachment) { - if (success != null) success.accept(result); - } - - @Override - public void failed(Throwable exc, Void attachment) { - if (fail != null) fail.accept(exc); - } - }; - } - - /** - * 获取格式为yyyy-MM-dd HH:mm:ss的当前时间 - * - * @return 格式为yyyy-MM-dd HH:mm:ss的时间值 - */ - public static String now() { - return String.format(format1, System.currentTimeMillis()); - } - - /** - * 获取格式为yyyy-MM-dd HH:mm:ss.fff的当前时间 - * - * @return 格式为yyyy-MM-dd HH:mm:ss.fff的时间值 - */ - public static String nowMillis() { - return String.format(format2, System.currentTimeMillis()); - } - - /** - * 将指定时间格式化为 yyyy-MM-dd HH:mm:ss - * - * @param time 待格式化的时间 - * - * @return 格式为yyyy-MM-dd HH:mm:ss的时间值 - */ - public static String formatTime(long time) { - return String.format(format1, time); - } - - /** - * 将时间值转换为长度为9的36进制值 - * - * @param time 时间值 - * - * @return 36进制时间值 - */ - public static String format36time(long time) { - return Long.toString(time, 36); - } - - /** - * 获取当天凌晨零点的格林时间 - * - * @return 毫秒数 - */ - public static long midnight() { - return midnight(System.currentTimeMillis()); - } - - /** - * 获取指定时间当天凌晨零点的格林时间 - * - * @param time 指定时间 - * - * @return 毫秒数 - */ - public static long midnight(long time) { - return (time + zoneRawOffset) / 86400000 * 86400000 - zoneRawOffset; - } - - /** - * 获取当天20151231格式的int值 - * - * @return 20151231格式的int值 - */ - public static int today() { - java.time.LocalDate today = java.time.LocalDate.now(); - return today.getYear() * 10000 + today.getMonthValue() * 100 + today.getDayOfMonth(); - } - - /** - * 获取当天151231格式的int值 - * - * @return 151231格式的int值 - */ - public static int todayYYMMDD() { - java.time.LocalDate today = java.time.LocalDate.now(); - return today.getYear() % 100 * 10000 + today.getMonthValue() * 100 + today.getDayOfMonth(); - } - - /** - * 获取当天1512312359格式的int值 - * - * @return 1512312359格式的int值 - */ - public static int todayYYMMDDHHmm() { - java.time.LocalDateTime today = java.time.LocalDateTime.now(); - return today.getYear() % 100 * 100_00_00_00 + today.getMonthValue() * 100_00_00 + today.getDayOfMonth() * 100_00 - + today.getHour() * 100 + today.getMinute(); - } - - /** - * 获取当天20151231235959格式的int值 - * - * @return 20151231235959格式的int值 - */ - public static long todayYYYYMMDDHHmmss() { - java.time.LocalDateTime today = java.time.LocalDateTime.now(); - return today.getYear() * 100_00_00_00_00L + today.getMonthValue() * 100_00_00_00 + today.getDayOfMonth() * 100_00_00 - + today.getHour() * 100_00 + today.getMinute() * 100 + today.getSecond(); - } - - /** - * 获取当天151231235959格式的int值 - * - * @return 151231235959格式的int值 - */ - public static long todayYYMMDDHHmmss() { - java.time.LocalDateTime today = java.time.LocalDateTime.now(); - return today.getYear() % 100 * 100_00_00_00_00L + today.getMonthValue() * 100_00_00_00 + today.getDayOfMonth() * 100_00_00 - + today.getHour() * 100_00 + today.getMinute() * 100 + today.getSecond(); - } - - /** - * 获取明天20151230格式的int值 - * - * @return 20151230格式的int值 - */ - public static int tomorrow() { - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.DAY_OF_YEAR, 1); - return cal.get(Calendar.YEAR) * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); - } - - /** - * 获取明天151230格式的int值 - * - * @return 151230格式的int值 - */ - public static int tomorrowYYMMDD() { - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.DAY_OF_YEAR, 1); - return cal.get(Calendar.YEAR) % 100 * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); - } - - /** - * 获取昨天20151230格式的int值 - * - * @return 20151230格式的int值 - */ - public static int yesterday() { - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.DAY_OF_YEAR, -1); - return cal.get(Calendar.YEAR) * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); - } - - /** - * 获取昨天151230格式的int值 - * - * @return 151230格式的int值 - */ - public static int yesterdayYYMMDD() { - Calendar cal = Calendar.getInstance(); - cal.add(Calendar.DAY_OF_YEAR, -1); - return cal.get(Calendar.YEAR) % 100 * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); - } - - /** - * 获取指定时间的20160202格式的int值 - * - * @param time 指定时间 - * - * @return 毫秒数 - */ - public static int yyyyMMdd(long time) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(time); - return cal.get(Calendar.YEAR) * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); - } - - /** - * 获取指定时间的160202格式的int值 - * - * @param time 指定时间 - * - * @return 毫秒数 - */ - public static int yyMMdd(long time) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(time); - return cal.get(Calendar.YEAR) % 100 * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); - } - - /** - * 获取当天16020223格式的int值 - * - * @param time 指定时间 - * - * @return 16020223格式的int值 - */ - public static int yyMMDDHHmm(long time) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(time); - return cal.get(Calendar.YEAR) % 100 * 100_00_00 + (cal.get(Calendar.MONTH) + 1) * 100_00 + cal.get(Calendar.DAY_OF_MONTH) * 100 + cal.get(Calendar.HOUR_OF_DAY); - } - - /** - * 获取时间点所在星期的周一 - * - * @param time 指定时间 - * - * @return 毫秒数 - */ - public static long monday(long time) { - ZoneId zid = ZoneId.systemDefault(); - Instant instant = Instant.ofEpochMilli(time); - LocalDate ld = instant.atZone(zid).toLocalDate(); - ld = ld.minusDays(ld.getDayOfWeek().getValue() - 1); - return ld.atStartOfDay(zid).toInstant().toEpochMilli(); - } - - /** - * 获取时间点所在星期的周日 - * - * @param time 指定时间 - * - * @return 毫秒数 - */ - public static long sunday(long time) { - ZoneId zid = ZoneId.systemDefault(); - Instant instant = Instant.ofEpochMilli(time); - LocalDate ld = instant.atZone(zid).toLocalDate(); - ld = ld.plusDays(7 - ld.getDayOfWeek().getValue()); - return ld.atStartOfDay(zid).toInstant().toEpochMilli(); - } - - /** - * 获取时间点所在月份的1号 - * - * @param time 指定时间 - * - * @return 毫秒数 - */ - public static long monthFirstDay(long time) { - ZoneId zid = ZoneId.systemDefault(); - Instant instant = Instant.ofEpochMilli(time); - LocalDate ld = instant.atZone(zid).toLocalDate().withDayOfMonth(1); - return ld.atStartOfDay(zid).toInstant().toEpochMilli(); - } - - /** - * 获取时间点所在月份的最后一天 - * - * @param time 指定时间 - * - * @return 毫秒数 - */ - public static long monthLastDay(long time) { - ZoneId zid = ZoneId.systemDefault(); - Instant instant = Instant.ofEpochMilli(time); - LocalDate ld = instant.atZone(zid).toLocalDate(); - ld = ld.withDayOfMonth(ld.lengthOfMonth()); - return ld.atStartOfDay(zid).toInstant().toEpochMilli(); - } - - /** - * 将int[]强制转换成byte[] - * - * @param value int[] - * - * @return byte[] - */ - public static byte[] intsToBytes(int[] value) { - if (value == null) return null; - byte[] bs = new byte[value.length]; - for (int i = 0; i < bs.length; i++) { - bs[i] = (byte) value[i]; - } - return bs; - } - - /** - * MD5加密 - * - * @param bs 待加密数据 - * - * @return md5值 - */ - public static String md5Hex(byte[] bs) { - return binToHexString(md5Bytes(bs)); - } - - /** - * MD5加密 - * - * @param bs 待加密数据 - * - * @return md5值 - */ - public static byte[] md5Bytes(byte[] bs) { - if (bs == null) return null; - MessageDigest md5; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); - } - return md5.digest(bs); - } - - /** - * MD5加密 - * - * @param str 待加密数据 - * - * @return md5值 - */ - public static String md5Hex(String str) { - return binToHexString(md5Bytes(str)); - } - - /** - * MD5加密 - * - * @param str 待加密数据 - * - * @return md5值 - */ - public static byte[] md5Bytes(String str) { - if (str == null) return null; - MessageDigest md5; - try { - md5 = MessageDigest.getInstance("MD5"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); - } - return md5.digest(str.getBytes()); - } - - /** - * SHA-256 - * - * @param bs 待hash数据 - * - * @return hash值 - */ - public static String sha256Hex(byte[] bs) { - return binToHexString(sha256Bytes(bs)); - } - - /** - * SHA-256 - * - * @param bs 待hash数据 - * - * @return hash值 - */ - public static byte[] sha256Bytes(byte[] bs) { - if (bs == null) return null; - MessageDigest digester; - try { - digester = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); - } - return digester.digest(bs); - } - - /** - * SHA-256 - * - * @param str 待hash数据 - * - * @return hash值 - */ - public static String sha256Hex(String str) { - return binToHexString(sha256Bytes(str)); - } - - /** - * SHA-256 - * - * @param str 待hash数据 - * - * @return hash值 - */ - public static byte[] sha256Bytes(String str) { - if (str == null) return null; - MessageDigest digester; - try { - digester = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException ex) { - throw new RuntimeException(ex); - } - return digester.digest(str.getBytes()); - } - - /** - * 将字节数组转换为16进制字符串 - * - * @param bytes 字节数组 - * - * @return 16进制字符串 - */ - public static String binToHexString(byte[] bytes) { - return new String(binToHex(bytes)); - } - - /** - * 将字节数组转换为16进制字符数组 - * - * @param bytes 字节数组 - * - * @return 16进制字符串的字符数组 - */ - public static char[] binToHex(byte[] bytes) { - return binToHex(bytes, 0, bytes.length); - } - - /** - * 将字节数组转换为16进制字符串 - * - * @param bytes 字节数组 - * @param offset 偏移量 - * @param len 长度 - * - * @return 16进制字符串 - */ - public static String binToHexString(byte[] bytes, int offset, int len) { - return new String(binToHex(bytes, offset, len)); - } - - /** - * 将字节数组转换为16进制字符数组 - * - * @param bytes 字节数组 - * @param offset 偏移量 - * @param len 长度 - * - * @return 16进制字符串的字符数组 - */ - public static char[] binToHex(byte[] bytes, int offset, int len) { - final char[] sb = new char[len * 2]; - final int end = offset + len; - int index = 0; - final char[] hexs = hex; - for (int i = offset; i < end; i++) { - byte b = bytes[i]; - sb[index++] = (hexs[((b >> 4) & 0xF)]); - sb[index++] = hexs[((b) & 0xF)]; - } - return sb; - } - - /** - * 将16进制字符串转换成字节数组 - * - * @param src 16进制字符串 - * - * @return 字节数组 - */ - public static byte[] hexToBin(CharSequence src) { - return hexToBin(src, 0, src.length()); - } - - /** - * - * 将16进制字符串转换成字节数组 - * - * @param src 16进制字符串 - * @param offset 偏移量 - * @param len 长度 - * - * @return 字节数组 - */ - public static byte[] hexToBin(CharSequence src, int offset, int len) { - final int size = (len + 1) / 2; - final byte[] bytes = new byte[size]; - String digits = "0123456789abcdef"; - for (int i = 0; i < size; i++) { - int ch1 = src.charAt(offset + i * 2); - if ('A' <= ch1 && 'F' >= ch1) ch1 = ch1 - 'A' + 'a'; - int ch2 = src.charAt(offset + i * 2 + 1); - if ('A' <= ch2 && 'F' >= ch2) ch2 = ch2 - 'A' + 'a'; - int pos1 = digits.indexOf(ch1); - if (pos1 < 0) throw new NumberFormatException(); - int pos2 = digits.indexOf(ch2); - if (pos2 < 0) throw new NumberFormatException(); - bytes[i] = (byte) (pos1 * 0x10 + pos2); - } - return bytes; - } - - /** - * - * 将16进制字符串转换成字节数组 - * - * @param str 16进制字符串 - * - * @return 字节数组 - */ - public static byte[] hexToBin(String str) { - return hexToBin(charArray(str)); - } - - /** - * - * 将16进制字符数组转换成字节数组 - * - * @param src 16进制字符数组 - * - * @return 字节数组 - */ - public static byte[] hexToBin(char[] src) { - return hexToBin(src, 0, src.length); - } - - /** - * 将16进制字符数组转换成字节数组 - * - * @param src 16进制字符数组 - * @param offset 偏移量 - * @param len 长度 - * - * @return 字节数组 - */ - public static byte[] hexToBin(char[] src, int offset, int len) { - final int size = (len + 1) / 2; - final byte[] bytes = new byte[size]; - String digits = "0123456789abcdef"; - for (int i = 0; i < size; i++) { - int ch1 = src[offset + i * 2]; - if ('A' <= ch1 && 'F' >= ch1) ch1 = ch1 - 'A' + 'a'; - int ch2 = src[offset + i * 2 + 1]; - if ('A' <= ch2 && 'F' >= ch2) ch2 = ch2 - 'A' + 'a'; - int pos1 = digits.indexOf(ch1); - if (pos1 < 0) throw new NumberFormatException(); - int pos2 = digits.indexOf(ch2); - if (pos2 < 0) throw new NumberFormatException(); - bytes[i] = (byte) (pos1 * 0x10 + pos2); - } - return bytes; - } - - //----------------------------------------------------------------------------- - /** - * 使用UTF-8编码将byte[]转换成char[] - * - * @param array byte[] - * - * @return char[] - */ - public static char[] decodeUTF8(final byte[] array) { - return decodeUTF8(array, 0, array.length); - } - - public static char[] decodeUTF8(final byte[] array, final int start, final int len) { - byte b; - int size = len; - final byte[] bytes = array; - final int limit = start + len; - for (int i = start; i < limit; i++) { - b = bytes[i]; - if ((b >> 5) == -2) {// 2 bytes, 11 bits: 110xxxxx 10xxxxxx - size--; - } else if ((b >> 4) == -2) {// 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx - size -= 2; - } else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - size -= 2; - } - } - final char[] text = new char[size]; - size = 0; - for (int i = start; i < limit;) { - b = bytes[i++]; - if (b >= 0) {// 1 byte, 7 bits: 0xxxxxxx - text[size++] = (char) b; - } else if ((b >> 5) == -2) {// 2 bytes, 11 bits: 110xxxxx 10xxxxxx - text[size++] = (char) (((b << 6) ^ bytes[i++]) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80))); - } else if ((b >> 4) == -2) {// 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx - text[size++] = (char) ((b << 12) ^ (bytes[i++] << 6) ^ (bytes[i++] ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); - } else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - int uc = ((b << 18) ^ (bytes[i++] << 12) ^ (bytes[i++] << 6) ^ (bytes[i++] ^ (((byte) 0xF0 << 18) ^ ((byte) 0x80 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); - text[size++] = Character.highSurrogate(uc); - text[size++] = Character.lowSurrogate(uc); - //测试代码 byte[] bs = {(byte)34, (byte)76, (byte)105, (byte)108, (byte)121, (byte)240, (byte)159, (byte)146, (byte)171, (byte)34}; - } - } - return text; - } - - public static byte[] encodeUTF8(final String value) { - if (value == null) return new byte[0]; - if (strCharFunction == null) return encodeUTF8(value.toCharArray()); - return encodeUTF8((char[]) strCharFunction.apply(value)); - } - - public static byte[] encodeUTF8(final char[] array) { - return encodeUTF8(array, 0, array.length); - } - - public static byte[] encodeUTF8(final char[] text, final int start, final int len) { - char c; - int size = 0; - final char[] chs = text; - final int limit = start + len; - for (int i = start; i < limit; i++) { - c = chs[i]; - if (c < 0x80) { - size++; - } else if (c < 0x800) { - size += 2; - } else if (Character.isSurrogate(c)) { - size += 2; - } else { - size += 3; - } - } - final byte[] bytes = new byte[size]; - size = 0; - for (int i = start; i < limit; i++) { - c = chs[i]; - if (c < 0x80) { - bytes[size++] = (byte) c; - } else if (c < 0x800) { - bytes[size++] = (byte) (0xc0 | (c >> 6)); - bytes[size++] = (byte) (0x80 | (c & 0x3f)); - } else if (Character.isSurrogate(c)) { //连取两个 - int uc = Character.toCodePoint(c, chs[i + 1]); - bytes[size++] = (byte) (0xf0 | ((uc >> 18))); - bytes[size++] = (byte) (0x80 | ((uc >> 12) & 0x3f)); - bytes[size++] = (byte) (0x80 | ((uc >> 6) & 0x3f)); - bytes[size++] = (byte) (0x80 | (uc & 0x3f)); - i++; - } else { - bytes[size++] = (byte) (0xe0 | ((c >> 12))); - bytes[size++] = (byte) (0x80 | ((c >> 6) & 0x3f)); - bytes[size++] = (byte) (0x80 | (c & 0x3f)); - } - } - return bytes; - } - - public static long getAddress(ByteBuffer buffer) { - return bufferAddrFunction.applyAsLong(buffer); - } - - public static boolean isLatin1(String value) { - if (value == null) return true; - if (strLatin1Function != null) { - return strLatin1Function.test(value); //LATIN1:0 UTF16:1 - } - char[] chs = charArray(value); - for (char ch : chs) { - if (ch >= 0x80) return false; - } - return true; - } - - public static char[] charArray(String value) { - if (value == null) return null; - if (strCharFunction == null) return value.toCharArray(); - return (char[]) strCharFunction.apply(value); - } - - public static char[] charArray(StringBuilder value) { - if (value == null) return null; - if (sbCharFunction == null) return value.toString().toCharArray(); - return (char[]) sbCharFunction.apply(value); - } - - //只能是单字节字符串 - public static byte[] byteArray(String latin1Value) { - if (latin1Value == null) return null; - if (strByteFunction == null) return latin1Value.getBytes(); - return (byte[]) strByteFunction.apply(latin1Value); - } - - //只能是单字节字符串 - public static byte[] byteArray(StringBuilder latin1Value) { - if (latin1Value == null) return null; - if (sbByteFunction == null) return latin1Value.toString().getBytes(); - return (byte[]) sbByteFunction.apply(latin1Value); - } - - public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] array) { - return encodeUTF8(buffer, array, 0, array.length); - } - - public static ByteBuffer encodeUTF8(final ByteBuffer buffer, int bytesLength, final char[] array) { - return encodeUTF8(buffer, bytesLength, array, 0, array.length); - } - - public static int encodeUTF8Length(String value) { - if (value == null) return -1; - if (strCharFunction == null) return encodeUTF8Length(value.toCharArray()); - return encodeUTF8Length((char[]) strCharFunction.apply(value)); - } - - public static int encodeUTF8Length(final char[] text) { - return encodeUTF8Length(text, 0, text.length); - } - - public static int encodeUTF8Length(final char[] text, final int start, final int len) { - char c; - int size = 0; - final char[] chs = text; - final int limit = start + len; - for (int i = start; i < limit; i++) { - c = chs[i]; - if (c < 0x80) { - size++; - } else if (c < 0x800) { - size += 2; - } else if (Character.isSurrogate(c)) { - size += 2; - } else { - size += 3; - } - } - return size; - } - - /** - * 将两个数字组装成一个long - * - * @param high 高位值 - * @param low 低位值 - * - * @return long值 - */ - public static long merge(int high, int low) { - return (0L + high) << 32 | low; - } - - public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] text, final int start, final int len) { - return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len); - } - - //返回的ByteBuffer为扩展buffer,为null表示参数中的buffer足够存储数据 - public static ByteBuffer encodeUTF8(final ByteBuffer buffer, int bytesLength, final char[] text, final int start, final int len) { - char c; - char[] chs = text; - final int limit = start + len; - int remain = buffer.remaining(); - final ByteBuffer buffer2 = remain >= bytesLength ? null : ByteBuffer.allocate(bytesLength - remain + 4); //最差情况buffer最后两byte没有填充 - ByteBuffer buf = buffer; - for (int i = start; i < limit; i++) { - c = chs[i]; - if (c < 0x80) { - if (buf.remaining() < 1) buf = buffer2; - buf.put((byte) c); - } else if (c < 0x800) { - if (buf.remaining() < 2) buf = buffer2; - buf.put((byte) (0xc0 | (c >> 6))); - buf.put((byte) (0x80 | (c & 0x3f))); - } else if (Character.isSurrogate(c)) { //连取两个 - if (buf.remaining() < 4) buf = buffer2; - int uc = Character.toCodePoint(c, chs[i + 1]); - buf.put((byte) (0xf0 | ((uc >> 18)))); - buf.put((byte) (0x80 | ((uc >> 12) & 0x3f))); - buf.put((byte) (0x80 | ((uc >> 6) & 0x3f))); - buf.put((byte) (0x80 | (uc & 0x3f))); - i++; - } else { - if (buf.remaining() < 3) buf = buffer2; - buf.put((byte) (0xe0 | ((c >> 12)))); - buf.put((byte) (0x80 | ((c >> 6) & 0x3f))); - buf.put((byte) (0x80 | (c & 0x3f))); - } - } - if (buffer2 != null) buffer2.flip(); - return buffer2; //返回扩展buffer - } - - public static String getTypeDescriptor(java.lang.reflect.Type type) { - if (type == null) return null; - if (type instanceof Class) { - Class d = (Class) type; - final StringBuilder sb = new StringBuilder(); - while (true) { - if (d.isPrimitive()) { - char car; - if (d == Integer.TYPE) { - car = 'I'; - } else if (d == Void.TYPE) { - car = 'V'; - } else if (d == Boolean.TYPE) { - car = 'Z'; - } else if (d == Byte.TYPE) { - car = 'B'; - } else if (d == Character.TYPE) { - car = 'C'; - } else if (d == Short.TYPE) { - car = 'S'; - } else if (d == Double.TYPE) { - car = 'D'; - } else if (d == Float.TYPE) { - car = 'F'; - } else /* if (d == Long.TYPE) */ { - car = 'J'; - } - return sb.append(car).toString(); - } else if (d.isArray()) { - sb.append('['); - d = d.getComponentType(); - } else { - sb.append('L'); - String name = d.getName(); - int len = name.length(); - for (int i = 0; i < len; ++i) { - char car = name.charAt(i); - sb.append(car == '.' ? '/' : car); - } - return sb.append(';').toString(); - } - } - } - if (type instanceof ParameterizedType) {// 例如: Map - ParameterizedType pt = (ParameterizedType) type; - final StringBuilder sb = new StringBuilder(); - String raw = getTypeDescriptor(pt.getRawType()); - sb.append(raw.substring(0, raw.length() - 1)).append('<'); - for (java.lang.reflect.Type item : pt.getActualTypeArguments()) { - sb.append(getTypeDescriptor(item)); - } - return sb.append(">;").toString(); - } - if (type instanceof WildcardType) { // 例如: - final WildcardType wt = (WildcardType) type; - final StringBuilder sb = new StringBuilder(); - java.lang.reflect.Type[] us = wt.getUpperBounds(); - java.lang.reflect.Type[] ls = wt.getLowerBounds(); - if (ls.length < 1) { - if (us.length == 1 && us[0] == Object.class) { - sb.append('*'); - } else { - for (java.lang.reflect.Type f : us) { - sb.append('+'); - sb.append(getTypeDescriptor(f)); - } - } - } - for (java.lang.reflect.Type f : ls) { - sb.append('-'); - sb.append(getTypeDescriptor(f)); - } - return sb.toString(); - } - //TypeVariable 不支持 - return null; - } - - //----------------------------------------------------------------------------- - public static javax.net.ssl.SSLContext getDefaultSSLContext() { - return DEFAULTSSL_CONTEXT; - } - - public static javax.net.ssl.HostnameVerifier getDefaultHostnameVerifier() { - return defaultVerifier; - } - - public static Socket createDefaultSSLSocket(InetSocketAddress address) throws IOException { - return createDefaultSSLSocket(address.getAddress(), address.getPort()); - } - - public static Socket createDefaultSSLSocket(InetAddress host, int port) throws IOException { - Socket socket = DEFAULTSSL_CONTEXT.getSocketFactory().createSocket(host, port); - return socket; - } - - public static String postHttpContent(String url) throws IOException { - return remoteHttpContent(null, "POST", url, 0, null, null).toString("UTF-8"); - } - - public static String postHttpContent(String url, int timeout) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, null, null).toString("UTF-8"); - } - - public static String postHttpContent(String url, String body) throws IOException { - return remoteHttpContent(null, "POST", url, 0, null, body).toString("UTF-8"); - } - - public static String postHttpContent(String url, int timeout, String body) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, null, body).toString("UTF-8"); - } - - public static String postHttpContent(String url, Map headers, String body) throws IOException { - return remoteHttpContent(null, "POST", url, 0, headers, body).toString("UTF-8"); - } - - public static String postHttpContent(String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, headers, body).toString("UTF-8"); - } - - public static String postHttpContent(SSLContext ctx, String url) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, null, null).toString("UTF-8"); - } - - public static String postHttpContent(SSLContext ctx, String url, int timeout) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, null, null).toString("UTF-8"); - } - - public static String postHttpContent(SSLContext ctx, String url, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, null, body).toString("UTF-8"); - } - - public static String postHttpContent(SSLContext ctx, String url, int timeout, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, null, body).toString("UTF-8"); - } - - public static String postHttpContent(SSLContext ctx, String url, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, headers, body).toString("UTF-8"); - } - - public static String postHttpContent(SSLContext ctx, String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, headers, body).toString("UTF-8"); - } - - public static String postHttpContent(String url, Charset charset) throws IOException { - return remoteHttpContent(null, "POST", url, 0, null, null).toString(charset.name()); - } - - public static String postHttpContent(String url, int timeout, Charset charset) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, null, null).toString(charset.name()); - } - - public static String postHttpContent(String url, Charset charset, String body) throws IOException { - return remoteHttpContent(null, "POST", url, 0, null, body).toString(charset.name()); - } - - public static String postHttpContent(String url, int timeout, Charset charset, String body) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, null, body).toString(charset.name()); - } - - public static String postHttpContent(String url, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(null, "POST", url, 0, headers, body).toString(charset.name()); - } - - public static String postHttpContent(String url, int timeout, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, headers, body).toString(charset.name()); - } - - public static String postHttpContent(SSLContext ctx, String url, Charset charset) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, null, null).toString(charset.name()); - } - - public static String postHttpContent(SSLContext ctx, String url, int timeout, Charset charset) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, null, null).toString(charset.name()); - } - - public static String postHttpContent(SSLContext ctx, String url, Charset charset, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, null, body).toString(charset.name()); - } - - public static String postHttpContent(SSLContext ctx, String url, int timeout, Charset charset, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, null, body).toString(charset.name()); - } - - public static String postHttpContent(SSLContext ctx, String url, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, headers, body).toString(charset.name()); - } - - public static String postHttpContent(SSLContext ctx, String url, int timeout, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, headers, body).toString(charset.name()); - } - - public static byte[] postHttpBytesContent(String url) throws IOException { - return remoteHttpContent(null, "POST", url, 0, null, null).toByteArray(); - } - - public static byte[] postHttpBytesContent(String url, int timeout) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, null, null).toByteArray(); - } - - public static byte[] postHttpBytesContent(SSLContext ctx, String url) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, null, null).toByteArray(); - } - - public static byte[] postHttpBytesContent(SSLContext ctx, String url, int timeout) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, null, null).toByteArray(); - } - - public static byte[] postHttpBytesContent(String url, Map headers, String body) throws IOException { - return remoteHttpContent(null, "POST", url, 0, headers, body).toByteArray(); - } - - public static byte[] postHttpBytesContent(String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(null, "POST", url, timeout, headers, body).toByteArray(); - } - - public static byte[] postHttpBytesContent(SSLContext ctx, String url, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, 0, headers, body).toByteArray(); - } - - public static byte[] postHttpBytesContent(SSLContext ctx, String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "POST", url, timeout, headers, body).toByteArray(); - } - - public static String getHttpContent(String url) throws IOException { - return remoteHttpContent(null, "GET", url, 0, null, null).toString("UTF-8"); - } - - public static String getHttpContent(String url, int timeout) throws IOException { - return remoteHttpContent(null, "GET", url, timeout, null, null).toString("UTF-8"); - } - - public static String getHttpContent(SSLContext ctx, String url) throws IOException { - return remoteHttpContent(ctx, "GET", url, 0, null, null).toString("UTF-8"); - } - - public static String getHttpContent(SSLContext ctx, String url, int timeout) throws IOException { - return remoteHttpContent(ctx, "GET", url, timeout, null, null).toString("UTF-8"); - } - - public static String getHttpContent(SSLContext ctx, String url, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "GET", url, 0, headers, body).toString("UTF-8"); - } - - public static String getHttpContent(SSLContext ctx, String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "GET", url, timeout, headers, body).toString("UTF-8"); - } - - public static String getHttpContent(String url, Map headers, String body) throws IOException { - return remoteHttpContent(null, "GET", url, 0, headers, body).toString("UTF-8"); - } - - public static String getHttpContent(String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(null, "GET", url, timeout, headers, body).toString("UTF-8"); - } - - public static String getHttpContent(String url, Charset charset) throws IOException { - return remoteHttpContent(null, "GET", url, 0, null, null).toString(charset.name()); - } - - public static String getHttpContent(String url, int timeout, Charset charset) throws IOException { - return remoteHttpContent(null, "GET", url, timeout, null, null).toString(charset.name()); - } - - public static String getHttpContent(SSLContext ctx, String url, Charset charset) throws IOException { - return remoteHttpContent(ctx, "GET", url, 0, null, null).toString(charset.name()); - } - - public static String getHttpContent(SSLContext ctx, String url, int timeout, Charset charset) throws IOException { - return remoteHttpContent(ctx, "GET", url, timeout, null, null).toString(charset.name()); - } - - public static String getHttpContent(SSLContext ctx, String url, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "GET", url, 0, headers, body).toString(charset.name()); - } - - public static String getHttpContent(SSLContext ctx, String url, int timeout, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "GET", url, timeout, headers, body).toString(charset.name()); - } - - public static String getHttpContent(String url, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(null, "GET", url, 0, headers, body).toString(charset.name()); - } - - public static String getHttpContent(String url, int timeout, Charset charset, Map headers, String body) throws IOException { - return remoteHttpContent(null, "GET", url, timeout, headers, body).toString(charset.name()); - } - - public static byte[] getHttpBytesContent(String url) throws IOException { - return remoteHttpContent(null, "GET", url, 0, null, null).toByteArray(); - } - - public static byte[] getHttpBytesContent(String url, int timeout) throws IOException { - return remoteHttpContent(null, "GET", url, timeout, null, null).toByteArray(); - } - - public static byte[] getHttpBytesContent(SSLContext ctx, String url) throws IOException { - return remoteHttpContent(ctx, "GET", url, 0, null, null).toByteArray(); - } - - public static byte[] getHttpBytesContent(SSLContext ctx, String url, int timeout) throws IOException { - return remoteHttpContent(ctx, "GET", url, timeout, null, null).toByteArray(); - } - - public static byte[] getHttpBytesContent(String url, Map headers, String body) throws IOException { - return remoteHttpContent(null, "GET", url, 0, headers, body).toByteArray(); - } - - public static byte[] getHttpBytesContent(String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(null, "GET", url, timeout, headers, body).toByteArray(); - } - - public static byte[] getHttpBytesContent(SSLContext ctx, String url, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "GET", url, 0, headers, body).toByteArray(); - } - - public static byte[] getHttpBytesContent(SSLContext ctx, String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(ctx, "GET", url, timeout, headers, body).toByteArray(); - } - - public static ByteArrayOutputStream remoteHttpContent(String method, String url, Map headers, String body) throws IOException { - return remoteHttpContent(null, method, url, 0, headers, body); - } - - public static ByteArrayOutputStream remoteHttpContent(String method, String url, int timeout, Map headers, String body) throws IOException { - return remoteHttpContent(null, method, url, timeout, headers, body); - } - - public static ByteArrayOutputStream remoteHttpContent(SSLContext ctx, String method, String url, int timeout, Map headers, String body) throws IOException { - HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); - boolean opening = true; - try { - conn.setConnectTimeout(timeout > 0 ? timeout : 3000); - conn.setReadTimeout(timeout > 0 ? timeout : 3000); - if (conn instanceof HttpsURLConnection) { - HttpsURLConnection httpsconn = ((HttpsURLConnection) conn); - httpsconn.setSSLSocketFactory((ctx == null ? DEFAULTSSL_CONTEXT : ctx).getSocketFactory()); - httpsconn.setHostnameVerifier(defaultVerifier); - } - conn.setRequestMethod(method); - if (headers != null) { - for (Map.Entry en : headers.entrySet()) { - conn.setRequestProperty(en.getKey(), en.getValue()); - } - } - if (body != null && !body.isEmpty()) { //conn.getOutputStream()会将GET强制变成POST - conn.setDoInput(true); - conn.setDoOutput(true); - conn.getOutputStream().write(body.getBytes(UTF_8)); - } - conn.connect(); - int rs = conn.getResponseCode(); - if (rs == 301 || rs == 302) { - String newurl = conn.getHeaderField("Location"); - conn.disconnect(); - opening = false; - return remoteHttpContent(ctx, method, newurl, timeout, headers, body); - } - InputStream in = (rs < 400 || rs == 404) && rs != 405 ? conn.getInputStream() : conn.getErrorStream(); - if ("gzip".equalsIgnoreCase(conn.getContentEncoding())) in = new GZIPInputStream(in); - ByteArrayOutputStream out = new ByteArrayOutputStream(1024); - byte[] bytes = new byte[1024]; - int pos; - while ((pos = in.read(bytes)) != -1) { - out.write(bytes, 0, pos); - } - in.close(); - return out; - } finally { - if (opening) conn.disconnect(); - } - } - - public static String read(InputStream in) throws IOException { - return read(in, "UTF-8"); - } - - public static String readThenClose(InputStream in) throws IOException { - return read(in, "UTF-8", true); - } - - public static String read(InputStream in, String charsetName) throws IOException { - return read(in, charsetName, false); - } - - private static String read(InputStream in, String charsetName, boolean close) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(1024); - byte[] bytes = new byte[1024]; - int pos; - while ((pos = in.read(bytes)) != -1) { - out.write(bytes, 0, pos); - } - if (close) in.close(); - return charsetName == null ? out.toString() : out.toString(charsetName); - } - - public static ByteArrayOutputStream readStream(InputStream in) throws IOException { - return readStream(in, false); - } - - public static ByteArrayOutputStream readStreamThenClose(InputStream in) throws IOException { - return readStream(in, true); - } - - private static ByteArrayOutputStream readStream(InputStream in, boolean close) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(1024); - byte[] bytes = new byte[1024]; - int pos; - while ((pos = in.read(bytes)) != -1) { - out.write(bytes, 0, pos); - } - if (close) in.close(); - return out; - } - - public static byte[] readBytes(File file) throws IOException { - return readBytesThenClose(new FileInputStream(file)); - } - - public static byte[] readBytes(InputStream in) throws IOException { - return readStream(in).toByteArray(); - } - - public static byte[] readBytesThenClose(InputStream in) throws IOException { - return readStreamThenClose(in).toByteArray(); - } -} +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.net.http.HttpClient; +import java.nio.*; +import java.nio.channels.CompletionHandler; +import java.nio.charset.*; +import static java.nio.charset.StandardCharsets.UTF_8; +import java.security.*; +import java.time.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.function.*; +import java.util.stream.Stream; +import java.util.zip.GZIPInputStream; + +/** + * + * 常见操作的工具类 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public final class Utility { + + private static final int zoneRawOffset = TimeZone.getDefault().getRawOffset(); + + static final String format1 = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS"; //yyyy-MM-dd HH:mm:ss + + static final String format2 = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL"; //yyyy-MM-dd HH:mm:ss.fff + + private static final char hex[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + private static final int MAX_POW2 = 1 << 30; + + private static final Class JAVA_RECORD_CLASS; + + private static final IntFunction futureArrayFunc = c -> new CompletableFuture[c]; + + static { + Class clz = null; + try { + clz = Thread.currentThread().getContextClassLoader().loadClass("java.lang.Record"); + } catch (Throwable t) { //JDK14以下版本会异常 + } + JAVA_RECORD_CLASS = clz; + } + + private static final String funcAnonymousUnsafeBinary = "cafebabe0000003701710a000200030700040c000500060100106a6176612f6c616e672f4f626a6563740100063c696e69743e01000328295607000801000f73756e2f6d6973632f556e7361666509000a000b07000c0c000d000e0100206f72672f7265646b616c652f7574696c2f416e6f6e796d6f7573556e73616665010006756e736166650100114c73756e2f6d6973632f556e736166653b0a000700100c00110012010006676574496e74010016284c6a6176612f6c616e672f4f626a6563743b4a29490a000700140c00150016010006707574496e74010017284c6a6176612f6c616e672f4f626a6563743b4a4929560a000700180c0019001a0100096765744f626a656374010027284c6a6176612f6c616e672f4f626a6563743b4a294c6a6176612f6c616e672f4f626a6563743b0a0007001c0c001d001e0100097075744f626a656374010028284c6a6176612f6c616e672f4f626a6563743b4a4c6a6176612f6c616e672f4f626a6563743b29560a000700200c0021002201000a676574426f6f6c65616e010016284c6a6176612f6c616e672f4f626a6563743b4a295a0a000700240c0025002601000a707574426f6f6c65616e010017284c6a6176612f6c616e672f4f626a6563743b4a5a29560a000700280c0029002a01000767657442797465010016284c6a6176612f6c616e672f4f626a6563743b4a29420a0007002c0c002d002e01000770757442797465010017284c6a6176612f6c616e672f4f626a6563743b4a4229560a000700300c0031003201000867657453686f7274010016284c6a6176612f6c616e672f4f626a6563743b4a29530a000700340c0035003601000870757453686f7274010017284c6a6176612f6c616e672f4f626a6563743b4a5329560a000700380c0039003a01000767657443686172010016284c6a6176612f6c616e672f4f626a6563743b4a29430a0007003c0c003d003e01000770757443686172010017284c6a6176612f6c616e672f4f626a6563743b4a4329560a000700400c004100420100076765744c6f6e67010016284c6a6176612f6c616e672f4f626a6563743b4a294a0a000700440c004500460100077075744c6f6e67010017284c6a6176612f6c616e672f4f626a6563743b4a4a29560a000700480c0049004a010008676574466c6f6174010016284c6a6176612f6c616e672f4f626a6563743b4a29460a0007004c0c004d004e010008707574466c6f6174010017284c6a6176612f6c616e672f4f626a6563743b4a4629560a000700500c00510052010009676574446f75626c65010016284c6a6176612f6c616e672f4f626a6563743b4a29440a000700540c00550056010009707574446f75626c65010017284c6a6176612f6c616e672f4f626a6563743b4a4429560a000700580c00290059010004284a29420a0007005b0c002d005c010005284a4229560a0007005e0c0031005f010004284a29530a000700610c00350062010005284a5329560a000700640c00390065010004284a29430a000700670c003d0068010005284a4329560a0007006a0c0011006b010004284a29490a0007006d0c0015006e010005284a4929560a000700700c00410071010004284a294a0a000700730c00450074010005284a4a29560a000700760c00490077010004284a29460a000700790c004d007a010005284a4629560a0007007c0c0051007d010004284a29440a0007007f0c00550080010005284a4429560a000700820c0083007101000a676574416464726573730a000700850c0086007401000a707574416464726573730a000700880c0089007101000e616c6c6f636174654d656d6f72790a0007008b0c008c008d0100107265616c6c6f636174654d656d6f7279010005284a4a294a0a0007008f0c009000910100097365744d656d6f7279010018284c6a6176612f6c616e672f4f626a6563743b4a4a4229560a000700930c00900094010006284a4a4229560a000700960c0097009801000a636f70794d656d6f727901002a284c6a6176612f6c616e672f4f626a6563743b4a4c6a6176612f6c616e672f4f626a6563743b4a4a29560a0007009a0c0097009b010006284a4a4a29560a0007009d0c009e009f01000a667265654d656d6f7279010004284a29560a000700a10c00a200a30100116f626a6563744669656c644f666673657401001c284c6a6176612f6c616e672f7265666c6563742f4669656c643b294a0a000700a50c00a600a30100117374617469634669656c644f66667365740a000700a80c00a900aa01000f7374617469634669656c644261736501002d284c6a6176612f6c616e672f7265666c6563742f4669656c643b294c6a6176612f6c616e672f4f626a6563743b0a000700ac0c00ad00ae01000f6172726179426173654f6666736574010014284c6a6176612f6c616e672f436c6173733b29490a000700b00c00b100ae01000f6172726179496e6465785363616c650a000700b30c00b400b501000b6164647265737353697a650100032829490a000700b70c00b800b50100087061676553697a650a000700ba0c00bb00bc010010616c6c6f63617465496e7374616e6365010025284c6a6176612f6c616e672f436c6173733b294c6a6176612f6c616e672f4f626a6563743b0a000700be0c00bf00c001000e7468726f77457863657074696f6e010018284c6a6176612f6c616e672f5468726f7761626c653b29560a000700c20c00c300c4010014636f6d70617265416e64537761704f626a65637401003a284c6a6176612f6c616e672f4f626a6563743b4a4c6a6176612f6c616e672f4f626a6563743b4c6a6176612f6c616e672f4f626a6563743b295a0a000700c60c00c700c8010011636f6d70617265416e6453776170496e74010018284c6a6176612f6c616e672f4f626a6563743b4a4949295a0a000700ca0c00cb00cc010012636f6d70617265416e64537761704c6f6e67010018284c6a6176612f6c616e672f4f626a6563743b4a4a4a295a0a000700ce0c00cf001a0100116765744f626a656374566f6c6174696c650a000700d10c00d2001e0100117075744f626a656374566f6c6174696c650a000700d40c00d5001201000e676574496e74566f6c6174696c650a000700d70c00d8001601000e707574496e74566f6c6174696c650a000700da0c00db0022010012676574426f6f6c65616e566f6c6174696c650a000700dd0c00de0026010012707574426f6f6c65616e566f6c6174696c650a000700e00c00e1002a01000f67657442797465566f6c6174696c650a000700e30c00e4002e01000f70757442797465566f6c6174696c650a000700e60c00e7003201001067657453686f7274566f6c6174696c650a000700e90c00ea003601001070757453686f7274566f6c6174696c650a000700ec0c00ed003a01000f67657443686172566f6c6174696c650a000700ef0c00f0003e01000f70757443686172566f6c6174696c650a000700f20c00f3004201000f6765744c6f6e67566f6c6174696c650a000700f50c00f6004601000f7075744c6f6e67566f6c6174696c650a000700f80c00f9004a010010676574466c6f6174566f6c6174696c650a000700fb0c00fc004e010010707574466c6f6174566f6c6174696c650a000700fe0c00ff0052010011676574446f75626c65566f6c6174696c650a000701010c01020056010011707574446f75626c65566f6c6174696c650a000701040c0105001e0100107075744f7264657265644f626a6563740a000701070c0108001601000d7075744f726465726564496e740a0007010a0c010b004601000e7075744f7264657265644c6f6e670a0007010d0c010e010f010006756e7061726b010015284c6a6176612f6c616e672f4f626a6563743b29560a000701110c011201130100047061726b010005285a4a29560a000701150c0116011701000e6765744c6f616441766572616765010006285b444929490a000701190c011a011b01000c676574416e64416464496e74010017284c6a6176612f6c616e672f4f626a6563743b4a4929490a0007011d0c011e011f01000d676574416e644164644c6f6e67010017284c6a6176612f6c616e672f4f626a6563743b4a4a294a0a000701210c0122011b01000c676574416e64536574496e740a000701240c0125011f01000d676574416e645365744c6f6e670a000701270c0128012901000f676574416e645365744f626a656374010039284c6a6176612f6c616e672f4f626a6563743b4a4c6a6176612f6c616e672f4f626a6563743b294c6a6176612f6c616e672f4f626a6563743b0a0007012b0c012c00060100096c6f616446656e63650a0007012e0c012f000601000a73746f726546656e63650a000701310c0132000601000966756c6c46656e63650a000701340c0135013601000d696e766f6b65436c65616e6572010018284c6a6176612f6e696f2f427974654275666665723b29560701380100176f72672f7265646b616c652f7574696c2f556e73616665010004436f646501000f4c696e654e756d6265725461626c650100124c6f63616c5661726961626c655461626c65010004746869730100224c6f72672f7265646b616c652f7574696c2f416e6f6e796d6f7573556e736166653b0100036f626a0100124c6a6176612f6c616e672f4f626a6563743b0100104d6574686f64506172616d65746572730100016f0100066f66667365740100014a01000178010001490100015a010001420100015301000143010001460100014401000761646472657373010005627974657301000576616c7565010007737263426173650100097372634f6666736574010008646573744261736501000a646573744f666673657401000a7372634164647265737301000b6465737441646472657373010001660100194c6a6176612f6c616e672f7265666c6563742f4669656c643b01000a6172726179436c6173730100114c6a6176612f6c616e672f436c6173733b0100164c6f63616c5661726961626c65547970655461626c650100144c6a6176612f6c616e672f436c6173733c2a3e3b0100095369676e6174757265010017284c6a6176612f6c616e672f436c6173733c2a3e3b2949010003636c7301000a457863657074696f6e730701600100206a6176612f6c616e672f496e7374616e74696174696f6e457863657074696f6e010028284c6a6176612f6c616e672f436c6173733c2a3e3b294c6a6176612f6c616e672f4f626a6563743b01000265650100154c6a6176612f6c616e672f5468726f7761626c653b010008657870656374656401000674687265616401000a69734162736f6c75746501000474696d650100076c6f61646176670100025b440100066e656c656d7301000564656c74610100086e657756616c756501000c6469726563744275666665720100154c6a6176612f6e696f2f427974654275666665723b01000a536f7572636546696c65010014416e6f6e796d6f7573556e736166652e6a6176610021000a00020001013700010012000d000e0000005700010005010f0002013900000049000200020000000d2ab700012a2bc00007b50009b100000002013a0000000e00030000000a0004000b000c000c013b0000001600020000000d013c013d00000000000d013e013f000101400000000501013e00000001001100120002013900000048000400040000000a2ab400092b20b6000fac00000002013a00000006000100000010013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001001500160002013900000058000500050000000c2ab400092b201504b60013b100000002013a0000000a000200000015000b0016013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440145000401400000000d0301410000014200000144000000010019001a0002013900000048000400040000000a2ab400092b20b60017b000000002013a0000000600010000001a013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001001d001e0002013900000058000500050000000c2ab400092b201904b6001bb100000002013a0000000a00020000001f000b0020013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c0144013f000401400000000d030141000001420000014400000001002100220002013900000048000400040000000a2ab400092b20b6001fac00000002013a00000006000100000024013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001002500260002013900000058000500050000000c2ab400092b201504b60023b100000002013a0000000a000200000029000b002a013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440146000401400000000d0301410000014200000144000000010029002a0002013900000048000400040000000a2ab400092b20b60027ac00000002013a0000000600010000002e013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001002d002e0002013900000058000500050000000c2ab400092b201504b6002bb100000002013a0000000a000200000033000b0034013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440147000401400000000d030141000001420000014400000001003100320002013900000048000400040000000a2ab400092b20b6002fac00000002013a00000006000100000038013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001003500360002013900000058000500050000000c2ab400092b201504b60033b100000002013a0000000a00020000003d000b003e013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440148000401400000000d0301410000014200000144000000010039003a0002013900000048000400040000000a2ab400092b20b60037ac00000002013a00000006000100000042013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001003d003e0002013900000058000500050000000c2ab400092b201504b6003bb100000002013a0000000a000200000047000b0048013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440149000401400000000d030141000001420000014400000001004100420002013900000048000400040000000a2ab400092b20b6003fad00000002013a0000000600010000004c013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001004500460002013900000058000600060000000c2ab400092b201604b60043b100000002013a0000000a000200000051000b0052013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440143000401400000000d0301410000014200000144000000010049004a0002013900000048000400040000000a2ab400092b20b60047ae00000002013a00000006000100000056013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001004d004e0002013900000058000500050000000c2ab400092b201704b6004bb100000002013a0000000a00020000005b000b005c013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c0144014a000401400000000d030141000001420000014400000001005100520002013900000048000400040000000a2ab400092b20b6004faf00000002013a00000006000100000060013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001005500560002013900000058000600060000000c2ab400092b201804b60053b100000002013a0000000a000200000065000b0066013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c0144014b000401400000000d03014100000142000001440000000100290059000201390000003d00030003000000092ab400091fb60057ac00000002013a0000000600010000006a013b00000016000200000009013c013d000000000009014c0143000101400000000501014c00000001002d005c000201390000004c000400040000000a2ab400091f1db6005ab100000002013a0000000a00020000006f00090070013b0000002000030000000a013c013d00000000000a014c014300010000000a01440147000301400000000902014c00000144000000010031005f000201390000003d00030003000000092ab400091fb6005dac00000002013a00000006000100000074013b00000016000200000009013c013d000000000009014c0143000101400000000501014c0000000100350062000201390000004c000400040000000a2ab400091f1db60060b100000002013a0000000a0002000000790009007a013b0000002000030000000a013c013d00000000000a014c014300010000000a01440148000301400000000902014c000001440000000100390065000201390000003d00030003000000092ab400091fb60063ac00000002013a0000000600010000007e013b00000016000200000009013c013d000000000009014c0143000101400000000501014c00000001003d0068000201390000004c000400040000000a2ab400091f1db60066b100000002013a0000000a00020000008300090084013b0000002000030000000a013c013d00000000000a014c014300010000000a01440149000301400000000902014c00000144000000010011006b000201390000003d00030003000000092ab400091fb60069ac00000002013a00000006000100000088013b00000016000200000009013c013d000000000009014c0143000101400000000501014c000000010015006e000201390000004c000400040000000a2ab400091f1db6006cb100000002013a0000000a00020000008d0009008e013b0000002000030000000a013c013d00000000000a014c014300010000000a01440145000301400000000902014c000001440000000100410071000201390000003d00030003000000092ab400091fb6006fad00000002013a00000006000100000092013b00000016000200000009013c013d000000000009014c0143000101400000000501014c0000000100450074000201390000004c000500050000000a2ab400091f21b60072b100000002013a0000000a00020000009700090098013b0000002000030000000a013c013d00000000000a014c014300010000000a01440143000301400000000902014c000001440000000100490077000201390000003d00030003000000092ab400091fb60075ae00000002013a0000000600010000009c013b00000016000200000009013c013d000000000009014c0143000101400000000501014c00000001004d007a000201390000004c000400040000000a2ab400091f25b60078b100000002013a0000000a0002000000a1000900a2013b0000002000030000000a013c013d00000000000a014c014300010000000a0144014a000301400000000902014c00000144000000010051007d000201390000003d00030003000000092ab400091fb6007baf00000002013a000000060001000000a6013b00000016000200000009013c013d000000000009014c0143000101400000000501014c0000000100550080000201390000004c000500050000000a2ab400091f29b6007eb100000002013a0000000a0002000000ab000900ac013b0000002000030000000a013c013d00000000000a014c014300010000000a0144014b000301400000000902014c000001440000000100830071000201390000003d00030003000000092ab400091fb60081ad00000002013a000000060001000000b0013b00000016000200000009013c013d000000000009014c0143000101400000000501014c0000000100860074000201390000004c000500050000000a2ab400091f21b60084b100000002013a0000000a0002000000b5000900b6013b0000002000030000000a013c013d00000000000a014c014300010000000a01440143000301400000000902014c000001440000000100890071000201390000003d00030003000000092ab400091fb60087ad00000002013a000000060001000000ba013b00000016000200000009013c013d000000000009014d0143000101400000000501014d00000001008c008d0002013900000048000500050000000a2ab400091f21b6008aad00000002013a000000060001000000bf013b0000002000030000000a013c013d00000000000a014c014300010000000a014d0143000301400000000902014c0000014d00000001009000910002013900000064000700070000000e2ab400092b2016041506b6008eb100000002013a0000000a0002000000c4000d00c5013b0000003400050000000e013c013d00000000000e0141013f00010000000e0142014300020000000e014d014300040000000e014e01470006014000000011040141000001420000014d0000014e00000001009000940002013900000058000600060000000c2ab400091f211505b60092b100000002013a0000000a0002000000c9000b00ca013b0000002a00040000000c013c013d00000000000c014c014300010000000c014d014300030000000c014e0147000501400000000d03014c0000014d0000014e0000000100970098000201390000007000090009000000102ab400092b20190416051607b60095b100000002013a0000000a0002000000ce000f00cf013b0000003e000600000010013c013d000000000010014f013f000100000010015001430002000000100151013f00040000001001520143000500000010014d0143000701400000001505014f0000015000000151000001520000014d000000010097009b0002013900000058000700070000000c2ab400091f211605b60099b100000002013a0000000a0002000000d3000b00d4013b0000002a00040000000c013c013d00000000000c0153014300010000000c0154014300030000000c014d0143000501400000000d030153000001540000014d00000001009e009f000201390000004100030003000000092ab400091fb6009cb100000002013a0000000a0002000000d8000800d9013b00000016000200000009013c013d000000000009014c0143000101400000000501014c0000000100a200a3000201390000003d00020002000000092ab400092bb600a0ad00000002013a000000060001000000dd013b00000016000200000009013c013d0000000000090155015600010140000000050101550000000100a600a3000201390000003d00020002000000092ab400092bb600a4ad00000002013a000000060001000000e2013b00000016000200000009013c013d0000000000090155015600010140000000050101550000000100a900aa000201390000003d00020002000000092ab400092bb600a7b000000002013a000000060001000000e7013b00000016000200000009013c013d0000000000090155015600010140000000050101550000000100ad00ae000301390000004f00020002000000092ab400092bb600abac00000003013a000000060001000000ec013b00000016000200000009013c013d00000000000901570158000101590000000c0001000000090157015a00010140000000050101570000015b00000002015c000100b100ae000301390000004f00020002000000092ab400092bb600afac00000003013a000000060001000000f1013b00000016000200000009013c013d00000000000901570158000101590000000c0001000000090157015a00010140000000050101570000015b00000002015c000100b400b5000101390000003200010001000000082ab40009b600b2ac00000002013a000000060001000000f6013b0000000c000100000008013c013d0000000100b800b5000101390000003200010001000000082ab40009b600b6ac00000002013a000000060001000000fb013b0000000c000100000008013c013d0000000100bb00bc000401390000004f00020002000000092ab400092bb600b9b000000003013a00000006000100000100013b00000016000200000009013c013d000000000009015d0158000101590000000c000100000009015d015a0001015e000000040001015f01400000000501015d0000015b000000020161000100bf00c0000201390000004100020002000000092ab400092bb600bdb100000002013a0000000a00020000010500080106013b00000016000200000009013c013d0000000000090162016300010140000000050101620000000100c300c40002013900000060000600060000000e2ab400092b2019041905b600c1ac00000002013a0000000600010000010a013b0000003400050000000e013c013d00000000000e0141013f00010000000e0142014300020000000e0164013f00040000000e0144013f00050140000000110401410000014200000164000001440000000100c700c80002013900000060000600060000000e2ab400092b2015041505b600c5ac00000002013a0000000600010000010f013b0000003400050000000e013c013d00000000000e0141013f00010000000e0142014300020000000e0164014500040000000e0144014500050140000000110401410000014200000164000001440000000100cb00cc0002013900000060000800080000000e2ab400092b2016041606b600c9ac00000002013a00000006000100000114013b0000003400050000000e013c013d00000000000e0141013f00010000000e0142014300020000000e0164014300040000000e0144014300060140000000110401410000014200000164000001440000000100cf001a0002013900000048000400040000000a2ab400092b20b600cdb000000002013a00000006000100000119013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100d2001e0002013900000058000500050000000c2ab400092b201904b600d0b100000002013a0000000a00020000011e000b011f013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c0144013f000401400000000d03014100000142000001440000000100d500120002013900000048000400040000000a2ab400092b20b600d3ac00000002013a00000006000100000123013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100d800160002013900000058000500050000000c2ab400092b201504b600d6b100000002013a0000000a000200000128000b0129013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440145000401400000000d03014100000142000001440000000100db00220002013900000048000400040000000a2ab400092b20b600d9ac00000002013a0000000600010000012d013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100de00260002013900000058000500050000000c2ab400092b201504b600dcb100000002013a0000000a000200000132000b0133013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440146000401400000000d03014100000142000001440000000100e1002a0002013900000048000400040000000a2ab400092b20b600dfac00000002013a00000006000100000137013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100e4002e0002013900000058000500050000000c2ab400092b201504b600e2b100000002013a0000000a00020000013c000b013d013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440147000401400000000d03014100000142000001440000000100e700320002013900000048000400040000000a2ab400092b20b600e5ac00000002013a00000006000100000141013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100ea00360002013900000058000500050000000c2ab400092b201504b600e8b100000002013a0000000a000200000146000b0147013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440148000401400000000d03014100000142000001440000000100ed003a0002013900000048000400040000000a2ab400092b20b600ebac00000002013a0000000600010000014b013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100f0003e0002013900000058000500050000000c2ab400092b201504b600eeb100000002013a0000000a000200000150000b0151013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440149000401400000000d03014100000142000001440000000100f300420002013900000048000400040000000a2ab400092b20b600f1ad00000002013a00000006000100000155013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100f600460002013900000058000600060000000c2ab400092b201604b600f4b100000002013a0000000a00020000015a000b015b013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440143000401400000000d03014100000142000001440000000100f9004a0002013900000048000400040000000a2ab400092b20b600f7ae00000002013a0000000600010000015f013b0000002000030000000a013c013d00000000000a0141013f00010000000a014201430002014000000009020141000001420000000100fc004e0002013900000058000500050000000c2ab400092b201704b600fab100000002013a0000000a000200000164000b0165013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c0144014a000401400000000d03014100000142000001440000000100ff00520002013900000048000400040000000a2ab400092b20b600fdaf00000002013a00000006000100000169013b0000002000030000000a013c013d00000000000a0141013f00010000000a0142014300020140000000090201410000014200000001010200560002013900000058000600060000000c2ab400092b201804b60100b100000002013a0000000a00020000016e000b016f013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c0144014b000401400000000d0301410000014200000144000000010105001e0002013900000058000500050000000c2ab400092b201904b60103b100000002013a0000000a000200000173000b0174013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c0144013f000401400000000d030141000001420000014400000001010800160002013900000058000500050000000c2ab400092b201504b60106b100000002013a0000000a000200000178000b0179013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440145000401400000000d030141000001420000014400000001010b00460002013900000058000600060000000c2ab400092b201604b60109b100000002013a0000000a00020000017d000b017e013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c01440143000401400000000d030141000001420000014400000001010e010f000201390000004100020002000000092ab400092bb6010cb100000002013a0000000a00020000018200080183013b00000016000200000009013c013d0000000000090165013f00010140000000050101650000000101120113000201390000004c000400040000000a2ab400091b20b60110b100000002013a0000000a00020000018700090188013b0000002000030000000a013c013d00000000000a0166014600010000000a0167014300020140000000090201660000016700000001011601170002013900000048000300030000000a2ab400092b1cb60114ac00000002013a0000000600010000018c013b0000002000030000000a013c013d00000000000a0168016900010000000a016a014500020140000000090201680000016a00000001011a011b0002013900000054000500050000000c2ab400092b201504b60118ac00000002013a00000006000100000191013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c016b0145000401400000000d030141000001420000016b00000001011e011f0002013900000054000600060000000c2ab400092b201604b6011cad00000002013a00000006000100000196013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c016b0143000401400000000d030141000001420000016b000000010122011b0002013900000054000500050000000c2ab400092b201504b60120ac00000002013a0000000600010000019b013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c016c0145000401400000000d030141000001420000016c000000010125011f0002013900000054000600060000000c2ab400092b201604b60123ad00000002013a000000060001000001a0013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c016c0143000401400000000d030141000001420000016c00000001012801290002013900000054000500050000000c2ab400092b201904b60126b000000002013a000000060001000001a5013b0000002a00040000000c013c013d00000000000c0141013f00010000000c0142014300020000000c016c013f000401400000000d030141000001420000016c00000001012c0006000101390000003600010001000000082ab40009b6012ab100000002013a0000000a0002000001aa000701ab013b0000000c000100000008013c013d00000001012f0006000101390000003600010001000000082ab40009b6012db100000002013a0000000a0002000001af000701b0013b0000000c000100000008013c013d0000000101320006000101390000003600010001000000082ab40009b60130b100000002013a0000000a0002000001b4000701b5013b0000000c000100000008013c013d0000000101350136000201390000004100020002000000092ab400092bb60133b100000002013a0000000a0002000001b9000801ba013b00000016000200000009013c013d000000000009016d016e000101400000000501016d00000001016f000000020170"; + + private static final Unsafe unsafeInstance; + + //------------------------------------------------------------------------------- + private static final Function strByteFunction; + + private static final Function sbByteFunction; + + private static final Function strCharFunction; + + private static final Function sbCharFunction; + + private static final Predicate strLatin1Function; + + private static final ToLongFunction bufferAddrFunction; + + private static final Object clientLock = new Object(); + + private static HttpClient httpClient; + + //private static final javax.net.ssl.SSLContext DEFAULTSSL_CONTEXT; + //private static final javax.net.ssl.HostnameVerifier defaultVerifier = (s, ss) -> true; + static { + Unsafe unsafe0 = null; + Function strCharFunction0 = null; + Function sbCharFunction0 = null; + Function strByteFunction0 = null; + Function sbByteFunction0 = null; + Predicate strLatin1Function0 = null; + ToLongFunction bufferAddrFunction0 = null; + + if (!"executable".equals(System.getProperty("org.graalvm.nativeimage.kind"))) { //not native-image + try { + Field f = String.class.getDeclaredField("value"); + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + final Class unsafeClass = loader.loadClass("sun.misc.Unsafe"); + final Field safeField = unsafeClass.getDeclaredField("theUnsafe"); + RedkaleClassLoader.putReflectionField("sun.misc.Unsafe", safeField); + safeField.setAccessible(true); + final Object usafe = safeField.get(null); + + Class unsafeClazz1 = null; + try { + unsafeClazz1 = (Class) loader.loadClass("org.re" + "dkale.util.AnonymousUnsafe"); + } catch (Throwable t) { + } + if (unsafeClazz1 == null) { + byte[] classBytes = hexToBin(funcAnonymousUnsafeBinary); + unsafeClazz1 = (Class) new ClassLoader(loader) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass("org.re" + "dkale.util.AnonymousUnsafe", classBytes); + RedkaleClassLoader.putDynClass(unsafeClazz1.getName(), classBytes, unsafeClazz1); + } + RedkaleClassLoader.putReflectionDeclaredConstructors(unsafeClazz1, unsafeClazz1.getName(), Object.class); + unsafe0 = unsafeClazz1.getConstructor(Object.class).newInstance(usafe); + + final Unsafe unsafe = unsafe0; + final long fd1 = unsafe0.objectFieldOffset(f); + final long fd2 = unsafe0.objectFieldOffset(StringBuilder.class.getSuperclass().getDeclaredField("value")); + final long fd3 = unsafe0.objectFieldOffset(String.class.getDeclaredField("coder")); + final long fd4 = unsafe0.objectFieldOffset(Buffer.class.getDeclaredField("address")); + + strByteFunction0 = new Function() { + public Object apply(Object t) { + return unsafe.getObject(t, fd1); + } + }; + sbByteFunction0 = new Function() { + public Object apply(Object t) { + return unsafe.getObject(t, fd2); + } + }; + strLatin1Function0 = new Predicate() { + public boolean test(Object t) { + return unsafe.getByte(t, fd3) == 0; //LATIN1:0 UTF16:1 + } + }; + bufferAddrFunction0 = new ToLongFunction() { + public long applyAsLong(Object t) { + return unsafe.getLong(t, fd4); + } + }; + } catch (Throwable e) { //不会发生 + e.printStackTrace(); + } + + } + unsafeInstance = unsafe0; + strCharFunction = strCharFunction0; + sbCharFunction = sbCharFunction0; + strByteFunction = strByteFunction0; + sbByteFunction = sbByteFunction0; + strLatin1Function = strLatin1Function0; + bufferAddrFunction = bufferAddrFunction0; + +// try { +// DEFAULTSSL_CONTEXT = javax.net.ssl.SSLContext.getInstance("SSL"); +// DEFAULTSSL_CONTEXT.init(null, new javax.net.ssl.TrustManager[]{new javax.net.ssl.X509TrustManager() { +// @Override +// public java.security.cert.X509Certificate[] getAcceptedIssuers() { +// return null; +// } +// +// @Override +// public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { +// } +// +// @Override +// public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { +// } +// }}, null); +// } catch (Exception e) { +// throw new RuntimeException(e); //不会发生 +// } + } + + private Utility() { + } + + public static Unsafe unsafe() { + return unsafeInstance; + } + + public static int cpus() { + return Runtime.getRuntime().availableProcessors(); + } + + /** + * @param value from which next positive power of two will be found. + * + * @return the next positive power of 2, this value if it is a power of 2. Negative values are mapped to 1. + * @throws IllegalArgumentException is value is more than MAX_POW2 or less than 0 + */ + public static int roundToPowerOfTwo(final int value) { + if (value > MAX_POW2) throw new IllegalArgumentException("There is no larger power of 2 int for value:" + value + " since it exceeds 2^31."); + if (value < 0) throw new IllegalArgumentException("Given value:" + value + ". Expecting value >= 0."); + return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); + } + + public static boolean isRecordGetter(Method method) { + return isRecordGetter(method.getDeclaringClass(), method); + } + + public static boolean isRecordGetter(Class clazz, Method method) { + if (JAVA_RECORD_CLASS == null) return false; + if (method.getReturnType() == void.class) return false; + if (method.getParameterCount() != 0) return false; + if (method.getName().equals("getClass")) return false; + Class clz = (clazz == null ? method.getDeclaringClass() : clazz); + if (!JAVA_RECORD_CLASS.isAssignableFrom(clz)) return false; + try { + return clz.getDeclaredField(method.getName()).getType() == method.getReturnType(); + } catch (Throwable t) { + return false; + } + } + + public static CompletableFuture orTimeout(CompletableFuture future, long timeout, TimeUnit unit) { + return future.orTimeout(timeout, unit); + } + + public static CompletableFuture completeOnTimeout(CompletableFuture future, T value, long timeout, TimeUnit unit) { + return future.completeOnTimeout(value, timeout, unit); + } + + public static CompletableFuture allOfFutures(Stream> stream, IntFunction func) { + CompletableFuture[] futures = stream.toArray(futureArrayFunc); + return CompletableFuture.allOf(futures).thenApply(v -> { + int size = futures.length; + T[] array = func.apply(size); + for (int i = 0; i < size; i++) { + array[i] = futures[i].join(); + } + return array; + }); + } + + public static CompletableFuture allOfFutures(CompletableFuture[] futures, IntFunction func) { + return CompletableFuture.allOf(futures).thenApply(v -> { + int size = futures.length; + T[] array = func.apply(size); + for (int i = 0; i < size; i++) { + array[i] = futures[i].join(); + } + return array; + }); + } + + public static CompletableFuture allOfFutures(Stream> stream, IntFunction func, BiConsumer consumer) { + CompletableFuture[] futures = stream.toArray(futureArrayFunc); + return CompletableFuture.allOf(futures).thenApply(v -> { + int size = futures.length; + T[] array = func.apply(size); + for (int i = 0; i < size; i++) { + T val = futures[i].join(); + consumer.accept(i, val); + array[i] = val; + } + return array; + }); + } + + public static CompletableFuture allOfFutures(CompletableFuture[] futures, IntFunction func, BiConsumer consumer) { + return CompletableFuture.allOf(futures).thenApply(v -> { + int size = futures.length; + T[] array = func.apply(size); + for (int i = 0; i < size; i++) { + T val = futures[i].join(); + consumer.accept(i, val); + array[i] = val; + } + return array; + }); + } + + /** + * 将多个key:value的字符串键值对组合成一个Map,items长度必须是偶数, 参数个数若是奇数的话,最后一个会被忽略 + * 类似 JDK9中的 Map.of 方法 + * + * @param items 键值对 + * + * @return Map + */ + public static HashMap ofMap(String... items) { + HashMap map = new LinkedHashMap<>(Math.max(1, items.length / 2)); + int len = items.length / 2; + for (int i = 0; i < len; i++) { + map.put(items[i * 2], items[i * 2 + 1]); + } + return map; + } + + /** + * 将多个key:value对应值组合成一个Map,items长度必须是偶数, 参数个数若是奇数的话,最后一个会被忽略 + * 类似 JDK9中的 Map.of 方法 + * + * @param 泛型 + * @param 泛型 + * @param items 键值对 + * + * @return Map + */ + public static HashMap ofMap(Object... items) { + HashMap map = new LinkedHashMap<>(Math.max(1, items.length / 2)); + int len = items.length / 2; + for (int i = 0; i < len; i++) { + map.put((K) items[i * 2], (V) items[i * 2 + 1]); + } + return map; + } + + /** + * 将多个Map合并到第一个Map中 + * + * @param 泛型 + * @param 泛型 + * @param maps Map + * + * @return Map + */ + public static Map merge(Map... maps) { + Map map = null; + for (Map m : maps) { + if (map == null) { + map = m; + } else if (m != null) { + map.putAll(m); + } + } + return map; + } + + /** + * 将多个元素组合成一个Set + * + * @param 泛型 + * @param items 元素 + * + * @return Set + */ + public static Set ofSet(T... items) { + Set set = new LinkedHashSet<>(items.length); + for (T item : items) set.add(item); + return set; + } + + /** + * 将多个元素组合成一个List
    + * 类似 JDK9中的 List.of 方法 + * + * @param 泛型 + * @param items 元素 + * + * @return List + */ + public static List ofList(T... items) { + List list = new ArrayList<>(items.length); + for (T item : items) list.add(item); + return list; + } + + /** + * 将多个元素组合成一个Array + * + * @param 泛型 + * @param items 元素 + * + * @return Array + */ + public static T[] ofArray(T... items) { + return items; + } + + /** + * 裁剪List,使其size不超过limit大小
    + * + * @param 泛型 + * @param list 集合 + * @param limit 大小 + * + * @return List + */ + public static List limit(List list, int limit) { + if (list == null || list.isEmpty() || list.size() <= limit) return list; + return list.subList(0, limit); + } + + /** + * 获取不带"-"的UUID值 + * + * @return 不带"-"UUID值 + */ + public static String uuid() { + return UUID.randomUUID().toString().replace("-", ""); + } + + /** + * 将一个或多个新元素添加到数组开始,数组中的元素自动后移 + * + * @param 泛型 + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static T[] unshift(final T[] array, final T... objs) { + if (array == null || array.length == 0) return objs; + final T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.length); + System.arraycopy(objs, 0, news, 0, objs.length); + System.arraycopy(array, 0, news, objs.length, array.length); + return news; + } + + /** + * 将一个或多个新元素添加到数组开始,数组中的元素自动后移 + * + * @param 泛型 + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static T[] unshift(final T[] array, final Collection objs) { + if (objs == null || objs.isEmpty()) return array; + if (array == null) { + T one = null; + for (T t : objs) { + if (t != null) one = t; + break; + } + if (one == null) return array; + T[] news = (T[]) Array.newInstance(one.getClass(), objs.size()); + return objs.toArray(news); + } + T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.size()); + int index = -1; + for (T t : objs) { + news[(++index)] = t; + } + System.arraycopy(array, 0, news, objs.size(), array.length); + return news; + } + + /** + * 获取int数组之和, 空数组返回0 + * + * @param array 数组 + * + * @return int + */ + public static int sum(final int... array) { + return sum(false, array); + } + + /** + * 获取int数组之和 + * + * @param check 是否检测空 + * @param array 数组 + * + * @return int + */ + public static int sum(boolean check, final int... array) { + if (array == null || array.length == 0) { + if (!check) return 0; + throw new NullPointerException("array is null or empty"); + } + int sum = 0; + for (int i : array) { + sum += i; + } + return sum; + } + + /** + * 获取long数组之和, 空数组返回0 + * + * @param array 数组 + * + * @return long + */ + public static long sum(final long... array) { + return sum(false, array); + } + + /** + * 获取long数组之和 + * + * @param check 是否检测空 + * @param array 数组 + * + * @return long + */ + public static long sum(boolean check, final long... array) { + if (array == null || array.length == 0) { + if (!check) return 0; + throw new NullPointerException("array is null or empty"); + } + long sum = 0L; + for (long i : array) { + sum += i; + } + return sum; + } + + /** + * 获取int数组最大值 + * + * @param array 数组 + * + * @return int + */ + public static int max(final int... array) { + if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); + int max = array[0]; + for (int i : array) { + if (i > max) i = max; + } + return max; + } + + /** + * 获取long数组最大值 + * + * @param array 数组 + * + * @return long + */ + public static long max(final long... array) { + if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); + long max = array[0]; + for (long i : array) { + if (i > max) i = max; + } + return max; + } + + /** + * 获取int数组最小值 + * + * @param array 数组 + * + * @return int + */ + public static long min(final int... array) { + if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); + int min = array[0]; + for (int i : array) { + if (i < min) i = min; + } + return min; + } + + /** + * 获取long数组最小值 + * + * @param array 数组 + * + * @return long + */ + public static long min(final long... array) { + if (array == null || array.length == 0) throw new NullPointerException("array is null or empty"); + long min = array[0]; + for (long i : array) { + if (i < min) i = min; + } + return min; + } + + /** + * 将char数组用分隔符拼接成字符串 + * + * @param array 数组 + * @param delimiter 分隔符 + * + * @return String + */ + public static String joining(final char[] array, final String delimiter) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + for (char i : array) { + if (sb.length() > 0) sb.append(delimiter); + sb.append(i); + } + return sb.toString(); + } + + /** + * 将int数组用分隔符拼接成字符串 + * + * @param array 数组 + * @param delimiter 分隔符 + * + * @return String + */ + public static String joining(final int[] array, final String delimiter) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + for (int i : array) { + if (sb.length() > 0) sb.append(delimiter); + sb.append(i); + } + return sb.toString(); + } + + /** + * 将long数组用分隔符拼接成字符串 + * + * @param array 数组 + * @param delimiter 分隔符 + * + * @return String + */ + public static String joining(final long[] array, final String delimiter) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + for (long i : array) { + if (sb.length() > 0) sb.append(delimiter); + sb.append(i); + } + return sb.toString(); + } + + /** + * 将对象数组用分隔符拼接成字符串 + * + * @param 泛型 + * @param array 数组 + * @param delimiter 分隔符 + * + * @return String + */ + public static String joining(final T[] array, final String delimiter) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + for (T i : array) { + if (sb.length() > 0) sb.append(delimiter); + sb.append(i); + } + return sb.toString(); + } + + /** + * 将对象数组用分隔符拼接成字符串 + * + * @param 泛型 + * @param array 数组 + * @param delimiter 分隔符 + * + * @return String + */ + public static String joining(final String[] array, final char delimiter) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + for (String i : array) { + if (sb.length() > 0) sb.append(delimiter); + sb.append(i); + } + return sb.toString(); + } + + /** + * 将对象数组用分隔符拼接成字符串 + * + * @param 泛型 + * @param array 数组 + * @param delimiter 分隔符 + * + * @return String + */ + public static String joiningHex(final byte[] array, final char delimiter) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + for (byte i : array) { + if (sb.length() > 0) sb.append(delimiter); + String s = Integer.toHexString(i & 0xff); + sb.append(s.length() > 1 ? "0x" : "0x0").append(s); + } + return sb.toString(); + } + + /** + * 将对象数组用分隔符拼接成字符串 + * + * @param 泛型 + * @param array 数组 + * @param offset 偏移量 + * @param length 长度 + * @param delimiter 分隔符 + * + * @return String + */ + public static String joiningHex(final byte[] array, int offset, int length, final char delimiter) { + if (array == null || array.length == 0) return ""; + StringBuilder sb = new StringBuilder(); + int len = offset + length; + for (int i = offset; i < len; i++) { + if (sb.length() > 0) sb.append(delimiter); + String s = Integer.toHexString(array[i] & 0xff); + sb.append(s.length() > 1 ? "0x" : "0x0").append(s); + } + return sb.toString(); + } + + /** + * 将一个或多个byte新元素添加到byte数组结尾 + * + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static byte[] append(final byte[] array, final byte... objs) { + if (array == null || array.length == 0) return objs; + if (objs == null || objs.length == 0) return array; + final byte[] news = new byte[array.length + objs.length]; + System.arraycopy(array, 0, news, 0, array.length); + System.arraycopy(objs, 0, news, array.length, objs.length); + return news; + } + + /** + * 将一个或多个byte新元素添加到byte数组结尾 + * + * @param array 原数组 + * @param objs 待追加数据 + * @param offset 待追加数据偏移量 + * @param length 待追加数据的长度 + * + * @return 新数组 + */ + public static byte[] append(final byte[] array, final byte[] objs, int offset, int length) { + if (array == null || array.length == 0) { + if (objs != null && offset == 0 && objs.length == length) return objs; + final byte[] news = new byte[length]; + System.arraycopy(objs, 0, news, 0, length); + return news; + } + if (objs == null || length == 0) return array; + final byte[] news = new byte[array.length + length]; + System.arraycopy(array, 0, news, 0, array.length); + System.arraycopy(objs, offset, news, array.length, length); + return news; + } + + /** + * 将一个或多个short新元素添加到short数组结尾 + * + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static short[] append(final short[] array, final short... objs) { + if (array == null || array.length == 0) return objs; + if (objs == null || objs.length == 0) return array; + final short[] news = new short[array.length + objs.length]; + System.arraycopy(array, 0, news, 0, array.length); + System.arraycopy(objs, 0, news, array.length, objs.length); + return news; + } + + /** + * 将一个或多个char新元素添加到char数组结尾 + * + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static char[] append(final char[] array, final char... objs) { + if (array == null || array.length == 0) return objs; + if (objs == null || objs.length == 0) return array; + final char[] news = new char[array.length + objs.length]; + System.arraycopy(array, 0, news, 0, array.length); + System.arraycopy(objs, 0, news, array.length, objs.length); + return news; + } + + /** + * 将一个或多个int新元素添加到int数组结尾 + * + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static int[] append(final int[] array, final int... objs) { + if (array == null || array.length == 0) return objs; + if (objs == null || objs.length == 0) return array; + final int[] news = new int[array.length + objs.length]; + System.arraycopy(array, 0, news, 0, array.length); + System.arraycopy(objs, 0, news, array.length, objs.length); + return news; + } + + /** + * 将一个或多个long新元素添加到long数组结尾 + * + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static long[] append(final long[] array, final long... objs) { + if (array == null || array.length == 0) return objs; + if (objs == null || objs.length == 0) return array; + final long[] news = new long[array.length + objs.length]; + System.arraycopy(array, 0, news, 0, array.length); + System.arraycopy(objs, 0, news, array.length, objs.length); + return news; + } + + /** + * 将一个或多个新元素添加到数组结尾 + * + * @param 泛型 + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static T[] append(final T[] array, final T... objs) { + if (array == null || array.length == 0) return objs; + if (objs == null || objs.length == 0) return array; + final T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.length); + System.arraycopy(array, 0, news, 0, array.length); + System.arraycopy(objs, 0, news, array.length, objs.length); + return news; + } + + /** + * 将一个或多个新元素添加到数组结尾 + * + * @param 泛型 + * @param array 原数组 + * @param objs 待追加数据 + * + * @return 新数组 + */ + public static T[] append(final T[] array, final Collection objs) { + if (objs == null || objs.isEmpty()) return array; + if (array == null) { + T one = null; + for (T t : objs) { + if (t != null) one = t; + break; + } + if (one == null) return array; + T[] news = (T[]) Array.newInstance(one.getClass(), objs.size()); + return objs.toArray(news); + } + T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length + objs.size()); + System.arraycopy(array, 0, news, 0, array.length); + int index = -1; + for (T t : objs) { + news[array.length + (++index)] = t; + } + return news; + } + + /** + * 将int数组倒序 + * + * @param array 原数组 + * + * @return 新数组 + */ + public static int[] reverseSort(final int[] array) { + if (array == null || array.length == 0) return array; + return Arrays.stream(array).boxed().sorted(Collections.reverseOrder()).mapToInt(x -> x).toArray(); + } + + /** + * 将long数组倒序 + * + * @param array 原数组 + * + * @return 新数组 + */ + public static long[] reverseSort(final long[] array) { + if (array == null || array.length == 0) return array; + return Arrays.stream(array).boxed().sorted(Collections.reverseOrder()).mapToLong(x -> x).toArray(); + } + + /** + * 将元素从数组中删除 + * + * @param 泛型 + * @param array 原数组 + * @param item 元素 + * + * @return 新数组 + */ + public static T[] remove(final T[] array, final T item) { + return remove(array, (i) -> Objects.equals(i, item)); + } + + /** + * 将符合条件的元素从数组中删除 + * + * @param 泛型 + * @param array 原数组 + * @param filter Predicate + * + * @return 新数组 + */ + public static T[] remove(final T[] array, final Predicate filter) { + if (array == null || array.length == 0 || filter == null) return array; + final T[] news = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length); + int index = 0; + for (int i = 0; i < news.length; i++) { + if (!filter.test(array[i])) { + news[index++] = array[i]; + } + } + if (index == array.length) return array; + final T[] rs = (T[]) Array.newInstance(array.getClass().getComponentType(), index); + System.arraycopy(news, 0, rs, 0, index); + return rs; + } + + /** + * 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除
    + * 例如:
    + * remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    + * + * @param array 原数组 + * @param items short[] + * + * @return 新数组 + */ + public static short[] removeMatch(final short[] array, final short... items) { + return remove(array, false, items); + } + + /** + * 将指定的int元素从数组中删除, repeat=true时相同的元素会根据items里重复次数来执行删除
    + * 例如:
    + * remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = []
    + * remove(new short[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3] + * + * @param array 原数组 + * @param repeat 是否重复删除相同的元素 + * @param items short[] + * + * @return 新数组 + */ + public static short[] remove(final short[] array, boolean repeat, final short... items) { + if (array == null || array.length == 0 || items == null || items.length == 0) return array; + final short[] news = new short[array.length]; + short[] subs = items; + int index = 0; + for (int i = 0; i < news.length; i++) { + if (subs.length > 0 && contains(subs, array[i])) { + if (!repeat) { + short[] newsubs = new short[subs.length - 1]; + int k = 0; + boolean done = false; + for (short v : subs) { + if (done) { + newsubs[k++] = v; + } else if (v == array[i]) { + done = true; + } else { + newsubs[k++] = v; + } + } + subs = newsubs; + } + } else { + news[index++] = array[i]; + } + } + if (index == array.length) return array; + final short[] rs = new short[index]; + System.arraycopy(news, 0, rs, 0, index); + return rs; + } + + /** + * 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除
    + * 例如:
    + * remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    + * + * @param array 原数组 + * @param items int[] + * + * @return 新数组 + */ + public static int[] removeMatch(final int[] array, final int... items) { + return remove(array, false, items); + } + + /** + * 将指定的int元素从数组中删除, repeat=false时相同的元素会根据items里重复次数来执行删除
    + * 例如:
    + * remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = []
    + * remove(new int[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3] + * + * @param array 原数组 + * @param repeat 是否重复删除相同的元素 + * @param items int[] + * + * @return 新数组 + */ + public static int[] remove(final int[] array, boolean repeat, final int... items) { + if (array == null || array.length == 0 || items == null || items.length == 0) return array; + final int[] news = new int[array.length]; + int[] subs = items; + int index = 0; + for (int i = 0; i < news.length; i++) { + if (subs.length > 0 && contains(subs, array[i])) { + if (!repeat) { + int[] newsubs = new int[subs.length - 1]; + int k = 0; + boolean done = false; + for (int v : subs) { + if (done) { + newsubs[k++] = v; + } else if (v == array[i]) { + done = true; + } else { + newsubs[k++] = v; + } + } + subs = newsubs; + } + } else { + news[index++] = array[i]; + } + } + if (index == array.length) return array; + final int[] rs = new int[index]; + System.arraycopy(news, 0, rs, 0, index); + return rs; + } + + /** + * 将指定的long元素从数组中删除, 相同的元素会根据items里重复次数来执行删除
    + * 例如:
    + * remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    + * + * @param array 原数组 + * @param items long[] + * + * @return 新数组 + */ + public static long[] removeMatch(final long[] array, final long... items) { + return remove(array, false, items); + } + + /** + * 将指定的long元素从数组中删除, repeat=false时相同的元素会根据items里重复次数来执行删除
    + * 例如:
    + * remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, true, 1, 1, 2, 3, 3) = []
    + * remove(new long[]{1, 1, 1, 2, 2, 3, 3, 3}, false, 1, 1, 2, 3, 3) = [1,2,3]
    + * + * @param array 原数组 + * @param repeat 是否重复删除相同的元素 + * @param items long[] + * + * @return 新数组 + */ + public static long[] remove(final long[] array, boolean repeat, final long... items) { + if (array == null || array.length == 0 || items == null || items.length == 0) return array; + final long[] news = new long[array.length]; + long[] subs = items; + int index = 0; + for (int i = 0; i < news.length; i++) { + if (subs.length > 0 && contains(subs, array[i])) { + if (!repeat) { + long[] newsubs = new long[subs.length - 1]; + int k = 0; + boolean done = false; + for (long v : subs) { + if (done) { + newsubs[k++] = v; + } else if (v == array[i]) { + done = true; + } else { + newsubs[k++] = v; + } + } + subs = newsubs; + } + } else { + news[index++] = array[i]; + } + } + if (index == array.length) return array; + final long[] rs = new long[index]; + System.arraycopy(news, 0, rs, 0, index); + return rs; + } + + /** + * 判断字符串是否包含指定的字符,包含返回true + * + * @param string 字符串 + * @param values 字符集合 + * + * @return boolean + */ + public static boolean contains(String string, char... values) { + if (string == null) return false; + for (char ch : Utility.charArray(string)) { + for (char ch2 : values) { + if (ch == ch2) return true; + } + } + return false; + } + + /** + * 比较两集合元素是否一样, 顺序不要求一样 + * + * @param 泛型 + * @param array1 集合 + * @param array2 集合 + * + * @return 元素是否完全相同 + */ + public static boolean equalsElement(T[] array1, T[] array2) { + if (array1 == null && array2 == null) return true; + if (array1 == null && array2 != null) return false; + if (array1 != null && array2 == null) return false; + if (array1.length != array2.length) return false; + return equalsElement(ofList(array1), ofList(array2)); + } + + /** + * 比较两集合元素是否一样, 顺序不要求一样 + * + * @param 泛型 + * @param col1 集合 + * @param col2 集合 + * + * @return 元素是否完全相同 + */ + public static boolean equalsElement(Collection col1, Collection col2) { + if (col1 == null && col2 == null) return true; + if (col1 == null && col2 != null) return false; + if (col1 != null && col2 == null) return false; + if (col1.size() != col2.size()) return false; + //{1,2,2}, {1,1,2} + List list = new ArrayList<>(col2); + for (T item : col1) { + if (!list.remove(item)) return false; + } + return list.isEmpty(); + } + + /** + * 比较两集合元素是否一样, 顺序不要求一样 + * + * @param 泛型 + * @param 泛型 + * @param map1 集合 + * @param map2 集合 + * + * @return 元素是否完全相同 + */ + public static boolean equalsElement(Map map1, Map map2) { + if (map1 == null && map2 == null) return true; + if (map1 == null && map2 != null) return false; + if (map1 != null && map2 == null) return false; + if (map1.size() != map2.size()) return false; + for (Map.Entry en : map1.entrySet()) { + if (!Objects.equals(en.getValue(), map2.get(en.getKey()))) return false; + } + return true; + } + + /** + * 判断指定值是否包含指定的数组中,包含返回true + * + * @param values 集合 + * @param value 单值 + * + * @return boolean + */ + public static boolean contains(char[] values, char value) { + if (values == null) return false; + for (char v : values) { + if (v == value) return true; + } + return false; + } + + /** + * 判断指定值是否包含指定的数组中,包含返回true + * + * @param values 集合 + * @param value 单值 + * + * @return boolean + */ + public static boolean contains(short[] values, short value) { + if (values == null) return false; + for (short v : values) { + if (v == value) return true; + } + return false; + } + + /** + * 判断指定值(不要包含相同的元素)是否包含指定的数组中,包含返回true + * + * @param values 集合 + * @param items 多值 + * + * @return boolean + */ + public static boolean contains(short[] values, short... items) { + if (values == null) return false; + for (short item : items) { + if (!contains(values, item)) return false; + } + return true; + } + + /** + * 判断指定值是否包含指定的数组中,包含返回true + * + * @param values 集合 + * @param value 单值 + * + * @return boolean + */ + public static boolean contains(int[] values, int value) { + if (values == null) return false; + for (int v : values) { + if (v == value) return true; + } + return false; + } + + /** + * 判断指定值(不要包含相同的元素)是否包含指定的数组中,包含返回true + * + * @param values 集合 + * @param items 多值 + * + * @return boolean + */ + public static boolean contains(int[] values, int... items) { + if (values == null) return false; + for (int item : items) { + if (!contains(values, item)) return false; + } + return true; + } + + /** + * 判断指定值是否包含指定的数组中,包含返回true + * + * @param values 集合 + * @param value 单值 + * + * @return boolean + */ + public static boolean contains(long[] values, long value) { + if (values == null) return false; + for (long v : values) { + if (v == value) return true; + } + return false; + } + + /** + * 判断指定值(不要包含相同的元素)是否包含指定的数组中,包含返回true + * + * @param values 集合 + * @param items 多值 + * + * @return boolean + */ + public static boolean contains(long[] values, long... items) { + if (values == null) return false; + for (long item : items) { + if (!contains(values, item)) return false; + } + return true; + } + + /** + * 判断指定值是否包含指定的数组中,包含返回true + * + * @param 泛型 + * @param values 集合 + * @param value 单值 + * + * @return boolean + */ + public static boolean contains(T[] values, T value) { + if (values == null) return false; + for (T v : values) { + if (Objects.equals(v, value)) return true; + } + return false; + } + + /** + * 判断指定值是否包含指定的数组中,包含返回true + * + * @param 泛型 + * @param values 集合 + * @param predicate 过滤条件 + * + * @return boolean + */ + public static boolean contains(T[] values, Predicate predicate) { + if (values == null) return false; + for (T v : values) { + if (predicate.test(v)) return true; + } + return false; + } + + /** + * 将指定的short元素是否数组中完全包含,重复元素的次数也要相同
    + * 例如:
    + * containsMatch(new short[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true
    + * containsMatch(new short[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false
    + * + * @param array 原数组 + * @param items short[] + * + * @return 是否完全包含 + */ + public static boolean containsMatch(final short[] array, final short... items) { + if (array == null) return false; + if (items == null || items.length == 0) return true; + if (array.length == 0 && items.length == 0) return true; + if (array.length < items.length) return false; + + short[] subs = array; + for (short item : items) { + if (!contains(subs, item)) return false; + short[] newsubs = new short[subs.length - 1]; + int k = 0; + boolean done = false; + for (short v : subs) { + if (done) { + newsubs[k++] = v; + } else if (v == item) { + done = true; + } else { + newsubs[k++] = v; + } + } + subs = newsubs; + } + return true; + } + + /** + * 将指定的int元素是否数组中完全包含,重复元素的次数也要相同
    + * 例如:
    + * containsMatch(new int[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true
    + * containsMatch(new int[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false
    + * + * @param array 原数组 + * @param items int[] + * + * @return 是否完全包含 + */ + public static boolean containsMatch(final int[] array, final int... items) { + if (array == null) return false; + if (items == null || items.length == 0) return true; + if (array.length == 0 && items.length == 0) return true; + if (array.length < items.length) return false; + + int[] subs = array; + for (int item : items) { + if (!contains(subs, item)) return false; + int[] newsubs = new int[subs.length - 1]; + int k = 0; + boolean done = false; + for (int v : subs) { + if (done) { + newsubs[k++] = v; + } else if (v == item) { + done = true; + } else { + newsubs[k++] = v; + } + } + subs = newsubs; + } + return true; + } + + /** + * 将指定的long元素是否数组中完全包含,重复元素的次数也要相同
    + * 例如:
    + * containsMatch(new long[]{1, 2, 2, 3, 3, 3}, 1, 2, 3, 3) = true
    + * containsMatch(new long[]{1, 2, 2, 3, 3, 3}, 1, 1, 2, 3, 3) = false
    + * + * @param array 原数组 + * @param items long[] + * + * @return 是否完全包含 + */ + public static boolean containsMatch(final long[] array, final long... items) { + if (array == null) return false; + if (items == null || items.length == 0) return true; + if (array.length == 0 && items.length == 0) return true; + if (array.length < items.length) return false; + + long[] subs = array; + for (long item : items) { + if (!contains(subs, item)) return false; + long[] newsubs = new long[subs.length - 1]; + int k = 0; + boolean done = false; + for (long v : subs) { + if (done) { + newsubs[k++] = v; + } else if (v == item) { + done = true; + } else { + newsubs[k++] = v; + } + } + subs = newsubs; + } + return true; + } + + /** + * 删除掉字符串数组中包含指定的字符串 + * + * @param columns 待删除数组 + * @param cols 需排除的字符串 + * + * @return 新字符串数组 + */ + public static String[] exclude(final String[] columns, final String... cols) { + if (columns == null || columns.length == 0 || cols == null || cols.length == 0) return columns; + int count = 0; + for (String column : columns) { + boolean flag = false; + for (String col : cols) { + if (column != null && column.equals(col)) { + flag = true; + break; + } + } + if (flag) count++; + } + if (count == 0) return columns; + if (count == columns.length) return new String[0]; + final String[] newcols = new String[columns.length - count]; + count = 0; + for (String column : columns) { + boolean flag = false; + for (String col : cols) { + if (column != null && column.equals(col)) { + flag = true; + break; + } + } + if (!flag) newcols[count++] = column; + } + return newcols; + } + + /** + * 将buffer的内容转换成字符串, string参数不为空时会追加在buffer内容字符串之前 + * + * @param string 字符串前缀 + * @param buffer ByteBuffer + * + * @return 字符串 + */ + public static String toString(String string, ByteBuffer buffer) { + if (buffer == null || !buffer.hasRemaining()) return string; + int pos = buffer.position(); + int limit = buffer.limit(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + buffer.position(pos); + buffer.limit(limit); + if (string == null) return new String(bytes, UTF_8); + return string + new String(bytes, UTF_8); + } + + /** + * 将buffer的内容转换成字符串并打印到控制台, string参数不为空时会追加在buffer内容字符串之前 + * + * @param string 字符串前缀 + * @param buffer ByteBuffer + * + */ + public static void println(String string, ByteBuffer buffer) { + if (buffer == null || !buffer.hasRemaining()) return; + int pos = buffer.position(); + int limit = buffer.limit(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + buffer.position(pos); + buffer.limit(limit); + println(string, bytes); + } + + /** + * 将字节数组的内容转换成字符串并打印到控制台, string参数不为空时会追加在字节数组内容字符串之前 + * + * @param string 字符串前缀 + * @param bytes 字节数组 + * + */ + public static void println(String string, byte... bytes) { + if (bytes == null) return; + StringBuilder sb = new StringBuilder(); + if (string != null) sb.append(string); + sb.append(bytes.length).append(".["); + boolean last = false; + for (byte b : bytes) { + if (last) sb.append(','); + int v = b & 0xff; + sb.append("0x"); + if (v < 16) sb.append('0'); + sb.append(Integer.toHexString(v)); + last = true; + } + sb.append(']'); + (System.out).println(sb); + } + + /** + * 返回本机的第一个内网IPv4地址, 没有则返回null + * + * @return IPv4地址 + */ + public static InetAddress localInetAddress() { + InetAddress back = null; + try { + Enumeration nifs = NetworkInterface.getNetworkInterfaces(); + while (nifs.hasMoreElements()) { + NetworkInterface nif = nifs.nextElement(); + if (!nif.isUp()) continue; + Enumeration eis = nif.getInetAddresses(); + while (eis.hasMoreElements()) { + InetAddress ia = eis.nextElement(); + if (ia.isLoopbackAddress() && ia instanceof Inet4Address) back = ia; + if (ia.isSiteLocalAddress() && ia instanceof Inet4Address) return ia; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return back; + } + + /** + * 创建 CompletionHandler 对象 + * + * @param 结果对象的泛型 + * @param 附件对象的泛型 + * @param success 成功的回调函数 + * @param fail 失败的回调函数 + * + * @return CompletionHandler + */ + public static CompletionHandler createAsyncHandler(final BiConsumer success, final BiConsumer fail) { + return new CompletionHandler() { + @Override + public void completed(V result, A attachment) { + if (success != null) success.accept(result, attachment); + } + + @Override + public void failed(Throwable exc, A attachment) { + if (fail != null) fail.accept(exc, attachment); + } + }; + } + + /** + * 创建没有返回结果的 CompletionHandler 对象 + * + * @param 附件对象的泛型 + * @param success 成功的回调函数 + * @param fail 失败的回调函数 + * + * @return CompletionHandler + */ + public static CompletionHandler createAsyncHandler(final Consumer success, final BiConsumer fail) { + return new CompletionHandler() { + @Override + public void completed(Void result, A attachment) { + if (success != null) success.accept(attachment); + } + + @Override + public void failed(Throwable exc, A attachment) { + if (fail != null) fail.accept(exc, attachment); + } + }; + } + + /** + * 创建没有附件对象的 CompletionHandler 对象 + * + * @param 结果对象的泛型 + * @param success 成功的回调函数 + * @param fail 失败的回调函数 + * + * @return CompletionHandler + */ + public static CompletionHandler createAsyncHandler(final Consumer success, final Consumer fail) { + return new CompletionHandler() { + @Override + public void completed(V result, Void attachment) { + if (success != null) success.accept(result); + } + + @Override + public void failed(Throwable exc, Void attachment) { + if (fail != null) fail.accept(exc); + } + }; + } + + /** + * 获取格式为yyyy-MM-dd HH:mm:ss的当前时间 + * + * @return 格式为yyyy-MM-dd HH:mm:ss的时间值 + */ + public static String now() { + return String.format(format1, System.currentTimeMillis()); + } + + /** + * 获取格式为yyyy-MM-dd HH:mm:ss.fff的当前时间 + * + * @return 格式为yyyy-MM-dd HH:mm:ss.fff的时间值 + */ + public static String nowMillis() { + return String.format(format2, System.currentTimeMillis()); + } + + /** + * 将指定时间格式化为 yyyy-MM-dd HH:mm:ss + * + * @param time 待格式化的时间 + * + * @return 格式为yyyy-MM-dd HH:mm:ss的时间值 + */ + public static String formatTime(long time) { + return String.format(format1, time); + } + + /** + * 将时间值转换为长度为9的36进制值 + * + * @param time 时间值 + * + * @return 36进制时间值 + */ + public static String format36time(long time) { + return Long.toString(time, 36); + } + + /** + * 获取当天凌晨零点的格林时间 + * + * @return 毫秒数 + */ + public static long midnight() { + return midnight(System.currentTimeMillis()); + } + + /** + * 获取指定时间当天凌晨零点的格林时间 + * + * @param time 指定时间 + * + * @return 毫秒数 + */ + public static long midnight(long time) { + return (time + zoneRawOffset) / 86400000 * 86400000 - zoneRawOffset; + } + + /** + * 获取当天20151231格式的int值 + * + * @return 20151231格式的int值 + */ + public static int today() { + java.time.LocalDate today = java.time.LocalDate.now(); + return today.getYear() * 10000 + today.getMonthValue() * 100 + today.getDayOfMonth(); + } + + /** + * 获取当天151231格式的int值 + * + * @return 151231格式的int值 + */ + public static int todayYYMMDD() { + java.time.LocalDate today = java.time.LocalDate.now(); + return today.getYear() % 100 * 10000 + today.getMonthValue() * 100 + today.getDayOfMonth(); + } + + /** + * 获取当天1512312359格式的int值 + * + * @return 1512312359格式的int值 + */ + public static int todayYYMMDDHHmm() { + java.time.LocalDateTime today = java.time.LocalDateTime.now(); + return today.getYear() % 100 * 100_00_00_00 + today.getMonthValue() * 100_00_00 + today.getDayOfMonth() * 100_00 + + today.getHour() * 100 + today.getMinute(); + } + + /** + * 获取当天20151231235959格式的int值 + * + * @return 20151231235959格式的int值 + */ + public static long todayYYYYMMDDHHmmss() { + java.time.LocalDateTime today = java.time.LocalDateTime.now(); + return today.getYear() * 100_00_00_00_00L + today.getMonthValue() * 100_00_00_00 + today.getDayOfMonth() * 100_00_00 + + today.getHour() * 100_00 + today.getMinute() * 100 + today.getSecond(); + } + + /** + * 获取当天151231235959格式的int值 + * + * @return 151231235959格式的int值 + */ + public static long todayYYMMDDHHmmss() { + java.time.LocalDateTime today = java.time.LocalDateTime.now(); + return today.getYear() % 100 * 100_00_00_00_00L + today.getMonthValue() * 100_00_00_00 + today.getDayOfMonth() * 100_00_00 + + today.getHour() * 100_00 + today.getMinute() * 100 + today.getSecond(); + } + + /** + * 获取明天20151230格式的int值 + * + * @return 20151230格式的int值 + */ + public static int tomorrow() { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DAY_OF_YEAR, 1); + return cal.get(Calendar.YEAR) * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); + } + + /** + * 获取明天151230格式的int值 + * + * @return 151230格式的int值 + */ + public static int tomorrowYYMMDD() { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DAY_OF_YEAR, 1); + return cal.get(Calendar.YEAR) % 100 * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); + } + + /** + * 获取昨天20151230格式的int值 + * + * @return 20151230格式的int值 + */ + public static int yesterday() { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DAY_OF_YEAR, -1); + return cal.get(Calendar.YEAR) * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); + } + + /** + * 获取昨天151230格式的int值 + * + * @return 151230格式的int值 + */ + public static int yesterdayYYMMDD() { + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.DAY_OF_YEAR, -1); + return cal.get(Calendar.YEAR) % 100 * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); + } + + /** + * 获取指定时间的20160202格式的int值 + * + * @param time 指定时间 + * + * @return 毫秒数 + */ + public static int yyyyMMdd(long time) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.YEAR) * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); + } + + /** + * 获取指定时间的160202格式的int值 + * + * @param time 指定时间 + * + * @return 毫秒数 + */ + public static int yyMMdd(long time) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.YEAR) % 100 * 10000 + (cal.get(Calendar.MONTH) + 1) * 100 + cal.get(Calendar.DAY_OF_MONTH); + } + + /** + * 获取当天16020223格式的int值 + * + * @param time 指定时间 + * + * @return 16020223格式的int值 + */ + public static int yyMMDDHHmm(long time) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(time); + return cal.get(Calendar.YEAR) % 100 * 100_00_00 + (cal.get(Calendar.MONTH) + 1) * 100_00 + cal.get(Calendar.DAY_OF_MONTH) * 100 + cal.get(Calendar.HOUR_OF_DAY); + } + + /** + * 获取时间点所在星期的周一 + * + * @param time 指定时间 + * + * @return 毫秒数 + */ + public static long monday(long time) { + ZoneId zid = ZoneId.systemDefault(); + Instant instant = Instant.ofEpochMilli(time); + LocalDate ld = instant.atZone(zid).toLocalDate(); + ld = ld.minusDays(ld.getDayOfWeek().getValue() - 1); + return ld.atStartOfDay(zid).toInstant().toEpochMilli(); + } + + /** + * 获取时间点所在星期的周日 + * + * @param time 指定时间 + * + * @return 毫秒数 + */ + public static long sunday(long time) { + ZoneId zid = ZoneId.systemDefault(); + Instant instant = Instant.ofEpochMilli(time); + LocalDate ld = instant.atZone(zid).toLocalDate(); + ld = ld.plusDays(7 - ld.getDayOfWeek().getValue()); + return ld.atStartOfDay(zid).toInstant().toEpochMilli(); + } + + /** + * 获取时间点所在月份的1号 + * + * @param time 指定时间 + * + * @return 毫秒数 + */ + public static long monthFirstDay(long time) { + ZoneId zid = ZoneId.systemDefault(); + Instant instant = Instant.ofEpochMilli(time); + LocalDate ld = instant.atZone(zid).toLocalDate().withDayOfMonth(1); + return ld.atStartOfDay(zid).toInstant().toEpochMilli(); + } + + /** + * 获取时间点所在月份的最后一天 + * + * @param time 指定时间 + * + * @return 毫秒数 + */ + public static long monthLastDay(long time) { + ZoneId zid = ZoneId.systemDefault(); + Instant instant = Instant.ofEpochMilli(time); + LocalDate ld = instant.atZone(zid).toLocalDate(); + ld = ld.withDayOfMonth(ld.lengthOfMonth()); + return ld.atStartOfDay(zid).toInstant().toEpochMilli(); + } + + /** + * 将int[]强制转换成byte[] + * + * @param value int[] + * + * @return byte[] + */ + public static byte[] intsToBytes(int[] value) { + if (value == null) return null; + byte[] bs = new byte[value.length]; + for (int i = 0; i < bs.length; i++) { + bs[i] = (byte) value[i]; + } + return bs; + } + + /** + * MD5加密 + * + * @param bs 待加密数据 + * + * @return md5值 + */ + public static String md5Hex(byte[] bs) { + return binToHexString(md5Bytes(bs)); + } + + /** + * MD5加密 + * + * @param bs 待加密数据 + * + * @return md5值 + */ + public static byte[] md5Bytes(byte[] bs) { + if (bs == null) return null; + MessageDigest md5; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + return md5.digest(bs); + } + + /** + * MD5加密 + * + * @param str 待加密数据 + * + * @return md5值 + */ + public static String md5Hex(String str) { + return binToHexString(md5Bytes(str)); + } + + /** + * MD5加密 + * + * @param str 待加密数据 + * + * @return md5值 + */ + public static byte[] md5Bytes(String str) { + if (str == null) return null; + MessageDigest md5; + try { + md5 = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + return md5.digest(str.getBytes()); + } + + /** + * SHA-256 + * + * @param bs 待hash数据 + * + * @return hash值 + */ + public static String sha256Hex(byte[] bs) { + return binToHexString(sha256Bytes(bs)); + } + + /** + * SHA-256 + * + * @param bs 待hash数据 + * + * @return hash值 + */ + public static byte[] sha256Bytes(byte[] bs) { + if (bs == null) return null; + MessageDigest digester; + try { + digester = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + return digester.digest(bs); + } + + /** + * SHA-256 + * + * @param str 待hash数据 + * + * @return hash值 + */ + public static String sha256Hex(String str) { + return binToHexString(sha256Bytes(str)); + } + + /** + * SHA-256 + * + * @param str 待hash数据 + * + * @return hash值 + */ + public static byte[] sha256Bytes(String str) { + if (str == null) return null; + MessageDigest digester; + try { + digester = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException ex) { + throw new RuntimeException(ex); + } + return digester.digest(str.getBytes()); + } + + /** + * 将字节数组转换为16进制字符串 + * + * @param bytes 字节数组 + * + * @return 16进制字符串 + */ + public static String binToHexString(byte[] bytes) { + return new String(binToHex(bytes)); + } + + /** + * 将字节数组转换为16进制字符数组 + * + * @param bytes 字节数组 + * + * @return 16进制字符串的字符数组 + */ + public static char[] binToHex(byte[] bytes) { + return binToHex(bytes, 0, bytes.length); + } + + /** + * 将字节数组转换为16进制字符串 + * + * @param bytes 字节数组 + * @param offset 偏移量 + * @param len 长度 + * + * @return 16进制字符串 + */ + public static String binToHexString(byte[] bytes, int offset, int len) { + return new String(binToHex(bytes, offset, len)); + } + + /** + * 将字节数组转换为16进制字符数组 + * + * @param bytes 字节数组 + * @param offset 偏移量 + * @param len 长度 + * + * @return 16进制字符串的字符数组 + */ + public static char[] binToHex(byte[] bytes, int offset, int len) { + final char[] sb = new char[len * 2]; + final int end = offset + len; + int index = 0; + final char[] hexs = hex; + for (int i = offset; i < end; i++) { + byte b = bytes[i]; + sb[index++] = (hexs[((b >> 4) & 0xF)]); + sb[index++] = hexs[((b) & 0xF)]; + } + return sb; + } + + /** + * 将16进制字符串转换成字节数组 + * + * @param src 16进制字符串 + * + * @return 字节数组 + */ + public static byte[] hexToBin(CharSequence src) { + return hexToBin(src, 0, src.length()); + } + + /** + * + * 将16进制字符串转换成字节数组 + * + * @param src 16进制字符串 + * @param offset 偏移量 + * @param len 长度 + * + * @return 字节数组 + */ + public static byte[] hexToBin(CharSequence src, int offset, int len) { + final int size = (len + 1) / 2; + final byte[] bytes = new byte[size]; + String digits = "0123456789abcdef"; + for (int i = 0; i < size; i++) { + int ch1 = src.charAt(offset + i * 2); + if ('A' <= ch1 && 'F' >= ch1) ch1 = ch1 - 'A' + 'a'; + int ch2 = src.charAt(offset + i * 2 + 1); + if ('A' <= ch2 && 'F' >= ch2) ch2 = ch2 - 'A' + 'a'; + int pos1 = digits.indexOf(ch1); + if (pos1 < 0) throw new NumberFormatException(); + int pos2 = digits.indexOf(ch2); + if (pos2 < 0) throw new NumberFormatException(); + bytes[i] = (byte) (pos1 * 0x10 + pos2); + } + return bytes; + } + + /** + * + * 将16进制字符串转换成字节数组 + * + * @param str 16进制字符串 + * + * @return 字节数组 + */ + public static byte[] hexToBin(String str) { + return hexToBin(charArray(str)); + } + + /** + * + * 将16进制字符数组转换成字节数组 + * + * @param src 16进制字符数组 + * + * @return 字节数组 + */ + public static byte[] hexToBin(char[] src) { + return hexToBin(src, 0, src.length); + } + + /** + * 将16进制字符数组转换成字节数组 + * + * @param src 16进制字符数组 + * @param offset 偏移量 + * @param len 长度 + * + * @return 字节数组 + */ + public static byte[] hexToBin(char[] src, int offset, int len) { + final int size = (len + 1) / 2; + final byte[] bytes = new byte[size]; + String digits = "0123456789abcdef"; + for (int i = 0; i < size; i++) { + int ch1 = src[offset + i * 2]; + if ('A' <= ch1 && 'F' >= ch1) ch1 = ch1 - 'A' + 'a'; + int ch2 = src[offset + i * 2 + 1]; + if ('A' <= ch2 && 'F' >= ch2) ch2 = ch2 - 'A' + 'a'; + int pos1 = digits.indexOf(ch1); + if (pos1 < 0) throw new NumberFormatException(); + int pos2 = digits.indexOf(ch2); + if (pos2 < 0) throw new NumberFormatException(); + bytes[i] = (byte) (pos1 * 0x10 + pos2); + } + return bytes; + } + + //----------------------------------------------------------------------------- + /** + * 使用UTF-8编码将byte[]转换成char[] + * + * @param array byte[] + * + * @return char[] + */ + public static char[] decodeUTF8(final byte[] array) { + return decodeUTF8(array, 0, array.length); + } + + public static char[] decodeUTF8(final byte[] array, final int start, final int len) { + byte b; + int size = len; + final byte[] bytes = array; + final int limit = start + len; + for (int i = start; i < limit; i++) { + b = bytes[i]; + if ((b >> 5) == -2) {// 2 bytes, 11 bits: 110xxxxx 10xxxxxx + size--; + } else if ((b >> 4) == -2) {// 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + size -= 2; + } else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + size -= 2; + } + } + final char[] text = new char[size]; + size = 0; + for (int i = start; i < limit;) { + b = bytes[i++]; + if (b >= 0) {// 1 byte, 7 bits: 0xxxxxxx + text[size++] = (char) b; + } else if ((b >> 5) == -2) {// 2 bytes, 11 bits: 110xxxxx 10xxxxxx + text[size++] = (char) (((b << 6) ^ bytes[i++]) ^ (((byte) 0xC0 << 6) ^ ((byte) 0x80))); + } else if ((b >> 4) == -2) {// 3 bytes, 16 bits: 1110xxxx 10xxxxxx 10xxxxxx + text[size++] = (char) ((b << 12) ^ (bytes[i++] << 6) ^ (bytes[i++] ^ (((byte) 0xE0 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); + } else if ((b >> 3) == -2) {// 4 bytes, 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + int uc = ((b << 18) ^ (bytes[i++] << 12) ^ (bytes[i++] << 6) ^ (bytes[i++] ^ (((byte) 0xF0 << 18) ^ ((byte) 0x80 << 12) ^ ((byte) 0x80 << 6) ^ ((byte) 0x80)))); + text[size++] = Character.highSurrogate(uc); + text[size++] = Character.lowSurrogate(uc); + //测试代码 byte[] bs = {(byte)34, (byte)76, (byte)105, (byte)108, (byte)121, (byte)240, (byte)159, (byte)146, (byte)171, (byte)34}; + } + } + return text; + } + + public static byte[] encodeUTF8(final String value) { + if (value == null) return new byte[0]; + if (strCharFunction == null) return encodeUTF8(value.toCharArray()); + return encodeUTF8((char[]) strCharFunction.apply(value)); + } + + public static byte[] encodeUTF8(final char[] array) { + return encodeUTF8(array, 0, array.length); + } + + public static byte[] encodeUTF8(final char[] text, final int start, final int len) { + char c; + int size = 0; + final char[] chs = text; + final int limit = start + len; + for (int i = start; i < limit; i++) { + c = chs[i]; + if (c < 0x80) { + size++; + } else if (c < 0x800) { + size += 2; + } else if (Character.isSurrogate(c)) { + size += 2; + } else { + size += 3; + } + } + final byte[] bytes = new byte[size]; + size = 0; + for (int i = start; i < limit; i++) { + c = chs[i]; + if (c < 0x80) { + bytes[size++] = (byte) c; + } else if (c < 0x800) { + bytes[size++] = (byte) (0xc0 | (c >> 6)); + bytes[size++] = (byte) (0x80 | (c & 0x3f)); + } else if (Character.isSurrogate(c)) { //连取两个 + int uc = Character.toCodePoint(c, chs[i + 1]); + bytes[size++] = (byte) (0xf0 | ((uc >> 18))); + bytes[size++] = (byte) (0x80 | ((uc >> 12) & 0x3f)); + bytes[size++] = (byte) (0x80 | ((uc >> 6) & 0x3f)); + bytes[size++] = (byte) (0x80 | (uc & 0x3f)); + i++; + } else { + bytes[size++] = (byte) (0xe0 | ((c >> 12))); + bytes[size++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + bytes[size++] = (byte) (0x80 | (c & 0x3f)); + } + } + return bytes; + } + + public static long getAddress(ByteBuffer buffer) { + return bufferAddrFunction.applyAsLong(buffer); + } + + public static boolean isLatin1(String value) { + if (value == null) return true; + if (strLatin1Function != null) { + return strLatin1Function.test(value); //LATIN1:0 UTF16:1 + } + char[] chs = charArray(value); + for (char ch : chs) { + if (ch >= 0x80) return false; + } + return true; + } + + public static char[] charArray(String value) { + if (value == null) return null; + if (strCharFunction == null) return value.toCharArray(); + return (char[]) strCharFunction.apply(value); + } + + public static char[] charArray(StringBuilder value) { + if (value == null) return null; + if (sbCharFunction == null) return value.toString().toCharArray(); + return (char[]) sbCharFunction.apply(value); + } + + //只能是单字节字符串 + public static byte[] latin1ByteArray(String latin1Value) { + if (latin1Value == null) return null; + if (strByteFunction == null) return latin1Value.getBytes(); + return (byte[]) strByteFunction.apply(latin1Value); + } + + //只能是单字节字符串 + public static byte[] latin1ByteArray(StringBuilder latin1Value) { + if (latin1Value == null) return null; + if (sbByteFunction == null) return latin1Value.toString().getBytes(); + return (byte[]) sbByteFunction.apply(latin1Value); + } + + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] array) { + return encodeUTF8(buffer, array, 0, array.length); + } + + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, int bytesLength, final char[] array) { + return encodeUTF8(buffer, bytesLength, array, 0, array.length); + } + + public static int encodeUTF8Length(String value) { + if (value == null) return -1; + if (strCharFunction == null) return encodeUTF8Length(value.toCharArray()); + return encodeUTF8Length((char[]) strCharFunction.apply(value)); + } + + public static int encodeUTF8Length(final char[] text) { + return encodeUTF8Length(text, 0, text.length); + } + + public static int encodeUTF8Length(final char[] text, final int start, final int len) { + char c; + int size = 0; + final char[] chs = text; + final int limit = start + len; + for (int i = start; i < limit; i++) { + c = chs[i]; + if (c < 0x80) { + size++; + } else if (c < 0x800) { + size += 2; + } else if (Character.isSurrogate(c)) { + size += 2; + } else { + size += 3; + } + } + return size; + } + + /** + * 将两个数字组装成一个long + * + * @param high 高位值 + * @param low 低位值 + * + * @return long值 + */ + public static long merge(int high, int low) { + return (0L + high) << 32 | low; + } + + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, final char[] text, final int start, final int len) { + return encodeUTF8(buffer, encodeUTF8Length(text, start, len), text, start, len); + } + + //返回的ByteBuffer为扩展buffer,为null表示参数中的buffer足够存储数据 + public static ByteBuffer encodeUTF8(final ByteBuffer buffer, int bytesLength, final char[] text, final int start, final int len) { + char c; + char[] chs = text; + final int limit = start + len; + int remain = buffer.remaining(); + final ByteBuffer buffer2 = remain >= bytesLength ? null : ByteBuffer.allocate(bytesLength - remain + 4); //最差情况buffer最后两byte没有填充 + ByteBuffer buf = buffer; + for (int i = start; i < limit; i++) { + c = chs[i]; + if (c < 0x80) { + if (buf.remaining() < 1) buf = buffer2; + buf.put((byte) c); + } else if (c < 0x800) { + if (buf.remaining() < 2) buf = buffer2; + buf.put((byte) (0xc0 | (c >> 6))); + buf.put((byte) (0x80 | (c & 0x3f))); + } else if (Character.isSurrogate(c)) { //连取两个 + if (buf.remaining() < 4) buf = buffer2; + int uc = Character.toCodePoint(c, chs[i + 1]); + buf.put((byte) (0xf0 | ((uc >> 18)))); + buf.put((byte) (0x80 | ((uc >> 12) & 0x3f))); + buf.put((byte) (0x80 | ((uc >> 6) & 0x3f))); + buf.put((byte) (0x80 | (uc & 0x3f))); + i++; + } else { + if (buf.remaining() < 3) buf = buffer2; + buf.put((byte) (0xe0 | ((c >> 12)))); + buf.put((byte) (0x80 | ((c >> 6) & 0x3f))); + buf.put((byte) (0x80 | (c & 0x3f))); + } + } + if (buffer2 != null) buffer2.flip(); + return buffer2; //返回扩展buffer + } + + public static String getTypeDescriptor(java.lang.reflect.Type type) { + if (type == null) return null; + if (type instanceof Class) { + Class d = (Class) type; + final StringBuilder sb = new StringBuilder(); + while (true) { + if (d.isPrimitive()) { + char car; + if (d == Integer.TYPE) { + car = 'I'; + } else if (d == Void.TYPE) { + car = 'V'; + } else if (d == Boolean.TYPE) { + car = 'Z'; + } else if (d == Byte.TYPE) { + car = 'B'; + } else if (d == Character.TYPE) { + car = 'C'; + } else if (d == Short.TYPE) { + car = 'S'; + } else if (d == Double.TYPE) { + car = 'D'; + } else if (d == Float.TYPE) { + car = 'F'; + } else /* if (d == Long.TYPE) */ { + car = 'J'; + } + return sb.append(car).toString(); + } else if (d.isArray()) { + sb.append('['); + d = d.getComponentType(); + } else { + sb.append('L'); + String name = d.getName(); + int len = name.length(); + for (int i = 0; i < len; ++i) { + char car = name.charAt(i); + sb.append(car == '.' ? '/' : car); + } + return sb.append(';').toString(); + } + } + } + if (type instanceof ParameterizedType) {// 例如: Map + ParameterizedType pt = (ParameterizedType) type; + final StringBuilder sb = new StringBuilder(); + String raw = getTypeDescriptor(pt.getRawType()); + sb.append(raw.substring(0, raw.length() - 1)).append('<'); + for (java.lang.reflect.Type item : pt.getActualTypeArguments()) { + sb.append(getTypeDescriptor(item)); + } + return sb.append(">;").toString(); + } + if (type instanceof WildcardType) { // 例如: + final WildcardType wt = (WildcardType) type; + final StringBuilder sb = new StringBuilder(); + java.lang.reflect.Type[] us = wt.getUpperBounds(); + java.lang.reflect.Type[] ls = wt.getLowerBounds(); + if (ls.length < 1) { + if (us.length == 1 && us[0] == Object.class) { + sb.append('*'); + } else { + for (java.lang.reflect.Type f : us) { + sb.append('+'); + sb.append(getTypeDescriptor(f)); + } + } + } + for (java.lang.reflect.Type f : ls) { + sb.append('-'); + sb.append(getTypeDescriptor(f)); + } + return sb.toString(); + } + //TypeVariable 不支持 + return null; + } + + //----------------------------------------------------------------------------- +// public static javax.net.ssl.SSLContext getDefaultSSLContext() { +// return DEFAULTSSL_CONTEXT; +// } +// +// public static javax.net.ssl.HostnameVerifier getDefaultHostnameVerifier() { +// return defaultVerifier; +// } +// +// public static Socket createDefaultSSLSocket(InetSocketAddress address) throws IOException { +// return createDefaultSSLSocket(address.getAddress(), address.getPort()); +// } +// +// public static Socket createDefaultSSLSocket(InetAddress host, int port) throws IOException { +// Socket socket = DEFAULTSSL_CONTEXT.getSocketFactory().createSocket(host, port); +// return socket; +// } + // + public static String postHttpContent(String url) throws IOException { + return remoteHttpContent("POST", url, 0, null, null).toString(StandardCharsets.UTF_8); + } + + public static String postHttpContent(String url, int timeout) throws IOException { + return remoteHttpContent("POST", url, timeout, null, null).toString(StandardCharsets.UTF_8); + } + + public static String postHttpContent(String url, String body) throws IOException { + return remoteHttpContent("POST", url, 0, null, body).toString(StandardCharsets.UTF_8); + } + + public static String postHttpContent(String url, int timeout, String body) throws IOException { + return remoteHttpContent("POST", url, timeout, null, body).toString(StandardCharsets.UTF_8); + } + + public static String postHttpContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent("POST", url, 0, headers, body).toString(StandardCharsets.UTF_8); + } + + public static String postHttpContent(String url, int timeout, Map headers, String body) throws IOException { + return remoteHttpContent("POST", url, timeout, headers, body).toString(StandardCharsets.UTF_8); + } + + public static String postHttpContent(String url, Charset charset) throws IOException { + return remoteHttpContent("POST", url, 0, null, null).toString(charset.name()); + } + + public static String postHttpContent(String url, int timeout, Charset charset) throws IOException { + return remoteHttpContent("POST", url, timeout, null, null).toString(charset.name()); + } + + public static String postHttpContent(String url, Charset charset, String body) throws IOException { + return remoteHttpContent("POST", url, 0, null, body).toString(charset.name()); + } + + public static String postHttpContent(String url, int timeout, Charset charset, String body) throws IOException { + return remoteHttpContent("POST", url, timeout, null, body).toString(charset.name()); + } + + public static String postHttpContent(String url, Charset charset, Map headers, String body) throws IOException { + return remoteHttpContent("POST", url, 0, headers, body).toString(charset.name()); + } + + public static String postHttpContent(String url, int timeout, Charset charset, Map headers, String body) throws IOException { + return remoteHttpContent("POST", url, timeout, headers, body).toString(charset.name()); + } + + public static byte[] postHttpBytesContent(String url) throws IOException { + return remoteHttpContent("POST", url, 0, null, null).toByteArray(); + } + + public static byte[] postHttpBytesContent(String url, int timeout) throws IOException { + return remoteHttpContent("POST", url, timeout, null, null).toByteArray(); + } + + public static byte[] postHttpBytesContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent("POST", url, 0, headers, body).toByteArray(); + } + + public static byte[] postHttpBytesContent(String url, int timeout, Map headers, String body) throws IOException { + return remoteHttpContent("POST", url, timeout, headers, body).toByteArray(); + } + + public static String getHttpContent(String url) throws IOException { + return remoteHttpContent("GET", url, 0, null, null).toString(StandardCharsets.UTF_8); + } + + public static String getHttpContent(String url, int timeout) throws IOException { + return remoteHttpContent("GET", url, timeout, null, null).toString(StandardCharsets.UTF_8); + } + + public static String getHttpContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent("GET", url, 0, headers, body).toString(StandardCharsets.UTF_8); + } + + public static String getHttpContent(String url, int timeout, Map headers, String body) throws IOException { + return remoteHttpContent("GET", url, timeout, headers, body).toString(StandardCharsets.UTF_8); + } + + public static String getHttpContent(String url, Charset charset) throws IOException { + return remoteHttpContent("GET", url, 0, null, null).toString(charset.name()); + } + + public static String getHttpContent(String url, int timeout, Charset charset) throws IOException { + return remoteHttpContent("GET", url, timeout, null, null).toString(charset.name()); + } + + public static String getHttpContent(String url, Charset charset, Map headers, String body) throws IOException { + return remoteHttpContent("GET", url, 0, headers, body).toString(charset.name()); + } + + public static String getHttpContent(String url, int timeout, Charset charset, Map headers, String body) throws IOException { + return remoteHttpContent("GET", url, timeout, headers, body).toString(charset.name()); + } + + public static byte[] getHttpBytesContent(String url) throws IOException { + return remoteHttpContent("GET", url, 0, null, null).toByteArray(); + } + + public static byte[] getHttpBytesContent(String url, int timeout) throws IOException { + return remoteHttpContent("GET", url, timeout, null, null).toByteArray(); + } + + public static byte[] getHttpBytesContent(String url, Map headers, String body) throws IOException { + return remoteHttpContent("GET", url, 0, headers, body).toByteArray(); + } + + public static byte[] getHttpBytesContent(String url, int timeout, Map headers, String body) throws IOException { + return remoteHttpContent("GET", url, timeout, headers, body).toByteArray(); + } + + public static ByteArrayOutputStream remoteHttpContent(String method, String url, Map headers, String body) throws IOException { + return remoteHttpContent(method, url, 0, headers, body); + } + + public static ByteArrayOutputStream remoteHttpContent(String method, String url, int timeout, Map headers, String body) throws IOException { + return remoteHttpContentAsync(method, url, timeout, headers, body).join(); + } + + public static CompletableFuture postHttpContentAsync(String url) { + return remoteHttpContentAsync("POST", url, 0, null, null).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture postHttpContentAsync(String url, int timeout) { + return remoteHttpContentAsync("POST", url, timeout, null, null).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture postHttpContentAsync(String url, String body) { + return remoteHttpContentAsync("POST", url, 0, null, body).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture postHttpContentAsync(String url, int timeout, String body) { + return remoteHttpContentAsync("POST", url, timeout, null, body).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture postHttpContentAsync(String url, Map headers, String body) { + return remoteHttpContentAsync("POST", url, 0, headers, body).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture postHttpContentAsync(String url, int timeout, Map headers, String body) { + return remoteHttpContentAsync("POST", url, timeout, headers, body).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture postHttpContentAsync(String url, Charset charset) { + return remoteHttpContentAsync("POST", url, 0, null, null).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture postHttpContentAsync(String url, int timeout, Charset charset) { + return remoteHttpContentAsync("POST", url, timeout, null, null).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture postHttpContentAsync(String url, Charset charset, String body) { + return remoteHttpContentAsync("POST", url, 0, null, body).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture postHttpContentAsync(HttpClient client, String url, Charset charset, String body) { + return remoteHttpContentAsync(client, "POST", url, 0, null, body).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture postHttpContentAsync(String url, int timeout, Charset charset, String body) { + return remoteHttpContentAsync("POST", url, timeout, null, body).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture postHttpContentAsync(String url, Charset charset, Map headers, String body) { + return remoteHttpContentAsync("POST", url, 0, headers, body).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture postHttpContentAsync(String url, int timeout, Charset charset, Map headers, String body) { + return remoteHttpContentAsync("POST", url, timeout, headers, body).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture postHttpBytesContentAsync(String url) { + return remoteHttpContentAsync("POST", url, 0, null, null).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture postHttpBytesContentAsync(String url, int timeout) { + return remoteHttpContentAsync("POST", url, timeout, null, null).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture postHttpBytesContentAsync(String url, Map headers, String body) { + return remoteHttpContentAsync("POST", url, 0, headers, body).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture postHttpBytesContentAsync(String url, int timeout, Map headers, String body) { + return remoteHttpContentAsync("POST", url, timeout, headers, body).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture getHttpContentAsync(String url) { + return remoteHttpContentAsync("GET", url, 0, null, null).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture getHttpContentAsync(String url, int timeout) { + return remoteHttpContentAsync("GET", url, timeout, null, null).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture getHttpContentAsync(String url, Map headers, String body) { + return remoteHttpContentAsync("GET", url, 0, headers, body).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture getHttpContentAsync(String url, int timeout, Map headers, String body) { + return remoteHttpContentAsync("GET", url, timeout, headers, body).thenApply(out -> out.toString(StandardCharsets.UTF_8)); + } + + public static CompletableFuture getHttpContentAsync(String url, Charset charset) { + return remoteHttpContentAsync("GET", url, 0, null, null).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture getHttpContentAsync(String url, int timeout, Charset charset) { + return remoteHttpContentAsync("GET", url, timeout, null, null).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture getHttpContentAsync(String url, Charset charset, Map headers, String body) { + return remoteHttpContentAsync("GET", url, 0, headers, body).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture getHttpContentAsync(String url, int timeout, Charset charset, Map headers, String body) { + return remoteHttpContentAsync("GET", url, timeout, headers, body).thenApply(out -> out.toString(charset)); + } + + public static CompletableFuture getHttpBytesContentAsync(String url) { + return remoteHttpContentAsync("GET", url, 0, null, null).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture getHttpBytesContentAsync(String url, int timeout) { + return remoteHttpContentAsync("GET", url, timeout, null, null).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture getHttpBytesContentAsync(String url, Map headers, String body) { + return remoteHttpContentAsync("GET", url, 0, headers, body).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture getHttpBytesContentAsync(String url, int timeout, Map headers, String body) { + return remoteHttpContentAsync("GET", url, timeout, headers, body).thenApply(out -> out.toByteArray()); + } + + public static CompletableFuture remoteHttpContentAsync(String method, String url, Map headers, String body) { + return remoteHttpContentAsync(method, url, 0, headers, body); + } + + public static CompletableFuture remoteHttpContentAsync(String method, String url, int timeout, Map headers, String body) { + return remoteHttpContentAsync(httpClient, method, url, timeout, headers, body); + } + + public static CompletableFuture remoteHttpContentAsync(java.net.http.HttpClient client, String method, String url, int timeout, Map headers, String body) { + java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest.newBuilder().uri(URI.create(url)) + .timeout(Duration.ofMillis(timeout > 0 ? timeout : 6000)) + .method(method, body == null ? java.net.http.HttpRequest.BodyPublishers.noBody() : java.net.http.HttpRequest.BodyPublishers.ofString(body)); + if (headers != null) headers.forEach((n, v) -> builder.header(n, v)); + java.net.http.HttpClient c = client == null ? httpClient : client; + if (c == null) { + synchronized (clientLock) { + if (httpClient == null) { + httpClient = java.net.http.HttpClient.newHttpClient(); + } + } + c = httpClient; + } + return c.sendAsync(builder.build(), java.net.http.HttpResponse.BodyHandlers.ofByteArray()) + .thenCompose((java.net.http.HttpResponse resp) -> { + final int rs = resp.statusCode(); + if (rs == 301 || rs == 302) { + Optional opt = resp.headers().firstValue("Location"); + if (opt.isPresent()) { + return remoteHttpContentAsync(client, method, opt.get(), timeout, headers, body); + } else { + return CompletableFuture.failedFuture(new IOException(url + " httpcode = " + rs + ", but not found Localtion")); + } + } + byte[] result = resp.body(); + if (rs == 200 || result != null) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + if (result != null) { + if ("gzip".equalsIgnoreCase(resp.headers().firstValue("content-encoding").orElse(null))) { + try { + GZIPInputStream in = new GZIPInputStream(new ByteArrayInputStream(result)); + in.transferTo(out); + } catch (IOException e) { + return CompletableFuture.failedFuture(e); + } + } else { + out.writeBytes(result); + } + } + return CompletableFuture.completedFuture(out); + } + return CompletableFuture.failedFuture(new IOException(url + " httpcode = " + rs)); + }); + } +// +// public static ByteArrayOutputStream remoteHttpContent(SSLContext ctx, String method, String url, int timeout, Map headers, String body) throws IOException { +// HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection(); +// boolean opening = true; +// try { +// conn.setConnectTimeout(timeout > 0 ? timeout : 3000); +// conn.setReadTimeout(timeout > 0 ? timeout : 3000); +// if (conn instanceof HttpsURLConnection) { +// HttpsURLConnection httpsconn = ((HttpsURLConnection) conn); +// httpsconn.setSSLSocketFactory((ctx == null ? DEFAULTSSL_CONTEXT : ctx).getSocketFactory()); +// httpsconn.setHostnameVerifier(defaultVerifier); +// } +// conn.setRequestMethod(method); +// if (headers != null) { +// for (Map.Entry en : headers.entrySet()) { +// conn.setRequestProperty(en.getKey(), en.getValue()); +// } +// } +// if (body != null && !body.isEmpty()) { //conn.getOutputStream()会将GET强制变成POST +// conn.setDoInput(true); +// conn.setDoOutput(true); +// conn.getOutputStream().write(body.getBytes(UTF_8)); +// } +// conn.connect(); +// int rs = conn.getResponseCode(); +// if (rs == 301 || rs == 302) { +// String newurl = conn.getHeaderField("Location"); +// conn.disconnect(); +// opening = false; +// return remoteHttpContent(ctx, method, newurl, timeout, headers, body); +// } +// InputStream in = (rs < 400 || rs == 404) && rs != 405 ? conn.getInputStream() : conn.getErrorStream(); +// if ("gzip".equalsIgnoreCase(conn.getContentEncoding())) in = new GZIPInputStream(in); +// ByteArrayOutputStream out = new ByteArrayOutputStream(1024); +// byte[] bytes = new byte[1024]; +// int pos; +// while ((pos = in.read(bytes)) != -1) { +// out.write(bytes, 0, pos); +// } +// in.close(); +// return out; +// } finally { +// if (opening) conn.disconnect(); +// } +// } + + public static String read(InputStream in) throws IOException { + return read(in, StandardCharsets.UTF_8, false); + } + + public static String readThenClose(InputStream in) throws IOException { + return read(in, StandardCharsets.UTF_8, true); + } + + public static String read(InputStream in, String charsetName) throws IOException { + return read(in, Charset.forName(charsetName), false); + } + + public static String read(InputStream in, Charset charset) throws IOException { + return read(in, charset, false); + } + + private static String read(InputStream in, Charset charset, boolean close) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte[] bytes = new byte[1024]; + int pos; + while ((pos = in.read(bytes)) != -1) { + out.write(bytes, 0, pos); + } + if (close) in.close(); + return charset == null ? out.toString() : out.toString(charset); + } + + public static ByteArrayOutputStream readStream(InputStream in) throws IOException { + return readStream(in, false); + } + + public static ByteArrayOutputStream readStreamThenClose(InputStream in) throws IOException { + return readStream(in, true); + } + + private static ByteArrayOutputStream readStream(InputStream in, boolean close) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + byte[] bytes = new byte[1024]; + int pos; + while ((pos = in.read(bytes)) != -1) { + out.write(bytes, 0, pos); + } + if (close) in.close(); + return out; + } + + public static byte[] readBytes(File file) throws IOException { + return readBytesThenClose(new FileInputStream(file)); + } + + public static byte[] readBytes(InputStream in) throws IOException { + return readStream(in).toByteArray(); + } + + public static byte[] readBytesThenClose(InputStream in) throws IOException { + return readStreamThenClose(in).toByteArray(); + } +} diff --git a/src/org/redkale/util/Version.java b/src/main/java/org/redkale/util/Version.java similarity index 95% rename from src/org/redkale/util/Version.java rename to src/main/java/org/redkale/util/Version.java index 2165d3fc7..a4f0ecbf0 100644 --- a/src/org/redkale/util/Version.java +++ b/src/main/java/org/redkale/util/Version.java @@ -1,29 +1,29 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.*; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 版本, 可用于标记Service的接口版本变化 - * - *

    - * 详情见: https://redkale.org - * - * @since 2.1.0 - * - * @author zhangjx - */ -@Inherited -@Documented -@Target({TYPE, METHOD}) -@Retention(RUNTIME) -public @interface Version { - - int value(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 版本, 可用于标记Service的接口版本变化 + * + *

    + * 详情见: https://redkale.org + * + * @since 2.1.0 + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({TYPE, METHOD}) +@Retention(RUNTIME) +public @interface Version { + + int value(); +} diff --git a/src/main/java/org/redkale/util/XmlReader.java b/src/main/java/org/redkale/util/XmlReader.java new file mode 100644 index 000000000..428969edd --- /dev/null +++ b/src/main/java/org/redkale/util/XmlReader.java @@ -0,0 +1,363 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.function.BiFunction; +import org.redkale.util.AnyValue.DefaultAnyValue; + +/** + * 简单的xml读取器, 只读element节点信息,其他信息(如: namespace、comment、docdecl等)都会丢弃 + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class XmlReader { + + protected int position = -1; + + private char[] text; + + private int limit; + + private Deque tags = new ArrayDeque<>(); + + protected int lineNumber; + + protected int columnNumber; + + protected BiFunction attrFunc; + + private static class TagNode { + + public String tag; + + public DefaultAnyValue config; + + public TagNode(String tag, DefaultAnyValue node) { + this.tag = tag; + this.config = node; + } + + } + + public XmlReader(String text) { + this(Utility.charArray(text)); + } + + public XmlReader(char[] text) { + setText(text, 0, text.length); + } + + public XmlReader(char[] text, int start, int len) { + setText(text, start, len); + } + + private void setText(char[] text, int start, int len) { + this.text = text; + this.position = start - 1; + this.limit = start + len - 1; + } + + public XmlReader attrFunc(BiFunction func) { + this.attrFunc = func; + return this; + } + + public AnyValue read() { + DefaultAnyValue root = DefaultAnyValue.create(); + char ch; + lineNumber++; + ByteArray array = new ByteArray(128); + while ((ch = nextGoodChar()) > 0) { + if (ch == '<') { + char ch2 = nextChar(); + if (ch2 == '!') { + char ch3 = nextChar(); + if (ch3 == '-') { + readComment(); + } else if (ch3 == '[') { + readCDSect(root, array); + } else if (ch3 == 'D') { + readDocdecl(root, array); + } else { + throw newException("unexpected character in markup " + ch3); + } + } else if (ch2 == '?') { + readXmlDecl(root, array); + } else if (ch2 == '/') { //节点结束 + String tag = readEndTag(); + if (tags.isEmpty()) throw newException("unexpected end tag " + tag); + if (!tags.getFirst().tag.equals(tag)) throw newException("expected end tag " + tags.getFirst().tag + " but " + tag); + tags.removeFirst(); + if (tags.isEmpty()) break; + } else { //节点开始 + readStartTag(root); + } + } else { + int start = this.position; + for (;;) { + ch = nextChar(); + if (ch == '<') { + backChar(ch); + break; + } + } + String content = escape(new String(this.text, start, this.position + 1 - start)); + if (tags.isEmpty()) { + root.addValue(AnyValue.XML_TEXT_NODE_NAME, content); + } else { + tags.getFirst().config.addValue(AnyValue.XML_TEXT_NODE_NAME, content); + } + } + } + return root; + } + + /** + * 读取下一个字符, 不跳过空白字符 + * + * @return 空白字符或有效字符 + */ + protected char nextChar() { + if (this.position == this.limit) throw newException("read EOF"); + char ch = this.text[++this.position]; + if (ch == '\n') { + this.lineNumber++; + this.columnNumber = 0; + } + this.columnNumber++; + return ch; + } + + /** + * 跳过空白字符, 返回一个非空白字符 + * + * @return 有效字符 + */ + protected char nextGoodChar() { + char c = nextChar(); + if (c > ' ') return c; + for (;;) { + c = nextChar(); + if (c > ' ') return c; + } + } + + /** + * 回退最后读取的字符 + * + * @param ch 后退的字符 + */ + protected void backChar(char ch) { + this.position--; + } + + //返回是否endtag, 即以 />结尾 + protected boolean readTagAttribute(String tag, DefaultAnyValue config) { + boolean first = true; + boolean endtag = false; + boolean endattr = false; + char ch = nextGoodChar(); + if (ch == '/') return true; + int start = this.position; + for (;;) { + if (ch == '=') { + break; + } else if (ch >= '0' && ch <= '9') { + if (first) throw newException("illegal character " + ch); + } else if (ch >= 'a' && ch <= 'z') { + } else if (ch >= 'A' && ch <= 'Z') { + } else if (ch == '.' || ch == '-' || ch == '_' || ch == ':') { + if (first) throw newException("illegal character " + ch); + } else { + throw newException("illegal character " + ch); + } + ch = nextChar(); + first = false; + } + String attrName = new String(this.text, start, this.position - start); + String attrValue; + ch = nextGoodChar(); + if (ch == '"' || ch == '\'') { + final char quote = ch; + start = this.position + 1; + for (;;) { + ch = nextChar(); + if (ch == quote) break; + } + attrValue = escape(new String(this.text, start, this.position - start)); + } else { + ch = nextGoodChar(); + start = this.position; + for (;;) { + if (ch == '/') { + endtag = true; + break; + } + if (ch == '>') { + endattr = true; + break; + } else if (ch <= ' ') { + break; + } + ch = nextChar(); + } + attrValue = escape(new String(this.text, start, this.position - start)); + } + if (attrFunc != null) attrValue = attrFunc.apply(attrName, attrValue); + config.addValue(attrName, attrValue); + if (endtag) return endtag; + if (endattr) return false; + ch = nextGoodChar(); + if (ch == '>') return false; + if (ch == '/') return true; + backChar(ch); + return readTagAttribute(tag, config); + } + + protected void readStartTag(DefaultAnyValue root) { + final int start = this.position; + boolean hasattr = false; + boolean endtag = false; + + char ch = nextGoodChar(); + for (;;) { + if (ch == '>') { + break; + } else if (ch == '/') { + endtag = true; + break; + } else if (ch <= ' ') { + hasattr = true; + break; + } + ch = nextChar(); + } + final String tag = new String(this.text, start, this.position - start).trim(); + DefaultAnyValue config = DefaultAnyValue.create(); + TagNode tagNode = new TagNode(tag, config); + if (tags.isEmpty()) { + root.addValue(tag, tagNode.config); + } else { + tags.getFirst().config.addValue(tag, tagNode.config); + } + if (hasattr) endtag = readTagAttribute(tag, config); + if (endtag) { + if (nextChar() != '>') throw newException("expected /> for tag end"); + return; + } + tags.addFirst(tagNode); + } + + protected String readEndTag() { + final int start = this.position + 1;//跳过'/' + char ch; + for (;;) { + ch = nextChar(); + if (ch == '>') break; + } + return new String(this.text, start, this.position - start).trim(); + } + + protected void readComment() { //读取到 ' && dash >= 2) { + break; + } else if (ch == '-') { + dash++; + } else { + dash = 0; + } + } + } + + protected void readDocdecl(DefaultAnyValue root, ByteArray array) {//读取到 ' + if (nextChar() != 'O') throw newException("expected ') { + array.putByte(ch); + root.setValue("", array.toString(StandardCharsets.UTF_8)); + break; + } + array.putByte(ch); + } + } + + protected void readCDSect(DefaultAnyValue root, ByteArray array) {//读取到 ' Char*)) ]]>' + if (nextChar() != 'C') throw newException("expected ') { + if (this.text[this.position - 1] != ']' && this.text[this.position - 2] != ']') { + throw newException("in cdsect after two dashes (]]) next character must be >"); + } + array.putByte(ch); + root.setValue("", array.toString(StandardCharsets.UTF_8)); + break; + } + array.putByte(ch); + } + } + + protected void readXmlDecl(DefaultAnyValue root, ByteArray array) {//读取到 + char ch; + array.clear(); + array.putByte('<'); + array.putByte('?'); + for (;;) { + ch = nextChar(); + if (ch == '>') { + if (this.text[this.position - 1] != '?') { + throw newException("in xmldecl after ? next character must be >"); + } + array.putByte(ch); + root.setValue("", array.toString(StandardCharsets.UTF_8)); + break; + } + array.putByte(ch); + } + } + + protected static String escape(String value) { + return value.replace(""", "/").replace("<", "<") + .replace(">", ">").replace("&", "&"); + } + + protected RuntimeException newException(String msg) { + return newException(msg, (Throwable) null); + } + + protected RuntimeException newException(String msg, Throwable chain) { + return new RuntimeException((msg == null ? "" : (msg + " ")) + + "(line: " + this.lineNumber + ", column: " + this.columnNumber + ", position:" + this.position + ") " + + (chain == null ? "" : ("caused by: " + chain))); + } +} diff --git a/src/org/redkale/util/package-info.java b/src/main/java/org/redkale/util/package-info.java similarity index 93% rename from src/org/redkale/util/package-info.java rename to src/main/java/org/redkale/util/package-info.java index 1b87e373b..2b8f05886 100644 --- a/src/org/redkale/util/package-info.java +++ b/src/main/java/org/redkale/util/package-info.java @@ -1,4 +1,4 @@ -/** - * Redkale工具包 - */ -package org.redkale.util; +/** + * Redkale工具包 + */ +package org.redkale.util; diff --git a/src/org/redkale/watch/WatchFilter.java b/src/main/java/org/redkale/watch/WatchFilter.java similarity index 95% rename from src/org/redkale/watch/WatchFilter.java rename to src/main/java/org/redkale/watch/WatchFilter.java index 2050e5b49..27d060849 100644 --- a/src/org/redkale/watch/WatchFilter.java +++ b/src/main/java/org/redkale/watch/WatchFilter.java @@ -1,18 +1,18 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.watch; - -import org.redkale.net.http.HttpFilter; - -/** - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class WatchFilter extends HttpFilter { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.watch; + +import org.redkale.net.http.HttpFilter; + +/** + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public abstract class WatchFilter extends HttpFilter { + +} diff --git a/src/org/redkale/watch/WatchService.java b/src/main/java/org/redkale/watch/WatchService.java similarity index 95% rename from src/org/redkale/watch/WatchService.java rename to src/main/java/org/redkale/watch/WatchService.java index bf6f32fb8..6f22776c5 100644 --- a/src/org/redkale/watch/WatchService.java +++ b/src/main/java/org/redkale/watch/WatchService.java @@ -1,20 +1,20 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.watch; - -import org.redkale.service.*; - -/** - * 只给WATCH协议的Server才能加载的Service,其他协议的Server均不能自动加载WatchService - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface WatchService extends Service { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.watch; + +import org.redkale.service.*; + +/** + * 只给WATCH协议的Server才能加载的Service,其他协议的Server均不能自动加载WatchService + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public interface WatchService extends Service { + +} diff --git a/src/org/redkale/watch/WatchServlet.java b/src/main/java/org/redkale/watch/WatchServlet.java similarity index 95% rename from src/org/redkale/watch/WatchServlet.java rename to src/main/java/org/redkale/watch/WatchServlet.java index 4739f3513..e8d3838a0 100644 --- a/src/org/redkale/watch/WatchServlet.java +++ b/src/main/java/org/redkale/watch/WatchServlet.java @@ -1,19 +1,19 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.watch; - -import org.redkale.net.http.HttpServlet; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class WatchServlet extends HttpServlet { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.watch; + +import org.redkale.net.http.HttpServlet; + +/** + * + *

    + * 详情见: https://redkale.org + * + * @author zhangjx + */ +public class WatchServlet extends HttpServlet { + +} diff --git a/src/org/redkale/watch/package-info.java b/src/main/java/org/redkale/watch/package-info.java similarity index 96% rename from src/org/redkale/watch/package-info.java rename to src/main/java/org/redkale/watch/package-info.java index eb4698e86..a110591db 100644 --- a/src/org/redkale/watch/package-info.java +++ b/src/main/java/org/redkale/watch/package-info.java @@ -1,4 +1,4 @@ -/** - * 提供Redkale服务的监控、动态部署、数据收集功能 - */ -package org.redkale.watch; +/** + * 提供Redkale服务的监控、动态部署、数据收集功能 + */ +package org.redkale.watch; diff --git a/src/org/redkale/boot/ApiDocsService.java b/src/org/redkale/boot/ApiDocsService.java deleted file mode 100644 index 50990410a..000000000 --- a/src/org/redkale/boot/ApiDocsService.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot; - -import java.io.*; -import java.lang.reflect.*; -import java.util.*; -import javax.persistence.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.http.*; -import org.redkale.source.*; -import org.redkale.util.*; - -/** - * API接口文档生成类,作用:生成Application实例中所有HttpServer的可用HttpServlet的API接口方法
    - * 继承 HttpBaseServlet 是为了获取 WebMapping 信息 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public final class ApiDocsService { - - private final Application app; //Application全局对象 - - public ApiDocsService(Application app) { - this.app = app; - } - - public void run() throws Exception { - List serverList = new ArrayList<>(); - Field __prefix = HttpServlet.class.getDeclaredField("_prefix"); - __prefix.setAccessible(true); - Map>> typesmap = new LinkedHashMap<>(); - for (NodeServer node : app.servers) { - if (!(node instanceof NodeHttpServer)) continue; - final Map map = new LinkedHashMap<>(); - serverList.add(map); - HttpServer server = node.getServer(); - map.put("address", server.getSocketAddress()); - List> servletsList = new ArrayList<>(); - map.put("servlets", servletsList); - for (HttpServlet servlet : server.getPrepareServlet().getServlets()) { - if (!(servlet instanceof HttpServlet)) continue; - WebServlet ws = servlet.getClass().getAnnotation(WebServlet.class); - if (ws == null) { - System.err.println(servlet + " not found @WebServlet"); - continue; - } - final Map servletmap = new LinkedHashMap<>(); - String prefix = (String) __prefix.get(servlet); - String[] urlregs = ws.value(); - if (prefix != null && !prefix.isEmpty()) { - for (int i = 0; i < urlregs.length; i++) { - urlregs[i] = prefix + urlregs[i]; - } - } - servletmap.put("urlregs", urlregs); - servletmap.put("moduleid", ws.moduleid()); - servletmap.put("name", ws.name()); - servletmap.put("comment", ws.comment()); - - List mappingsList = new ArrayList<>(); - servletmap.put("mappings", mappingsList); - final Class selfClz = servlet.getClass(); - Class clz = servlet.getClass(); - HashSet actionurls = new HashSet<>(); - do { - if (Modifier.isAbstract(clz.getModifiers())) break; - for (Method method : clz.getMethods()) { - if (method.getParameterCount() != 2) continue; - HttpMapping action = method.getAnnotation(HttpMapping.class); - if (action == null) continue; - if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法 - final Map mappingmap = new LinkedHashMap<>(); - if (actionurls.contains(action.url())) continue; - mappingmap.put("url", prefix + action.url()); - actionurls.add(action.url()); - mappingmap.put("auth", action.auth()); - mappingmap.put("actionid", action.actionid()); - mappingmap.put("comment", action.comment()); - List paramsList = new ArrayList<>(); - mappingmap.put("params", paramsList); - List results = new ArrayList<>(); - for (final Class rtype : action.results()) { - results.add(rtype.getName()); - if (typesmap.containsKey(rtype.getName())) continue; - if(rtype.getName().startsWith("java.")) continue; - if(rtype.getName().startsWith("javax.")) continue; - final boolean filter = FilterBean.class.isAssignableFrom(rtype); - final Map> typemap = new LinkedHashMap<>(); - Class loop = rtype; - do { - if (loop == null || loop.isInterface()) break; - for (Field field : loop.getDeclaredFields()) { - if (Modifier.isFinal(field.getModifiers())) continue; - if (Modifier.isStatic(field.getModifiers())) continue; - - Map fieldmap = new LinkedHashMap<>(); - fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName()); - - Comment comment = field.getAnnotation(Comment.class); - Column col = field.getAnnotation(Column.class); - FilterColumn fc = field.getAnnotation(FilterColumn.class); - if (comment != null) { - fieldmap.put("comment", comment.value()); - } else if (col != null) { - fieldmap.put("comment", col.comment()); - } else if (fc != null) { - fieldmap.put("comment", fc.comment()); - } - fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null)); - fieldmap.put("updatable", (filter || col == null || col.updatable())); - if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) { - if (field.getAnnotation(RestAddress.class) != null) continue; - } - - typemap.put(field.getName(), fieldmap); - } - } while ((loop = loop.getSuperclass()) != Object.class); - typesmap.put(rtype.getName(), typemap); - } - mappingmap.put("results", results); - for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) { - final Map parammap = new LinkedHashMap<>(); - final boolean isarray = param.type().isArray(); - final Class ptype = isarray ? param.type().getComponentType() : param.type(); - parammap.put("name", param.name()); - parammap.put("radix", param.radix()); - parammap.put("type", ptype.getName() + (isarray ? "[]" : "")); - parammap.put("src", param.src()); - parammap.put("comment", param.comment()); - parammap.put("required", param.required()); - paramsList.add(parammap); - if (ptype.isPrimitive() || ptype == String.class) continue; - if (typesmap.containsKey(ptype.getName())) continue; - if(ptype.getName().startsWith("java.")) continue; - if(ptype.getName().startsWith("javax.")) continue; - - final Map> typemap = new LinkedHashMap<>(); - Class loop = ptype; - final boolean filter = FilterBean.class.isAssignableFrom(loop); - do { - if (loop == null || loop.isInterface()) break; - for (Field field : loop.getDeclaredFields()) { - if (Modifier.isFinal(field.getModifiers())) continue; - if (Modifier.isStatic(field.getModifiers())) continue; - - Map fieldmap = new LinkedHashMap<>(); - fieldmap.put("type", field.getType().isArray() ? (field.getType().getComponentType().getName() + "[]") : field.getGenericType().getTypeName()); - - Column col = field.getAnnotation(Column.class); - FilterColumn fc = field.getAnnotation(FilterColumn.class); - Comment comment = field.getAnnotation(Comment.class); - if (comment != null) { - fieldmap.put("comment", comment.value()); - } else if (col != null) { - fieldmap.put("comment", col.comment()); - } else if (fc != null) { - fieldmap.put("comment", fc.comment()); - } - fieldmap.put("primary", !filter && (field.getAnnotation(Id.class) != null)); - fieldmap.put("updatable", (filter || col == null || col.updatable())); - - if (servlet.getClass().getAnnotation(Rest.RestDyn.class) != null) { - if (field.getAnnotation(RestAddress.class) != null) continue; - } - - typemap.put(field.getName(), fieldmap); - } - } while ((loop = loop.getSuperclass()) != Object.class); - - typesmap.put(ptype.getName(), typemap); - } - mappingmap.put("result", action.result()); - mappingsList.add(mappingmap); - } - } while ((clz = clz.getSuperclass()) != HttpServlet.class); - mappingsList.sort((o1, o2) -> ((String) o1.get("url")).compareTo((String) o2.get("url"))); - servletsList.add(servletmap); - } - servletsList.sort((o1, o2) -> { - String[] urlregs1 = (String[]) o1.get("urlregs"); - String[] urlregs2 = (String[]) o2.get("urlregs"); - return urlregs1.length > 0 ? (urlregs2.length > 0 ? urlregs1[0].compareTo(urlregs2[0]) : 1) : -1; - }); - } - Map resultmap = new LinkedHashMap<>(); - resultmap.put("servers", serverList); - resultmap.put("types", typesmap); - final String json = JsonConvert.root().convertTo(resultmap); - final FileOutputStream out = new FileOutputStream(new File(app.getHome(), "apidoc.json")); - out.write(json.getBytes("UTF-8")); - out.close(); - File doctemplate = new File(app.getConfPath().toString(), "apidoc-template.html"); - InputStream in = null; - if (doctemplate.isFile() && doctemplate.canRead()) { - in = new FileInputStream(doctemplate); - } - if (in == null) in = ApiDocsService.class.getResourceAsStream("apidoc-template.html"); - String content = Utility.read(in).replace("'${content}'", json); - in.close(); - FileOutputStream outhtml = new FileOutputStream(new File(app.getHome(), "apidoc.html")); - outhtml.write(content.getBytes("UTF-8")); - outhtml.close(); - } - -} diff --git a/src/org/redkale/boot/watch/SourceWatchService.java b/src/org/redkale/boot/watch/SourceWatchService.java deleted file mode 100644 index f354af155..000000000 --- a/src/org/redkale/boot/watch/SourceWatchService.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.boot.watch; - -import java.io.*; -import java.lang.reflect.Method; -import java.util.*; -import javax.annotation.Resource; -import org.redkale.boot.Application; -import org.redkale.net.http.*; -import org.redkale.service.*; -import org.redkale.source.*; -import org.redkale.util.*; - -/** - * WATCH服务, 操作DataSource源 - * - * @author zhangjx - */ -@RestService(name = "source", catalog = "watch", repair = false) -public class SourceWatchService extends AbstractWatchService { - - @Comment("不存在的Source") - public static final int RET_SOURCE_NOT_EXISTS = 1605_0001; - - @Comment("Source不支持getReadPoolSource/getWritePoolSource方法") - public static final int RET_SOURCE_CHANGE_METHOD_NOT_EXISTS = 1605_0002; - - @Comment("PoolSource调用change方法失败") - public static final int RET_SOURCE_METHOD_INVOKE_NOT_EXISTS = 1605_0003; - - @Resource(name = "APP_HOME") - protected File home; - - @Resource - protected Application application; - - @RestMapping(name = "change", auth = false, comment = "动态更改DataSource的配置") - public RetResult addNode(@RestParam(name = "name", comment = "DataSource的标识") final String name, - @RestParam(name = "properties", comment = "配置") Properties properties, - @RestParam(name = "xmlpath", comment = "配置文件路径") String xmlpath) throws IOException { - if (name == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (name)"); - if (properties == null && xmlpath != null) { - File f = new File(xmlpath); - if (!f.isFile()) f = new File(home, xmlpath); - if (!f.isFile()) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found file (" + xmlpath + ")"); - FileInputStream in = new FileInputStream(f); - Map map = DataSources.loadPersistenceXml(in); - properties = map.get(name); - } - if (properties == null) return new RetResult(RET_WATCH_PARAMS_ILLEGAL, "not found param (properties)"); - DataSource source = null; - for (DataSource s : application.getDataSources()) { - String resName = ((Resourcable) s).resourceName(); - if (resName == null) continue; - if (!resName.equals(name)) continue; - source = s; - break; - } - if (source == null) return new RetResult(RET_SOURCE_NOT_EXISTS, "not found source (name = " + name + ")"); - Method readPoolMethod = null; - Method writePoolMethod = null; - Class stype = source.getClass(); - do { - for (Method m : stype.getDeclaredMethods()) { - if (!PoolSource.class.isAssignableFrom(m.getReturnType())) continue; - if (m.getParameterCount() != 0) continue; - if (m.getName().equals("getReadPoolSource")) { - readPoolMethod = m; - } else if (m.getName().equals("getWritePoolSource")) { - writePoolMethod = m; - } - } - } while ((stype = stype.getSuperclass()) != Object.class); - if (readPoolMethod == null) return new RetResult(RET_SOURCE_CHANGE_METHOD_NOT_EXISTS, "not found source method(getReadPoolSource)"); - if (writePoolMethod == null) return new RetResult(RET_SOURCE_CHANGE_METHOD_NOT_EXISTS, "not found source method(getWritePoolSource)"); - readPoolMethod.setAccessible(true); - writePoolMethod.setAccessible(true); - try { - PoolSource readPoolSource = (PoolSource) readPoolMethod.invoke(source); - PoolSource writePoolSource = (PoolSource) writePoolMethod.invoke(source); - readPoolSource.change(properties); - writePoolSource.change(properties); - return RetResult.success(); - } catch (Exception e) { - return new RetResult(RET_SOURCE_METHOD_INVOKE_NOT_EXISTS, "poolsource invoke method('change') error"); - } - } - - @RestMapping(name = "test1", auth = false, comment = "预留") - public RetResult test1() { - return RetResult.success(); - } - - @RestMapping(name = "test2", auth = false, comment = "预留") - public RetResult test2() { - return RetResult.success(); - } - - @RestMapping(name = "test3", auth = false, comment = "预留") - public RetResult test3() { - return RetResult.success(); - } - - @RestMapping(name = "test4", auth = false, comment = "预留") - public RetResult test4() { - return RetResult.success(); - } - - @RestMapping(name = "test5", auth = false, comment = "预留") - public RetResult test5() { - return RetResult.success(); - } - - @RestMapping(name = "test6", auth = false, comment = "预留") - public RetResult test6() { - return RetResult.success(); - } -} diff --git a/src/org/redkale/net/AsyncAioTcpConnection.java b/src/org/redkale/net/AsyncAioTcpConnection.java deleted file mode 100644 index 9e028123c..000000000 --- a/src/org/redkale/net/AsyncAioTcpConnection.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.Set; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.*; -import javax.net.ssl.SSLContext; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Deprecated //@since 2.3.0 -class AsyncAioTcpConnection extends AsyncConnection { - - //private final Semaphore semaphore = new Semaphore(1); - private int readTimeoutSeconds; - - private int writeTimeoutSeconds; - - private final AsynchronousSocketChannel channel; - - private final SocketAddress remoteAddress; - - private BlockingQueue writeQueue; - - public AsyncAioTcpConnection(int bufferCapacity, Supplier bufferSupplier, Consumer bufferConsumer, - final AsynchronousSocketChannel ch, final SSLContext sslContext, final SocketAddress addr0, - final int readTimeoutSeconds, final int writeTimeoutSeconds, - final AtomicLong livingCounter, final AtomicLong closedCounter) { - super(false, bufferCapacity, bufferSupplier, bufferConsumer, sslContext, livingCounter, closedCounter); - this.channel = ch; - this.readTimeoutSeconds = readTimeoutSeconds; - this.writeTimeoutSeconds = writeTimeoutSeconds; - SocketAddress addr = addr0; - if (addr == null) { - try { - addr = ch.getRemoteAddress(); - } catch (Exception e) { - //do nothing - } - } - this.remoteAddress = addr; - } - - @Override - public boolean shutdownInput() { - try { - this.channel.shutdownInput(); - return true; - } catch (IOException e) { - return false; - } - } - - @Override - public boolean shutdownOutput() { - try { - this.channel.shutdownOutput(); - return true; - } catch (IOException e) { - return false; - } - } - - @Override - public boolean setOption(SocketOption name, T value) { - try { - this.channel.setOption(name, value); - return true; - } catch (IOException e) { - return false; - } - } - - @Override - public Set> supportedOptions() { - return this.channel.supportedOptions(); - } - - @Override - public void read(CompletionHandler handler) { - this.readtime = System.currentTimeMillis(); - ByteBuffer dst = pollReadBuffer(); - if (readTimeoutSeconds > 0) { - channel.read(dst, readTimeoutSeconds, TimeUnit.SECONDS, dst, handler); - } else { - channel.read(dst, dst, handler); - } - } - -// private void nextWrite(Throwable exc, A attachment) { -// BlockingQueue queue = this.writeQueue; -// if (queue != null && exc != null && !isOpen()) { -// WriteEntry entry; -// while ((entry = queue.poll()) != null) { -// try { -// entry.writeHandler.failed(exc, entry.writeAttachment); -// } catch (Throwable e) { -// e.printStackTrace(System.err); -// } -// } -// return; -// } -// WriteEntry entry = queue == null ? null : queue.poll(); -// -// if (entry != null) { -// try { -// if (entry.writeOneBuffer == null) { -// write(false, entry.writeBuffers, entry.writeOffset, entry.writeLength, entry.writeAttachment, entry.writeHandler); -// } else { -// write(false, entry.writeOneBuffer, entry.writeAttachment, entry.writeHandler); -// } -// } catch (Exception e) { -// entry.writeHandler.failed(e, entry.writeAttachment); -// } -// } else { -// semaphore.release(); -// } -// } - @Override - public void write(ByteBuffer src, A attachment, CompletionHandler handler) { - write(true, src, attachment, handler); - } - - private void write(boolean acquire, ByteBuffer src, A attachment, CompletionHandler handler) { - WriteOneCompletionHandler newHandler = new WriteOneCompletionHandler(src, handler); - if (!channel.isOpen()) { - newHandler.failed(new ClosedChannelException(), attachment); - return; - } - this.writetime = System.currentTimeMillis(); - try { - if (writeTimeoutSeconds > 0) { - channel.write(src, writeTimeoutSeconds, TimeUnit.SECONDS, attachment, newHandler); - } else { - channel.write(src, attachment, newHandler); - } - } catch (Exception e) { - newHandler.failed(e, attachment); - } - } - - @Override - public void write(ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler handler) { - write(true, srcs, offset, length, attachment, handler); - } - - private void write(boolean acquire, ByteBuffer[] srcs, int offset, int length, A attachment, final CompletionHandler handler) { -// if (acquire && !semaphore.tryAcquire()) { -// if (this.writeQueue == null) { -// synchronized (semaphore) { -// if (this.writeQueue == null) { -// this.writeQueue = new LinkedBlockingDeque<>(); -// } -// } -// } -// this.writeQueue.add(new WriteEntry(srcs, offset, length, attachment, handler)); -// return; -// } - WriteMoreCompletionHandler newHandler = new WriteMoreCompletionHandler(srcs, offset, length, handler); - if (!channel.isOpen()) { - newHandler.failed(new ClosedChannelException(), attachment); - return; - } - this.writetime = System.currentTimeMillis(); - try { - channel.write(srcs, offset, length, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS, attachment, newHandler); - } catch (Exception e) { - newHandler.failed(e, attachment); - } - } - - @Override - public void setReadTimeoutSeconds(int readTimeoutSeconds) { - this.readTimeoutSeconds = readTimeoutSeconds; - } - - @Override - public void setWriteTimeoutSeconds(int writeTimeoutSeconds) { - this.writeTimeoutSeconds = writeTimeoutSeconds; - } - - @Override - public int getReadTimeoutSeconds() { - return this.readTimeoutSeconds; - } - - @Override - public int getWriteTimeoutSeconds() { - return this.writeTimeoutSeconds; - } - - @Override - public final SocketAddress getRemoteAddress() { - return remoteAddress; - } - - @Override - public SocketAddress getLocalAddress() { - try { - return channel.getLocalAddress(); - } catch (IOException e) { - return null; - } - } - - @Override - public final ReadableByteChannel readableByteChannel() { - return new ReadableByteChannel() { - @Override - public int read(ByteBuffer dst) throws IOException { - try { - return channel.read(dst).get(readTimeoutSeconds > 0 ? readTimeoutSeconds : 6, TimeUnit.SECONDS); - } catch (Exception e) { - throw new IOException(e); - } - } - - @Override - @SuppressWarnings("InfiniteRecursion") - public boolean isOpen() { - return isOpen(); - } - - @Override - @SuppressWarnings("InfiniteRecursion") - public void close() throws IOException { - close(); - } - }; - } - - @Override - public final WritableByteChannel writableByteChannel() { - return new WritableByteChannel() { - @Override - public int write(ByteBuffer src) throws IOException { - try { - return channel.write(src).get(readTimeoutSeconds > 0 ? readTimeoutSeconds : 6, TimeUnit.SECONDS); - } catch (Exception e) { - throw new IOException(e); - } - } - - @Override - @SuppressWarnings("InfiniteRecursion") - public boolean isOpen() { - return isOpen(); - } - - @Override - @SuppressWarnings("InfiniteRecursion") - public void close() throws IOException { - close(); - } - }; - } - - @Override - public InputStream newInputStream() { - return Channels.newInputStream(readableByteChannel()); - } - - @Override - public final void close() throws IOException { - super.close(); - channel.shutdownInput(); - channel.shutdownOutput(); - channel.close(); - BlockingQueue queue = this.writeQueue; - if (queue == null) return; - WriteEntry entry; - Exception ex = null; - while ((entry = queue.poll()) != null) { - if (ex == null) ex = new ClosedChannelException(); - try { - entry.writeHandler.failed(ex, entry.writeAttachment); - } catch (Exception e) { - } - } - } - - @Override - public final boolean isOpen() { - return channel.isOpen(); - } - - @Override - public final boolean isTCP() { - return true; - } - - @Override - protected void continueRead() { - throw new UnsupportedOperationException("Not supported yet."); - } - - private class WriteMoreCompletionHandler implements CompletionHandler { - - private final CompletionHandler writeHandler; - - private final ByteBuffer[] writeBuffers; - - private int writeOffset; - - private int writeLength; - - private int writeCount; - - public WriteMoreCompletionHandler(ByteBuffer[] buffers, int offset, int length, CompletionHandler handler) { - this.writeBuffers = buffers; - this.writeOffset = offset; - this.writeLength = length; - this.writeHandler = handler; - } - - @Override - public void completed(Long result, A attachment) { - if (result >= 0) { - writeCount += result; - try { - int incre = -1; - for (int i = writeOffset; i < (writeOffset + writeLength); i++) { - if (writeBuffers[i].hasRemaining()) { - incre = i - writeOffset; - break; - } - } - if (incre >= 0) { - writeOffset += incre; - writeLength -= incre; - channel.write(writeBuffers, writeOffset, writeLength, writeTimeoutSeconds > 0 ? writeTimeoutSeconds : 60, TimeUnit.SECONDS, attachment, this); - return; - } - } catch (Exception e) { - failed(e, attachment); - return; - } -// try { - writeHandler.completed(writeCount, attachment); -// } finally { -// nextWrite(null, attachment); -// } - } else { -// try { - writeHandler.completed(result.intValue(), attachment); -// } finally { -// nextWrite(null, attachment); -// } - } - } - - @Override - public void failed(Throwable exc, A attachment) { -// try { - writeHandler.failed(exc, attachment); -// } finally { -// nextWrite(exc, attachment); -// } - } - - } - - private class WriteOneCompletionHandler implements CompletionHandler { - - private final CompletionHandler writeHandler; - - private final ByteBuffer writeOneBuffer; - - public WriteOneCompletionHandler(ByteBuffer buffer, CompletionHandler handler) { - this.writeOneBuffer = buffer; - this.writeHandler = handler; - } - - @Override - public void completed(Integer result, A attachment) { - try { - if (writeOneBuffer.hasRemaining()) { - channel.write(writeOneBuffer, attachment, this); - return; - } - } catch (Exception e) { - failed(e, attachment); - return; - } -// try { - writeHandler.completed(result, attachment); -// } finally { -// nextWrite(null, attachment); -// } - - } - - @Override - public void failed(Throwable exc, A attachment) { -// try { - writeHandler.failed(exc, attachment); -// } finally { -// nextWrite(exc, attachment); -// } - } - - } - - private static class WriteEntry { - - ByteBuffer writeOneBuffer; - - ByteBuffer[] writeBuffers; - - int writingCount; - - int writeOffset; - - int writeLength; - - Object writeAttachment; - - CompletionHandler writeHandler; - - public WriteEntry(ByteBuffer writeOneBuffer, Object writeAttachment, CompletionHandler writeHandler) { - this.writeOneBuffer = writeOneBuffer; - this.writeAttachment = writeAttachment; - this.writeHandler = writeHandler; - } - - public WriteEntry(ByteBuffer[] writeBuffers, int writeOffset, int writeLength, Object writeAttachment, CompletionHandler writeHandler) { - this.writeBuffers = writeBuffers; - this.writeOffset = writeOffset; - this.writeLength = writeLength; - this.writeAttachment = writeAttachment; - this.writeHandler = writeHandler; - } - } -} diff --git a/src/org/redkale/net/AsyncAioTcpProtocolServer.java b/src/org/redkale/net/AsyncAioTcpProtocolServer.java deleted file mode 100644 index 897f37ca3..000000000 --- a/src/org/redkale/net/AsyncAioTcpProtocolServer.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.IOException; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.Set; -import java.util.concurrent.atomic.*; -import java.util.logging.Level; -import org.redkale.boot.Application; -import org.redkale.util.*; - -/** - * 协议底层Server - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Deprecated //@since 2.3.0 -class AsyncAioTcpProtocolServer extends ProtocolServer { - - //创建数 - private final AtomicLong createCounter = new AtomicLong(); - - //关闭数 - private final AtomicLong closedCounter = new AtomicLong(); - - //在线数 - private final AtomicLong livingCounter = new AtomicLong(); - - private AsynchronousChannelGroup group; - - private AsynchronousServerSocketChannel serverChannel; - - public AsyncAioTcpProtocolServer(Context context) { - super(context); - } - - @Override - public void open(AnyValue config) throws IOException { - //group = AsynchronousChannelGroup.withThreadPool(context.executor); - final AtomicInteger workcounter = new AtomicInteger(); - group = AsynchronousChannelGroup.withFixedThreadPool(Runtime.getRuntime().availableProcessors(), (Runnable r) -> { - int c = workcounter.incrementAndGet(); - String threadname = "Redkale-AioThread-" + (c > 9 ? c : ("0" + c)); - Thread t = new WorkThread(threadname, application.getWorkExecutor(), r); - return t; - }); - this.serverChannel = AsynchronousServerSocketChannel.open(group); - - final Set> options = this.serverChannel.supportedOptions(); - if (options.contains(StandardSocketOptions.TCP_NODELAY)) { - this.serverChannel.setOption(StandardSocketOptions.TCP_NODELAY, true); - } - if (options.contains(StandardSocketOptions.SO_KEEPALIVE)) { - this.serverChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); - } - if (options.contains(StandardSocketOptions.SO_REUSEADDR)) { - this.serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - } - if (options.contains(StandardSocketOptions.SO_RCVBUF)) { - this.serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); - } - if (options.contains(StandardSocketOptions.SO_SNDBUF)) { - this.serverChannel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); - } - } - - @Override - public void bind(SocketAddress local, int backlog) throws IOException { - this.serverChannel.bind(local, backlog); - } - - @Override - public void setOption(SocketOption name, T value) throws IOException { - this.serverChannel.setOption(name, value); - } - - @Override - public Set> supportedOptions() { - return this.serverChannel.supportedOptions(); - } - - @Override - public void accept(Application application, Server server) throws IOException { - AtomicLong createBufferCounter = new AtomicLong(); - AtomicLong cycleBufferCounter = new AtomicLong(); - ObjectPool bufferPool = server.createBufferPool(createBufferCounter, cycleBufferCounter, server.bufferPoolSize); - AtomicLong createResponseCounter = new AtomicLong(); - AtomicLong cycleResponseCounter = new AtomicLong(); - ObjectPool responsePool = server.createResponsePool(createResponseCounter, cycleResponseCounter, server.responsePoolSize); - final AsynchronousServerSocketChannel serchannel = this.serverChannel; - serchannel.accept(null, new CompletionHandler() { - - @Override - public void completed(final AsynchronousSocketChannel channel, Void attachment) { - serchannel.accept(null, this); - if (maxconns > 0 && livingCounter.get() >= maxconns) { - try { - channel.close(); - } catch (Exception e) { - } - return; - } - createCounter.incrementAndGet(); - livingCounter.incrementAndGet(); - try { - channel.setOption(StandardSocketOptions.TCP_NODELAY, true); - channel.setOption(StandardSocketOptions.SO_KEEPALIVE, true); - channel.setOption(StandardSocketOptions.SO_REUSEADDR, true); - channel.setOption(StandardSocketOptions.SO_RCVBUF, 16 * 1024); - channel.setOption(StandardSocketOptions.SO_SNDBUF, 16 * 1024); - - AsyncConnection conn = new AsyncAioTcpConnection(server.bufferCapacity, bufferPool, bufferPool, channel, - context.getSSLContext(), null, context.readTimeoutSeconds, context.writeTimeoutSeconds, livingCounter, closedCounter); - new ProtocolCodec(context, responsePool, responsePool, conn).run(null); - } catch (Throwable e) { - context.logger.log(Level.INFO, channel + " accept error", e); - } - } - - @Override - public void failed(Throwable exc, Void attachment) { - serchannel.accept(null, this); - //if (exc != null) context.logger.log(Level.FINEST, AsynchronousServerSocketChannel.class.getSimpleName() + " accept erroneous", exc); - } - }); - } - - @Override - public long getCreateConnectionCount() { - return this.createCounter.get(); - } - - @Override - public long getClosedConnectionCount() { - return this.closedCounter.get(); - } - - @Override - public long getLivingConnectionCount() { - return this.livingCounter.get(); - } - - @Override - public void close() throws IOException { - this.serverChannel.close(); - } - -} diff --git a/src/org/redkale/net/AsyncConnection.java b/src/org/redkale/net/AsyncConnection.java deleted file mode 100644 index 5236d555e..000000000 --- a/src/org/redkale/net/AsyncConnection.java +++ /dev/null @@ -1,525 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.*; -import java.util.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import javax.net.ssl.SSLContext; -import org.redkale.util.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class AsyncConnection implements ChannelContext, Channel, AutoCloseable { - - private SSLContext sslContext; - - private Map attributes; //用于存储绑定在Connection上的对象集合 - - private Object subobject; //用于存储绑定在Connection上的对象, 同attributes, 只绑定单个对象时尽量使用subobject而非attributes - - protected volatile long readtime; - - protected volatile long writetime; - - protected final boolean client; - - protected final int bufferCapacity; - - private final Supplier bufferSupplier; - - private final Consumer bufferConsumer; - - private ByteBufferWriter pipelineWriter; - - private PipelineDataNode pipelineDataNode; - - private ByteBuffer readBuffer; - - //在线数 - private AtomicLong livingCounter; - - //关闭数 - private AtomicLong closedCounter; - - private Consumer beforeCloseListener; - - //关联的事件数, 小于1表示没有事件 - private final AtomicInteger eventing = new AtomicInteger(); - - //用于服务端的Socket, 等同于一直存在的readCompletionHandler - ProtocolCodec protocolCodec; - - protected AsyncConnection(boolean client, final int bufferCapacity, ObjectPool bufferPool, SSLContext sslContext, - final AtomicLong livingCounter, final AtomicLong closedCounter) { - this(client, bufferCapacity, bufferPool, bufferPool, sslContext, livingCounter, closedCounter); - } - - protected AsyncConnection(boolean client, final int bufferCapacity, Supplier bufferSupplier, Consumer bufferConsumer, - SSLContext sslContext, final AtomicLong livingCounter, final AtomicLong closedCounter) { - Objects.requireNonNull(bufferSupplier); - Objects.requireNonNull(bufferConsumer); - this.client = client; - this.bufferCapacity = bufferCapacity; - this.bufferSupplier = bufferSupplier; - this.bufferConsumer = bufferConsumer; - this.sslContext = sslContext; - this.livingCounter = livingCounter; - this.closedCounter = closedCounter; - } - - public Supplier getBufferSupplier() { - return this.bufferSupplier; - } - - public Consumer getBufferConsumer() { - return this.bufferConsumer; - } - - public final long getLastReadTime() { - return readtime; - } - - public final long getLastWriteTime() { - return writetime; - } - - public final int increEventing() { - return eventing.incrementAndGet(); - } - - public final int decreEventing() { - return eventing.decrementAndGet(); - } - - protected abstract void continueRead(); - - public abstract boolean isOpen(); - - public abstract boolean isTCP(); - - public abstract boolean shutdownInput(); - - public abstract boolean shutdownOutput(); - - public abstract boolean setOption(SocketOption name, T value); - - public abstract Set> supportedOptions(); - - public abstract SocketAddress getRemoteAddress(); - - public abstract SocketAddress getLocalAddress(); - - public abstract int getReadTimeoutSeconds(); - - public abstract int getWriteTimeoutSeconds(); - - public abstract void setReadTimeoutSeconds(int readTimeoutSeconds); - - public abstract void setWriteTimeoutSeconds(int writeTimeoutSeconds); - - public abstract ReadableByteChannel readableByteChannel(); - - public abstract WritableByteChannel writableByteChannel(); - - protected abstract InputStream newInputStream(); - - public abstract void read(CompletionHandler handler); - - //src会写完才会回调 - public abstract void write(ByteBuffer src, A attachment, CompletionHandler handler); - - //srcs会写完才会回调 - public final void write(ByteBuffer[] srcs, A attachment, CompletionHandler handler) { - write(srcs, 0, srcs.length, attachment, handler); - } - - public final void write(byte[] bytes, CompletionHandler handler) { - write(bytes, 0, bytes.length, null, 0, 0, null, null, handler); - } - - public final void write(ByteTuple array, CompletionHandler handler) { - write(array.content(), array.offset(), array.length(), null, 0, 0, null, null, handler); - } - - public final void write(byte[] bytes, int offset, int length, CompletionHandler handler) { - write(bytes, offset, length, null, 0, 0, null, null, handler); - } - - public final void write(ByteTuple header, ByteTuple body, CompletionHandler handler) { - write(header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length(), null, null, handler); - } - - public void write(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength, Consumer bodyCallback, Object bodyAttachment, CompletionHandler handler) { - final ByteBuffer buffer = pollWriteBuffer(); - if (buffer.remaining() >= headerLength + bodyLength) { - buffer.put(headerContent, headerOffset, headerLength); - if (bodyLength > 0) { - buffer.put(bodyContent, bodyOffset, bodyLength); - if (bodyCallback != null) bodyCallback.accept(bodyAttachment); - } - buffer.flip(); - CompletionHandler newhandler = new CompletionHandler() { - @Override - public void completed(Integer result, Void attachment) { - offerBuffer(buffer); - handler.completed(result, attachment); - } - - @Override - public void failed(Throwable exc, Void attachment) { - offerBuffer(buffer); - handler.failed(exc, attachment); - } - }; - write(buffer, null, newhandler); - } else { - ByteBufferWriter writer = ByteBufferWriter.create(bufferSupplier, buffer); - writer.put(headerContent, headerOffset, headerLength); - if (bodyLength > 0) { - writer.put(bodyContent, bodyOffset, bodyLength); - if (bodyCallback != null) bodyCallback.accept(bodyAttachment); - } - final ByteBuffer[] buffers = writer.toBuffers(); - CompletionHandler newhandler = new CompletionHandler() { - @Override - public void completed(Integer result, Void attachment) { - offerBuffer(buffers); - handler.completed(result, attachment); - } - - @Override - public void failed(Throwable exc, Void attachment) { - offerBuffer(buffers); - handler.failed(exc, attachment); - } - }; - write(buffers, null, newhandler); - } - } - - //srcs会写完才会回调 - public abstract void write(ByteBuffer[] srcs, int offset, int length, A attachment, CompletionHandler handler); - - public void setReadBuffer(ByteBuffer buffer) { - if (this.readBuffer != null) throw new RuntimeException("repeat AsyncConnection.setReadBuffer"); - this.readBuffer = buffer; - } - - public boolean hasPipelineData() { - ByteBufferWriter writer = this.pipelineWriter; - return writer != null && writer.position() > 0; - } - - public final void flushPipelineData(CompletionHandler handler) { - flushPipelineData(null, handler); - } - - public void flushPipelineData(A attachment, CompletionHandler handler) { - ByteBufferWriter writer = this.pipelineWriter; - this.pipelineWriter = null; - if (writer == null) { - handler.completed(0, attachment); - } else { - ByteBuffer[] srcs = writer.toBuffers(); - CompletionHandler newhandler = new CompletionHandler() { - @Override - public void completed(Integer result, A attachment) { - offerBuffer(srcs); - handler.completed(result, attachment); - } - - @Override - public void failed(Throwable exc, A attachment) { - offerBuffer(srcs); - handler.failed(exc, attachment); - } - }; - if (srcs.length == 1) { - write(srcs[0], attachment, newhandler); - } else { - write(srcs, attachment, newhandler); - } - } - } - - //返回 是否over - public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple array) { - return writePipelineData(pipelineIndex, pipelineCount, array.content(), array.offset(), array.length()); - } - - //返回 是否over - public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] bs, int offset, int length) { - synchronized (this) { - ByteBufferWriter writer = this.pipelineWriter; - if (writer == null) { - writer = ByteBufferWriter.create(getBufferSupplier()); - this.pipelineWriter = writer; - } - if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) { - writer.put(bs, offset, length); - return (pipelineIndex == pipelineCount); - } else { - PipelineDataNode dataNode = this.pipelineDataNode; - if (dataNode == null) { - dataNode = new PipelineDataNode(); - this.pipelineDataNode = dataNode; - } - if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值 - dataNode.pipelineCount = pipelineCount; - } - dataNode.put(pipelineIndex, bs, offset, length); - if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) { - for (PipelineDataItem item : dataNode.arrayItems()) { - writer.put(item.data); - } - this.pipelineDataNode = null; - return true; - } - return false; - } - } - } - - //返回 是否over - public final boolean writePipelineData(int pipelineIndex, int pipelineCount, ByteTuple header, ByteTuple body) { - return writePipelineData(pipelineIndex, pipelineCount, header.content(), header.offset(), header.length(), body == null ? null : body.content(), body == null ? 0 : body.offset(), body == null ? 0 : body.length()); - } - - //返回 是否over - public boolean writePipelineData(int pipelineIndex, int pipelineCount, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { - synchronized (this) { - ByteBufferWriter writer = this.pipelineWriter; - if (writer == null) { - writer = ByteBufferWriter.create(getBufferSupplier()); - this.pipelineWriter = writer; - } - if (this.pipelineDataNode == null && pipelineIndex == writer.getWriteBytesCounter() + 1) { - writer.put(headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); - return (pipelineIndex == pipelineCount); - } else { - PipelineDataNode dataNode = this.pipelineDataNode; - if (dataNode == null) { - dataNode = new PipelineDataNode(); - this.pipelineDataNode = dataNode; - } - if (pipelineIndex == pipelineCount) { //此时pipelineCount为最大值 - dataNode.pipelineCount = pipelineCount; - } - dataNode.put(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); - if (writer.getWriteBytesCounter() + dataNode.itemsize == dataNode.pipelineCount) { - for (PipelineDataItem item : dataNode.arrayItems()) { - writer.put(item.data); - } - this.pipelineDataNode = null; - return true; - } - return false; - } - } - } - - private static class PipelineDataNode { - - public int pipelineCount; - - public int itemsize; - - private PipelineDataItem head; - - private PipelineDataItem tail; - - public PipelineDataItem[] arrayItems() { - PipelineDataItem[] items = new PipelineDataItem[itemsize]; - PipelineDataItem item = head; - int i = 0; - while (item != null) { - items[i] = item; - item = item.next; - items[i].next = null; - i++; - } - Arrays.sort(items); - return items; - } - - public void put(int pipelineIndex, byte[] bs, int offset, int length) { - if (tail == null) { - head = new PipelineDataItem(pipelineIndex, bs, offset, length); - tail = head; - } else { - PipelineDataItem item = new PipelineDataItem(pipelineIndex, bs, offset, length); - tail.next = item; - tail = item; - } - itemsize++; - } - - public void put(int pipelineIndex, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { - if (tail == null) { - head = new PipelineDataItem(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); - tail = head; - } else { - PipelineDataItem item = new PipelineDataItem(pipelineIndex, headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength); - tail.next = item; - tail = item; - } - itemsize++; - } - - } - - private static class PipelineDataItem implements Comparable { - - final byte[] data; - - final int index; - - public PipelineDataItem next; - - public PipelineDataItem(int index, byte[] bs, int offset, int length) { - this.index = index; - this.data = Arrays.copyOfRange(bs, offset, offset + length); - } - - public PipelineDataItem(int index, byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { - this.index = index; - this.data = bodyLength > 0 ? copyOfRange(headerContent, headerOffset, headerLength, bodyContent, bodyOffset, bodyLength) - : Arrays.copyOfRange(headerContent, headerOffset, headerOffset + headerLength); - } - - private static byte[] copyOfRange(byte[] headerContent, int headerOffset, int headerLength, byte[] bodyContent, int bodyOffset, int bodyLength) { - byte[] result = new byte[headerLength + bodyLength]; - System.arraycopy(headerContent, headerOffset, result, 0, headerLength); - System.arraycopy(bodyContent, bodyOffset, result, headerLength, bodyLength); - return result; - } - - @Override - public int compareTo(PipelineDataItem o) { - return this.index - o.index; - } - - @Override - public String toString() { - return "{\"index\":" + index + "}"; - } - } - - public ByteBuffer pollReadBuffer() { - ByteBuffer rs = this.readBuffer; - if (rs != null) { - this.readBuffer = null; - return rs; - } - return bufferSupplier.get(); - } - - public void offerBuffer(ByteBuffer buffer) { - if (buffer == null) return; - bufferConsumer.accept(buffer); - } - - public void offerBuffer(ByteBuffer... buffers) { - if (buffers == null) return; - Consumer consumer = this.bufferConsumer; - for (ByteBuffer buffer : buffers) { - consumer.accept(buffer); - } - } - - public ByteBuffer pollWriteBuffer() { - return bufferSupplier.get(); - } - - public void dispose() {//同close, 只是去掉throws IOException - try { - this.close(); - } catch (IOException io) { - } - } - - public AsyncConnection beforeCloseListener(Consumer beforeCloseListener) { - this.beforeCloseListener = beforeCloseListener; - return this; - } - - @Override - public void close() throws IOException { - if (closedCounter != null) { - closedCounter.incrementAndGet(); - closedCounter = null; - } - if (livingCounter != null) { - livingCounter.decrementAndGet(); - livingCounter = null; - } - if (beforeCloseListener != null) { - try { - beforeCloseListener.accept(this); - } catch (Exception io) { - } - } - if (this.readBuffer != null) { - Consumer consumer = this.bufferConsumer; - if (consumer != null) consumer.accept(this.readBuffer); - } - if (attributes == null) return; - try { - for (Object obj : attributes.values()) { - if (obj instanceof AutoCloseable) ((AutoCloseable) obj).close(); - } - attributes.clear(); - } catch (Exception io) { - } - } - - @SuppressWarnings("unchecked") - public final T getSubobject() { - return (T) this.subobject; - } - - public void setSubobject(Object value) { - this.subobject = value; - } - - @Override - public void setAttribute(String name, Object value) { - if (this.attributes == null) this.attributes = new HashMap<>(); - this.attributes.put(name, value); - } - - @Override - @SuppressWarnings("unchecked") - public final T getAttribute(String name) { - return (T) (this.attributes == null ? null : this.attributes.get(name)); - } - - @Override - public final void removeAttribute(String name) { - if (this.attributes != null) this.attributes.remove(name); - } - - @Override - public final Map getAttributes() { - return this.attributes; - } - - @Override - public final void clearAttribute() { - if (this.attributes != null) this.attributes.clear(); - } - -} diff --git a/src/org/redkale/net/AsyncGroup.java b/src/org/redkale/net/AsyncGroup.java deleted file mode 100644 index cd7e16522..000000000 --- a/src/org/redkale/net/AsyncGroup.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.net.SocketAddress; -import java.util.concurrent.*; - -/** - * Client模式的AsyncConnection连接构造器 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * - * @since 2.3.0 - */ -public abstract class AsyncGroup { - - public CompletableFuture createTCP(final SocketAddress address) { - return createTCP(address, 0, 0); - } - - public abstract CompletableFuture createTCP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds); - - public CompletableFuture createUDP(final SocketAddress address) { - return createUDP(address, 0, 0); - } - - public abstract CompletableFuture createUDP(final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds); - - public CompletableFuture create(final boolean tcp, final SocketAddress address) { - return tcp ? createTCP(address) : createUDP(address); - } - - public CompletableFuture create(final boolean tcp, final SocketAddress address, final int readTimeoutSeconds, final int writeTimeoutSeconds) { - return tcp ? createTCP(address, readTimeoutSeconds, writeTimeoutSeconds) : createUDP(address, readTimeoutSeconds, writeTimeoutSeconds); - } - - public abstract ScheduledFuture scheduleTimeout(Runnable callable, long delay, TimeUnit unit); -} diff --git a/src/org/redkale/net/SSLClientAuth.java b/src/org/redkale/net/SSLClientAuth.java deleted file mode 100644 index 2819f533b..000000000 --- a/src/org/redkale/net/SSLClientAuth.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public enum SSLClientAuth { - NONE, - NEED, - WANT, - CLIENT; -} diff --git a/src/org/redkale/net/SSLCreator.java b/src/org/redkale/net/SSLCreator.java deleted file mode 100644 index cef79ec65..000000000 --- a/src/org/redkale/net/SSLCreator.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net; - -import java.io.*; -import java.security.*; -import java.security.cert.*; -import javax.net.ssl.*; -import org.redkale.util.*; - -/** - * 根据配置生成SSLContext - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public interface SSLCreator { - - default SSLContext create(Server server, AnyValue sslConf) throws Exception { - String keyfile = sslConf.getValue("keystorefile"); - String keypass = sslConf.getValue("keystorepass", ""); - KeyManager[] keyManagers = null; - if (keyfile != null) { - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - KeyStore ks = KeyStore.getInstance("JKS"); - ks.load(new FileInputStream(keyfile), keypass.toCharArray()); - kmf.init(ks, keypass.toCharArray()); - keyManagers = kmf.getKeyManagers(); - } - - String trustfile = sslConf.getValue("truststorefile"); - String trustpass = sslConf.getValue("truststorepass", ""); - TrustManager[] trustManagers; - if (trustfile != null) { - KeyStore ts = KeyStore.getInstance("JKS"); - ts.load(new FileInputStream(trustfile), trustpass.toCharArray()); - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); - trustManagers = tmf.getTrustManagers(); - } else { - trustManagers = new TrustManager[]{new X509TrustManager() { - @Override - public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - } - - @Override - public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return new X509Certificate[0]; - } - }}; - } - SSLContext sslContext = SSLContext.getInstance("TLS"); - sslContext.init(keyManagers, trustManagers, new SecureRandom()); - return sslContext; - } -} diff --git a/src/org/redkale/net/client/ClientRequest.java b/src/org/redkale/net/client/ClientRequest.java deleted file mode 100644 index 6c47cf25e..000000000 --- a/src/org/redkale/net/client/ClientRequest.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.client; - -import java.util.function.*; -import org.redkale.util.ByteArray; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @since 2.3.0 - */ -public interface ClientRequest extends BiConsumer { - - public static class ClientBytesRequest implements ClientRequest { - - protected byte[] bytes; - - public ClientBytesRequest(byte[] bytes) { - this.bytes = bytes; - } - - @Override - public void accept(ClientConnection conn, ByteArray array) { - array.put(bytes); - } - - } -} diff --git a/src/org/redkale/net/http/HttpParam.java b/src/org/redkale/net/http/HttpParam.java deleted file mode 100644 index 5e2841a61..000000000 --- a/src/org/redkale/net/http/HttpParam.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.net.http; - -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * 配合 @HttpMapping 使用。 - * 用于对@HttpMapping方法中参数描述 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Documented -@Target({METHOD}) -@Retention(RUNTIME) -@Repeatable(HttpParam.HttpParams.class) -public @interface HttpParam { - - String name(); //参数名 - - Class type(); //参数的数据类型 - - String comment() default ""; //备注描述 - - HttpParamSourceType src() default HttpParamSourceType.PARAMETER; //参数来源类型 - - int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制 - - boolean required() default true; //参数是否必传 - - /** - * 配合 @HttpParam 使用。 - * 用于对@HttpParam中参数的来源类型 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ - public static enum HttpParamSourceType { - - PARAMETER, HEADER, COOKIE; - } - - @Documented - @Target({METHOD}) - @Retention(RUNTIME) - @interface HttpParams { - - HttpParam[] value(); - } - -} diff --git a/src/org/redkale/source/DataJdbcSource.java b/src/org/redkale/source/DataJdbcSource.java deleted file mode 100644 index 78ec6bc54..000000000 --- a/src/org/redkale/source/DataJdbcSource.java +++ /dev/null @@ -1,694 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.io.Serializable; -import java.net.URL; -import java.sql.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.function.*; -import java.util.logging.Level; -import org.redkale.net.*; -import org.redkale.service.Local; -import org.redkale.util.*; - -/** - * DataSource的JDBC实现类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -@Local -@AutoLoad(false) -@SuppressWarnings("unchecked") -@ResourceType(DataSource.class) -public class DataJdbcSource extends DataSqlSource { - - public DataJdbcSource(String unitName, URL persistxml, Properties readprop, Properties writeprop) { - super(unitName, persistxml, readprop, writeprop); - } - - @Override - protected final String prepareParamSign(int index) { - return "?"; - } - - @Override - protected final boolean isAsync() { - return false; - } - - @Override - protected PoolSource createPoolSource(DataSource source, AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop) { - return new PoolJdbcSource(this.name, this.persistxml, rwtype, queue, semaphore, prop, this.logger); - } - - @Override - protected CompletableFuture insertDB(EntityInfo info, T... entitys) { - Connection conn = null; - try { - int c = 0; - conn = writePool.poll(); - final String sql = info.getInsertPrepareSQL(entitys[0]); - final Class primaryType = info.getPrimary().type(); - final Attribute primary = info.getPrimary(); - Attribute[] attrs = info.insertAttributes; - conn.setReadOnly(false); - conn.setAutoCommit(true); - PreparedStatement prestmt = createInsertPreparedStatement(conn, sql, info, entitys); - try { - int[] cs = prestmt.executeBatch(); - int c1 = 0; - for (int cc : cs) { - c1 += cc; - } - c = c1; - } catch (SQLException se) { - if (info.getTableStrategy() == null || !info.isTableNotExist(se)) throw se; - synchronized (info.disTableLock()) { - final String catalog = conn.getCatalog(); - final String newTable = info.getTable(entitys[0]); - final String tablekey = newTable.indexOf('.') > 0 ? newTable : (catalog + '.' + newTable); - if (!info.containsDisTable(tablekey)) { - try { - Statement st = conn.createStatement(); - st.execute(info.getTableCopySQL(newTable)); - st.close(); - info.addDisTable(tablekey); - } catch (SQLException sqle) { //多进程并发时可能会出现重复建表 - if (newTable.indexOf('.') > 0 && info.isTableNotExist(se)) { - Statement st; - try { - st = conn.createStatement(); - st.execute("CREATE DATABASE " + newTable.substring(0, newTable.indexOf('.'))); - st.close(); - } catch (SQLException sqle1) { - logger.log(Level.SEVERE, "create database(" + newTable.substring(0, newTable.indexOf('.')) + ") error", sqle1); - } - try { - st = conn.createStatement(); - st.execute(info.getTableCopySQL(newTable)); - st.close(); - info.addDisTable(tablekey); - } catch (SQLException sqle2) { - logger.log(Level.SEVERE, "create table2(" + info.getTableCopySQL(newTable) + ") error", sqle2); - } - } else { - logger.log(Level.SEVERE, "create table(" + info.getTableCopySQL(newTable) + ") error", sqle); - } - } - } - } - prestmt.close(); - prestmt = createInsertPreparedStatement(conn, sql, info, entitys); - int[] cs = prestmt.executeBatch(); - int c1 = 0; - for (int cc : cs) { - c1 += cc; - } - c = c1; - } - prestmt.close(); - //------------------------------------------------------------ - if (info.isLoggable(logger, Level.FINEST)) { //打印调试信息 - char[] sqlchars = sql.toCharArray(); - for (final T value : entitys) { - //----------------------------- - StringBuilder sb = new StringBuilder(128); - int i = 0; - for (char ch : sqlchars) { - if (ch == '?') { - Object obj = info.getSQLValue(attrs[i++], value); - if (obj != null && obj.getClass().isArray()) { - sb.append("'[length=").append(java.lang.reflect.Array.getLength(obj)).append("]'"); - } else { - sb.append(info.formatSQLValue(obj, sqlFormatter)); - } - } else { - sb.append(ch); - } - } - String debugsql = sb.toString(); - if (info.isLoggable(logger, Level.FINEST, debugsql)) logger.finest(info.getType().getSimpleName() + " insert sql=" + debugsql.replaceAll("(\r|\n)", "\\n")); - } - } //打印结束 - return CompletableFuture.completedFuture(c); - } catch (SQLException e) { - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) writePool.offerConnection(conn); - } - } - - protected PreparedStatement createInsertPreparedStatement(final Connection conn, final String sql, - final EntityInfo info, T... entitys) throws SQLException { - Attribute[] attrs = info.insertAttributes; - final PreparedStatement prestmt = conn.prepareStatement(sql); - - for (final T value : entitys) { - batchStatementParameters(conn, prestmt, info, attrs, value); - prestmt.addBatch(); - } - return prestmt; - } - - protected int batchStatementParameters(Connection conn, PreparedStatement prestmt, EntityInfo info, Attribute[] attrs, T entity) throws SQLException { - int i = 0; - for (Attribute attr : attrs) { - Object val = info.getSQLValue(attr, entity); - if (val instanceof byte[]) { - Blob blob = conn.createBlob(); - blob.setBytes(1, (byte[]) val); - prestmt.setObject(++i, blob); - } else if (val instanceof Boolean) { - prestmt.setObject(++i, ((Boolean) val) ? (byte) 1 : (byte) 0); - } else if (val instanceof AtomicInteger) { - prestmt.setObject(++i, ((AtomicInteger) val).get()); - } else if (val instanceof AtomicLong) { - prestmt.setObject(++i, ((AtomicLong) val).get()); - } else if (val != null && !(val instanceof Number) && !(val instanceof CharSequence) && !(val instanceof java.util.Date) - && !val.getClass().getName().startsWith("java.sql.") && !val.getClass().getName().startsWith("java.time.")) { - prestmt.setObject(++i, info.jsonConvert.convertTo(attr.genericType(), val)); - } else if (val == null && info.isNotNullJson(attr)) { - prestmt.setObject(++i, ""); - } else { - prestmt.setObject(++i, val); - } - } - return i; - } - - @Override - protected CompletableFuture deleteDB(EntityInfo info, Flipper flipper, String sql) { - Connection conn = null; - try { - conn = writePool.poll(); - conn.setReadOnly(false); - conn.setAutoCommit(true); - sql += ((flipper == null || flipper.getLimit() < 1) ? "" : (" LIMIT " + flipper.getLimit())); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " delete sql=" + sql); - final Statement stmt = conn.createStatement(); - int c = stmt.executeUpdate(sql); - stmt.close(); - return CompletableFuture.completedFuture(c); - } catch (SQLException e) { - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) writePool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture clearTableDB(EntityInfo info, final String table, String sql) { - Connection conn = null; - try { - conn = writePool.poll(); - conn.setReadOnly(false); - conn.setAutoCommit(true); - final Statement stmt = conn.createStatement(); - int c = stmt.executeUpdate(sql); - stmt.close(); - return CompletableFuture.completedFuture(c); - } catch (SQLException e) { - if (info.isTableNotExist(e)) return CompletableFuture.completedFuture(-1); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) writePool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture dropTableDB(EntityInfo info, final String table, String sql) { - Connection conn = null; - try { - conn = writePool.poll(); - conn.setReadOnly(false); - conn.setAutoCommit(true); - final Statement stmt = conn.createStatement(); - int c = stmt.executeUpdate(sql); - stmt.close(); - if (info.getTableStrategy() != null) { - String tablekey = table.indexOf('.') > 0 ? table : (conn.getCatalog() + '.' + table); - info.removeDisTable(tablekey); - } - return CompletableFuture.completedFuture(c); - } catch (SQLException e) { - if (info.isTableNotExist(e)) return CompletableFuture.completedFuture(-1); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) writePool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture updateDB(EntityInfo info, ChannelContext context, T... entitys) { - Connection conn = null; - try { - conn = writePool.poll(); - conn.setReadOnly(false); - conn.setAutoCommit(true); - final String updateSQL = info.getUpdatePrepareSQL(entitys[0]); - final PreparedStatement prestmt = conn.prepareStatement(updateSQL); - Attribute[] attrs = info.updateAttributes; - final boolean debugfinest = info.isLoggable(logger, Level.FINEST); - char[] sqlchars = debugfinest ? updateSQL.toCharArray() : null; - final Attribute primary = info.getPrimary(); - for (final T value : entitys) { - int k = batchStatementParameters(conn, prestmt, info, attrs, value); - prestmt.setObject(++k, primary.get(value)); - prestmt.addBatch();//------------------------------------------------------------ - if (debugfinest) { //打印调试信息 - //----------------------------- - int i = 0; - StringBuilder sb = new StringBuilder(128); - for (char ch : sqlchars) { - if (ch == '?') { - Object obj = i == attrs.length ? info.getSQLValue(primary, value) : info.getSQLValue(attrs[i++], value); - if (obj != null && obj.getClass().isArray()) { - sb.append("'[length=").append(java.lang.reflect.Array.getLength(obj)).append("]'"); - } else { - sb.append(info.formatSQLValue(obj, sqlFormatter)); - } - } else { - sb.append(ch); - } - } - String debugsql = sb.toString(); - if (info.isLoggable(logger, Level.FINEST, debugsql)) logger.finest(info.getType().getSimpleName() + " updates sql=" + debugsql.replaceAll("(\r|\n)", "\\n")); - } //打印结束 - } - int[] pc = prestmt.executeBatch(); - int c = 0; - for (int p : pc) { - if (p >= 0) c += p; - } - prestmt.close(); - return CompletableFuture.completedFuture(c); - } catch (SQLException e) { - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) writePool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture updateDB(EntityInfo info, Flipper flipper, String sql, boolean prepared, Object... params) { - Connection conn = null; - try { - conn = writePool.poll(); - conn.setReadOnly(false); - conn.setAutoCommit(true); - if (prepared) { - final PreparedStatement prestmt = conn.prepareStatement(sql); - int index = 0; - for (Object param : params) { - Blob blob = conn.createBlob(); - blob.setBytes(1, (byte[]) param); - prestmt.setBlob(++index, blob); - } - int c = prestmt.executeUpdate(); - prestmt.close(); - return CompletableFuture.completedFuture(c); - } else { - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " update sql=" + sql); - final Statement stmt = conn.createStatement(); - int c = stmt.executeUpdate(sql); - stmt.close(); - return CompletableFuture.completedFuture(c); - } - } catch (SQLException e) { - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) writePool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture> getNumberMapDB(EntityInfo info, String sql, FilterFuncColumn... columns) { - Connection conn = null; - final Map map = new HashMap<>(); - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final Statement stmt = conn.createStatement(); - ResultSet set = stmt.executeQuery(sql); - if (set.next()) { - int index = 0; - for (FilterFuncColumn ffc : columns) { - for (String col : ffc.cols()) { - Object o = set.getObject(++index); - Number rs = ffc.getDefvalue(); - if (o != null) rs = (Number) o; - map.put(ffc.col(col), rs); - } - } - } - set.close(); - stmt.close(); - return CompletableFuture.completedFuture(map); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(map); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture getNumberResultDB(EntityInfo info, String sql, Number defVal, String column) { - Connection conn = null; - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final Statement stmt = conn.createStatement(); - Number rs = defVal; - ResultSet set = stmt.executeQuery(sql); - if (set.next()) { - Object o = set.getObject(1); - if (o != null) rs = (Number) o; - } - set.close(); - stmt.close(); - return CompletableFuture.completedFuture(rs); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(defVal); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture> queryColumnMapDB(EntityInfo info, String sql, String keyColumn) { - Connection conn = null; - Map rs = new LinkedHashMap<>(); - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final Statement stmt = conn.createStatement(); - ResultSet set = stmt.executeQuery(sql); - ResultSetMetaData rsd = set.getMetaData(); - boolean smallint = rsd == null ? false : rsd.getColumnType(1) == Types.SMALLINT; - while (set.next()) { - rs.put((K) (smallint ? set.getShort(1) : set.getObject(1)), (N) set.getObject(2)); - } - set.close(); - stmt.close(); - return CompletableFuture.completedFuture(rs); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(rs); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture> queryColumnMapDB(EntityInfo info, String sql, final ColumnNode[] funcNodes, final String[] groupByColumns) { - Connection conn = null; - Map rs = new LinkedHashMap<>(); - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final Statement stmt = conn.createStatement(); - ResultSet set = stmt.executeQuery(sql); - ResultSetMetaData rsd = set.getMetaData(); - boolean[] smallints = null; - while (set.next()) { - int index = 0; - Serializable[] keys = new Serializable[groupByColumns.length]; - if (smallints == null) { - smallints = new boolean[keys.length]; - for (int i = 0; i < keys.length; i++) { - smallints[i] = rsd == null ? false : rsd.getColumnType(i + 1) == Types.SMALLINT; - } - } - for (int i = 0; i < keys.length; i++) { - keys[i] = (Serializable) ((smallints[i] && index == 0) ? set.getShort(++index) : set.getObject(++index)); - } - Number[] vals = new Number[funcNodes.length]; - for (int i = 0; i < vals.length; i++) { - vals[i] = (Number) set.getObject(++index); - } - rs.put(keys, vals); - } - set.close(); - stmt.close(); - return CompletableFuture.completedFuture(rs); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(rs); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture findDB(EntityInfo info, ChannelContext context, String sql, boolean onlypk, SelectColumn selects) { - Connection conn = null; - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - ps.setFetchSize(1); - final ResultSet set = ps.executeQuery(); - T rs = set.next() ? info.getEntityValue(selects, set) : null; - set.close(); - ps.close(); - return CompletableFuture.completedFuture(rs); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(null); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture findColumnDB(EntityInfo info, String sql, boolean onlypk, String column, Serializable defValue) { - Connection conn = null; - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final Attribute attr = info.getAttribute(column); - final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - ps.setFetchSize(1); - final ResultSet set = ps.executeQuery(); - Serializable val = defValue; - if (set.next()) { - val = info.getFieldValue(attr, set, 1); - } - set.close(); - ps.close(); - return CompletableFuture.completedFuture(val == null ? defValue : val); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(defValue); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture existsDB(EntityInfo info, String sql, boolean onlypk) { - Connection conn = null; - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - final ResultSet set = ps.executeQuery(); - boolean rs = set.next() ? (set.getInt(1) > 0) : false; - set.close(); - ps.close(); - if (info.isLoggable(logger, Level.FINEST, sql)) logger.finest(info.getType().getSimpleName() + " exists (" + rs + ") sql=" + sql); - return CompletableFuture.completedFuture(rs); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(false); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - @Override - protected CompletableFuture> querySheetDB(EntityInfo info, final boolean readcache, boolean needtotal, final boolean distinct, SelectColumn selects, Flipper flipper, FilterNode node) { - Connection conn = null; - try { - conn = readPool.poll(); - //conn.setReadOnly(true); - final SelectColumn sels = selects; - final List list = new ArrayList(); - final Map joinTabalis = node == null ? null : node.getJoinTabalis(); - final CharSequence join = node == null ? null : node.createSQLJoin(this, false, joinTabalis, new HashSet<>(), info); - final CharSequence where = node == null ? null : node.createSQLExpress(info, joinTabalis); - final String dbtype = this.readPool.getDbtype(); - if ("mysql".equals(dbtype) || "postgresql".equals(dbtype)) { - final String listsql = "SELECT " + (distinct ? "DISTINCT " : "") + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) - + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + createSQLOrderby(info, flipper) + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getLimit() + " OFFSET " + flipper.getOffset())); - if (readcache && info.isLoggable(logger, Level.FINEST, listsql)) { - logger.finest(info.getType().getSimpleName() + " query sql=" + listsql); - } - PreparedStatement ps = conn.prepareStatement(listsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - ResultSet set = ps.executeQuery(); - while (set.next()) { - list.add(getEntityValue(info, sels, set)); - } - set.close(); - ps.close(); - long total = list.size(); - if (needtotal) { - final String countsql = "SELECT " + (distinct ? "DISTINCT COUNT(" + info.getQueryColumns("a", selects) + ")" : "COUNT(*)") + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)); - if (readcache && info.isLoggable(logger, Level.FINEST, countsql)) { - logger.finest(info.getType().getSimpleName() + " query countsql=" + countsql); - } - ps = conn.prepareStatement(countsql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - set = ps.executeQuery(); - if (set.next()) total = set.getLong(1); - set.close(); - ps.close(); - } - return CompletableFuture.completedFuture(new Sheet<>(total, list)); - } - final String sql = "SELECT " + (distinct ? "DISTINCT " : "") + info.getQueryColumns("a", selects) + " FROM " + info.getTable(node) + " a" + (join == null ? "" : join) - + ((where == null || where.length() == 0) ? "" : (" WHERE " + where)) + info.createSQLOrderby(flipper); - if (readcache && info.isLoggable(logger, Level.FINEST, sql)) { - logger.finest(info.getType().getSimpleName() + " query sql=" + sql + (flipper == null || flipper.getLimit() < 1 ? "" : (" LIMIT " + flipper.getLimit() + " OFFSET " + flipper.getOffset()))); - } - //conn.setReadOnly(true); - final PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - if (flipper != null && flipper.getLimit() > 0) ps.setFetchSize(flipper.getLimit()); - final ResultSet set = ps.executeQuery(); - if (flipper != null && flipper.getOffset() > 0) set.absolute(flipper.getOffset()); - final int limit = flipper == null || flipper.getLimit() < 1 ? Integer.MAX_VALUE : flipper.getLimit(); - int i = 0; - while (set.next()) { - i++; - list.add(info.getEntityValue(sels, set)); - if (limit <= i) break; - } - long total = list.size(); - if (needtotal && flipper != null) { - set.last(); - total = set.getRow(); - } - set.close(); - ps.close(); - return CompletableFuture.completedFuture(new Sheet<>(total, list)); - } catch (SQLException e) { - if (info.getTableStrategy() != null && info.isTableNotExist(e)) return CompletableFuture.completedFuture(new Sheet<>(0, new ArrayList())); - CompletableFuture future = new CompletableFuture(); - future.completeExceptionally(e); - return future;//return CompletableFuture.failedFuture(e); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } - - /** - * 直接本地执行SQL语句进行增删改操作,远程模式不可用
    - * 通常用于复杂的更新操作
    - * - * @param sql SQL语句 - * - * @return 结果数组 - */ - @Local - @Override - public int directExecute(String sql) { - return directExecute(new String[]{sql})[0]; - } - - /** - * 直接本地执行SQL语句进行增删改操作,远程模式不可用
    - * 通常用于复杂的更新操作
    - * - * @param sqls SQL语句 - * - * @return 结果数组 - */ - @Local - @Override - public int[] directExecute(String... sqls) { - if (sqls.length == 0) return new int[0]; - Connection conn = writePool.poll(); - try { - conn.setReadOnly(false); - final Statement stmt = conn.createStatement(); - final int[] rs = new int[sqls.length]; - int i = -1; - for (String sql : sqls) { - rs[++i] = stmt.execute(sql) ? 1 : 0; - } - stmt.close(); - return rs; - } catch (SQLException e) { - throw new RuntimeException(e); - } finally { - if (conn != null) writePool.offerConnection(conn); - } - } - - /** - * 直接本地执行SQL语句进行查询,远程模式不可用
    - * 通常用于复杂的关联查询
    - * - * @param 泛型 - * @param sql SQL语句 - * @param handler 回调函数 - * - * @return 结果 - */ - @Local - @Override - public V directQuery(String sql, Function handler) { - final Connection conn = readPool.poll(); - try { - if (logger.isLoggable(Level.FINEST)) logger.finest("direct query sql=" + sql); - //conn.setReadOnly(true); - final Statement statement = conn.createStatement(); - //final PreparedStatement statement = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - final ResultSet set = statement.executeQuery(sql);// ps.executeQuery(); - V rs = handler.apply(set); - set.close(); - statement.close(); - return rs; - } catch (Exception ex) { - throw new RuntimeException(ex); - } finally { - if (conn != null) readPool.offerConnection(conn); - } - } -} diff --git a/src/org/redkale/source/PoolJdbcSource.java b/src/org/redkale/source/PoolJdbcSource.java deleted file mode 100644 index eefd15181..000000000 --- a/src/org/redkale/source/PoolJdbcSource.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.lang.reflect.Method; -import java.net.URL; -import java.sql.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import javax.sql.*; -import static org.redkale.source.DataSources.*; - -/** - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public class PoolJdbcSource extends PoolSource { - - protected final ConnectionPoolDataSource source; - - protected final ArrayBlockingQueue queue; - - protected final ConnectionEventListener listener; - - protected final String unitName; - - protected final URL persistxml; - - public PoolJdbcSource(String unitName, URL persistxml, String rwtype, ArrayBlockingQueue aqueue, Semaphore semaphore, Properties prop, Logger logger) { - super(rwtype, semaphore, prop, logger); - this.unitName = unitName; - this.persistxml = persistxml; - this.source = createDataSource(prop); - this.queue = aqueue == null ? new ArrayBlockingQueue<>(this.maxconns) : aqueue; - this.listener = new ConnectionEventListener() { - - @Override - public void connectionClosed(ConnectionEvent event) { - PooledConnection pc = (PooledConnection) event.getSource(); - if (queue.offer(pc)) { - saveCounter.incrementAndGet(); - } else { - try { - pc.close(); - } catch (Exception e) { - logger.log(Level.INFO, DataSource.class.getSimpleName() + " " + pc + " close error", e); - } - } - } - - @Override - public void connectionErrorOccurred(ConnectionEvent event) { - usingCounter.decrementAndGet(); - if ("08S01".equals(event.getSQLException().getSQLState())) return; //MySQL特性, 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException - logger.log(Level.WARNING, "connectionErronOccurred [" + event.getSQLException().getSQLState() + "]", event.getSQLException()); - } - }; - } - - public static boolean checkSource(Properties property) { - final String source = sourceImpl(property.getProperty(JDBC_SOURCE, property.getProperty(JDBC_DRIVER)), property.getProperty(JDBC_URL)); - try { - Thread.currentThread().getContextClassLoader().loadClass(source); - } catch (Exception e) { - return false; - } - return true; - } - - private static ConnectionPoolDataSource createDataSource(Properties property) { - try { - return createDataSource(property.getProperty(JDBC_SOURCE, property.getProperty(JDBC_DRIVER)), - property.getProperty(JDBC_URL), property.getProperty(JDBC_USER), property.getProperty(JDBC_PWD)); - } catch (Exception ex) { - throw new RuntimeException("(" + property + ") have no jdbc parameters", ex); - } - } - - private static String sourceImpl(String source0, String url) { - String source = source0; - if (source0 == null || source0.isEmpty()) { - if (url.startsWith("jdbc:mysql:")) { - source0 = "com.mysql.jdbc.Driver"; - } else if (url.startsWith("jdbc:mariadb:")) { - source0 = "org.mariadb.jdbc.Driver"; - } else if (url.startsWith("jdbc:oracle:")) { - source0 = "oracle.jdbc.driver.OracleDriver"; - } else if (url.startsWith("jdbc:postgresql:")) { - source0 = "org.postgresql.Driver"; - } else if (url.startsWith("jdbc:microsoft:sqlserver:")) { - source0 = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; - } else if (url.startsWith("jdbc:h2")) { - source0 = "org.h2.Driver"; - } - } - if (source0 != null && source0.contains("Driver")) { //为了兼容JPA的配置文件 - switch (source0) { - case "org.mariadb.jdbc.Driver": - source = "org.mariadb.jdbc.MySQLDataSource"; - break; - case "com.mysql.cj.jdbc.Driver": - case "com.mysql.jdbc.Driver": - try { - Thread.currentThread().getContextClassLoader().loadClass("com.mysql.cj.jdbc.MysqlConnectionPoolDataSource"); - source = "com.mysql.cj.jdbc.MysqlConnectionPoolDataSource"; - } catch (Throwable e) { - source = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource"; - } - break; - case "oracle.jdbc.driver.OracleDriver": - source = "oracle.jdbc.pool.OracleConnectionPoolDataSource"; - break; - case "org.postgresql.Driver": - source = "org.postgresql.ds.PGConnectionPoolDataSource"; - break; - case "com.microsoft.sqlserver.jdbc.SQLServerDriver": - source = "com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource"; - break; - case "org.h2.Driver": - source = "org.h2.jdbcx.JdbcDataSource"; - break; - } - } - return source; - } - - private static ConnectionPoolDataSource createDataSource(String source0, String url, String user, String password) throws Exception { - final String source = sourceImpl(source0, url); - final Class clazz = Thread.currentThread().getContextClassLoader().loadClass(source); - Object pdsource = clazz.getDeclaredConstructor().newInstance(); - if (source.contains(".postgresql.")) { - Class driver = Thread.currentThread().getContextClassLoader().loadClass("org.postgresql.Driver"); - Properties properties = (Properties) driver.getMethod("parseURL", String.class, Properties.class).invoke(null, url, new Properties()); - clazz.getMethod("setServerName", String.class).invoke(pdsource, properties.getProperty("PGHOST")); - clazz.getMethod("setDatabaseName", String.class).invoke(pdsource, properties.getProperty("PGDBNAME")); - clazz.getMethod("setPortNumber", int.class).invoke(pdsource, Integer.parseInt(properties.getProperty("PGPORT", "5432"))); - } else { - Method seturlm; - try { - seturlm = clazz.getMethod("setUrl", String.class); - } catch (Exception e) { - seturlm = clazz.getMethod("setURL", String.class); - } - seturlm.invoke(pdsource, url); - } - clazz.getMethod("setUser", String.class).invoke(pdsource, user); - clazz.getMethod("setPassword", String.class).invoke(pdsource, password); - return (ConnectionPoolDataSource) pdsource; - } - - @Override - protected int getDefaultPort() { - return 0; - } - - @Override - public void change(Properties property) { - Method seturlm; - Class clazz = source.getClass(); - String newurl = property.getProperty(JDBC_URL, this.url); - String newuser = property.getProperty(JDBC_USER, this.username); - String newpassword = property.getProperty(JDBC_PWD, this.password); - if (Objects.equals(this.url, newurl) && Objects.equals(this.username, newuser) && Objects.equals(this.password, newpassword)) return; - try { - try { - seturlm = clazz.getMethod("setUrl", String.class); - } catch (Exception e) { - seturlm = clazz.getMethod("setURL", String.class); - } - seturlm.invoke(source, newurl); - clazz.getMethod("setUser", String.class).invoke(source, newuser); - clazz.getMethod("setPassword", String.class).invoke(source, newpassword); - this.url = newurl; - this.username = newuser; - this.password = newpassword; - logger.log(Level.INFO, DataSource.class.getSimpleName() + "(" + unitName + "." + rwtype + ") change (" + property + ")"); - } catch (Exception e) { - logger.log(Level.SEVERE, DataSource.class.getSimpleName() + " dynamic change JDBC (url userName password) error", e); - } - } - - @Override - public void offerConnection(final C connection) { - Connection conn = (Connection) connection; - if (conn == null) return; - try { - conn.close(); - } catch (Exception e) { - logger.log(Level.WARNING, "closeSQLConnection abort", e); - } - } - - @Override - public Connection poll() { - return poll(0, null); - } - - @Override - public CompletableFuture pollAsync() { - return CompletableFuture.completedFuture(poll()); - } - - private Connection poll(final int count, SQLException e) { - if (count >= 3) { - logger.log(Level.WARNING, "create pooled connection error", e); - throw new RuntimeException(e); - } - PooledConnection result = queue.poll(); - if (result == null) { - if (usingCounter.get() >= maxconns) { - try { - result = queue.poll(6, TimeUnit.SECONDS); - } catch (Exception t) { - logger.log(Level.WARNING, "take pooled connection error", t); - } - } - if (result == null) { - try { - result = source.getPooledConnection(); - result.addConnectionEventListener(listener); - usingCounter.incrementAndGet(); - } catch (SQLException ex) { - return poll(count + 1, ex); - } - creatCounter.incrementAndGet(); - } - } else { - cycleCounter.incrementAndGet(); - } - Connection conn; - try { - conn = result.getConnection(); - if (!conn.isValid(1)) { - logger.info("sql connection is not vaild"); - usingCounter.decrementAndGet(); - return poll(0, null); - } - } catch (SQLException ex) { - if (!"08S01".equals(ex.getSQLState())) {//MySQL特性, 长时间连接没使用会抛出com.mysql.jdbc.exceptions.jdbc4.CommunicationsException - logger.log(Level.FINER, "result.getConnection from pooled connection abort [" + ex.getSQLState() + "]", ex); - } - return poll(0, null); - } - return conn; - } - - @Override - public void close() { - queue.stream().forEach(x -> { - try { - x.close(); - } catch (Exception e) { - } - }); - } - -} diff --git a/src/org/redkale/source/PoolSource.java b/src/org/redkale/source/PoolSource.java deleted file mode 100644 index 6d7cfa165..000000000 --- a/src/org/redkale/source/PoolSource.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.net.InetSocketAddress; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Logger; -import static org.redkale.source.DataSources.*; - -/** - * 连接池类 - * - *

    - * 详情见: https://redkale.org - * - * @author zhangjx - */ -public abstract class PoolSource { - - protected final AtomicLong closeCounter = new AtomicLong(); - - protected final AtomicLong usingCounter = new AtomicLong(); - - protected final AtomicLong creatCounter = new AtomicLong(); - - protected final AtomicLong cycleCounter = new AtomicLong(); - - protected final AtomicLong saveCounter = new AtomicLong(); - - protected final Logger logger; - - protected final String rwtype; // "" 或 "read" 或 "write" - - protected final String dbtype; - - protected int maxconns; - - protected Semaphore semaphore; - - protected int connectTimeoutSeconds; - - protected int readTimeoutSeconds; - - protected int writeTimeoutSeconds; - - protected String url; - - protected InetSocketAddress servaddr; - - protected String username; - - protected String password; - - protected String database; - - protected String encoding; - - protected Properties props; - - protected Properties attributes = new Properties(); - - @SuppressWarnings("OverridableMethodCallInConstructor") - public PoolSource(String rwtype, Semaphore semaphore, Properties prop, Logger logger) { - this.logger = logger; - this.rwtype = rwtype; - this.props = prop; - this.url = prop.getProperty(JDBC_URL); - this.username = prop.getProperty(JDBC_USER, ""); - this.password = prop.getProperty(JDBC_PWD, ""); - this.encoding = prop.getProperty(JDBC_ENCODING, ""); - this.connectTimeoutSeconds = Integer.decode(prop.getProperty(JDBC_CONNECTTIMEOUT_SECONDS, "6")); - this.readTimeoutSeconds = Integer.decode(prop.getProperty(JDBC_READTIMEOUT_SECONDS, "6")); - this.writeTimeoutSeconds = Integer.decode(prop.getProperty(JDBC_WRITETIMEOUT_SECONDS, "6")); - this.maxconns = Math.max(8, Integer.decode(prop.getProperty(JDBC_CONNECTIONS_LIMIT, "" + Runtime.getRuntime().availableProcessors()))); - this.semaphore = semaphore == null ? new Semaphore(this.maxconns) : semaphore; - String dbtype0 = ""; - { //jdbc:mysql:// jdbc:microsoft:sqlserver:// 取://之前的到最后一个:之间的字符串 - int pos = this.url.indexOf("://"); - if (pos > 0) { - String url0 = this.url.substring(0, pos); - pos = url0.lastIndexOf(':'); - if (pos > 0) dbtype0 = url0.substring(pos + 1); - } else { //jdbc:oracle:thin:@localhost:1521 - String url0 = url.substring(url.indexOf(":") + 1); - pos = url0.indexOf(':'); - if (pos > 0) dbtype0 = url0.substring(0, pos); - } - if ("mysqlx".equalsIgnoreCase(dbtype0)) dbtype0 = "mysql"; //MySQL X DevAPI - } - this.dbtype = dbtype0.toLowerCase(); - parseAddressAndDbnameAndAttrs(); - - if ("oracle".equals(this.dbtype)) { - this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) > 0"); - this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "INSTR(${keystr}, ${column}) = 0"); - if (!this.props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) { - this.props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42000;42S02"); - } - if (!this.props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) { - //注意:此语句复制表结构会导致默认值和主键信息的丢失 - this.props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS SELECT * FROM ${oldtable} WHERE 1=2"); - } - } else if ("sqlserver".equals(this.dbtype)) { - this.props.setProperty(JDBC_CONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) > 0"); - this.props.setProperty(JDBC_NOTCONTAIN_SQLTEMPLATE, "CHARINDEX(${column}, ${keystr}) = 0"); - } else if ("postgresql".equals(this.dbtype)) { - if (!this.props.containsKey(JDBC_TABLECOPY_SQLTEMPLATE)) { //注意:此语句复制表结构会导致默认值和主键信息的丢失 - //注意:postgresql不支持跨库复制表结构 - this.props.setProperty(JDBC_TABLECOPY_SQLTEMPLATE, "CREATE TABLE ${newtable} AS (SELECT * FROM ${oldtable} LIMIT 0)"); - } - if (!this.props.containsKey(JDBC_TABLENOTEXIST_SQLSTATES)) { - this.props.setProperty(JDBC_TABLENOTEXIST_SQLSTATES, "42P01;3F000"); - } - } else if ("mysql".equals(this.dbtype)) { - if (this.encoding.isEmpty()) this.encoding = attributes.getProperty("characterEncoding", ""); - } - } - - protected void parseAddressAndDbnameAndAttrs() { - if (this.url.startsWith("jdbc:h2:")) return; - String url0 = this.url.substring(this.url.indexOf("://") + 3); - int pos = url0.indexOf('?'); //127.0.0.1:5432/db?charset=utr8&xxx=yy - if (pos > 0) { - String params = url0.substring(pos + 1).replace("&", "&"); - for (String param : params.split("&")) { - int p = param.indexOf('='); - if (p < 1) continue; - this.attributes.put(param.substring(0, p), param.substring(p + 1)); - } - url0 = url0.substring(0, pos); - } - pos = url0.indexOf('/'); //127.0.0.1:5432/db - if (pos > 0) { - this.database = url0.substring(pos + 1); - url0 = url0.substring(0, pos); - } - pos = url0.indexOf(':'); - if (pos > 0) { - this.servaddr = new InetSocketAddress(url0.substring(0, pos), Integer.parseInt(url0.substring(pos + 1))); - } else { - this.servaddr = new InetSocketAddress(url0, getDefaultPort()); - } - } - - protected abstract int getDefaultPort(); - - public abstract void change(Properties property); - - public abstract C poll(); - - public abstract CompletableFuture pollAsync(); - - public abstract void offerConnection(final C conn); - - public abstract void close(); - - public Semaphore getSemaphore() { - return semaphore; - } - - public void setSemaphore(Semaphore semaphore) { - this.semaphore = semaphore; - } - - public int getMaxconns() { - return maxconns; - } - - public void setMaxconns(int maxconns) { - this.maxconns = maxconns; - } - - public final String getDbtype() { - return dbtype; - } - - public final long getCloseCount() { - return closeCounter.longValue(); - } - - public final long getUsingCount() { - return usingCounter.longValue(); - } - - public final long getCreatCount() { - return creatCounter.longValue(); - } - - public final long getCycleCount() { - return cycleCounter.longValue(); - } - - public final long getSaveCount() { - return saveCounter.longValue(); - } - - public final int getConnectTimeoutSeconds() { - return connectTimeoutSeconds; - } - - public final int getReadTimeoutSeconds() { - return readTimeoutSeconds; - } - - public final int getWriteTimeoutSeconds() { - return writeTimeoutSeconds; - } - - public final String getUrl() { - return url; - } - - public final InetSocketAddress getServaddr() { - return servaddr; - } - - public final String getUsername() { - return username; - } - - public final String getPassword() { - return password; - } - - public final String getDatabase() { - return database; - } - -} diff --git a/src/org/redkale/source/PoolTcpSource.java b/src/org/redkale/source/PoolTcpSource.java deleted file mode 100644 index 828d20ace..000000000 --- a/src/org/redkale/source/PoolTcpSource.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import java.nio.ByteBuffer; -import java.nio.channels.CompletionHandler; -import java.sql.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.logging.*; -import org.redkale.net.*; -import static org.redkale.source.DataSources.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public abstract class PoolTcpSource extends PoolSource { - - protected AsyncGroup asyncGroup; - - protected ScheduledThreadPoolExecutor scheduler; - - protected ArrayBlockingQueue> pollQueue; - - protected ArrayBlockingQueue connQueue; - - public PoolTcpSource(AsyncGroup asyncGroup, String rwtype, ArrayBlockingQueue queue, Semaphore semaphore, Properties prop, Logger logger) { - super(rwtype, semaphore, prop, logger); - this.asyncGroup = asyncGroup; - this.connQueue = queue == null ? new ArrayBlockingQueue<>(this.maxconns) : queue; - this.pollQueue = new ArrayBlockingQueue(this.connQueue.remainingCapacity() * 1000); - this.scheduler = new ScheduledThreadPoolExecutor(1, (Runnable r) -> { - final Thread t = new Thread(r, "Redkale-PoolSource-Scheduled-Thread"); - t.setDaemon(true); - return t; - }); - this.scheduler.scheduleAtFixedRate(() -> { - runPingTask(); - }, 60, 30, TimeUnit.SECONDS); - } - - public void updateConnQueue(ArrayBlockingQueue queue) { - ArrayBlockingQueue old = this.connQueue; - this.connQueue = queue; - AsyncConnection conn; - final Semaphore localSemaphore = this.semaphore; - while ((conn = old.poll()) != null) { - queue.offer(conn); - if (localSemaphore != null) localSemaphore.tryAcquire(); - } - } - - private void runPingTask() { - try { - if (connQueue.isEmpty()) return; - long time = System.currentTimeMillis() - 30 * 1000; - AsyncConnection first = connQueue.peek(); - if (first == null || first.getLastReadTime() >= time || first.getLastWriteTime() >= time) return; - pollAsync().whenComplete((conn, e) -> { - if (e != null) return; - if (conn.getLastReadTime() >= time || conn.getLastWriteTime() >= time) {//半分钟内已经用过 - offerConnection(conn); - return; - } - CompletableFuture future = sendPingCommand(conn); - if (future == null) { //不支持ping - offerConnection(conn); - return; - } - future.whenComplete((conn2, e2) -> { - if (e2 != null) return; - offerConnection(conn2); - runPingTask(); - }); - }); - } catch (Exception e) { - logger.log(Level.FINEST, "PoolSource task ping failed", e); - } - } - - @Override - public void offerConnection(final C connection) { - AsyncConnection conn = (AsyncConnection) connection; - if (conn == null) return; - if (conn.isOpen()) { - CompletableFuture future = pollQueue.poll(); - if (future != null && future.complete(conn)) return; - - if (connQueue.offer(conn)) { - saveCounter.incrementAndGet(); - usingCounter.decrementAndGet(); - return; - } - } - //usingCounter 会在close方法中执行 - CompletableFuture closefuture = null; - try { - closefuture = sendCloseCommand(conn); - } catch (Exception e) { - } - if (closefuture == null) { - conn.dispose(); - } else { - closefuture.whenComplete((c, t) -> { - if (c != null) c.dispose(); - }); - } - } - - @Override - public void change(Properties prop) { - String newurl = prop.getProperty(JDBC_URL, this.url); - String newuser = prop.getProperty(JDBC_USER, this.username); - String newpassword = prop.getProperty(JDBC_PWD, this.password); - if (Objects.equals(this.url, newurl) && Objects.equals(this.username, newuser) && Objects.equals(this.password, newpassword)) return; - this.url = newurl; - this.username = newuser; - this.password = newpassword; - parseAddressAndDbnameAndAttrs(); - } - - @Override - public final AsyncConnection poll() { - return pollAsync().join(); - } - - protected abstract ByteArray reqConnectBuffer(AsyncConnection conn); - - protected abstract void respConnectBuffer(final ByteBuffer buffer, CompletableFuture future, AsyncConnection conn); - - @Override - public CompletableFuture pollAsync() { - - AsyncConnection conn0 = connQueue.poll(); - if (conn0 != null && conn0.isOpen()) { - cycleCounter.incrementAndGet(); - usingCounter.incrementAndGet(); - return CompletableFuture.completedFuture(conn0); - } - final Semaphore localSemaphore = this.semaphore; - if (!localSemaphore.tryAcquire()) { - final CompletableFuture future = Utility.orTimeout(new CompletableFuture<>(), 10, TimeUnit.SECONDS); - future.whenComplete((r, t) -> pollQueue.remove(future)); - if (pollQueue.offer(future)) return future; - future.completeExceptionally(new SQLException("create datasource connection error")); - return future; - } - return asyncGroup.createTCP(this.servaddr, this.readTimeoutSeconds, this.writeTimeoutSeconds).thenCompose(conn -> { - conn.beforeCloseListener((c) -> { - localSemaphore.release(); - closeCounter.incrementAndGet(); - usingCounter.decrementAndGet(); - }); - CompletableFuture future = new CompletableFuture(); - if (conn.getSubobject() == null) conn.setSubobject(new ByteArray()); - final ByteArray array = reqConnectBuffer(conn); - if (array == null) { - conn.read(new CompletionHandler() { - @Override - public void completed(Integer result, ByteBuffer rbuffer) { - if (result < 0) { - failed(new SQLException("Read Buffer Error"), rbuffer); - return; - } - rbuffer.flip(); - respConnectBuffer(rbuffer, future, conn); - } - - @Override - public void failed(Throwable exc, ByteBuffer rbuffer) { - conn.offerBuffer(rbuffer); - future.completeExceptionally(exc); - conn.dispose(); - } - }); - } else { - conn.write(array, new CompletionHandler() { - @Override - public void completed(Integer result0, Void attachment0) { - if (result0 < 0) { - failed(new SQLException("Write Buffer Error"), attachment0); - return; - } - conn.read(new CompletionHandler() { - @Override - public void completed(Integer result, ByteBuffer rbuffer) { - if (result < 0) { - failed(new SQLException("Read Buffer Error"), rbuffer); - return; - } - rbuffer.flip(); - respConnectBuffer(rbuffer, future, conn); - } - - @Override - public void failed(Throwable exc, ByteBuffer rbuffer) { - conn.offerBuffer(rbuffer); - future.completeExceptionally(exc); - conn.dispose(); - } - }); - } - - @Override - public void failed(Throwable exc, Void attachment0) { - future.completeExceptionally(exc); - conn.dispose(); - } - }); - } - return future; - }).whenComplete((c, t) -> { - if (t == null) { - creatCounter.incrementAndGet(); - usingCounter.incrementAndGet(); - } else { - localSemaphore.release(); - } - }); - } - - public ArrayBlockingQueue> getPollQueue() { - return pollQueue; - } - - public void setPollQueue(ArrayBlockingQueue> pollQueue) { - this.pollQueue = pollQueue; - } - - public ArrayBlockingQueue getConnQueue() { - return connQueue; - } - - public void setConnQueue(ArrayBlockingQueue connQueue) { - this.connQueue = connQueue; - } - - @Override - public void close() { - this.scheduler.shutdownNow(); - final List futures = new ArrayList<>(); - connQueue.stream().forEach(x -> { - CompletableFuture future = null; - try { - future = sendCloseCommand(x); - } catch (Exception e) { - } - if (future == null) { - x.dispose(); - } else { - futures.add(future.whenComplete((c, t) -> { - if (c != null) c.dispose(); - })); - } - }); - if (!futures.isEmpty()) { - CompletableFuture.allOf(new CompletableFuture[futures.size()]).join(); - } - } - - protected abstract CompletableFuture sendPingCommand(final AsyncConnection conn); - - protected abstract CompletableFuture sendCloseCommand(final AsyncConnection conn); -} diff --git a/src/org/redkale/util/RedkaleClassLoader.java b/src/org/redkale/util/RedkaleClassLoader.java deleted file mode 100644 index 570823cf1..000000000 --- a/src/org/redkale/util/RedkaleClassLoader.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.util; - -import java.lang.reflect.*; -import java.net.*; -import java.nio.file.Paths; -import java.util.HashSet; - -/** - * - * @author zhangjx - */ -public class RedkaleClassLoader extends URLClassLoader { - - public RedkaleClassLoader(ClassLoader parent) { - super(new URL[0], parent); - } - - public Class loadClass(String name, byte[] b) { - return defineClass(name, b, 0, b.length); - } - - @Override - public void addURL(URL url) { - super.addURL(url); - } - - @Override - public URL[] getURLs() { - return super.getURLs(); - } - - public URL[] getAllURLs() { - ClassLoader loader = this; - HashSet set = new HashSet<>(); - String appPath = System.getProperty("java.class.path"); - if (appPath != null && !appPath.isEmpty()) { - for (String path : appPath.replace("://", "&&").replace(":\\", "##").replace(':', ';').split(";")) { - try { - set.add(Paths.get(path.replace("&&", "://").replace("##", ":\\")).toRealPath().toFile().toURI().toURL()); - } catch (Exception e) { - } - } - } - do { - String loaderName = loader.getClass().getName(); - if (loaderName.startsWith("sun.") && loaderName.contains("ExtClassLoader")) continue; - if (loader instanceof URLClassLoader) { - for (URL url : ((URLClassLoader) loader).getURLs()) { - set.add(url); - } - } else { //可能JDK9及以上 - loader.getResource("org.redkale"); //必须要运行一次,确保URLClassPath的值被填充完毕 - Class loaderClazz = loader.getClass(); - Object ucp = null; - do { //读取 java.base/jdk.internal.loader.BuiltinClassLoader的URLClassPath ucp值 - try { - //需要在命令行里加入: --add-opens java.base/jdk.internal.loader=ALL-UNNAMED - Field field = loaderClazz.getDeclaredField("ucp"); - field.setAccessible(true); - ucp = field.get(loader); - break; - } catch (Throwable e) { - } - } while ((loaderClazz = loaderClazz.getSuperclass()) != Object.class); - if (ucp != null) { //URLClassPath - URL[] urls = null; - try { //读取 java.base/jdk.internal.loader.URLClassPath的urls值 - Method method = ucp.getClass().getMethod("getURLs"); - urls = (URL[]) method.invoke(ucp); - } catch (Exception e) { - } - if (urls != null) { - for (URL url : urls) { - set.add(url); - } - } - } - } - } while ((loader = loader.getParent()) != null); - return set.toArray(new URL[set.size()]); - } -} diff --git a/test/org/redkale/test/asm/AsmCreator.java b/src/test/java/org/redkale/test/asm/AsmCreator.java similarity index 97% rename from test/org/redkale/test/asm/AsmCreator.java rename to src/test/java/org/redkale/test/asm/AsmCreator.java index 8de08f93e..b43ea6976 100644 --- a/test/org/redkale/test/asm/AsmCreator.java +++ b/src/test/java/org/redkale/test/asm/AsmCreator.java @@ -1,39 +1,39 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.asm; - -import java.io.*; -import org.redkale.util.Utility; - -/** - * - * @author zhangjx - */ -public class AsmCreator { - - public static void main(String[] args) throws Throwable { - boolean realasm = false; //从http://forge.ow2.org/projects/asm/ 下载最新asm的src放在 srcasmroot 目录下 - File srcasmroot = new File("D:/JAVA/JDK源码/JDK11源码/java.base/jdk/internal/org/objectweb/asm"); - if (realasm) srcasmroot = new File("D:/JAVA/JDK源码/org/objectweb/asm"); - File destasmroot = new File("D:/Java-Projects/RedkaleProject/src/org/redkale/asm"); - String line = null; - LineNumberReader txtin = new LineNumberReader(new FileReader(new File(destasmroot, "asm.txt"))); - while ((line = txtin.readLine()) != null) { - line = line.trim(); - if (!line.endsWith(".java")) continue; - File srcfile = new File(srcasmroot, line); - if (!srcfile.isFile()) continue; - File destfile = new File(destasmroot, line); - String content = Utility.readThenClose(new FileInputStream(srcfile)); - FileOutputStream out = new FileOutputStream(destfile); - out.write(content.replace("jdk.internal.org.objectweb", "org.redkale").replace("org.objectweb", "org.redkale") - .replace("", "<tt>").replace("", "</tt>") - .replace("{@link org.redkale.asm.tree.MethodNode#getLabelNode} method.", "").getBytes()); - out.close(); - } - //需要屏蔽ClassReader中判断checks the class version的部分 - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.asm; + +import java.io.*; +import org.redkale.util.Utility; + +/** + * + * @author zhangjx + */ +public class AsmCreator { + + public static void main(String[] args) throws Throwable { + boolean realasm = false; //从http://forge.ow2.org/projects/asm/ 下载最新asm的src放在 srcasmroot 目录下 + File srcasmroot = new File("D:/JAVA/JDK源码/JDK11源码/java.base/jdk/internal/org/objectweb/asm"); + if (realasm) srcasmroot = new File("D:/JAVA/JDK源码/org/objectweb/asm"); + File destasmroot = new File("D:/Java-Projects/RedkaleProject/src/org/redkale/asm"); + String line = null; + LineNumberReader txtin = new LineNumberReader(new FileReader(new File(destasmroot, "asm.txt"))); + while ((line = txtin.readLine()) != null) { + line = line.trim(); + if (!line.endsWith(".java")) continue; + File srcfile = new File(srcasmroot, line); + if (!srcfile.isFile()) continue; + File destfile = new File(destasmroot, line); + String content = Utility.readThenClose(new FileInputStream(srcfile)); + FileOutputStream out = new FileOutputStream(destfile); + out.write(content.replace("jdk.internal.org.objectweb", "org.redkale").replace("org.objectweb", "org.redkale") + .replace("", "<tt>").replace("", "</tt>") + .replace("{@link org.redkale.asm.tree.MethodNode#getLabelNode} method.", "").getBytes()); + out.close(); + } + //需要屏蔽ClassReader中判断checks the class version的部分 + } +} diff --git a/test/org/redkale/test/convert/BiFunctionConvertMain.java b/src/test/java/org/redkale/test/convert/BiFunctionConvertTest.java similarity index 83% rename from test/org/redkale/test/convert/BiFunctionConvertMain.java rename to src/test/java/org/redkale/test/convert/BiFunctionConvertTest.java index 7732dacf6..b714a0711 100644 --- a/test/org/redkale/test/convert/BiFunctionConvertMain.java +++ b/src/test/java/org/redkale/test/convert/BiFunctionConvertTest.java @@ -1,68 +1,72 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import org.redkale.convert.ConvertField; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.Attribute; - -/** - * - * @author zhangjx - */ -public class BiFunctionConvertMain { - - public static class GamePlayer { - - public int userid; - - public String username; - - public int[] cards; - } - - public static class GameTable { - - public int tableid; - - public GamePlayer[] players; - } - - public static void main(String[] args) throws Throwable { - GamePlayer player1 = new GamePlayer(); - player1.userid = 1; - player1.username = "玩家1"; - player1.cards = new int[]{11, 12, 13, 14, 15}; - GamePlayer player2 = new GamePlayer(); - player2.userid = 2; - player2.username = "玩家2"; - player2.cards = new int[]{21, 22, 23, 24, 25}; - GamePlayer player3 = new GamePlayer(); - player3.userid = 3; - player3.username = "玩家3"; - player3.cards = new int[]{31, 32, 33, 34, 35}; - GameTable table = new GameTable(); - table.tableid = 100; - table.players = new GamePlayer[]{player1, player2, player3}; - JsonConvert convert1 = JsonConvert.root(); - System.out.println(convert1.convertTo(table)); - JsonConvert convert2 = convert1.newConvert((Attribute t, Object u) -> { - if (t.field().equals("cards") && u instanceof GamePlayer) { - int userid = ((GamePlayer) u).userid; - if (userid == 3) return null; //玩家3的cards不输出 - return t.get(u); - } - return t.get(u); - }, (Object u) -> { - if (table != u) return null; - //return new ConvertField[]{new ConvertField("extcol1", 30), new ConvertField("extcol2", "扩展字段值")}; - return ConvertField.ofArray("extcol1", 30, "extcol2", "扩展字段值"); - }); - System.out.println(convert2.convertTo(table)); - //{"players":[{"cards":[11,12,13,14,15],"userid":1,"username":"玩家1"},{"cards":[21,22,23,24,25],"userid":2,"username":"玩家2"},{"cards":[31,32,33,34,35],"userid":3,"username":"玩家3"}],"tableid":100} - //{"players":[{"cards":[11,12,13,14,15],"userid":1,"username":"玩家1"},{"cards":[21,22,23,24,25],"userid":2,"username":"玩家2"},{"userid":3,"username":"玩家3"}],"tableid":100,"extcol1":30,"extcol2":"扩展字段值"} - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.redkale.convert.ConvertField; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.Attribute; + +/** + * @author zhangjx + */ +public class BiFunctionConvertTest { + + public static class GamePlayer { + + public int userid; + + public String username; + + public int[] cards; + } + + public static class GameTable { + + public int tableid; + + public GamePlayer[] players; + } + + @Test + public void run() throws Throwable { + GamePlayer player1 = new GamePlayer(); + player1.userid = 1; + player1.username = "玩家1"; + player1.cards = new int[]{11, 12, 13, 14, 15}; + GamePlayer player2 = new GamePlayer(); + player2.userid = 2; + player2.username = "玩家2"; + player2.cards = new int[]{21, 22, 23, 24, 25}; + GamePlayer player3 = new GamePlayer(); + player3.userid = 3; + player3.username = "玩家3"; + player3.cards = new int[]{31, 32, 33, 34, 35}; + GameTable table = new GameTable(); + table.tableid = 100; + table.players = new GamePlayer[]{player1, player2, player3}; + JsonConvert convert1 = JsonConvert.root(); + System.out.println(convert1.convertTo(table)); + JsonConvert convert2 = convert1.newConvert((Attribute t, Object u) -> { + if (t.field().equals("cards") && u instanceof GamePlayer) { + int userid = ((GamePlayer) u).userid; + if (userid == 3) return null; //玩家3的cards不输出 + return t.get(u); + } + return t.get(u); + }, (Object u) -> { + if (table != u) return null; + //return new ConvertField[]{new ConvertField("extcol1", 30), new ConvertField("extcol2", "扩展字段值")}; + return ConvertField.ofArray("extcol1", 30, "extcol2", "扩展字段值"); + }); + System.out.println(convert2.convertTo(table)); + Assertions.assertEquals("{\"players\":[{\"cards\":[11,12,13,14,15],\"userid\":1,\"username\":\"玩家1\"},{\"cards\":[21,22,23,24,25],\"userid\":2,\"username\":\"玩家2\"},{\"userid\":3,\"username\":\"玩家3\"}],\"tableid\":100,\"extcol1\":30,\"extcol2\":\"扩展字段值\"}", + convert2.convertTo(table)); + //{"players":[{"cards":[11,12,13,14,15],"userid":1,"username":"玩家1"},{"cards":[21,22,23,24,25],"userid":2,"username":"玩家2"},{"cards":[31,32,33,34,35],"userid":3,"username":"玩家3"}],"tableid":100} + //{"players":[{"cards":[11,12,13,14,15],"userid":1,"username":"玩家1"},{"cards":[21,22,23,24,25],"userid":2,"username":"玩家2"},{"userid":3,"username":"玩家3"}],"tableid":100,"extcol1":30,"extcol2":"扩展字段值"} + } +} diff --git a/src/test/java/org/redkale/test/convert/BsonMainTest.java b/src/test/java/org/redkale/test/convert/BsonMainTest.java new file mode 100644 index 000000000..11db6dff5 --- /dev/null +++ b/src/test/java/org/redkale/test/convert/BsonMainTest.java @@ -0,0 +1,276 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.io.*; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.redkale.convert.bson.BsonByteBufferWriter; +import org.redkale.convert.bson.BsonFactory; +import org.redkale.util.Utility; +import org.redkale.convert.bson.BsonConvert; + +import java.nio.*; +import java.util.*; +import javax.persistence.*; + +import org.redkale.convert.json.*; +import org.redkale.util.*; + +/** + * @author zhangjx + */ +public class BsonMainTest { + + @Test + public void run1() throws Throwable { + Serializable[] sers = new Serializable[]{"aaa", 4}; + final BsonConvert convert = BsonFactory.root().getConvert(); + byte[] bytes = convert.convertTo(sers); + Utility.println("---", bytes); + byte[] checks = new byte[]{0x00, 0x00, 0x00, 0x02, 0x7f, 0x01, 0x41, 0x00, 0x00, 0x00, 0x03, 0x61, 0x61, 0x61, 0x01, 0x69, 0x00, 0x00, 0x00, 0x04}; + Assertions.assertArrayEquals(checks, bytes); + Serializable[] a = convert.convertFrom(Serializable[].class, bytes); + Assertions.assertEquals("[aaa, 4]", Arrays.toString(a)); + } + + @Test + public void run2() throws Exception { + final BsonConvert convert = BsonFactory.root().getConvert(); + SimpleChildEntity entry = SimpleChildEntity.create(); + byte[] bytes = convert.convertTo(SimpleEntity.class, entry); + System.out.println("长度: " + bytes.length); + Assertions.assertEquals(260, bytes.length); + BsonByteBufferWriter writer = convert.pollBsonWriter(() -> ByteBuffer.allocate(1)); + convert.convertTo(writer, SimpleEntity.class, entry); + ByteBuffer[] buffers = writer.toBuffers(); + int len = 0; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (ByteBuffer b : buffers) { + len += b.remaining(); + byte[] ts = new byte[b.remaining()]; + b.get(ts); + out.write(ts); + b.flip(); + } + System.out.println("长度: " + len); + Assertions.assertEquals(260, len); + SimpleChildEntity entry2 = convert.convertFrom(SimpleChildEntity.class, buffers); + System.out.println(entry); + Assertions.assertEquals(entry.toString(), entry2.toString()); + } + + @Test + public void run3() throws Exception { + final BsonConvert convert = BsonFactory.root().getConvert(); + SimpleChildEntity entry = SimpleChildEntity.create(); + byte[] bytes = convert.convertTo(SimpleEntity.class, entry); + Utility.println(null, bytes); + System.out.println(JsonConvert.root().convertTo(entry)); + SimpleEntity rs = convert.convertFrom(SimpleEntity.class, bytes); + Assertions.assertEquals(JsonConvert.root().convertTo(entry), rs.toString()); + + ComplextEntity bean = new ComplextEntity(); + byte[] bytes2 = convert.convertTo(Object.class, bean); + final int len = bytes2.length; + BsonByteBufferWriter writer = convert.pollBsonWriter(() -> ByteBuffer.allocate(len / 2)); + convert.convertTo(writer, bean); + bytes2 = writer.toArray(); + System.out.println(convert.convertFrom(ComplextEntity.class, bytes2).toString()); + Assertions.assertEquals("{\"flag\":true,\"userid\":0}", convert.convertFrom(ComplextEntity.class, bytes2).toString()); + } + + @Test + public void run4() throws Exception { + final BsonConvert convert = BsonFactory.root().getConvert(); + SimpleChildEntity entry = SimpleChildEntity.create(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + convert.convertTo(out, SimpleEntity.class, entry); + byte[] bytes = out.toByteArray(); + Utility.println(null, bytes); + SimpleEntity rs = convert.convertFrom(SimpleEntity.class, new ByteArrayInputStream(bytes)); + System.out.println(rs.toString()); + Assertions.assertEquals(JsonConvert.root().convertTo(entry), rs.toString()); + } + + @Test + public void run5() throws Exception { + final BsonConvert convert = BsonFactory.root().getConvert(); + + LinkedHashMap map = new LinkedHashMap(); + map.put("1", 1); + map.put("2", "a2"); + byte[] bs = convert.convertTo(Object.class, map); + Object mapobj = convert.convertFrom(Object.class, bs); + System.out.println(mapobj); + Assertions.assertEquals("{1=1, 2=a2}", mapobj.toString()); + } + + @Test + public void run6() throws Exception { + final BsonConvert convert = BsonFactory.root().getConvert(); + + Optional val = Optional.ofNullable("haha"); + byte[] bs = convert.convertTo(val); + Object obj = convert.convertFrom(Optional.class, bs); + System.out.println(obj); + Assertions.assertEquals("Optional[haha]", obj.toString()); + bs = convert.convertTo(Object.class, val); + obj = convert.convertFrom(Object.class, bs); + Assertions.assertEquals("Optional[haha]", obj.toString()); + bs = convert.convertTo(new TypeToken>() { + }.getType(), val); + obj = convert.convertFrom(new TypeToken>() { + }.getType(), bs); + Assertions.assertEquals("Optional[haha]", obj.toString()); + System.out.println(JsonConvert.root().convertTo(val)); + Assertions.assertEquals("\"haha\"", JsonConvert.root().convertTo(val)); + } + + @Test + public void run7() throws Throwable { + Two two = new Two(); + two.setKey("key111"); + two.setCode(12345); + List list = new ArrayList<>(); + list.add("haha"); + two.setList(list); + Map map = new HashMap<>(); + map.put("222", "333"); + two.setStringMap(map); + + List records = new ArrayList<>(); + records.add(ConvertRecord.createDefault()); + two.setRecords(records); + + Map rmap = new HashMap<>(); + rmap.put("222", ConvertRecord.createDefault()); + two.setRecordMap(rmap); + + byte[] bs = BsonFactory.root().getConvert().convertTo(two); + + One one = BsonFactory.root().getConvert().convertFrom(One.class, bs); + System.out.println(one); + Assertions.assertEquals("{\"bytes\":[3,4,5],\"code\":12345,\"ints\":[3000,4000,5000],\"key\":\"key111\"}", one.toString()); + } + + @Test + public void run8() throws Exception { + final JsonConvert jsonConvert = JsonConvert.root(); + final BsonConvert bsonConvert = BsonFactory.root().getConvert(); + ConstructorArgsEntity bean = new ConstructorArgsEntity(12345678, "哈哈"); + bean.setCreatetime(12345678901L); + String json = jsonConvert.convertTo(bean); + System.out.println(json); + Assertions.assertEquals("{\"createtime\":12345678901,\"name\":\"哈哈\",\"userid\":12345678}", json); + Assertions.assertEquals(jsonConvert.convertFrom(ConstructorArgsEntity.class, json).toString(), json); + byte[] bytes = bsonConvert.convertTo(bean); + Assertions.assertEquals(bsonConvert.convertFrom(ConstructorArgsEntity.class, bytes).toString(), json); + } + + public static class ComplextEntity { + + @Id + private int userid; + + private String chname = ""; + + @Transient + private boolean flag = true; + + @Transient + private List children; + + @Transient + private SimpleEntity user; + + public int getUserid() { + return userid; + } + + public void setUserid(int userid) { + this.userid = userid; + } + + public String getChname() { + return chname; + } + + public void setChname(String chname) { + this.chname = chname; + } + + public boolean isFlag() { + return flag; + } + + public void setFlag(boolean flag) { + this.flag = flag; + } + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } + + public SimpleEntity getUser() { + return user; + } + + public void setUser(SimpleEntity user) { + this.user = user; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } + + public static class ConstructorArgsEntity { + + private final int userid; + + private String name; + + private long createtime; + + @ConstructorParameters({"userid", "name"}) + public ConstructorArgsEntity(int userid, String name) { + this.userid = userid; + this.name = name; + } + + public int getUserid() { + return userid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getCreatetime() { + return createtime; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/src/test/java/org/redkale/test/convert/ConvertImplTest.java b/src/test/java/org/redkale/test/convert/ConvertImplTest.java new file mode 100644 index 000000000..7035fca6b --- /dev/null +++ b/src/test/java/org/redkale/test/convert/ConvertImplTest.java @@ -0,0 +1,45 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import org.junit.jupiter.api.*; +import org.redkale.convert.ConvertImpl; +import org.redkale.convert.json.JsonConvert; + +/** + * + * @author zhangjx + */ +public class ConvertImplTest { + + @Test + public void run1() throws Throwable { + String json = "{'name':'hellow'}"; + OneEntity one = JsonConvert.root().convertFrom(OneEntity.class, json); + Assertions.assertTrue(one instanceof OneImpl); + } + + @ConvertImpl(OneImpl.class) + public static interface OneEntity { + + public String getName(); + } + + public static class OneImpl implements OneEntity { + + private String name; + + @Override + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + } +} diff --git a/test/org/redkale/test/convert/ConvertRecord.java b/src/test/java/org/redkale/test/convert/ConvertRecord.java similarity index 96% rename from test/org/redkale/test/convert/ConvertRecord.java rename to src/test/java/org/redkale/test/convert/ConvertRecord.java index 55ac8fe35..7c002ebdb 100644 --- a/test/org/redkale/test/convert/ConvertRecord.java +++ b/src/test/java/org/redkale/test/convert/ConvertRecord.java @@ -1,192 +1,192 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - - -import java.util.*; - -/** - * - * @author redkale - */ -public class ConvertRecord { - - private String aname; - - private String desc = ""; - - private int id = (int) System.currentTimeMillis(); - - private int[] integers; - - private long[] longs; - - private List strings; - - private Map map; - - public static ConvertRecord createDefault() { - ConvertRecord v = new ConvertRecord(); - v.setAname("this is name\n \"test"); - v.setId(1000000001); - v.setIntegers(new int[]{12, 34, 56, 78, 90, 123, 456, 789}); - v.setLongs(new long[]{10000012L, 10000034L, 10000056L, 10000078L, -10000090L, -100000123L, -100000456L, -100000789L}); - List list = new ArrayList<>(); - list.add("str_a"); - list.add("str_b"); - list.add("str_c"); - v.setStrings(list); - Map map = new HashMap<>(); - map.put("key_a", 111); - map.put("key_b", 222); - map.put("key_c", 333); - v.setMap(map); - return v; - } - - public static void main(String[] args) throws Exception { - final ConvertRecord entry = ConvertRecord.createDefault(); - run(ConvertRecord.class, entry); - } - - public static void run(final Class type, final T entry) throws Exception { - /** - final org.redkale.convert.json.JsonConvert convert = org.redkale.convert.json.JsonFactory.root().getConvert(); - final String entryString = convert.convertTo(entry); - convert.convertFrom(type, entryString); - System.out.println("redkale-convert: " + convert.convertTo(entry)); - - com.alibaba.fastjson.JSON.parseObject(entryString, type); - System.out.println("fastjson 1.2.7: " + com.alibaba.fastjson.JSON.toJSONString(entry)); - - final com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); - mapper.readValue(entryString, type); - System.out.println("jackson 2.7.2: " + mapper.writeValueAsString(entry)); - - final com.google.gson.Gson gson = new com.google.gson.Gson(); - gson.fromJson(entryString, type); - System.out.println("google-gson 2.4: " + gson.toJson(entry)); - - System.out.println("------------------------------------------------"); - System.out.println("组件 序列化耗时(ms) 反序列化耗时(ms)"); - final int count = 10_0000; - long s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - convert.convertTo(entry); - } - long e = System.currentTimeMillis() - s; - System.out.print("redkale-convert " + e); - - s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - convert.convertFrom(type, entryString); - } - e = System.currentTimeMillis() - s; - System.out.println("\t " + e); - - //---------------------------------------------------------------------------- - s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - com.alibaba.fastjson.JSON.toJSONString(entry); - } - e = System.currentTimeMillis() - s; - System.out.print("fastjson 1.2.7 " + e); - - s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - com.alibaba.fastjson.JSON.parseObject(entryString, type); - } - e = System.currentTimeMillis() - s; - System.out.println("\t " + e); - //---------------------------------------------------------------------------- - s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - mapper.writeValueAsString(entry); - } - e = System.currentTimeMillis() - s; - System.out.print("jackson 2.7.2 " + e); - - s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - mapper.readValue(entryString, type); - } - e = System.currentTimeMillis() - s; - System.out.println("\t " + e); - //---------------------------------------------------------------------------- - s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - gson.toJson(entry); - } - e = System.currentTimeMillis() - s; - System.out.print("google-gson 2.4 " + e); - - s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - gson.fromJson(entryString, type); - } - e = System.currentTimeMillis() - s; - System.out.println("\t " + e); - //---------------------------------------------------------------------------- - */ - } - - public String getAname() { - return aname; - } - - public void setAname(String aname) { - this.aname = aname; - } - - public String getDesc() { - return desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public int[] getIntegers() { - return integers; - } - - public void setIntegers(int[] integers) { - this.integers = integers; - } - - public long[] getLongs() { - return longs; - } - - public void setLongs(long[] longs) { - this.longs = longs; - } - - public List getStrings() { - return strings; - } - - public void setStrings(List strings) { - this.strings = strings; - } - - public Map getMap() { - return map; - } - - public void setMap(Map map) { - this.map = map; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + + +import java.util.*; + +/** + * + * @author redkale + */ +public class ConvertRecord { + + private String aname; + + private String desc = ""; + + private int id = (int) System.currentTimeMillis(); + + private int[] integers; + + private long[] longs; + + private List strings; + + private Map map; + + public static ConvertRecord createDefault() { + ConvertRecord v = new ConvertRecord(); + v.setAname("this is name\n \"test"); + v.setId(1000000001); + v.setIntegers(new int[]{12, 34, 56, 78, 90, 123, 456, 789}); + v.setLongs(new long[]{10000012L, 10000034L, 10000056L, 10000078L, -10000090L, -100000123L, -100000456L, -100000789L}); + List list = new ArrayList<>(); + list.add("str_a"); + list.add("str_b"); + list.add("str_c"); + v.setStrings(list); + Map map = new HashMap<>(); + map.put("key_a", 111); + map.put("key_b", 222); + map.put("key_c", 333); + v.setMap(map); + return v; + } + + public static void main(String[] args) throws Exception { + final ConvertRecord entry = ConvertRecord.createDefault(); + run(ConvertRecord.class, entry); + } + + public static void run(final Class type, final T entry) throws Exception { + /** + final org.redkale.convert.json.JsonConvert convert = org.redkale.convert.json.JsonFactory.root().getConvert(); + final String entryString = convert.convertTo(entry); + convert.convertFrom(type, entryString); + System.out.println("redkale-convert: " + convert.convertTo(entry)); + + com.alibaba.fastjson.JSON.parseObject(entryString, type); + System.out.println("fastjson 1.2.7: " + com.alibaba.fastjson.JSON.toJSONString(entry)); + + final com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper(); + mapper.readValue(entryString, type); + System.out.println("jackson 2.7.2: " + mapper.writeValueAsString(entry)); + + final com.google.gson.Gson gson = new com.google.gson.Gson(); + gson.fromJson(entryString, type); + System.out.println("google-gson 2.4: " + gson.toJson(entry)); + + System.out.println("------------------------------------------------"); + System.out.println("组件 序列化耗时(ms) 反序列化耗时(ms)"); + final int count = 10_0000; + long s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + convert.convertTo(entry); + } + long e = System.currentTimeMillis() - s; + System.out.print("redkale-convert " + e); + + s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + convert.convertFrom(type, entryString); + } + e = System.currentTimeMillis() - s; + System.out.println("\t " + e); + + //---------------------------------------------------------------------------- + s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + com.alibaba.fastjson.JSON.toJSONString(entry); + } + e = System.currentTimeMillis() - s; + System.out.print("fastjson 1.2.7 " + e); + + s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + com.alibaba.fastjson.JSON.parseObject(entryString, type); + } + e = System.currentTimeMillis() - s; + System.out.println("\t " + e); + //---------------------------------------------------------------------------- + s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + mapper.writeValueAsString(entry); + } + e = System.currentTimeMillis() - s; + System.out.print("jackson 2.7.2 " + e); + + s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + mapper.readValue(entryString, type); + } + e = System.currentTimeMillis() - s; + System.out.println("\t " + e); + //---------------------------------------------------------------------------- + s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + gson.toJson(entry); + } + e = System.currentTimeMillis() - s; + System.out.print("google-gson 2.4 " + e); + + s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + gson.fromJson(entryString, type); + } + e = System.currentTimeMillis() - s; + System.out.println("\t " + e); + //---------------------------------------------------------------------------- + */ + } + + public String getAname() { + return aname; + } + + public void setAname(String aname) { + this.aname = aname; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int[] getIntegers() { + return integers; + } + + public void setIntegers(int[] integers) { + this.integers = integers; + } + + public long[] getLongs() { + return longs; + } + + public void setLongs(long[] longs) { + this.longs = longs; + } + + public List getStrings() { + return strings; + } + + public void setStrings(List strings) { + this.strings = strings; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + +} diff --git a/test/org/redkale/test/convert/Fortune.java b/src/test/java/org/redkale/test/convert/Fortune.java similarity index 95% rename from test/org/redkale/test/convert/Fortune.java rename to src/test/java/org/redkale/test/convert/Fortune.java index ff66c3ed4..5f94b545e 100644 --- a/test/org/redkale/test/convert/Fortune.java +++ b/src/test/java/org/redkale/test/convert/Fortune.java @@ -1,56 +1,56 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import javax.persistence.Id; -import org.redkale.convert.json.JsonConvert; - -/** - * - * @author zhangjx - */ -public class Fortune implements Comparable { - - @Id - private int id; - - private String message = ""; - - public Fortune() { - } - - public Fortune(int id, String message) { - this.id = id; - this.message = message; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - @Override - public int compareTo(Fortune o) { - return message.compareTo(o.message); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import javax.persistence.Id; +import org.redkale.convert.json.JsonConvert; + +/** + * + * @author zhangjx + */ +public class Fortune implements Comparable { + + @Id + private int id; + + private String message = ""; + + public Fortune() { + } + + public Fortune(int id, String message) { + this.id = id; + this.message = message; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public int compareTo(Fortune o) { + return message.compareTo(o.message); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } \ No newline at end of file diff --git a/test/org/redkale/test/convert/GenericEntity.java b/src/test/java/org/redkale/test/convert/GenericEntity.java similarity index 95% rename from test/org/redkale/test/convert/GenericEntity.java rename to src/test/java/org/redkale/test/convert/GenericEntity.java index 04e86fa9f..493022107 100644 --- a/test/org/redkale/test/convert/GenericEntity.java +++ b/src/test/java/org/redkale/test/convert/GenericEntity.java @@ -1,110 +1,110 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import org.redkale.util.TypeToken; -import org.redkale.convert.json.JsonFactory; -import java.lang.reflect.*; -import java.util.*; -import org.redkale.convert.json.*; - -/** - * 支持泛型的 - * - * @author zhangjx - * @param - * @param - * @param - */ -public class GenericEntity { - - private K name; - - private List list; - - private Entry entry; - - public static void main(String[] args) throws Throwable { - GenericEntity bean = new GenericEntity<>(); - bean.setName("你好"); - List list = new ArrayList<>(); - list.add(1234567890L); - bean.setList(list); - bean.setEntry(new Entry<>("aaaa", SimpleEntity.create())); - final Type type = new TypeToken>() { - }.getType(); - JsonFactory.root().tiny(true); - String json = JsonConvert.root().convertTo(bean); - System.out.println(json); - System.out.println(JsonConvert.root().convertFrom(type, json).toString()); - } - - @Override - public String toString() { - return "{\"entry\":" + entry + ",\"list\":" + list + ",\"name\":\"" + name + "\"}"; - } - - public K getName() { - return name; - } - - public void setName(K name) { - this.name = name; - } - - public List getList() { - return list; - } - - public void setList(List list) { - this.list = list; - } - - public Entry getEntry() { - return entry; - } - - public void setEntry(Entry entry) { - this.entry = entry; - } - - public static class Entry { - - private K key; - - private V value; - - public Entry() { - } - - public Entry(K key, V value) { - this.key = key; - this.value = value; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public K getKey() { - return key; - } - - public void setKey(K key) { - this.key = key; - } - - public V getValue() { - return value; - } - - public void setValue(V value) { - this.value = value; - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import org.redkale.util.TypeToken; +import org.redkale.convert.json.JsonFactory; +import java.lang.reflect.*; +import java.util.*; +import org.redkale.convert.json.*; + +/** + * 支持泛型的 + * + * @author zhangjx + * @param + * @param + * @param + */ +public class GenericEntity { + + private K name; + + private List list; + + private Entry entry; + + public static void main(String[] args) throws Throwable { + GenericEntity bean = new GenericEntity<>(); + bean.setName("你好"); + List list = new ArrayList<>(); + list.add(1234567890L); + bean.setList(list); + bean.setEntry(new Entry<>("aaaa", SimpleEntity.create())); + final Type type = new TypeToken>() { + }.getType(); + JsonFactory.root().tiny(true); + String json = JsonConvert.root().convertTo(bean); + System.out.println(json); + System.out.println(JsonConvert.root().convertFrom(type, json).toString()); + } + + @Override + public String toString() { + return "{\"entry\":" + entry + ",\"list\":" + list + ",\"name\":\"" + name + "\"}"; + } + + public K getName() { + return name; + } + + public void setName(K name) { + this.name = name; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public Entry getEntry() { + return entry; + } + + public void setEntry(Entry entry) { + this.entry = entry; + } + + public static class Entry { + + private K key; + + private V value; + + public Entry() { + } + + public Entry(K key, V value) { + this.key = key; + this.value = value; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public K getKey() { + return key; + } + + public void setKey(K key) { + this.key = key; + } + + public V getValue() { + return value; + } + + public void setValue(V value) { + this.value = value; + } + + } +} diff --git a/test/org/redkale/test/convert/InnerCoderEntity.java b/src/test/java/org/redkale/test/convert/InnerCoderEntity.java similarity index 97% rename from test/org/redkale/test/convert/InnerCoderEntity.java rename to src/test/java/org/redkale/test/convert/InnerCoderEntity.java index ba761182a..3e01cf905 100644 --- a/test/org/redkale/test/convert/InnerCoderEntity.java +++ b/src/test/java/org/redkale/test/convert/InnerCoderEntity.java @@ -1,119 +1,119 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import org.redkale.convert.*; -import org.redkale.convert.bson.*; -import org.redkale.convert.json.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class InnerCoderEntity { - - private final String val; - - private final int id; - - private InnerCoderEntity(int id, String value) { - this.id = id; - this.val = value; - } - - public static InnerCoderEntity create(int id, String value) { - return new InnerCoderEntity(id, value); - } - - /** - * 该方法提供给Convert组件自动加载。 - * 1) 方法名可以随意。 - 2) 方法必须是static - 3)方法的参数有且只能有一个, 且必须是org.redkale.convert.ConvertFactory或子类。 - —3.1) 参数类型为org.redkale.convert.ConvertFactory 表示适合JSON和BSON。 - —3.2) 参数类型为org.redkale.convert.json.JsonFactory 表示仅适合JSON。 - —3.3) 参数类型为org.redkale.convert.bson.BsonFactory 表示仅适合BSON。 - 4)方法的返回类型必须是org.redkale.convert.Decodeable/org.redkale.convert.Encodeable/org.redkale.convert.SimpledCoder - 若返回类型不是org.redkale.convert.SimpledCoder, 就必须提供两个方法: 一个返回Decodeable 一个返回 Encodeable。 - * - * @param factory - * @return - */ - private static SimpledCoder createConvertCoder(final org.redkale.convert.ConvertFactory factory) { - return new SimpledCoder() { - - //必须与EnMember[] 顺序一致 - private final DeMember[] deMembers = new DeMember[]{ - DeMember.create(factory, InnerCoderEntity.class, "id"), - DeMember.create(factory, InnerCoderEntity.class, "val")}; - - //必须与DeMember[] 顺序一致 - private final EnMember[] enMembers = new EnMember[]{ - EnMember.create(factory, InnerCoderEntity.class, "id"), - EnMember.create(factory, InnerCoderEntity.class, "val")}; - - @Override - public void convertTo(Writer out, InnerCoderEntity value) { - if (value == null) { - out.writeObjectNull(InnerCoderEntity.class); - return; - } - out.writeObjectB(value); - for (EnMember member : enMembers) { - out.writeObjectField(member, value); - } - out.writeObjectE(value); - } - - @Override - public InnerCoderEntity convertFrom(Reader in) { - if (in.readObjectB(InnerCoderEntity.class) == null) return null; - int index = 0; - final Object[] params = new Object[deMembers.length]; - while (in.hasNext()) { - DeMember member = in.readFieldName(deMembers); //读取字段名 - in.readBlank(); //读取字段名与字段值之间的间隔符,JSON则是跳过冒号: - if (member == null) { - in.skipValue(); //跳过不存在的字段的值, 一般不会发生 - } else { - params[index++] = member.read(in); - } - } - in.readObjectE(InnerCoderEntity.class); - return InnerCoderEntity.create(params[0] == null ? 0 : (Integer) params[0], (String) params[1]); - } - }; - } - - public int getId() { - return id; - } - - public String getVal() { - return val; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public static void main(String[] args) throws Exception { - InnerCoderEntity record = InnerCoderEntity.create(200, "haha"); - final JsonConvert convert = JsonConvert.root(); - String json = convert.convertTo(record); - System.out.println(json); - System.out.println(convert.convertFrom(InnerCoderEntity.class, json).toString()); - - final BsonConvert convert2 = BsonFactory.root().getConvert(); - byte[] bs = convert2.convertTo(InnerCoderEntity.class, null); - Utility.println("--", bs); - InnerCoderEntity r = convert2.convertFrom(InnerCoderEntity.class, bs); - System.out.println(r); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import org.redkale.convert.*; +import org.redkale.convert.bson.*; +import org.redkale.convert.json.*; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class InnerCoderEntity { + + private final String val; + + private final int id; + + private InnerCoderEntity(int id, String value) { + this.id = id; + this.val = value; + } + + public static InnerCoderEntity create(int id, String value) { + return new InnerCoderEntity(id, value); + } + + /** + * 该方法提供给Convert组件自动加载。 + * 1) 方法名可以随意。 + 2) 方法必须是static + 3)方法的参数有且只能有一个, 且必须是org.redkale.convert.ConvertFactory或子类。 + —3.1) 参数类型为org.redkale.convert.ConvertFactory 表示适合JSON和BSON。 + —3.2) 参数类型为org.redkale.convert.json.JsonFactory 表示仅适合JSON。 + —3.3) 参数类型为org.redkale.convert.bson.BsonFactory 表示仅适合BSON。 + 4)方法的返回类型必须是org.redkale.convert.Decodeable/org.redkale.convert.Encodeable/org.redkale.convert.SimpledCoder + 若返回类型不是org.redkale.convert.SimpledCoder, 就必须提供两个方法: 一个返回Decodeable 一个返回 Encodeable。 + * + * @param factory + * @return + */ + private static SimpledCoder createConvertCoder(final org.redkale.convert.ConvertFactory factory) { + return new SimpledCoder() { + + //必须与EnMember[] 顺序一致 + private final DeMember[] deMembers = new DeMember[]{ + DeMember.create(factory, InnerCoderEntity.class, "id"), + DeMember.create(factory, InnerCoderEntity.class, "val")}; + + //必须与DeMember[] 顺序一致 + private final EnMember[] enMembers = new EnMember[]{ + EnMember.create(factory, InnerCoderEntity.class, "id"), + EnMember.create(factory, InnerCoderEntity.class, "val")}; + + @Override + public void convertTo(Writer out, InnerCoderEntity value) { + if (value == null) { + out.writeObjectNull(InnerCoderEntity.class); + return; + } + out.writeObjectB(value); + for (EnMember member : enMembers) { + out.writeObjectField(member, value); + } + out.writeObjectE(value); + } + + @Override + public InnerCoderEntity convertFrom(Reader in) { + if (in.readObjectB(InnerCoderEntity.class) == null) return null; + int index = 0; + final Object[] params = new Object[deMembers.length]; + while (in.hasNext()) { + DeMember member = in.readFieldName(deMembers); //读取字段名 + in.readBlank(); //读取字段名与字段值之间的间隔符,JSON则是跳过冒号: + if (member == null) { + in.skipValue(); //跳过不存在的字段的值, 一般不会发生 + } else { + params[index++] = member.read(in); + } + } + in.readObjectE(InnerCoderEntity.class); + return InnerCoderEntity.create(params[0] == null ? 0 : (Integer) params[0], (String) params[1]); + } + }; + } + + public int getId() { + return id; + } + + public String getVal() { + return val; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public static void main(String[] args) throws Exception { + InnerCoderEntity record = InnerCoderEntity.create(200, "haha"); + final JsonConvert convert = JsonConvert.root(); + String json = convert.convertTo(record); + System.out.println(json); + System.out.println(convert.convertFrom(InnerCoderEntity.class, json).toString()); + + final BsonConvert convert2 = BsonFactory.root().getConvert(); + byte[] bs = convert2.convertTo(InnerCoderEntity.class, null); + Utility.println("--", bs); + InnerCoderEntity r = convert2.convertFrom(InnerCoderEntity.class, bs); + System.out.println(r); + } + +} diff --git a/test/org/redkale/test/convert/JsonTestMain.java b/src/test/java/org/redkale/test/convert/JsonTestMain.java similarity index 77% rename from test/org/redkale/test/convert/JsonTestMain.java rename to src/test/java/org/redkale/test/convert/JsonTestMain.java index ef653cf65..69a1181f4 100644 --- a/test/org/redkale/test/convert/JsonTestMain.java +++ b/src/test/java/org/redkale/test/convert/JsonTestMain.java @@ -1,76 +1,91 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.io.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.convert.json.JsonFactory; -import java.nio.*; -import java.util.*; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public class JsonTestMain { - - public static void main(String[] args) throws Exception { - JsonFactory factory = JsonFactory.root().tiny(true); - final JsonConvert convert = JsonConvert.root(); - String json = "{\"access_token\":\"null\",\"priv\":null, vvv:nulla,\"priv2\":\"nulla\",\"expires_in\":7200, \"aa\":\"\"}"; - Map map = convert.convertFrom(JsonConvert.TYPE_MAP_STRING_STRING, json); - System.out.println(map); - System.out.println(convert.convertTo(map)); - ByteBuffer[] buffers = convert.convertTo(() -> ByteBuffer.allocate(1024), map); - byte[] bs = new byte[buffers[0].remaining()]; - buffers[0].get(bs); - System.out.println(new String(bs)); - main2(args); - main3(args); - } - - public static void main2(String[] args) throws Exception { - final JsonConvert convert = JsonConvert.root(); - SimpleChildEntity entry = SimpleChildEntity.create(); - String json = convert.convertTo(SimpleEntity.class, entry); - System.out.println("长度: " + json.length()); - JsonByteBufferWriter writer = new JsonByteBufferWriter(false, ()->ByteBuffer.allocate(1)) { - }; - convert.convertTo(writer, SimpleEntity.class, entry); - ByteBuffer[] buffers = writer.toBuffers(); - int len = 0; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - for (ByteBuffer b : buffers) { - len += b.remaining(); - byte[] ts = new byte[b.remaining()]; - b.get(ts); - out.write(ts); - b.flip(); - } - System.out.println("长度: " + len); - System.out.println(json); - SimpleChildEntity entry2 = convert.convertFrom(SimpleChildEntity.class, buffers); - System.out.println(entry); - System.out.println(entry2); - } - - public static void main3(String[] args) throws Exception { - final JsonConvert convert = JsonConvert.root(); - SimpleChildEntity entry = SimpleChildEntity.create(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - convert.convertTo(out, SimpleEntity.class, entry); - String json = out.toString("UTF-8"); - System.out.println("长度: " + json.length()); - System.out.println(json); - ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); - SimpleChildEntity entry2 = convert.convertFrom(SimpleChildEntity.class, in); - System.out.println(entry); - System.out.println(entry2); - Map rs = (Map) convert.convertFrom(entry2.toString()); - System.out.println(convert.convertTo(rs)); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.io.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.convert.json.JsonFactory; +import java.nio.*; +import java.util.*; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.redkale.convert.json.*; + +/** + * + * @author zhangjx + */ +public class JsonTestMain { + + @Test + public void run1() throws Throwable { + JsonFactory factory = JsonFactory.root().tiny(true); + final JsonConvert convert = JsonConvert.root(); + String json = "{\"access_token\":\"null\",\"priv\":null, vvv:nulla,\"priv2\":\"nulla\",\"expires_in\":7200, \"aa\":\"\"}"; + Map map = convert.convertFrom(JsonConvert.TYPE_MAP_STRING_STRING, json); + System.out.println(map); + String rs = convert.convertTo(map); + System.out.println(rs); + ByteBuffer[] buffers = convert.convertTo(() -> ByteBuffer.allocate(1024), map); + byte[] bs = new byte[buffers[0].remaining()]; + buffers[0].get(bs); + System.out.println(new String(bs)); + Assertions.assertEquals(rs, new String(bs)); + } + + @Test + public void run2() throws Throwable { + final JsonConvert convert = JsonConvert.root(); + SimpleChildEntity entry = SimpleChildEntity.create(); + String json = convert.convertTo(SimpleEntity.class, entry); + System.out.println("长度: " + json.length()); + JsonByteBufferWriter writer = new JsonByteBufferWriter(false, () -> ByteBuffer.allocate(1)) { + }; + convert.convertTo(writer, SimpleEntity.class, entry); + ByteBuffer[] buffers = writer.toBuffers(); + int len = 0; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + for (ByteBuffer b : buffers) { + len += b.remaining(); + byte[] ts = new byte[b.remaining()]; + b.get(ts); + out.write(ts); + b.flip(); + } + System.out.println("长度: " + len); + System.out.println(json); + SimpleChildEntity entry2 = convert.convertFrom(SimpleChildEntity.class, buffers); + System.out.println(entry); + System.out.println(entry2); + } + + @Test + public void run3() throws Throwable { + final JsonConvert convert = JsonConvert.root(); + SimpleChildEntity entry = SimpleChildEntity.create(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + convert.convertTo(out, SimpleEntity.class, entry); + String json = out.toString("UTF-8"); + System.out.println("长度: " + json.length()); + System.out.println(json); + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + SimpleChildEntity entry2 = convert.convertFrom(SimpleChildEntity.class, in); + System.out.println(entry); + System.out.println(entry2); + Map rs = (Map) convert.convertFrom(entry2.toString()); + System.out.println(convert.convertTo(rs)); + } + + @Test + public void run4() throws Throwable { + final JsonConvert convert = JsonConvert.root(); + java.sql.Date date = new java.sql.Date(System.currentTimeMillis()); + String json = convert.convertTo(date); + System.out.println("java.sql.Date 值: " + json); + java.sql.Date rs = convert.convertFrom(java.sql.Date.class, json); + System.out.println(convert.convertTo(rs)); + } +} diff --git a/test/org/redkale/test/convert/Message.java b/src/test/java/org/redkale/test/convert/Message.java similarity index 95% rename from test/org/redkale/test/convert/Message.java rename to src/test/java/org/redkale/test/convert/Message.java index 7729783e1..758792eac 100644 --- a/test/org/redkale/test/convert/Message.java +++ b/src/test/java/org/redkale/test/convert/Message.java @@ -1,91 +1,91 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.nio.charset.StandardCharsets; -import java.util.*; -import org.redkale.convert.*; -import org.redkale.convert.json.*; -import org.redkale.util.ByteArray; - -/** - * - * @author zhangjx - */ -public final class Message { - - protected boolean flag; - - private int[] ints; - - private List longs; - - @ConvertSmallString - private String message; - - public Message() { - } - - public List getLongs() { - return longs; - } - - public void setLongs(List longs) { - this.longs = longs; - } - - public int[] getInts() { - return ints; - } - - public void setInts(int[] ints) { - this.ints = ints; - } - - public Message(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public boolean isFlag() { - return flag; - } - - public void setFlag(boolean flag) { - this.flag = flag; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public static void main(String[] args) throws Throwable { - Message msg = new Message(); - msg.message = "dddd"; - List longs = new ArrayList<>(); - longs.add(2222L); - longs.add(3333L); - msg.longs = longs; - msg.ints = new int[]{2, 3, 4}; - JsonConvert convert = JsonFactory.root().getConvert(); - Encodeable encoder = JsonFactory.root().loadEncoder(Message.class); - System.out.println(encoder); - ByteArray array = new ByteArray(); - array.put("数据: ".getBytes(StandardCharsets.UTF_8)); - JsonConvert.root().convertToBytes(array, msg); - System.out.println(array); - Message[] mss = new Message[]{msg}; - System.out.println(JsonConvert.root().convertTo(mss)); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.nio.charset.StandardCharsets; +import java.util.*; +import org.redkale.convert.*; +import org.redkale.convert.json.*; +import org.redkale.util.ByteArray; + +/** + * + * @author zhangjx + */ +public final class Message { + + protected boolean flag; + + private int[] ints; + + private List longs; + + @ConvertSmallString + private String message; + + public Message() { + } + + public List getLongs() { + return longs; + } + + public void setLongs(List longs) { + this.longs = longs; + } + + public int[] getInts() { + return ints; + } + + public void setInts(int[] ints) { + this.ints = ints; + } + + public Message(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isFlag() { + return flag; + } + + public void setFlag(boolean flag) { + this.flag = flag; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public static void main(String[] args) throws Throwable { + Message msg = new Message(); + msg.message = "dddd"; + List longs = new ArrayList<>(); + longs.add(2222L); + longs.add(3333L); + msg.longs = longs; + msg.ints = new int[]{2, 3, 4}; + JsonConvert convert = JsonFactory.root().getConvert(); + Encodeable encoder = JsonFactory.root().loadEncoder(Message.class); + System.out.println(encoder); + ByteArray array = new ByteArray(); + array.put("数据: ".getBytes(StandardCharsets.UTF_8)); + JsonConvert.root().convertToBytes(array, msg); + System.out.println(array); + Message[] mss = new Message[]{msg}; + System.out.println(JsonConvert.root().convertTo(mss)); + } +} diff --git a/test/org/redkale/test/convert/One.java b/src/test/java/org/redkale/test/convert/One.java similarity index 95% rename from test/org/redkale/test/convert/One.java rename to src/test/java/org/redkale/test/convert/One.java index 0fbf8d415..a649d4489 100644 --- a/test/org/redkale/test/convert/One.java +++ b/src/test/java/org/redkale/test/convert/One.java @@ -1,89 +1,89 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.util.Arrays; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.Utility; - -/** - * - * @author zhangjx - */ -public class One { - - protected String key; - - protected int code; - - protected byte[] bytes = new byte[]{3, 4, 5}; - - protected int[] ints = new int[]{3000, 4000, 5000}; - - public One(int code) { - this.code = code; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public int getCode() { - return code; - } - - public void setCode(int code) { - this.code = code; - } - - public byte[] getBytes() { - return bytes; - } - - public void setBytes(byte[] bytes) { - this.bytes = bytes; - } - - public int[] getInts() { - return ints; - } - - public void setInts(int[] ints) { - this.ints = ints; - } - - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public static void main(String[] args) throws Throwable { - int count = 100_0000; - One one = new One(234); - one.bytes = new byte[]{1, 2, 3}; - one.key = "哈哈"; - - System.out.println(Arrays.toString(Utility.encodeUTF8(JsonConvert.root().convertTo(one)))); - System.out.println(Arrays.toString(JsonConvert.root().convertToBytes(one))); - long s = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - JsonConvert.root().convertTo(one).getBytes(); - } - long e = System.currentTimeMillis() - s; - - long s2 = System.currentTimeMillis(); - for (int i = 0; i < count; i++) { - JsonConvert.root().convertToBytes(one); - } - long e2 = System.currentTimeMillis() - s2; - System.out.println(e); - System.out.println(e2); - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.util.Arrays; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.Utility; + +/** + * + * @author zhangjx + */ +public class One { + + protected String key; + + protected int code; + + protected byte[] bytes = new byte[]{3, 4, 5}; + + protected int[] ints = new int[]{3000, 4000, 5000}; + + public One(int code) { + this.code = code; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public int getCode() { + return code; + } + + public void setCode(int code) { + this.code = code; + } + + public byte[] getBytes() { + return bytes; + } + + public void setBytes(byte[] bytes) { + this.bytes = bytes; + } + + public int[] getInts() { + return ints; + } + + public void setInts(int[] ints) { + this.ints = ints; + } + + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public static void main(String[] args) throws Throwable { + int count = 100_0000; + One one = new One(234); + one.bytes = new byte[]{1, 2, 3}; + one.key = "哈哈"; + + System.out.println(Arrays.toString(Utility.encodeUTF8(JsonConvert.root().convertTo(one)))); + System.out.println(Arrays.toString(JsonConvert.root().convertToBytes(one))); + long s = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + JsonConvert.root().convertTo(one).getBytes(); + } + long e = System.currentTimeMillis() - s; + + long s2 = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + JsonConvert.root().convertToBytes(one); + } + long e2 = System.currentTimeMillis() - s2; + System.out.println(e); + System.out.println(e2); + + } +} diff --git a/test/org/redkale/test/convert/SimpleChildEntity.java b/src/test/java/org/redkale/test/convert/SimpleChildEntity.java similarity index 96% rename from test/org/redkale/test/convert/SimpleChildEntity.java rename to src/test/java/org/redkale/test/convert/SimpleChildEntity.java index e96010038..a3cff1d69 100644 --- a/test/org/redkale/test/convert/SimpleChildEntity.java +++ b/src/test/java/org/redkale/test/convert/SimpleChildEntity.java @@ -1,60 +1,60 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.net.*; -import org.redkale.convert.ConvertEntity; -import java.util.*; - -/** - * - * @author zhangjx - */ -@ConvertEntity("myname") -public class SimpleChildEntity extends SimpleEntity { - - private short st = -1234; - - private String extend; - - public static SimpleChildEntity create() { - SimpleChildEntity v = new SimpleChildEntity(); - v.setName("this is name\n \"test"); - v.setId(1000000001); - v.setAddrs(new int[]{22222, 33333, 44444, 55555, 66666, 77777, 88888, 99999}); - v.setStrings(new String[]{"zzz", "yyy", "xxx"}); - List list = new ArrayList<>(); - list.add("aaaa"); - list.add("bbbb"); - list.add("cccc"); - v.setLists(list); - Map map = new HashMap<>(); - map.put("AAA", 111); - map.put("BBB", 222); - map.put("CCC", 333); - v.setMap(map); - v.setExtend("hahaha"); - v.setAddr(new InetSocketAddress("127.0.0.1", 6666)); - return v; - } - - public short getSt() { - return st; - } - - public void setSt(short st) { - this.st = st; - } - - public String getExtend() { - return extend; - } - - public void setExtend(String extend) { - this.extend = extend; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.net.*; +import org.redkale.convert.ConvertEntity; +import java.util.*; + +/** + * + * @author zhangjx + */ +@ConvertEntity("myname") +public class SimpleChildEntity extends SimpleEntity { + + private short st = -1234; + + private String extend; + + public static SimpleChildEntity create() { + SimpleChildEntity v = new SimpleChildEntity(); + v.setName("this is name\n \"test"); + v.setId(1000000001); + v.setAddrs(new int[]{22222, 33333, 44444, 55555, 66666, 77777, 88888, 99999}); + v.setStrings(new String[]{"zzz", "yyy", "xxx"}); + List list = new ArrayList<>(); + list.add("aaaa"); + list.add("bbbb"); + list.add("cccc"); + v.setLists(list); + Map map = new HashMap<>(); + map.put("AAA", 111); + map.put("BBB", 222); + map.put("CCC", 333); + v.setMap(map); + v.setExtend("hahaha"); + v.setAddr(new InetSocketAddress("127.0.0.1", 6666)); + return v; + } + + public short getSt() { + return st; + } + + public void setSt(short st) { + this.st = st; + } + + public String getExtend() { + return extend; + } + + public void setExtend(String extend) { + this.extend = extend; + } + +} diff --git a/test/org/redkale/test/convert/SimpleEntity.java b/src/test/java/org/redkale/test/convert/SimpleEntity.java similarity index 95% rename from test/org/redkale/test/convert/SimpleEntity.java rename to src/test/java/org/redkale/test/convert/SimpleEntity.java index 3af940b6d..ad3908d34 100644 --- a/test/org/redkale/test/convert/SimpleEntity.java +++ b/src/test/java/org/redkale/test/convert/SimpleEntity.java @@ -1,137 +1,137 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.net.*; -import org.redkale.util.Creator; -import java.util.*; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public class SimpleEntity { - - private String name; - - private String desc = ""; - - private int id = (int) System.currentTimeMillis(); - - private int[] addrs; - - private List lists; - - private String[] strings; - - private Map map; - - private InetSocketAddress addr; - - public static SimpleEntity create() { - SimpleEntity v = new SimpleEntity(); - v.setName("this is name\n \"test"); - v.setId(1000000001); - v.setAddrs(new int[]{22222, 33333, 44444, 55555, 66666, 77777, 88888, 99999}); - v.setStrings(new String[]{"zzz", "yyy", "xxx"}); - List list = new ArrayList<>(); - list.add("aaaa"); - list.add("bbbb"); - list.add("cccc"); - v.setLists(list); - Map map = new HashMap<>(); - map.put("AAA", 111); - map.put("BBB", 222); - map.put("CCC", 333); - v.setMap(map); - v.setAddr(new InetSocketAddress("127.0.0.1", 6666)); - return v; - } - - public static void main(String[] args) throws Exception { - System.out.println(JsonConvert.root().convertTo(create())); - Creator creator = Creator.create(SimpleEntity.class); //Creator.create(10, SimpleEntity.class); - SimpleEntity entry = creator.create(); - System.out.println(entry); - for (int i = 0; i < 10000000; i++) { - creator.create(); - } - System.gc(); - Thread.sleep(2000); - System.out.println(creator.create()); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public InetSocketAddress getAddr() { - return addr; - } - - public void setAddr(InetSocketAddress addr) { - this.addr = addr; - } - - public int[] getAddrs() { - return addrs; - } - - public void setAddrs(int[] addrs) { - this.addrs = addrs; - } - - public List getLists() { - return lists; - } - - public void setLists(List lists) { - this.lists = lists; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public Map getMap() { - return map; - } - - public void setMap(Map map) { - this.map = map; - } - - public String getDesc() { - return desc; - } - - public void setDesc(String desc) { - this.desc = desc; - } - - public String[] getStrings() { - return strings; - } - - public void setStrings(String[] strings) { - this.strings = strings; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.net.*; +import org.redkale.util.Creator; +import java.util.*; +import org.redkale.convert.json.*; + +/** + * + * @author zhangjx + */ +public class SimpleEntity { + + private String name; + + private String desc = ""; + + private int id = (int) System.currentTimeMillis(); + + private int[] addrs; + + private List lists; + + private String[] strings; + + private Map map; + + private InetSocketAddress addr; + + public static SimpleEntity create() { + SimpleEntity v = new SimpleEntity(); + v.setName("this is name\n \"test"); + v.setId(1000000001); + v.setAddrs(new int[]{22222, 33333, 44444, 55555, 66666, 77777, 88888, 99999}); + v.setStrings(new String[]{"zzz", "yyy", "xxx"}); + List list = new ArrayList<>(); + list.add("aaaa"); + list.add("bbbb"); + list.add("cccc"); + v.setLists(list); + Map map = new HashMap<>(); + map.put("AAA", 111); + map.put("BBB", 222); + map.put("CCC", 333); + v.setMap(map); + v.setAddr(new InetSocketAddress("127.0.0.1", 6666)); + return v; + } + + public static void main(String[] args) throws Exception { + System.out.println(JsonConvert.root().convertTo(create())); + Creator creator = Creator.create(SimpleEntity.class); //Creator.create(10, SimpleEntity.class); + SimpleEntity entry = creator.create(); + System.out.println(entry); + for (int i = 0; i < 10000000; i++) { + creator.create(); + } + System.gc(); + Thread.sleep(2000); + System.out.println(creator.create()); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public InetSocketAddress getAddr() { + return addr; + } + + public void setAddr(InetSocketAddress addr) { + this.addr = addr; + } + + public int[] getAddrs() { + return addrs; + } + + public void setAddrs(int[] addrs) { + this.addrs = addrs; + } + + public List getLists() { + return lists; + } + + public void setLists(List lists) { + this.lists = lists; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String[] getStrings() { + return strings; + } + + public void setStrings(String[] strings) { + this.strings = strings; + } + +} diff --git a/test/org/redkale/test/convert/Two.java b/src/test/java/org/redkale/test/convert/Two.java similarity index 59% rename from test/org/redkale/test/convert/Two.java rename to src/test/java/org/redkale/test/convert/Two.java index a67e244d0..ac8c61855 100644 --- a/test/org/redkale/test/convert/Two.java +++ b/src/test/java/org/redkale/test/convert/Two.java @@ -1,96 +1,72 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.util.*; -import org.redkale.convert.bson.BsonFactory; - -/** - * - * @author zhangjx - */ -public class Two extends One { - - public Two() { - super(90100119); - } - - protected List list; - - protected Map stringMap; - - protected List records; - - protected Map recordMap; - - public Map getStringMap() { - return stringMap; - } - - public void setStringMap(Map stringMap) { - this.stringMap = stringMap; - } - - String ip; - - public String getIp() { - return ip; - } - - public void setIp(String ip) { - this.ip = ip; - } - - public List getList() { - return list; - } - - public void setList(List list) { - this.list = list; - } - - public List getRecords() { - return records; - } - - public void setRecords(List records) { - this.records = records; - } - - public Map getRecordMap() { - return recordMap; - } - - public void setRecordMap(Map recordMap) { - this.recordMap = recordMap; - } - - public static void main(String[] args) throws Throwable { - Two two = new Two(); - two.setKey("key111"); - two.setCode(12345); - List list = new ArrayList<>(); - list.add("haha"); - two.setList(list); - Map map = new HashMap<>(); - map.put("222", "333"); - two.setStringMap(map); - - List records = new ArrayList<>(); - records.add(ConvertRecord.createDefault()); - two.setRecords(records); - - Map rmap = new HashMap<>(); - rmap.put("222", ConvertRecord.createDefault()); - two.setRecordMap(rmap); - - byte[] bs = BsonFactory.root().getConvert().convertTo(two); - - One one =BsonFactory.root().getConvert().convertFrom(One.class, bs); - System.out.println(one); - } - +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.util.*; +import org.redkale.convert.bson.BsonFactory; + +/** + * + * @author zhangjx + */ +public class Two extends One { + + public Two() { + super(90100119); + } + + protected List list; + + protected Map stringMap; + + protected List records; + + protected Map recordMap; + + public Map getStringMap() { + return stringMap; + } + + public void setStringMap(Map stringMap) { + this.stringMap = stringMap; + } + + String ip; + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public List getList() { + return list; + } + + public void setList(List list) { + this.list = list; + } + + public List getRecords() { + return records; + } + + public void setRecords(List records) { + this.records = records; + } + + public Map getRecordMap() { + return recordMap; + } + + public void setRecordMap(Map recordMap) { + this.recordMap = recordMap; + } + + } \ No newline at end of file diff --git a/test/org/redkale/test/convert/World.java b/src/test/java/org/redkale/test/convert/World.java similarity index 95% rename from test/org/redkale/test/convert/World.java rename to src/test/java/org/redkale/test/convert/World.java index ca3272a79..d294ca4df 100644 --- a/test/org/redkale/test/convert/World.java +++ b/src/test/java/org/redkale/test/convert/World.java @@ -1,57 +1,57 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import javax.persistence.Id; -import org.redkale.convert.json.JsonConvert; - -/** - * - * @author zhangjx - */ -public class World implements Comparable { - - @Id - private int id; - - private int randomNumber; - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public int getRandomNumber() { - return randomNumber; - } - - public void setRandomNumber(int randomNumber) { - this.randomNumber = randomNumber; - } - - @Override - public int compareTo(World o) { - return Integer.compare(id, o.id); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - public static void main(String[] args) throws Throwable { - World[] worlds = new World[20]; - int index = 8866; - for(int i =0;i { + + @Id + private int id; + + private int randomNumber; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getRandomNumber() { + return randomNumber; + } + + public void setRandomNumber(int randomNumber) { + this.randomNumber = randomNumber; + } + + @Override + public int compareTo(World o) { + return Integer.compare(id, o.id); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + public static void main(String[] args) throws Throwable { + World[] worlds = new World[20]; + int index = 8866; + for(int i =0;i { - - protected final byte[] idFieldBytes = "\"id\":".getBytes(); - - protected final byte[] messageCommaFieldBytes = ",\"message\":".getBytes(); - - public _DyncFortuneJsonEncoder(JsonFactory factory, Type type) { - super(factory, type); - } - - @Override - public void convertTo(JsonWriter out, Fortune value) { - if (value == null) { - out.writeObjectNull(null); - return; - } - if (!out.isExtFuncEmpty()) { - objectEncoder.convertTo(out, value); - return; - } - - out.writeTo('{'); - - out.writeTo(idFieldBytes); - out.writeInt(value.getId()); - - String message = value.getMessage(); - if (message != null) { - out.writeTo(messageCommaFieldBytes); - out.writeString(message); - } - - out.writeTo('}'); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.lang.reflect.Type; +import org.redkale.convert.json.*; + +/** + * + * @author zhangjx + */ +public class _DyncFortuneJsonEncoder extends JsonDynEncoder { + + protected final byte[] idFieldBytes = "\"id\":".getBytes(); + + protected final byte[] messageCommaFieldBytes = ",\"message\":".getBytes(); + + public _DyncFortuneJsonEncoder(JsonFactory factory, Type type) { + super(factory, type); + } + + @Override + public void convertTo(JsonWriter out, Fortune value) { + if (value == null) { + out.writeObjectNull(null); + return; + } + if (!out.isExtFuncEmpty()) { + objectEncoder.convertTo(out, value); + return; + } + + out.writeTo('{'); + + out.writeTo(idFieldBytes); + out.writeInt(value.getId()); + + String message = value.getMessage(); + if (message != null) { + out.writeTo(messageCommaFieldBytes); + out.writeString(message); + } + + out.writeTo('}'); + } +} diff --git a/test/org/redkale/test/convert/_DyncMessageJsonEncoder.java b/src/test/java/org/redkale/test/convert/_DyncMessageJsonEncoder.java similarity index 96% rename from test/org/redkale/test/convert/_DyncMessageJsonEncoder.java rename to src/test/java/org/redkale/test/convert/_DyncMessageJsonEncoder.java index c9a54ed14..453503769 100644 --- a/test/org/redkale/test/convert/_DyncMessageJsonEncoder.java +++ b/src/test/java/org/redkale/test/convert/_DyncMessageJsonEncoder.java @@ -1,51 +1,51 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.lang.reflect.Type; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public class _DyncMessageJsonEncoder extends JsonDynEncoder { - - protected final byte[] messageFieldBytes = "\"message\":".getBytes(); - - protected final byte[] messageCommaFieldBytes = ",\"message\":".getBytes(); - - public _DyncMessageJsonEncoder(JsonFactory factory, Type type) { - super(factory, type); - } - - @Override - public void convertTo(JsonWriter out, Message value) { - if (value == null) { - out.writeObjectNull(null); - return; - } - if (!out.isExtFuncEmpty()) { - objectEncoder.convertTo(out, value); - return; - } - - out.writeTo('{'); - boolean comma = false; - String message = value.getMessage(); - if (message != null) { - if (comma) { - out.writeTo(messageCommaFieldBytes); - } else { - out.writeTo(messageFieldBytes); - comma = true; - } - out.writeLatin1To(true, message); //out.writeString(message); - } - out.writeTo('}'); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.lang.reflect.Type; +import org.redkale.convert.json.*; + +/** + * + * @author zhangjx + */ +public class _DyncMessageJsonEncoder extends JsonDynEncoder { + + protected final byte[] messageFieldBytes = "\"message\":".getBytes(); + + protected final byte[] messageCommaFieldBytes = ",\"message\":".getBytes(); + + public _DyncMessageJsonEncoder(JsonFactory factory, Type type) { + super(factory, type); + } + + @Override + public void convertTo(JsonWriter out, Message value) { + if (value == null) { + out.writeObjectNull(null); + return; + } + if (!out.isExtFuncEmpty()) { + objectEncoder.convertTo(out, value); + return; + } + + out.writeTo('{'); + boolean comma = false; + String message = value.getMessage(); + if (message != null) { + if (comma) { + out.writeTo(messageCommaFieldBytes); + } else { + out.writeTo(messageFieldBytes); + comma = true; + } + out.writeLatin1To(true, message); //out.writeString(message); + } + out.writeTo('}'); + } + +} diff --git a/test/org/redkale/test/convert/_DyncWorldJsonEncoder.java b/src/test/java/org/redkale/test/convert/_DyncWorldJsonEncoder.java similarity index 96% rename from test/org/redkale/test/convert/_DyncWorldJsonEncoder.java rename to src/test/java/org/redkale/test/convert/_DyncWorldJsonEncoder.java index 3b41a0a9b..a650cab5d 100644 --- a/test/org/redkale/test/convert/_DyncWorldJsonEncoder.java +++ b/src/test/java/org/redkale/test/convert/_DyncWorldJsonEncoder.java @@ -1,47 +1,47 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.lang.reflect.Type; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public class _DyncWorldJsonEncoder extends JsonDynEncoder { - - protected final byte[] idFieldBytes = "\"id\":".getBytes(); - - protected final byte[] randomNumberFieldBytes = ",\"randomNumber\":".getBytes(); - - public _DyncWorldJsonEncoder(JsonFactory factory, Type type) { - super(factory, type); - } - - @Override - public void convertTo(JsonWriter out, World value) { - if (value == null) { - out.writeObjectNull(null); - return; - } - if (!out.isExtFuncEmpty()) { - objectEncoder.convertTo(out, value); - return; - } - - out.writeTo('{'); - boolean comma = false; - - out.writeTo(idFieldBytes); - out.writeInt(value.getId()); - - out.writeTo(randomNumberFieldBytes); - out.writeInt(value.getRandomNumber()); - - out.writeTo('}'); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert; + +import java.lang.reflect.Type; +import org.redkale.convert.json.*; + +/** + * + * @author zhangjx + */ +public class _DyncWorldJsonEncoder extends JsonDynEncoder { + + protected final byte[] idFieldBytes = "\"id\":".getBytes(); + + protected final byte[] randomNumberFieldBytes = ",\"randomNumber\":".getBytes(); + + public _DyncWorldJsonEncoder(JsonFactory factory, Type type) { + super(factory, type); + } + + @Override + public void convertTo(JsonWriter out, World value) { + if (value == null) { + out.writeObjectNull(null); + return; + } + if (!out.isExtFuncEmpty()) { + objectEncoder.convertTo(out, value); + return; + } + + out.writeTo('{'); + boolean comma = false; + + out.writeTo(idFieldBytes); + out.writeInt(value.getId()); + + out.writeTo(randomNumberFieldBytes); + out.writeInt(value.getRandomNumber()); + + out.writeTo('}'); + } +} diff --git a/test/org/redkale/test/convert/media/Image.java b/src/test/java/org/redkale/test/convert/media/Image.java similarity index 95% rename from test/org/redkale/test/convert/media/Image.java rename to src/test/java/org/redkale/test/convert/media/Image.java index 14af488d6..c9963582d 100644 --- a/test/org/redkale/test/convert/media/Image.java +++ b/src/test/java/org/redkale/test/convert/media/Image.java @@ -1,117 +1,117 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert.media; - -/** - * - * @author redkale - */ -public class Image implements java.io.Serializable { - - private static final long serialVersionUID = 1L; - - public enum Size { - SMALL, LARGE - } - - private String uri; - - private String title; // Can be null - - private int width; - - private int height; - - private Size size; - - public Image() { - } - - public Image(String uri, String title, int width, int height, Size size) { - this.height = height; - this.title = title; - this.uri = uri; - this.width = width; - this.size = size; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Image image = (Image) o; - - if (height != image.height) return false; - if (width != image.width) return false; - if (size != image.size) return false; - if (title != null ? !title.equals(image.title) : image.title != null) return false; - return !(uri != null ? !uri.equals(image.uri) : image.uri != null); - } - - @Override - public int hashCode() { - int result = uri != null ? uri.hashCode() : 0; - result = 31 * result + (title != null ? title.hashCode() : 0); - result = 31 * result + width; - result = 31 * result + height; - result = 31 * result + (size != null ? size.hashCode() : 0); - return result; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[Image "); - sb.append("uri=").append((uri)); - sb.append(", title=").append((title)); - sb.append(", width=").append(width); - sb.append(", height=").append(height); - sb.append(", size=").append(size); - sb.append("]"); - return sb.toString(); - } - - public void setUri(String uri) { - this.uri = uri; - } - - public void setTitle(String title) { - this.title = title; - } - - public void setWidth(int width) { - this.width = width; - } - - public void setHeight(int height) { - this.height = height; - } - - public void setSize(Size size) { - this.size = size; - } - - public String getUri() { - return uri; - } - - public String getTitle() { - return title; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public Size getSize() { - return size; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert.media; + +/** + * + * @author redkale + */ +public class Image implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + public enum Size { + SMALL, LARGE + } + + private String uri; + + private String title; // Can be null + + private int width; + + private int height; + + private Size size; + + public Image() { + } + + public Image(String uri, String title, int width, int height, Size size) { + this.height = height; + this.title = title; + this.uri = uri; + this.width = width; + this.size = size; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Image image = (Image) o; + + if (height != image.height) return false; + if (width != image.width) return false; + if (size != image.size) return false; + if (title != null ? !title.equals(image.title) : image.title != null) return false; + return !(uri != null ? !uri.equals(image.uri) : image.uri != null); + } + + @Override + public int hashCode() { + int result = uri != null ? uri.hashCode() : 0; + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + width; + result = 31 * result + height; + result = 31 * result + (size != null ? size.hashCode() : 0); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[Image "); + sb.append("uri=").append((uri)); + sb.append(", title=").append((title)); + sb.append(", width=").append(width); + sb.append(", height=").append(height); + sb.append(", size=").append(size); + sb.append("]"); + return sb.toString(); + } + + public void setUri(String uri) { + this.uri = uri; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setSize(Size size) { + this.size = size; + } + + public String getUri() { + return uri; + } + + public String getTitle() { + return title; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public Size getSize() { + return size; + } +} diff --git a/test/org/redkale/test/convert/media/Media.java b/src/test/java/org/redkale/test/convert/media/Media.java similarity index 96% rename from test/org/redkale/test/convert/media/Media.java rename to src/test/java/org/redkale/test/convert/media/Media.java index 699d02ccf..67394ed03 100644 --- a/test/org/redkale/test/convert/media/Media.java +++ b/src/test/java/org/redkale/test/convert/media/Media.java @@ -1,203 +1,203 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert.media; - -import java.util.*; - -/** - * - * @author redkale - */ -public class Media implements java.io.Serializable { - - public enum Player { - JAVA, FLASH; - - } - - private String uri; - - private String title; // Can be unset. - - private int width; - - private int height; - - private String format; - - private long duration; - - private long size; - - private int bitrate; // Can be unset. - - private List persons; - - private Player player; - - private String copyright; // Can be unset. - - public Media() { - } - - public Media(String uri, String title, int width, int height, String format, long duration, long size, - int bitrate, boolean hasBitrate, List persons, Player player, String copyright) { - this.uri = uri; - this.title = title; - this.width = width; - this.height = height; - this.format = format; - this.duration = duration; - this.size = size; - this.bitrate = bitrate; - - this.persons = persons; - this.player = player; - this.copyright = copyright; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Media media = (Media) o; - if (bitrate != media.bitrate) return false; - if (duration != media.duration) return false; - if (height != media.height) return false; - if (size != media.size) return false; - if (width != media.width) return false; - if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false; - if (format != null ? !format.equals(media.format) : media.format != null) return false; - if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false; - if (player != media.player) return false; - if (title != null ? !title.equals(media.title) : media.title != null) return false; - if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false; - return true; - } - - @Override - public int hashCode() { - int result = uri != null ? uri.hashCode() : 0; - result = 31 * result + (title != null ? title.hashCode() : 0); - result = 31 * result + width; - result = 31 * result + height; - result = 31 * result + (format != null ? format.hashCode() : 0); - result = 31 * result + (int) (duration ^ (duration >>> 32)); - result = 31 * result + (int) (size ^ (size >>> 32)); - result = 31 * result + bitrate; - result = 31 * result + (persons != null ? persons.hashCode() : 0); - result = 31 * result + (player != null ? player.hashCode() : 0); - result = 31 * result + (copyright != null ? copyright.hashCode() : 0); - return result; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[Media "); - sb.append("uri=").append((uri)); - sb.append(", title=").append((title)); - sb.append(", width=").append(width); - sb.append(", height=").append(height); - sb.append(", format=").append((format)); - sb.append(", duration=").append(duration); - sb.append(", size=").append(size); - sb.append(", bitrate=").append(String.valueOf(bitrate)); - sb.append(", persons=").append((persons)); - sb.append(", player=").append(player); - sb.append(", copyright=").append((copyright)); - sb.append("]"); - return sb.toString(); - } - - public void setUri(String uri) { - this.uri = uri; - } - - public void setTitle(String title) { - this.title = title; - } - - public void setWidth(int width) { - this.width = width; - } - - public void setHeight(int height) { - this.height = height; - } - - public void setFormat(String format) { - this.format = format; - } - - public void setDuration(long duration) { - this.duration = duration; - } - - public void setSize(long size) { - this.size = size; - } - - public void setBitrate(int bitrate) { - this.bitrate = bitrate; - } - - public void setPersons(List persons) { - this.persons = persons; - } - - public void setPlayer(Player player) { - this.player = player; - } - - public void setCopyright(String copyright) { - this.copyright = copyright; - } - - public String getUri() { - return uri; - } - - public String getTitle() { - return title; - } - - public int getWidth() { - return width; - } - - public int getHeight() { - return height; - } - - public String getFormat() { - return format; - } - - public long getDuration() { - return duration; - } - - public long getSize() { - return size; - } - - public int getBitrate() { - return bitrate; - } - - public List getPersons() { - return persons; - } - - public Player getPlayer() { - return player; - } - - public String getCopyright() { - return copyright; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert.media; + +import java.util.*; + +/** + * + * @author redkale + */ +public class Media implements java.io.Serializable { + + public enum Player { + JAVA, FLASH; + + } + + private String uri; + + private String title; // Can be unset. + + private int width; + + private int height; + + private String format; + + private long duration; + + private long size; + + private int bitrate; // Can be unset. + + private List persons; + + private Player player; + + private String copyright; // Can be unset. + + public Media() { + } + + public Media(String uri, String title, int width, int height, String format, long duration, long size, + int bitrate, boolean hasBitrate, List persons, Player player, String copyright) { + this.uri = uri; + this.title = title; + this.width = width; + this.height = height; + this.format = format; + this.duration = duration; + this.size = size; + this.bitrate = bitrate; + + this.persons = persons; + this.player = player; + this.copyright = copyright; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Media media = (Media) o; + if (bitrate != media.bitrate) return false; + if (duration != media.duration) return false; + if (height != media.height) return false; + if (size != media.size) return false; + if (width != media.width) return false; + if (copyright != null ? !copyright.equals(media.copyright) : media.copyright != null) return false; + if (format != null ? !format.equals(media.format) : media.format != null) return false; + if (persons != null ? !persons.equals(media.persons) : media.persons != null) return false; + if (player != media.player) return false; + if (title != null ? !title.equals(media.title) : media.title != null) return false; + if (uri != null ? !uri.equals(media.uri) : media.uri != null) return false; + return true; + } + + @Override + public int hashCode() { + int result = uri != null ? uri.hashCode() : 0; + result = 31 * result + (title != null ? title.hashCode() : 0); + result = 31 * result + width; + result = 31 * result + height; + result = 31 * result + (format != null ? format.hashCode() : 0); + result = 31 * result + (int) (duration ^ (duration >>> 32)); + result = 31 * result + (int) (size ^ (size >>> 32)); + result = 31 * result + bitrate; + result = 31 * result + (persons != null ? persons.hashCode() : 0); + result = 31 * result + (player != null ? player.hashCode() : 0); + result = 31 * result + (copyright != null ? copyright.hashCode() : 0); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[Media "); + sb.append("uri=").append((uri)); + sb.append(", title=").append((title)); + sb.append(", width=").append(width); + sb.append(", height=").append(height); + sb.append(", format=").append((format)); + sb.append(", duration=").append(duration); + sb.append(", size=").append(size); + sb.append(", bitrate=").append(String.valueOf(bitrate)); + sb.append(", persons=").append((persons)); + sb.append(", player=").append(player); + sb.append(", copyright=").append((copyright)); + sb.append("]"); + return sb.toString(); + } + + public void setUri(String uri) { + this.uri = uri; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setWidth(int width) { + this.width = width; + } + + public void setHeight(int height) { + this.height = height; + } + + public void setFormat(String format) { + this.format = format; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + public void setSize(long size) { + this.size = size; + } + + public void setBitrate(int bitrate) { + this.bitrate = bitrate; + } + + public void setPersons(List persons) { + this.persons = persons; + } + + public void setPlayer(Player player) { + this.player = player; + } + + public void setCopyright(String copyright) { + this.copyright = copyright; + } + + public String getUri() { + return uri; + } + + public String getTitle() { + return title; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public String getFormat() { + return format; + } + + public long getDuration() { + return duration; + } + + public long getSize() { + return size; + } + + public int getBitrate() { + return bitrate; + } + + public List getPersons() { + return persons; + } + + public Player getPlayer() { + return player; + } + + public String getCopyright() { + return copyright; + } +} diff --git a/test/org/redkale/test/convert/media/MediaContent.java b/src/test/java/org/redkale/test/convert/media/MediaContent.java similarity index 96% rename from test/org/redkale/test/convert/media/MediaContent.java rename to src/test/java/org/redkale/test/convert/media/MediaContent.java index 50efeaddf..2f8823643 100644 --- a/test/org/redkale/test/convert/media/MediaContent.java +++ b/src/test/java/org/redkale/test/convert/media/MediaContent.java @@ -1,109 +1,109 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert.media; - -import java.util.*; -import org.redkale.convert.json.*; -import org.redkale.test.convert.*; - -/** - * - * @author redkale - */ -public class MediaContent implements java.io.Serializable { - - private Media media; - - private List images; - - public MediaContent() { - } - - public MediaContent(Media media, List images) { - this.media = media; - this.images = images; - } - - public static void main(String[] args) throws Exception { - final MediaContent entry = MediaContent.createDefault(); - ConvertRecord.run(MediaContent.class, entry); - } - - public static MediaContent createDefault() { - String str = "{" - + " media : {" - + " uri : \"http://javaone.com/keynote.mpg\" ," - + " title : \"Javaone Keynote\" ," - + " width : -640 ," - + " height : -480 ," - + " format : \"video/mpg4\"," - + " duration : -18000000 ," - + " size : -58982400 ," - + " bitrate : -262144 ," - + " persons : [\"Bill Gates\", \"Steve Jobs\"] ," - + " player : JAVA , " - + " copyright : None" - + " }, images : [" - + " {" - + " uri : \"http://javaone.com/keynote_large.jpg\"," - + " title : \"Javaone Keynote\"," - + " width : -1024," - + " height : -768," - + " size : LARGE" - + " }, {" - + " uri : \"http://javaone.com/keynote_small.jpg\", " - + " title : \"Javaone Keynote\" , " - + " width : -320 , " - + " height : -240 , " - + " size : SMALL" - + " }" - + " ]" - + "}"; - return JsonFactory.root().getConvert().convertFrom(MediaContent.class, str); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MediaContent that = (MediaContent) o; - if (images != null ? !images.equals(that.images) : that.images != null) return false; - return !(media != null ? !media.equals(that.media) : that.media != null); - } - - @Override - public int hashCode() { - int result = media != null ? media.hashCode() : 0; - result = 31 * result + (images != null ? images.hashCode() : 0); - return result; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[MediaContent: "); - sb.append("media=").append(media); - sb.append(", images=").append(images); - sb.append("]"); - return sb.toString(); - } - - public void setMedia(Media media) { - this.media = media; - } - - public void setImages(List images) { - this.images = images; - } - - public Media getMedia() { - return media; - } - - public List getImages() { - return images; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.convert.media; + +import java.util.*; +import org.redkale.convert.json.*; +import org.redkale.test.convert.*; + +/** + * + * @author redkale + */ +public class MediaContent implements java.io.Serializable { + + private Media media; + + private List images; + + public MediaContent() { + } + + public MediaContent(Media media, List images) { + this.media = media; + this.images = images; + } + + public static void main(String[] args) throws Exception { + final MediaContent entry = MediaContent.createDefault(); + ConvertRecord.run(MediaContent.class, entry); + } + + public static MediaContent createDefault() { + String str = "{" + + " media : {" + + " uri : \"http://javaone.com/keynote.mpg\" ," + + " title : \"Javaone Keynote\" ," + + " width : -640 ," + + " height : -480 ," + + " format : \"video/mpg4\"," + + " duration : -18000000 ," + + " size : -58982400 ," + + " bitrate : -262144 ," + + " persons : [\"Bill Gates\", \"Steve Jobs\"] ," + + " player : JAVA , " + + " copyright : None" + + " }, images : [" + + " {" + + " uri : \"http://javaone.com/keynote_large.jpg\"," + + " title : \"Javaone Keynote\"," + + " width : -1024," + + " height : -768," + + " size : LARGE" + + " }, {" + + " uri : \"http://javaone.com/keynote_small.jpg\", " + + " title : \"Javaone Keynote\" , " + + " width : -320 , " + + " height : -240 , " + + " size : SMALL" + + " }" + + " ]" + + "}"; + return JsonFactory.root().getConvert().convertFrom(MediaContent.class, str); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + MediaContent that = (MediaContent) o; + if (images != null ? !images.equals(that.images) : that.images != null) return false; + return !(media != null ? !media.equals(that.media) : that.media != null); + } + + @Override + public int hashCode() { + int result = media != null ? media.hashCode() : 0; + result = 31 * result + (images != null ? images.hashCode() : 0); + return result; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[MediaContent: "); + sb.append("media=").append(media); + sb.append(", images=").append(images); + sb.append("]"); + return sb.toString(); + } + + public void setMedia(Media media) { + this.media = media; + } + + public void setImages(List images) { + this.images = images; + } + + public Media getMedia() { + return media; + } + + public List getImages() { + return images; + } +} diff --git a/test/org/redkale/test/http/HttpRequestDesc.java b/src/test/java/org/redkale/test/http/HttpRequestDesc.java similarity index 97% rename from test/org/redkale/test/http/HttpRequestDesc.java rename to src/test/java/org/redkale/test/http/HttpRequestDesc.java index 7f3dca914..1f77e82f4 100644 --- a/test/org/redkale/test/http/HttpRequestDesc.java +++ b/src/test/java/org/redkale/test/http/HttpRequestDesc.java @@ -1,348 +1,348 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.http; - -import java.io.*; -import java.lang.reflect.Type; -import java.net.*; -import java.nio.charset.*; -import java.util.*; -import org.redkale.convert.json.*; -import org.redkale.net.http.*; -import org.redkale.util.AnyValue; - -/** - * - * @author zhangjx - */ -public interface HttpRequestDesc { - - //获取客户端地址IP - public SocketAddress getRemoteAddress(); - - //获取客户端地址IP, 与getRemoteAddres() 的区别在于: - //本方法优先取header中指定为RemoteAddress名的值,没有则返回getRemoteAddres()的getHostAddress()。 - //本方法适用于服务前端有如nginx的代理服务器进行中转,通过getRemoteAddres()是获取不到客户端的真实IP。 - public String getRemoteAddr(); - - //获取请求内容指定的编码字符串 - public String getBody(Charset charset); - - //获取请求内容的UTF-8编码字符串 - public String getBodyUTF8(); - - //获取请求内容的byte[] - public byte[] getBody(); - - //获取请求内容的JavaBean对象 - public T getBodyJson(java.lang.reflect.Type type); - - //获取请求内容的JavaBean对象 - public T getBodyJson(JsonConvert convert, java.lang.reflect.Type type); - - //获取文件上传对象 - public MultiContext getMultiContext(); - - //获取文件上传信息列表 等价于 getMultiContext().parts(); - public Iterable multiParts() throws IOException; - - //设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser - //数据类型由@HttpUserType指定 - public HttpRequest setCurrentUser(T user); - - //获取当前用户信息 数据类型由@HttpUserType指定 - public T currentUser(); - - //获取模块ID,来自@HttpServlet.moduleid() - public int getModuleid(); - - //获取操作ID,来自@HttpMapping.actionid() - public int getActionid(); - - //获取sessionid - public String getSessionid(boolean autoCreate); - - //更新sessionid - public String changeSessionid(); - - //指定值更新sessionid - public String changeSessionid(String newsessionid); - - //使sessionid失效 - public void invalidateSession(); - - //获取所有Cookie对象 - public java.net.HttpCookie[] getCookies(); - - //获取Cookie值 - public String getCookie(String name); - - //获取Cookie值, 没有返回默认值 - public String getCookie(String name, String defaultValue); - - //获取协议名 http、https、ws、wss等 - public String getProtocol(); - - //获取请求方法 GET、POST等 - public String getMethod(); - - //获取Content-Type的header值 - public String getContentType(); - - //获取请求内容的长度, 为-1表示内容长度不确定 - public long getContentLength(); - - //获取Connection的Header值 - public String getConnection(); - - //获取Host的Header值 - public String getHost(); - - //获取请求的URL - public String getRequestURI(); - - //截取getRequestURI最后的一个/后面的部分 - public String getRequstURILastPath(); - - // 获取请求URL最后的一个/后面的部分的short值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: short type = request.getRequstURILastPath((short)0); //type = 2 - public short getRequstURILastPath(short defvalue); - - // 获取请求URL最后的一个/后面的部分的short值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: short type = request.getRequstURILastPath((short)0); //type = 2 - public short getRequstURILastPath(int radix, short defvalue); - - // 获取请求URL最后的一个/后面的部分的int值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: int type = request.getRequstURILastPath(0); //type = 2 - public int getRequstURILastPath(int defvalue); - - // 获取请求URL最后的一个/后面的部分的int值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: int type = request.getRequstURILastPath(0); //type = 2 - public int getRequstURILastPath(int radix, int defvalue); - - // 获取请求URL最后的一个/后面的部分的float值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: float type = request.getRequstURILastPath(0.f); //type = 2.f - public float getRequstURILastPath(float defvalue); - - // 获取请求URL最后的一个/后面的部分的long值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: long type = request.getRequstURILastPath(0L); //type = 2 - public long getRequstURILastPath(long defvalue); - - // 获取请求URL最后的一个/后面的部分的long值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: long type = request.getRequstURILastPath(0L); //type = 2 - public long getRequstURILastPath(int radix, long defvalue); - - // 获取请求URL最后的一个/后面的部分的double值
    - // 例如请求URL /pipes/record/query/2
    - // 获取type参数: double type = request.getRequstURILastPath(0.0); //type = 2.0 - public double getRequstURILastPath(double defvalue); - - //从prefix之后截取getRequestURI再对"/"进行分隔 - public String[] getRequstURIPaths(String prefix); - - // 获取请求URL分段中含prefix段的值 - // 例如请求URL /pipes/record/query/name:hello - // 获取name参数: String name = request.getRequstURIPath("name:", "none"); - public String getRequstURIPath(String prefix, String defaultValue); - - // 获取请求URL分段中含prefix段的short值 - // 例如请求URL /pipes/record/query/type:10 - // 获取type参数: short type = request.getRequstURIPath("type:", (short)0); - public short getRequstURIPath(String prefix, short defaultValue); - - // 获取请求URL分段中含prefix段的short值 - // 例如请求URL /pipes/record/query/type:a - // 获取type参数: short type = request.getRequstURIPath(16, "type:", (short)0); type = 10 - public short getRequstURIPath(int radix, String prefix, short defvalue); - - // 获取请求URL分段中含prefix段的int值 - // 例如请求URL /pipes/record/query/offset:2/limit:50 - // 获取offset参数: int offset = request.getRequstURIPath("offset:", 1); - // 获取limit参数: int limit = request.getRequstURIPath("limit:", 20); - public int getRequstURIPath(String prefix, int defaultValue); - - // 获取请求URL分段中含prefix段的int值 - // 例如请求URL /pipes/record/query/offset:2/limit:10 - // 获取offset参数: int offset = request.getRequstURIPath("offset:", 1); - // 获取limit参数: int limit = request.getRequstURIPath(16, "limit:", 20); // limit = 16 - public int getRequstURIPath(int radix, String prefix, int defaultValue); - - // 获取请求URL分段中含prefix段的float值 - // 例如请求URL /pipes/record/query/point:40.0 - // 获取time参数: float point = request.getRequstURIPath("point:", 0.0f); - public float getRequstURIPath(String prefix, float defvalue); - - // 获取请求URL分段中含prefix段的long值 - // 例如请求URL /pipes/record/query/time:1453104341363/id:40 - // 获取time参数: long time = request.getRequstURIPath("time:", 0L); - public long getRequstURIPath(String prefix, long defaultValue); - - // 获取请求URL分段中含prefix段的long值 - // 例如请求URL /pipes/record/query/time:1453104341363/id:40 - // 获取time参数: long time = request.getRequstURIPath("time:", 0L); - public long getRequstURIPath(int radix, String prefix, long defvalue); - - // 获取请求URL分段中含prefix段的double值
    - // 例如请求URL /pipes/record/query/point:40.0
    - // 获取time参数: double point = request.getRequstURIPath("point:", 0.0); - public double getRequstURIPath(String prefix, double defvalue); - - //获取所有的header名 - public AnyValue getHeaders(); - - //将请求Header转换成Map - public Map getHeadersToMap(Map map); - - //获取所有的header名 - public String[] getHeaderNames(); - - // 获取指定的header值 - public String getHeader(String name); - - //获取指定的header值, 没有返回默认值 - public String getHeader(String name, String defaultValue); - - //获取指定的header的json值 - public T getJsonHeader(Type type, String name); - - //获取指定的header的json值 - public T getJsonHeader(JsonConvert convert, Type type, String name); - - //获取指定的header的boolean值, 没有返回默认boolean值 - public boolean getBooleanHeader(String name, boolean defaultValue); - - // 获取指定的header的short值, 没有返回默认short值 - public short getShortHeader(String name, short defaultValue); - - // 获取指定的header的short值, 没有返回默认short值 - public short getShortHeader(int radix, String name, short defaultValue); - - // 获取指定的header的short值, 没有返回默认short值 - public short getShortHeader(String name, int defaultValue); - - // 获取指定的header的short值, 没有返回默认short值 - public short getShortHeader(int radix, String name, int defaultValue); - - //获取指定的header的int值, 没有返回默认int值 - public int getIntHeader(String name, int defaultValue); - - //获取指定的header的int值, 没有返回默认int值 - public int getIntHeader(int radix, String name, int defaultValue); - - // 获取指定的header的long值, 没有返回默认long值 - public long getLongHeader(String name, long defaultValue); - - // 获取指定的header的long值, 没有返回默认long值 - public long getLongHeader(int radix, String name, long defaultValue); - - // 获取指定的header的float值, 没有返回默认float值 - public float getFloatHeader(String name, float defaultValue); - - //获取指定的header的double值, 没有返回默认double值 - public double getDoubleHeader(String name, double defaultValue); - - //获取请求参数总对象 - public AnyValue getParameters(); - - //将请求参数转换成Map - public Map getParametersToMap(Map map); - - //将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx - //不会返回null,没有参数返回空字符串 - public String getParametersToString(); - - //将请求参数转换成String, 字符串格式为: prefix + bean1={}&id=13&name=xxx - //拼接前缀, 如果无参数,返回的字符串不会含有拼接前缀 - //不会返回null,没有参数返回空字符串 - public String getParametersToString(String prefix); - - //获取所有参数名 - public String[] getParameterNames(); - - //获取指定的参数值 - public String getParameter(String name); - - //获取指定的参数值, 没有返回默认值 - public String getParameter(String name, String defaultValue); - - //获取指定的参数json值 - public T getJsonParameter(Type type, String name); - - //获取指定的参数json值 - public T getJsonParameter(JsonConvert convert, Type type, String name); - - //获取指定的参数boolean值, 没有返回默认boolean值 - public boolean getBooleanParameter(String name, boolean defaultValue); - - //获取指定的参数short值, 没有返回默认short值 - public short getShortParameter(String name, short defaultValue); - - //获取指定的参数short值, 没有返回默认short值 - public short getShortParameter(int radix, String name, short defaultValue); - - //获取指定的参数short值, 没有返回默认short值 - public short getShortParameter(int radix, String name, int defaultValue); - - //获取指定的参数int值, 没有返回默认int值 - public int getIntParameter(String name, int defaultValue); - - //获取指定的参数int值, 没有返回默认int值 - public int getIntParameter(int radix, String name, int defaultValue); - - //获取指定的参数long值, 没有返回默认long值 - public long getLongParameter(String name, long defaultValue); - - //获取指定的参数long值, 没有返回默认long值 - public long getLongParameter(int radix, String name, long defaultValue); - - //获取指定的参数float值, 没有返回默认float值 - public float getFloatParameter(String name, float defaultValue); - - //获取指定的参数double值, 没有返回默认double值 - public double getDoubleParameter(String name, double defaultValue); - - //获取翻页对象 同 getFlipper("flipper", false, 0); - public org.redkale.source.Flipper getFlipper(); - - //获取翻页对象 同 getFlipper("flipper", needcreate, 0); - public org.redkale.source.Flipper getFlipper(boolean needcreate); - - //获取翻页对象 同 getFlipper("flipper", false, maxLimit); - public org.redkale.source.Flipper getFlipper(int maxLimit); - - //获取翻页对象 同 getFlipper("flipper", needcreate, maxLimit) - public org.redkale.source.Flipper getFlipper(boolean needcreate, int maxLimit); - - //获取翻页对象 http://redkale.org/pipes/records/list/offset:0/limit:20/sort:createtime%20ASC - //http://redkale.org/pipes/records/list?flipper={'offset':0,'limit':20, 'sort':'createtime ASC'} - //以上两种接口都可以获取到翻页对象 - public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit); - - //获取HTTP上下文对象 - public HttpContext getContext(); - - //获取所有属性值, servlet执行完后会被清空 - public Map getAttributes(); - - //获取指定属性值, servlet执行完后会被清空 - public T getAttribute(String name); - - //删除指定属性 - public void removeAttribute(String name); - - //设置属性值, servlet执行完后会被清空 - public void setAttribute(String name, Object value); - - //获取request创建时间 - public long getCreatetime(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.http; + +import java.io.*; +import java.lang.reflect.Type; +import java.net.*; +import java.nio.charset.*; +import java.util.*; +import org.redkale.convert.json.*; +import org.redkale.net.http.*; +import org.redkale.util.AnyValue; + +/** + * + * @author zhangjx + */ +public interface HttpRequestDesc { + + //获取客户端地址IP + public SocketAddress getRemoteAddress(); + + //获取客户端地址IP, 与getRemoteAddres() 的区别在于: + //本方法优先取header中指定为RemoteAddress名的值,没有则返回getRemoteAddres()的getHostAddress()。 + //本方法适用于服务前端有如nginx的代理服务器进行中转,通过getRemoteAddres()是获取不到客户端的真实IP。 + public String getRemoteAddr(); + + //获取请求内容指定的编码字符串 + public String getBody(Charset charset); + + //获取请求内容的UTF-8编码字符串 + public String getBodyUTF8(); + + //获取请求内容的byte[] + public byte[] getBody(); + + //获取请求内容的JavaBean对象 + public T getBodyJson(java.lang.reflect.Type type); + + //获取请求内容的JavaBean对象 + public T getBodyJson(JsonConvert convert, java.lang.reflect.Type type); + + //获取文件上传对象 + public MultiContext getMultiContext(); + + //获取文件上传信息列表 等价于 getMultiContext().parts(); + public Iterable multiParts() throws IOException; + + //设置当前用户信息, 通常在HttpServlet.preExecute方法里设置currentUser + //数据类型由@HttpUserType指定 + public HttpRequest setCurrentUser(T user); + + //获取当前用户信息 数据类型由@HttpUserType指定 + public T currentUser(); + + //获取模块ID,来自@HttpServlet.moduleid() + public int getModuleid(); + + //获取操作ID,来自@HttpMapping.actionid() + public int getActionid(); + + //获取sessionid + public String getSessionid(boolean autoCreate); + + //更新sessionid + public String changeSessionid(); + + //指定值更新sessionid + public String changeSessionid(String newsessionid); + + //使sessionid失效 + public void invalidateSession(); + + //获取所有Cookie对象 + public java.net.HttpCookie[] getCookies(); + + //获取Cookie值 + public String getCookie(String name); + + //获取Cookie值, 没有返回默认值 + public String getCookie(String name, String defaultValue); + + //获取协议名 http、https、ws、wss等 + public String getProtocol(); + + //获取请求方法 GET、POST等 + public String getMethod(); + + //获取Content-Type的header值 + public String getContentType(); + + //获取请求内容的长度, 为-1表示内容长度不确定 + public long getContentLength(); + + //获取Connection的Header值 + public String getConnection(); + + //获取Host的Header值 + public String getHost(); + + //获取请求的URL + public String getRequestURI(); + + //截取getRequestURI最后的一个/后面的部分 + public String getRequstURILastPath(); + + // 获取请求URL最后的一个/后面的部分的short值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: short type = request.getRequstURILastPath((short)0); //type = 2 + public short getRequstURILastPath(short defvalue); + + // 获取请求URL最后的一个/后面的部分的short值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: short type = request.getRequstURILastPath((short)0); //type = 2 + public short getRequstURILastPath(int radix, short defvalue); + + // 获取请求URL最后的一个/后面的部分的int值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: int type = request.getRequstURILastPath(0); //type = 2 + public int getRequstURILastPath(int defvalue); + + // 获取请求URL最后的一个/后面的部分的int值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: int type = request.getRequstURILastPath(0); //type = 2 + public int getRequstURILastPath(int radix, int defvalue); + + // 获取请求URL最后的一个/后面的部分的float值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: float type = request.getRequstURILastPath(0.f); //type = 2.f + public float getRequstURILastPath(float defvalue); + + // 获取请求URL最后的一个/后面的部分的long值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: long type = request.getRequstURILastPath(0L); //type = 2 + public long getRequstURILastPath(long defvalue); + + // 获取请求URL最后的一个/后面的部分的long值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: long type = request.getRequstURILastPath(0L); //type = 2 + public long getRequstURILastPath(int radix, long defvalue); + + // 获取请求URL最后的一个/后面的部分的double值
    + // 例如请求URL /pipes/record/query/2
    + // 获取type参数: double type = request.getRequstURILastPath(0.0); //type = 2.0 + public double getRequstURILastPath(double defvalue); + + //从prefix之后截取getRequestURI再对"/"进行分隔 + public String[] getRequstURIPaths(String prefix); + + // 获取请求URL分段中含prefix段的值 + // 例如请求URL /pipes/record/query/name:hello + // 获取name参数: String name = request.getRequstURIPath("name:", "none"); + public String getRequstURIPath(String prefix, String defaultValue); + + // 获取请求URL分段中含prefix段的short值 + // 例如请求URL /pipes/record/query/type:10 + // 获取type参数: short type = request.getRequstURIPath("type:", (short)0); + public short getRequstURIPath(String prefix, short defaultValue); + + // 获取请求URL分段中含prefix段的short值 + // 例如请求URL /pipes/record/query/type:a + // 获取type参数: short type = request.getRequstURIPath(16, "type:", (short)0); type = 10 + public short getRequstURIPath(int radix, String prefix, short defvalue); + + // 获取请求URL分段中含prefix段的int值 + // 例如请求URL /pipes/record/query/offset:2/limit:50 + // 获取offset参数: int offset = request.getRequstURIPath("offset:", 1); + // 获取limit参数: int limit = request.getRequstURIPath("limit:", 20); + public int getRequstURIPath(String prefix, int defaultValue); + + // 获取请求URL分段中含prefix段的int值 + // 例如请求URL /pipes/record/query/offset:2/limit:10 + // 获取offset参数: int offset = request.getRequstURIPath("offset:", 1); + // 获取limit参数: int limit = request.getRequstURIPath(16, "limit:", 20); // limit = 16 + public int getRequstURIPath(int radix, String prefix, int defaultValue); + + // 获取请求URL分段中含prefix段的float值 + // 例如请求URL /pipes/record/query/point:40.0 + // 获取time参数: float point = request.getRequstURIPath("point:", 0.0f); + public float getRequstURIPath(String prefix, float defvalue); + + // 获取请求URL分段中含prefix段的long值 + // 例如请求URL /pipes/record/query/time:1453104341363/id:40 + // 获取time参数: long time = request.getRequstURIPath("time:", 0L); + public long getRequstURIPath(String prefix, long defaultValue); + + // 获取请求URL分段中含prefix段的long值 + // 例如请求URL /pipes/record/query/time:1453104341363/id:40 + // 获取time参数: long time = request.getRequstURIPath("time:", 0L); + public long getRequstURIPath(int radix, String prefix, long defvalue); + + // 获取请求URL分段中含prefix段的double值
    + // 例如请求URL /pipes/record/query/point:40.0
    + // 获取time参数: double point = request.getRequstURIPath("point:", 0.0); + public double getRequstURIPath(String prefix, double defvalue); + + //获取所有的header名 + public AnyValue getHeaders(); + + //将请求Header转换成Map + public Map getHeadersToMap(Map map); + + //获取所有的header名 + public String[] getHeaderNames(); + + // 获取指定的header值 + public String getHeader(String name); + + //获取指定的header值, 没有返回默认值 + public String getHeader(String name, String defaultValue); + + //获取指定的header的json值 + public T getJsonHeader(Type type, String name); + + //获取指定的header的json值 + public T getJsonHeader(JsonConvert convert, Type type, String name); + + //获取指定的header的boolean值, 没有返回默认boolean值 + public boolean getBooleanHeader(String name, boolean defaultValue); + + // 获取指定的header的short值, 没有返回默认short值 + public short getShortHeader(String name, short defaultValue); + + // 获取指定的header的short值, 没有返回默认short值 + public short getShortHeader(int radix, String name, short defaultValue); + + // 获取指定的header的short值, 没有返回默认short值 + public short getShortHeader(String name, int defaultValue); + + // 获取指定的header的short值, 没有返回默认short值 + public short getShortHeader(int radix, String name, int defaultValue); + + //获取指定的header的int值, 没有返回默认int值 + public int getIntHeader(String name, int defaultValue); + + //获取指定的header的int值, 没有返回默认int值 + public int getIntHeader(int radix, String name, int defaultValue); + + // 获取指定的header的long值, 没有返回默认long值 + public long getLongHeader(String name, long defaultValue); + + // 获取指定的header的long值, 没有返回默认long值 + public long getLongHeader(int radix, String name, long defaultValue); + + // 获取指定的header的float值, 没有返回默认float值 + public float getFloatHeader(String name, float defaultValue); + + //获取指定的header的double值, 没有返回默认double值 + public double getDoubleHeader(String name, double defaultValue); + + //获取请求参数总对象 + public AnyValue getParameters(); + + //将请求参数转换成Map + public Map getParametersToMap(Map map); + + //将请求参数转换成String, 字符串格式为: bean1={}&id=13&name=xxx + //不会返回null,没有参数返回空字符串 + public String getParametersToString(); + + //将请求参数转换成String, 字符串格式为: prefix + bean1={}&id=13&name=xxx + //拼接前缀, 如果无参数,返回的字符串不会含有拼接前缀 + //不会返回null,没有参数返回空字符串 + public String getParametersToString(String prefix); + + //获取所有参数名 + public String[] getParameterNames(); + + //获取指定的参数值 + public String getParameter(String name); + + //获取指定的参数值, 没有返回默认值 + public String getParameter(String name, String defaultValue); + + //获取指定的参数json值 + public T getJsonParameter(Type type, String name); + + //获取指定的参数json值 + public T getJsonParameter(JsonConvert convert, Type type, String name); + + //获取指定的参数boolean值, 没有返回默认boolean值 + public boolean getBooleanParameter(String name, boolean defaultValue); + + //获取指定的参数short值, 没有返回默认short值 + public short getShortParameter(String name, short defaultValue); + + //获取指定的参数short值, 没有返回默认short值 + public short getShortParameter(int radix, String name, short defaultValue); + + //获取指定的参数short值, 没有返回默认short值 + public short getShortParameter(int radix, String name, int defaultValue); + + //获取指定的参数int值, 没有返回默认int值 + public int getIntParameter(String name, int defaultValue); + + //获取指定的参数int值, 没有返回默认int值 + public int getIntParameter(int radix, String name, int defaultValue); + + //获取指定的参数long值, 没有返回默认long值 + public long getLongParameter(String name, long defaultValue); + + //获取指定的参数long值, 没有返回默认long值 + public long getLongParameter(int radix, String name, long defaultValue); + + //获取指定的参数float值, 没有返回默认float值 + public float getFloatParameter(String name, float defaultValue); + + //获取指定的参数double值, 没有返回默认double值 + public double getDoubleParameter(String name, double defaultValue); + + //获取翻页对象 同 getFlipper("flipper", false, 0); + public org.redkale.source.Flipper getFlipper(); + + //获取翻页对象 同 getFlipper("flipper", needcreate, 0); + public org.redkale.source.Flipper getFlipper(boolean needcreate); + + //获取翻页对象 同 getFlipper("flipper", false, maxLimit); + public org.redkale.source.Flipper getFlipper(int maxLimit); + + //获取翻页对象 同 getFlipper("flipper", needcreate, maxLimit) + public org.redkale.source.Flipper getFlipper(boolean needcreate, int maxLimit); + + //获取翻页对象 http://redkale.org/pipes/records/list/offset:0/limit:20/sort:createtime%20ASC + //http://redkale.org/pipes/records/list?flipper={'offset':0,'limit':20, 'sort':'createtime ASC'} + //以上两种接口都可以获取到翻页对象 + public org.redkale.source.Flipper getFlipper(String name, boolean needcreate, int maxLimit); + + //获取HTTP上下文对象 + public HttpContext getContext(); + + //获取所有属性值, servlet执行完后会被清空 + public Map getAttributes(); + + //获取指定属性值, servlet执行完后会被清空 + public T getAttribute(String name); + + //删除指定属性 + public void removeAttribute(String name); + + //设置属性值, servlet执行完后会被清空 + public void setAttribute(String name, Object value); + + //获取request创建时间 + public long getCreatetime(); +} diff --git a/test/org/redkale/test/http/HttpResponseDesc.java b/src/test/java/org/redkale/test/http/HttpResponseDesc.java similarity index 97% rename from test/org/redkale/test/http/HttpResponseDesc.java rename to src/test/java/org/redkale/test/http/HttpResponseDesc.java index c67d4283d..6c89e5bcc 100644 --- a/test/org/redkale/test/http/HttpResponseDesc.java +++ b/src/test/java/org/redkale/test/http/HttpResponseDesc.java @@ -1,175 +1,175 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.http; - -import java.io.*; -import java.lang.reflect.*; -import java.net.*; -import java.nio.*; -import java.nio.channels.CompletionHandler; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.*; -import org.redkale.convert.Convert; -import org.redkale.convert.json.*; -import org.redkale.net.http.*; - -/** - * - * @author zhangjx - */ -public interface HttpResponseDesc { - - //增加Cookie值 - public HttpResponse addCookie(HttpCookie... cookies); - - //增加Cookie值 - public HttpResponse addCookie(Collection cookies); - - //创建CompletionHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出 - public CompletionHandler createAsyncHandler(); - - //传入的CompletionHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数 - public H createAsyncHandler(Class handlerClass); - - //获取ByteBuffer生成器 - public Supplier getBufferSupplier(); - - //设置状态码 - public void setStatus(int status); - - //获取状态码 - public int getStatus(); - - //获取 ContentType - public String getContentType(); - - //设置 ContentType - public HttpResponse setContentType(String contentType); - - //获取内容长度 - public long getContentLength(); - - //设置内容长度 - public HttpResponse setContentLength(long contentLength); - - //设置Header值 - public HttpResponse setHeader(String name, Object value); - - //添加Header值 - public HttpResponse addHeader(String name, Object value); - - //添加Header值 - public HttpResponse addHeader(Map map); - - //跳过header的输出 - //通常应用场景是,调用者的输出内容里已经包含了HTTP的响应头信息,因此需要调用此方法避免重复输出HTTP响应头信息。 - public HttpResponse skipHeader(); - - //异步输出指定内容 - public
    void sendBody(ByteBuffer buffer, A attachment, CompletionHandler handler); - - //异步输出指定内容 - public void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler handler); - - //关闭HTTP连接,如果是keep-alive则不强制关闭 - public void finish(); - - //强制关闭HTTP连接 - public void finish(boolean kill); - - //将对象以JSON格式输出 - public void finishJson(Object obj); - - //将对象数组用Map的形式以JSON格式输出 - //例如: finishMap("a",2,"b",3) 输出结果为 {"a":2,"b":3} - public void finishMapJson(final Object... objs); - - //将对象以JSON格式输出 - public void finishJson(JsonConvert convert, Object obj); - - //将对象数组用Map的形式以JSON格式输出 - //例如: finishMap("a",2,"b",3) 输出结果为 {"a":2,"b":3} - public void finishMapJson(final JsonConvert convert, final Object... objs); - - //将对象以JSON格式输出 - public void finishJson(Type type, Object obj); - - //将对象以JSON格式输出 - public void finishJson(final JsonConvert convert, final Type type, final Object obj); - - //将对象以JSON格式输出 - public void finishJson(final Object... objs); - - //将RetResult对象以JSON格式输出 - public void finishJson(final org.redkale.service.RetResult ret); - - //将RetResult对象以JSON格式输出 - public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret); - - //将CompletableFuture的结果对象以JSON格式输出 - public void finishJson(final CompletableFuture future); - - //将CompletableFuture的结果对象以JSON格式输出 - public void finishJson(final JsonConvert convert, final CompletableFuture future); - - //将CompletableFuture的结果对象以JSON格式输出 - public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future); - - //将HttpResult的结果对象以JSON格式输出 - public void finishJson(final HttpResult result); - - //将HttpResult的结果对象以JSON格式输出 - public void finishJson(final JsonConvert convert, final HttpResult result); - - //将指定字符串以响应结果输出 - public void finish(String obj); - - //以指定响应码附带内容输出, message 可以为null - public void finish(int status, String message); - - //将结果对象输出 - public void finish(final Object obj); - - //将结果对象输出 - public void finish(final Convert convert, final Object obj); - - //将结果对象输出 - public void finish(final Convert convert, final Type type, final Object obj); - - //以304状态码输出 - public void finish304(); - - //以404状态码输出 - public void finish404(); - - //将指定byte[]按响应结果输出 - public void finish(final byte[] bs); - - //将指定ByteBuffer按响应结果输出 - public void finish(ByteBuffer buffer); - - //将指定ByteBuffer按响应结果输出 - //kill 输出后是否强制关闭连接 - public void finish(boolean kill, ByteBuffer buffer); - - //将指定ByteBuffer数组按响应结果输出 - public void finish(ByteBuffer... buffers); - - //将指定ByteBuffer数组按响应结果输出 - //kill 输出后是否强制关闭连接 - public void finish(boolean kill, ByteBuffer... buffers); - - //将指定文件按响应结果输出 - public void finish(File file) throws IOException; - - //将文件按指定文件名输出 - public void finish(final String filename, File file) throws IOException; - - //HttpResponse回收时回调的监听方法 - public void recycleListener(BiConsumer recycleListener); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.http; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.nio.*; +import java.nio.channels.CompletionHandler; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.*; +import org.redkale.convert.Convert; +import org.redkale.convert.json.*; +import org.redkale.net.http.*; + +/** + * + * @author zhangjx + */ +public interface HttpResponseDesc { + + //增加Cookie值 + public HttpResponse addCookie(HttpCookie... cookies); + + //增加Cookie值 + public HttpResponse addCookie(Collection cookies); + + //创建CompletionHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出 + public CompletionHandler createAsyncHandler(); + + //传入的CompletionHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数 + public H createAsyncHandler(Class handlerClass); + + //获取ByteBuffer生成器 + public Supplier getBufferSupplier(); + + //设置状态码 + public void setStatus(int status); + + //获取状态码 + public int getStatus(); + + //获取 ContentType + public String getContentType(); + + //设置 ContentType + public HttpResponse setContentType(String contentType); + + //获取内容长度 + public long getContentLength(); + + //设置内容长度 + public HttpResponse setContentLength(long contentLength); + + //设置Header值 + public HttpResponse setHeader(String name, Object value); + + //添加Header值 + public HttpResponse addHeader(String name, Object value); + + //添加Header值 + public HttpResponse addHeader(Map map); + + //跳过header的输出 + //通常应用场景是,调用者的输出内容里已经包含了HTTP的响应头信息,因此需要调用此方法避免重复输出HTTP响应头信息。 + public HttpResponse skipHeader(); + + //异步输出指定内容 + public void sendBody(ByteBuffer buffer, A attachment, CompletionHandler handler); + + //异步输出指定内容 + public void sendBody(ByteBuffer[] buffers, A attachment, CompletionHandler handler); + + //关闭HTTP连接,如果是keep-alive则不强制关闭 + public void finish(); + + //强制关闭HTTP连接 + public void finish(boolean kill); + + //将对象以JSON格式输出 + public void finishJson(Object obj); + + //将对象数组用Map的形式以JSON格式输出 + //例如: finishMap("a",2,"b",3) 输出结果为 {"a":2,"b":3} + public void finishMapJson(final Object... objs); + + //将对象以JSON格式输出 + public void finishJson(JsonConvert convert, Object obj); + + //将对象数组用Map的形式以JSON格式输出 + //例如: finishMap("a",2,"b",3) 输出结果为 {"a":2,"b":3} + public void finishMapJson(final JsonConvert convert, final Object... objs); + + //将对象以JSON格式输出 + public void finishJson(Type type, Object obj); + + //将对象以JSON格式输出 + public void finishJson(final JsonConvert convert, final Type type, final Object obj); + + //将对象以JSON格式输出 + public void finishJson(final Object... objs); + + //将RetResult对象以JSON格式输出 + public void finishJson(final org.redkale.service.RetResult ret); + + //将RetResult对象以JSON格式输出 + public void finishJson(final JsonConvert convert, final org.redkale.service.RetResult ret); + + //将CompletableFuture的结果对象以JSON格式输出 + public void finishJson(final CompletableFuture future); + + //将CompletableFuture的结果对象以JSON格式输出 + public void finishJson(final JsonConvert convert, final CompletableFuture future); + + //将CompletableFuture的结果对象以JSON格式输出 + public void finishJson(final JsonConvert convert, final Type type, final CompletableFuture future); + + //将HttpResult的结果对象以JSON格式输出 + public void finishJson(final HttpResult result); + + //将HttpResult的结果对象以JSON格式输出 + public void finishJson(final JsonConvert convert, final HttpResult result); + + //将指定字符串以响应结果输出 + public void finish(String obj); + + //以指定响应码附带内容输出, message 可以为null + public void finish(int status, String message); + + //将结果对象输出 + public void finish(final Object obj); + + //将结果对象输出 + public void finish(final Convert convert, final Object obj); + + //将结果对象输出 + public void finish(final Convert convert, final Type type, final Object obj); + + //以304状态码输出 + public void finish304(); + + //以404状态码输出 + public void finish404(); + + //将指定byte[]按响应结果输出 + public void finish(final byte[] bs); + + //将指定ByteBuffer按响应结果输出 + public void finish(ByteBuffer buffer); + + //将指定ByteBuffer按响应结果输出 + //kill 输出后是否强制关闭连接 + public void finish(boolean kill, ByteBuffer buffer); + + //将指定ByteBuffer数组按响应结果输出 + public void finish(ByteBuffer... buffers); + + //将指定ByteBuffer数组按响应结果输出 + //kill 输出后是否强制关闭连接 + public void finish(boolean kill, ByteBuffer... buffers); + + //将指定文件按响应结果输出 + public void finish(File file) throws IOException; + + //将文件按指定文件名输出 + public void finish(final String filename, File file) throws IOException; + + //HttpResponse回收时回调的监听方法 + public void recycleListener(BiConsumer recycleListener); + +} diff --git a/test/org/redkale/test/http/SocketMain.java b/src/test/java/org/redkale/test/http/SocketMain.java similarity index 97% rename from test/org/redkale/test/http/SocketMain.java rename to src/test/java/org/redkale/test/http/SocketMain.java index 0ac3aaf83..5e07a8560 100644 --- a/test/org/redkale/test/http/SocketMain.java +++ b/src/test/java/org/redkale/test/http/SocketMain.java @@ -1,36 +1,36 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.http; - -import java.net.Socket; - -/** - * - * @author zhangjx - */ -public class SocketMain { - - public static void main(String[] args) throws Throwable { - Socket socket = new Socket("127.0.0.1", 6060); - socket.getOutputStream().write("GET /json1 HTTP/1.1\r\nAccpet: aaa\r\nConnection: keep-alive\r\n\r\nGET /json2 HTTP/1.1\r\nAccpet: a".getBytes()); - socket.getOutputStream().flush(); - Thread.sleep(1000); - socket.getOutputStream().write("aa\r\nConnection: keep-alive\r\n\r".getBytes()); - socket.getOutputStream().flush(); - Thread.sleep(1000); - socket.getOutputStream().write("\nGET /json3 HTTP/1.1\r\nAccpet: aaa\r\nConnection: keep-alive\r\n\r".getBytes()); - socket.getOutputStream().flush(); - Thread.sleep(1000); - socket.getOutputStream().write("\n".getBytes()); - socket.getOutputStream().flush(); - byte[] bs = new byte[10240]; - int rs = socket.getInputStream().read(bs); - System.out.println(new String(bs, 0, rs)); - rs = socket.getInputStream().read(bs); - System.out.println(new String(bs, 0, rs)); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.http; + +import java.net.Socket; + +/** + * + * @author zhangjx + */ +public class SocketMain { + + public static void main(String[] args) throws Throwable { + Socket socket = new Socket("127.0.0.1", 6060); + socket.getOutputStream().write("GET /json1 HTTP/1.1\r\nAccpet: aaa\r\nConnection: keep-alive\r\n\r\nGET /json2 HTTP/1.1\r\nAccpet: a".getBytes()); + socket.getOutputStream().flush(); + Thread.sleep(1000); + socket.getOutputStream().write("aa\r\nConnection: keep-alive\r\n\r".getBytes()); + socket.getOutputStream().flush(); + Thread.sleep(1000); + socket.getOutputStream().write("\nGET /json3 HTTP/1.1\r\nAccpet: aaa\r\nConnection: keep-alive\r\n\r".getBytes()); + socket.getOutputStream().flush(); + Thread.sleep(1000); + socket.getOutputStream().write("\n".getBytes()); + socket.getOutputStream().flush(); + byte[] bs = new byte[10240]; + int rs = socket.getInputStream().read(bs); + System.out.println(new String(bs, 0, rs)); + rs = socket.getInputStream().read(bs); + System.out.println(new String(bs, 0, rs)); + } + +} diff --git a/test/org/redkale/test/http/UploadTestServlet.java b/src/test/java/org/redkale/test/http/UploadTestServlet.java similarity index 97% rename from test/org/redkale/test/http/UploadTestServlet.java rename to src/test/java/org/redkale/test/http/UploadTestServlet.java index 01bb550fe..12c7a850c 100644 --- a/test/org/redkale/test/http/UploadTestServlet.java +++ b/src/test/java/org/redkale/test/http/UploadTestServlet.java @@ -1,50 +1,50 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.http; - -import org.redkale.net.http.HttpServlet; -import org.redkale.net.http.MultiPart; -import org.redkale.net.http.HttpRequest; -import org.redkale.net.http.HttpResponse; -import java.io.*; - -/** - * - * @author zhangjx - */ -//@WebServlet({"/uploadtest/form", "/uploadtest/send"}) -public class UploadTestServlet extends HttpServlet { - - @Override - public void execute(HttpRequest request, HttpResponse response) throws IOException { - if (request.getRequestURI().contains("/uploadtest/send")) { - send(request, response); - } else { - form(request, response); - } - } - - public void form(HttpRequest req, HttpResponse resp) throws IOException { - resp.setContentType("text/html"); - resp.finish( - "" - + "

    " - + ""); - } - - public void send(HttpRequest req, HttpResponse resp) throws IOException { - for (MultiPart entry : req.multiParts()) { - entry.skip(); - System.out.println(entry); - } - System.exit(0); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.http; + +import org.redkale.net.http.HttpServlet; +import org.redkale.net.http.MultiPart; +import org.redkale.net.http.HttpRequest; +import org.redkale.net.http.HttpResponse; +import java.io.*; + +/** + * + * @author zhangjx + */ +//@WebServlet({"/uploadtest/form", "/uploadtest/send"}) +public class UploadTestServlet extends HttpServlet { + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + if (request.getRequestURI().contains("/uploadtest/send")) { + send(request, response); + } else { + form(request, response); + } + } + + public void form(HttpRequest req, HttpResponse resp) throws IOException { + resp.setContentType("text/html"); + resp.finish( + "" + + "
    " + + "描述:         文件1:

    " + + "描述:         文件2:

    " + + "描述:         文件3:

    " + + "描述:         

    " + + "              
    " + + ""); + } + + public void send(HttpRequest req, HttpResponse resp) throws IOException { + for (MultiPart entry : req.multiParts()) { + entry.skip(); + System.out.println(entry); + } + System.exit(0); + } +} diff --git a/test/org/redkale/test/http/WebSocketDesc.java b/src/test/java/org/redkale/test/http/WebSocketDesc.java similarity index 97% rename from test/org/redkale/test/http/WebSocketDesc.java rename to src/test/java/org/redkale/test/http/WebSocketDesc.java index c7ff624d4..9acafb2c2 100644 --- a/test/org/redkale/test/http/WebSocketDesc.java +++ b/src/test/java/org/redkale/test/http/WebSocketDesc.java @@ -1,204 +1,204 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.http; - -import java.io.*; -import java.net.*; -import java.nio.ByteBuffer; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Supplier; -import java.util.stream.Stream; -import org.redkale.convert.Convert; -import org.redkale.net.http.*; - -/** - * - * @author zhangjx - */ -public interface WebSocketDesc { - - //给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 返回结果0表示成功,非0表示错误码 - public CompletableFuture send(Object message); - - //给自身发送消息, 消息类型是key-value键值对 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMap(Object... messages); - - //给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 返回结果0表示成功,非0表示错误码 - public CompletableFuture send(Object message, boolean last); - - //给自身发送消息, 消息类型是key-value键值对 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMap(boolean last, Object... messages); - - //给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功,非0表示错误码 - public CompletableFuture send(Convert convert, Object message); - - //给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功,非0表示错误码 - public CompletableFuture send(Convert convert, Object message, boolean last); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Object message, G... userids); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Object message, Stream userids); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Convert convert, Object message, G... userids); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Convert convert, Object message, Stream userids); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Object message, boolean last, G... userids); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Object message, boolean last, Stream userids); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Convert convert, Object message, boolean last, G... userids); - - //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendMessage(Convert convert, Object message, boolean last, Stream userids); - - //给所有人广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(final Object message); - - //给所有人广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(final Convert convert, final Object message); - - //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message); - - //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message); - - //给所有人广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(final Object message, boolean last); - - //给所有人广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(final Convert convert, final Object message, boolean last); - - //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message, boolean last); - - //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 - public CompletableFuture broadcastMessage(WebSocketRange wsrange, Convert convert, final Object message, boolean last); - - //给指定userid的WebSocket节点发送操作 - public CompletableFuture sendAction(final WebSocketAction action, Serializable... userids); - - //广播操作, 给所有人发操作指令 - public CompletableFuture broadcastAction(final WebSocketAction action); - - //获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表 - public CompletableFuture> getRpcNodeAddresses(final Serializable userid); - - //获取在线用户的详细连接信息 - public CompletableFuture>> getRpcNodeWebSocketAddresses(final Serializable userid); - - //发送PING消息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendPing(); - - //发送PING消息,附带其他信息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendPing(byte[] data); - - //发送PONG消息,附带其他信息 返回结果0表示成功,非0表示错误码 - public CompletableFuture sendPong(byte[] data); - - //强制关闭用户的所有WebSocket - public CompletableFuture forceCloseWebSocket(Serializable userid); - - //更改本WebSocket的userid - public CompletableFuture changeUserid(final G newuserid); - - - //获取指定userid的WebSocket数组, 没有返回null 此方法用于单用户多连接模式 - /* protected */ Stream getLocalWebSockets(G userid); - - - //获取指定userid的WebSocket数组, 没有返回null 此方法用于单用户单连接模式 - /* protected */ WebSocket findLocalWebSocket(G userid); - - - //获取当前进程节点所有在线的WebSocket - /* protected */ Collection getLocalWebSockets(); - - - //获取ByteBuffer资源池 - /* protected */ Supplier getByteBufferSupplier(); - - - //返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法 - /* protected */ CompletableFuture onOpen(final HttpRequest request); - - - //创建userid, null表示异常, 必须实现该方法 - /* protected abstract */ CompletableFuture createUserid(); - - - //WebSocket.broadcastMessage时的过滤条件 - /* protected */ boolean predicate(WebSocketRange wsrange); - - //WebSokcet连接成功后的回调方法 - public void onConnected(); - - //ping后的回调方法 - public void onPing(byte[] bytes); - - //pong后的回调方法 - public void onPong(byte[] bytes); - - //接收到消息的回调方法 - public void onMessage(T message, boolean last); - - //接收到文本消息的回调方法 - public void onMessage(String message, boolean last); - - //接收到二进制消息的回调方法 - public void onMessage(byte[] bytes, boolean last); - - //关闭的回调方法,调用此方法时WebSocket已经被关闭 - public void onClose(int code, String reason); - - //获取当前WebSocket下的属性 - public T getAttribute(String name); - - //移出当前WebSocket下的属性 - public T removeAttribute(String name); - - //给当前WebSocket下的增加属性 - public void setAttribute(String name, Object value); - - //获取当前WebSocket所属的userid - public G getUserid(); - - //获取当前WebSocket的会话ID, 不会为null - public Serializable getSessionid(); - - //获取客户端直接地址, 当WebSocket连接是由代理服务器转发的,则该值固定为代理服务器的IP地址 - public SocketAddress getRemoteAddress(); - - //获取客户端真实地址 同 HttpRequest.getRemoteAddr() - public String getRemoteAddr(); - - //获取WebSocket创建时间 - public long getCreatetime(); - - //获取最后一次发送消息的时间 - public long getLastSendTime(); - - //获取最后一次读取消息的时间 - public long getLastReadTime(); - - //获取最后一次ping的时间 - public long getLastPingTime(); - - //显式地关闭WebSocket - public void close(); - - //WebSocket是否已关闭 - public boolean isClosed(); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.http; + +import java.io.*; +import java.net.*; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.Supplier; +import java.util.stream.Stream; +import org.redkale.convert.Convert; +import org.redkale.net.http.*; + +/** + * + * @author zhangjx + */ +public interface WebSocketDesc { + + //给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 返回结果0表示成功,非0表示错误码 + public CompletableFuture send(Object message); + + //给自身发送消息, 消息类型是key-value键值对 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMap(Object... messages); + + //给自身发送消息, 消息类型是String或byte[]或可JavaBean对象 返回结果0表示成功,非0表示错误码 + public CompletableFuture send(Object message, boolean last); + + //给自身发送消息, 消息类型是key-value键值对 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMap(boolean last, Object... messages); + + //给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功,非0表示错误码 + public CompletableFuture send(Convert convert, Object message); + + //给自身发送消息, 消息类型是JavaBean对象 返回结果0表示成功,非0表示错误码 + public CompletableFuture send(Convert convert, Object message, boolean last); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Object message, G... userids); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Object message, Stream userids); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Convert convert, Object message, G... userids); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Convert convert, Object message, Stream userids); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Object message, boolean last, G... userids); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Object message, boolean last, Stream userids); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Convert convert, Object message, boolean last, G... userids); + + //给指定userid的WebSocket节点发送 二进制消息/文本消息/JavaBean对象消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendMessage(Convert convert, Object message, boolean last, Stream userids); + + //给所有人广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(final Object message); + + //给所有人广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(final Convert convert, final Object message); + + //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message); + + //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Convert convert, final Object message); + + //给所有人广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(final Object message, boolean last); + + //给所有人广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(final Convert convert, final Object message, boolean last); + + //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(final WebSocketRange wsrange, final Object message, boolean last); + + //给符合条件的人群广播消息, 返回结果0表示成功,非0表示错误码 + public CompletableFuture broadcastMessage(WebSocketRange wsrange, Convert convert, final Object message, boolean last); + + //给指定userid的WebSocket节点发送操作 + public CompletableFuture sendAction(final WebSocketAction action, Serializable... userids); + + //广播操作, 给所有人发操作指令 + public CompletableFuture broadcastAction(final WebSocketAction action); + + //获取用户在线的SNCP节点地址列表,不是分布式则返回元素数量为1,且元素值为null的列表 + public CompletableFuture> getRpcNodeAddresses(final Serializable userid); + + //获取在线用户的详细连接信息 + public CompletableFuture>> getRpcNodeWebSocketAddresses(final Serializable userid); + + //发送PING消息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendPing(); + + //发送PING消息,附带其他信息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendPing(byte[] data); + + //发送PONG消息,附带其他信息 返回结果0表示成功,非0表示错误码 + public CompletableFuture sendPong(byte[] data); + + //强制关闭用户的所有WebSocket + public CompletableFuture forceCloseWebSocket(Serializable userid); + + //更改本WebSocket的userid + public CompletableFuture changeUserid(final G newuserid); + + + //获取指定userid的WebSocket数组, 没有返回null 此方法用于单用户多连接模式 + /* protected */ Stream getLocalWebSockets(G userid); + + + //获取指定userid的WebSocket数组, 没有返回null 此方法用于单用户单连接模式 + /* protected */ WebSocket findLocalWebSocket(G userid); + + + //获取当前进程节点所有在线的WebSocket + /* protected */ Collection getLocalWebSockets(); + + + //获取ByteBuffer资源池 + /* protected */ Supplier getByteBufferSupplier(); + + + //返回sessionid, null表示连接不合法或异常,默认实现是request.sessionid(true),通常需要重写该方法 + /* protected */ CompletableFuture onOpen(final HttpRequest request); + + + //创建userid, null表示异常, 必须实现该方法 + /* protected abstract */ CompletableFuture createUserid(); + + + //WebSocket.broadcastMessage时的过滤条件 + /* protected */ boolean predicate(WebSocketRange wsrange); + + //WebSokcet连接成功后的回调方法 + public void onConnected(); + + //ping后的回调方法 + public void onPing(byte[] bytes); + + //pong后的回调方法 + public void onPong(byte[] bytes); + + //接收到消息的回调方法 + public void onMessage(T message, boolean last); + + //接收到文本消息的回调方法 + public void onMessage(String message, boolean last); + + //接收到二进制消息的回调方法 + public void onMessage(byte[] bytes, boolean last); + + //关闭的回调方法,调用此方法时WebSocket已经被关闭 + public void onClose(int code, String reason); + + //获取当前WebSocket下的属性 + public T getAttribute(String name); + + //移出当前WebSocket下的属性 + public T removeAttribute(String name); + + //给当前WebSocket下的增加属性 + public void setAttribute(String name, Object value); + + //获取当前WebSocket所属的userid + public G getUserid(); + + //获取当前WebSocket的会话ID, 不会为null + public Serializable getSessionid(); + + //获取客户端直接地址, 当WebSocket连接是由代理服务器转发的,则该值固定为代理服务器的IP地址 + public SocketAddress getRemoteAddress(); + + //获取客户端真实地址 同 HttpRequest.getRemoteAddr() + public String getRemoteAddr(); + + //获取WebSocket创建时间 + public long getCreatetime(); + + //获取最后一次发送消息的时间 + public long getLastSendTime(); + + //获取最后一次读取消息的时间 + public long getLastReadTime(); + + //获取最后一次ping的时间 + public long getLastPingTime(); + + //显式地关闭WebSocket + public void close(); + + //WebSocket是否已关闭 + public boolean isClosed(); +} diff --git a/test/org/redkale/test/net/TransportTest.java b/src/test/java/org/redkale/test/net/TransportTest.java similarity index 95% rename from test/org/redkale/test/net/TransportTest.java rename to src/test/java/org/redkale/test/net/TransportTest.java index b4903db02..4c7737818 100644 --- a/test/org/redkale/test/net/TransportTest.java +++ b/src/test/java/org/redkale/test/net/TransportTest.java @@ -1,74 +1,74 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.net; - -import java.net.InetSocketAddress; -import java.util.*; -import java.util.concurrent.CountDownLatch; -import org.redkale.net.*; -import org.redkale.net.http.HttpServer; -import org.redkale.net.sncp.Sncp; -import org.redkale.util.AnyValue.DefaultAnyValue; - -/** - * - * @author zhangjx - */ -public class TransportTest { - - private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL"; - - public static void main(String[] args) throws Throwable { - System.setProperty("net.transport.checkinterval", "2"); - List addrs = new ArrayList<>(); - addrs.add(new InetSocketAddress("127.0.0.1", 22001)); - addrs.add(new InetSocketAddress("127.0.0.1", 22002)); - addrs.add(new InetSocketAddress("127.0.0.1", 22003)); - addrs.add(new InetSocketAddress("127.0.0.1", 22004)); - for (InetSocketAddress servaddr : addrs) { - //if (servaddr.getPort() % 100 == 4) continue; - HttpServer server = new HttpServer(); - DefaultAnyValue servconf = DefaultAnyValue.create("port", servaddr.getPort()); - server.init(servconf); - server.start(null); - } - addrs.add(new InetSocketAddress("127.0.0.1", 22005)); - final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); - asyncGroup.start(); - Thread.sleep(1000); - TransportFactory factory = TransportFactory.create(asyncGroup, 0, 0); - DefaultAnyValue conf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, 5); - factory.init(conf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining()); - Transport transport = factory.createTransportTCP("", null, addrs); - System.out.println(String.format(format, System.currentTimeMillis())); - try { - CountDownLatch cdl = new CountDownLatch(20); - for (int i = 0; i < 20; i++) { - transport.pollConnection(null).whenComplete((r, t) -> { - cdl.countDown(); - System.out.println("连接: " + r.getRemoteAddress()); - }); - } - cdl.await(); - HttpServer server = new HttpServer(); - DefaultAnyValue servconf = DefaultAnyValue.create("port", 22005); - server.init(servconf); - server.start(null); - Thread.sleep(4000); - CountDownLatch cdl2 = new CountDownLatch(20); - for (int i = 0; i < 20; i++) { - transport.pollConnection(null).whenComplete((r, t) -> { - cdl2.countDown(); - System.out.println("连接: " + r.getRemoteAddress()); - }); - } - cdl2.await(); - } finally { - System.out.println(String.format(format, System.currentTimeMillis())); - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.net; + +import java.net.InetSocketAddress; +import java.util.*; +import java.util.concurrent.CountDownLatch; +import org.redkale.net.*; +import org.redkale.net.http.HttpServer; +import org.redkale.net.sncp.Sncp; +import org.redkale.util.AnyValue.DefaultAnyValue; + +/** + * + * @author zhangjx + */ +public class TransportTest { + + private static final String format = "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%tL"; + + public static void main(String[] args) throws Throwable { + System.setProperty("net.transport.checkinterval", "2"); + List addrs = new ArrayList<>(); + addrs.add(new InetSocketAddress("127.0.0.1", 22001)); + addrs.add(new InetSocketAddress("127.0.0.1", 22002)); + addrs.add(new InetSocketAddress("127.0.0.1", 22003)); + addrs.add(new InetSocketAddress("127.0.0.1", 22004)); + for (InetSocketAddress servaddr : addrs) { + //if (servaddr.getPort() % 100 == 4) continue; + HttpServer server = new HttpServer(); + DefaultAnyValue servconf = DefaultAnyValue.create("port", servaddr.getPort()); + server.init(servconf); + server.start(); + } + addrs.add(new InetSocketAddress("127.0.0.1", 22005)); + final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); + asyncGroup.start(); + Thread.sleep(1000); + TransportFactory factory = TransportFactory.create(asyncGroup, 0, 0); + DefaultAnyValue conf = DefaultAnyValue.create(TransportFactory.NAME_PINGINTERVAL, 5); + factory.init(conf, Sncp.PING_BUFFER, Sncp.PONG_BUFFER.remaining()); + Transport transport = factory.createTransportTCP("", null, addrs); + System.out.println(String.format(format, System.currentTimeMillis())); + try { + CountDownLatch cdl = new CountDownLatch(20); + for (int i = 0; i < 20; i++) { + transport.pollConnection(null).whenComplete((r, t) -> { + cdl.countDown(); + System.out.println("连接: " + r.getRemoteAddress()); + }); + } + cdl.await(); + HttpServer server = new HttpServer(); + DefaultAnyValue servconf = DefaultAnyValue.create("port", 22005); + server.init(servconf); + server.start(); + Thread.sleep(4000); + CountDownLatch cdl2 = new CountDownLatch(20); + for (int i = 0; i < 20; i++) { + transport.pollConnection(null).whenComplete((r, t) -> { + cdl2.countDown(); + System.out.println("连接: " + r.getRemoteAddress()); + }); + } + cdl2.await(); + } finally { + System.out.println(String.format(format, System.currentTimeMillis())); + } + } + +} diff --git a/test/org/redkale/test/rest/CreateTimeSimpleCoder.java b/src/test/java/org/redkale/test/rest/CreateTimeSimpleCoder.java similarity index 95% rename from test/org/redkale/test/rest/CreateTimeSimpleCoder.java rename to src/test/java/org/redkale/test/rest/CreateTimeSimpleCoder.java index ffa0bbf3e..eff4b69a5 100644 --- a/test/org/redkale/test/rest/CreateTimeSimpleCoder.java +++ b/src/test/java/org/redkale/test/rest/CreateTimeSimpleCoder.java @@ -1,40 +1,40 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.rest; - -import java.text.SimpleDateFormat; -import org.redkale.convert.*; - -/** - * - * @author zhangjx - * @param R - * @param W - */ -public class CreateTimeSimpleCoder extends SimpledCoder { - - private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - - @Override - public void convertTo(W out, Long value) { - if (value == null) { - out.writeNull(); - } else { - out.writeString(format.format(new java.util.Date(value))); - } - } - - @Override - public Long convertFrom(R in) { - String val = in.readString(); - if (val == null) return 0L; - try { - return format.parse(val).getTime(); - } catch (Exception e) { - return 0L; - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import java.text.SimpleDateFormat; +import org.redkale.convert.*; + +/** + * + * @author zhangjx + * @param R + * @param W + */ +public class CreateTimeSimpleCoder extends SimpledCoder { + + private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + @Override + public void convertTo(W out, Long value) { + if (value == null) { + out.writeNull(); + } else { + out.writeString(format.format(new java.util.Date(value))); + } + } + + @Override + public Long convertFrom(R in) { + String val = in.readString(); + if (val == null) return 0L; + try { + return format.parse(val).getTime(); + } catch (Exception e) { + return 0L; + } + } +} diff --git a/test/org/redkale/test/rest/HelloAsyncHandler.java b/src/test/java/org/redkale/test/rest/HelloAsyncHandler.java similarity index 96% rename from test/org/redkale/test/rest/HelloAsyncHandler.java rename to src/test/java/org/redkale/test/rest/HelloAsyncHandler.java index d3f9feff7..b80f8599e 100644 --- a/test/org/redkale/test/rest/HelloAsyncHandler.java +++ b/src/test/java/org/redkale/test/rest/HelloAsyncHandler.java @@ -1,26 +1,26 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.rest; - -import java.nio.channels.CompletionHandler; - -/** - * - * @author zhangjx - */ -public class HelloAsyncHandler implements CompletionHandler { - - @Override - public void completed(Object result, Object attachment) { - System.out.println("-----HelloAsyncHandler--------result : " + result + ", attachment: " + attachment); - } - - @Override - public void failed(Throwable exc, Object attachment) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import java.nio.channels.CompletionHandler; + +/** + * + * @author zhangjx + */ +public class HelloAsyncHandler implements CompletionHandler { + + @Override + public void completed(Object result, Object attachment) { + System.out.println("-----HelloAsyncHandler--------result : " + result + ", attachment: " + attachment); + } + + @Override + public void failed(Throwable exc, Object attachment) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/test/org/redkale/test/rest/HelloBean.java b/src/test/java/org/redkale/test/rest/HelloBean.java similarity index 95% rename from test/org/redkale/test/rest/HelloBean.java rename to src/test/java/org/redkale/test/rest/HelloBean.java index fed43501d..313c3378c 100644 --- a/test/org/redkale/test/rest/HelloBean.java +++ b/src/test/java/org/redkale/test/rest/HelloBean.java @@ -1,69 +1,69 @@ -package org.redkale.test.rest; - -import org.redkale.convert.json.JsonFactory; -import org.redkale.net.http.*; -import org.redkale.source.FilterBean; - -public class HelloBean implements FilterBean { - - private int helloid; - - @RestHeader(name = "User-Agent") - private String useragent; //从Http Header中获取浏览器信息 - - @RestCookie(name = "hello-cookie") - private String rescookie; //从Cookie中获取名为hello-cookie的值 - - @RestAddress - private String clientaddr; //客户端请求IP - - @RestSessionid - private String sessionid; //用户Sessionid, 未登录时为null - - /** 以下省略getter setter方法 */ - public int getHelloid() { - return helloid; - } - - public void setHelloid(int helloid) { - this.helloid = helloid; - } - - public String getUseragent() { - return useragent; - } - - public void setUseragent(String useragent) { - this.useragent = useragent; - } - - public String getRescookie() { - return rescookie; - } - - public void setRescookie(String rescookie) { - this.rescookie = rescookie; - } - - public String getClientaddr() { - return clientaddr; - } - - public void setClientaddr(String clientaddr) { - this.clientaddr = clientaddr; - } - - public String getSessionid() { - return sessionid; - } - - public void setSessionid(String sessionid) { - this.sessionid = sessionid; - } - - @Override - public String toString() { - return JsonFactory.root().getConvert().convertTo(this); - } - -} +package org.redkale.test.rest; + +import org.redkale.convert.json.JsonFactory; +import org.redkale.net.http.*; +import org.redkale.source.FilterBean; + +public class HelloBean implements FilterBean { + + private int helloid; + + @RestHeader(name = "User-Agent") + private String useragent; //从Http Header中获取浏览器信息 + + @RestCookie(name = "hello-cookie") + private String rescookie; //从Cookie中获取名为hello-cookie的值 + + @RestAddress + private String clientaddr; //客户端请求IP + + @RestSessionid + private String sessionid; //用户Sessionid, 未登录时为null + + /** 以下省略getter setter方法 */ + public int getHelloid() { + return helloid; + } + + public void setHelloid(int helloid) { + this.helloid = helloid; + } + + public String getUseragent() { + return useragent; + } + + public void setUseragent(String useragent) { + this.useragent = useragent; + } + + public String getRescookie() { + return rescookie; + } + + public void setRescookie(String rescookie) { + this.rescookie = rescookie; + } + + public String getClientaddr() { + return clientaddr; + } + + public void setClientaddr(String clientaddr) { + this.clientaddr = clientaddr; + } + + public String getSessionid() { + return sessionid; + } + + public void setSessionid(String sessionid) { + this.sessionid = sessionid; + } + + @Override + public String toString() { + return JsonFactory.root().getConvert().convertTo(this); + } + +} diff --git a/test/org/redkale/test/rest/HelloEntity.java b/src/test/java/org/redkale/test/rest/HelloEntity.java similarity index 94% rename from test/org/redkale/test/rest/HelloEntity.java rename to src/test/java/org/redkale/test/rest/HelloEntity.java index d0fd1bf31..930522d67 100644 --- a/test/org/redkale/test/rest/HelloEntity.java +++ b/src/test/java/org/redkale/test/rest/HelloEntity.java @@ -1,152 +1,152 @@ -package org.redkale.test.rest; - -import java.util.Map; -import javax.persistence.Id; -import org.redkale.convert.json.JsonFactory; -import org.redkale.net.http.*; -import org.redkale.source.VirtualEntity; - -@VirtualEntity -public class HelloEntity { - - @Id - private int helloid; - - private String helloname; - - private int creator; - - private long updatetime; - - private long createtime; - - @RestHeader(name = "hello-res") - private String resname; - - @RestBody - private String bodystr; - - @RestBody - private byte[] bodys; - - @RestUploadFile - private byte[] uploads; - - @RestBody - private Map bodymap; - - @RestAddress - private String clientaddr; - - @RestURI - private String uri; - - public HelloEntity() { - } - - public HelloEntity(int id) { - this.helloid = id; - } - - /** 以下省略getter setter方法 */ - public int getHelloid() { - return helloid; - } - - public void setHelloid(int helloid) { - this.helloid = helloid; - } - - public String getHelloname() { - return helloname; - } - - public void setHelloname(String helloname) { - this.helloname = helloname; - } - - public long getUpdatetime() { - return updatetime; - } - - public void setUpdatetime(long updatetime) { - this.updatetime = updatetime; - } - - public long getCreatetime() { - return createtime; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } - - public int getCreator() { - return creator; - } - - public void setCreator(int creator) { - this.creator = creator; - } - - public String getClientaddr() { - return clientaddr; - } - - public void setClientaddr(String clientaddr) { - this.clientaddr = clientaddr; - } - - public String getResname() { - return resname; - } - - public void setResname(String resname) { - this.resname = resname; - } - - public String getBodystr() { - return bodystr; - } - - public void setBodystr(String bodystr) { - this.bodystr = bodystr; - } - - public byte[] getBodys() { - return bodys; - } - - public void setBodys(byte[] bodys) { - this.bodys = bodys; - } - - public Map getBodymap() { - return bodymap; - } - - public void setBodymap(Map bodymap) { - this.bodymap = bodymap; - } - - public byte[] getUploads() { - return uploads; - } - - public void setUploads(byte[] uploads) { - this.uploads = uploads; - } - - public String getUri() { - return uri; - } - - public void setUri(String uri) { - this.uri = uri; - } - - @Override - public String toString() { - return JsonFactory.root().getConvert().convertTo(this); - } -} +package org.redkale.test.rest; + +import java.util.Map; +import javax.persistence.Id; +import org.redkale.convert.json.JsonFactory; +import org.redkale.net.http.*; +import org.redkale.source.VirtualEntity; + +@VirtualEntity +public class HelloEntity { + + @Id + private int helloid; + + private String helloname; + + private int creator; + + private long updatetime; + + private long createtime; + + @RestHeader(name = "hello-res") + private String resname; + + @RestBody + private String bodystr; + + @RestBody + private byte[] bodys; + + @RestUploadFile + private byte[] uploads; + + @RestBody + private Map bodymap; + + @RestAddress + private String clientaddr; + + @RestURI + private String uri; + + public HelloEntity() { + } + + public HelloEntity(int id) { + this.helloid = id; + } + + /** 以下省略getter setter方法 */ + public int getHelloid() { + return helloid; + } + + public void setHelloid(int helloid) { + this.helloid = helloid; + } + + public String getHelloname() { + return helloname; + } + + public void setHelloname(String helloname) { + this.helloname = helloname; + } + + public long getUpdatetime() { + return updatetime; + } + + public void setUpdatetime(long updatetime) { + this.updatetime = updatetime; + } + + public long getCreatetime() { + return createtime; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } + + public int getCreator() { + return creator; + } + + public void setCreator(int creator) { + this.creator = creator; + } + + public String getClientaddr() { + return clientaddr; + } + + public void setClientaddr(String clientaddr) { + this.clientaddr = clientaddr; + } + + public String getResname() { + return resname; + } + + public void setResname(String resname) { + this.resname = resname; + } + + public String getBodystr() { + return bodystr; + } + + public void setBodystr(String bodystr) { + this.bodystr = bodystr; + } + + public byte[] getBodys() { + return bodys; + } + + public void setBodys(byte[] bodys) { + this.bodys = bodys; + } + + public Map getBodymap() { + return bodymap; + } + + public void setBodymap(Map bodymap) { + this.bodymap = bodymap; + } + + public byte[] getUploads() { + return uploads; + } + + public void setUploads(byte[] uploads) { + this.uploads = uploads; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + @Override + public String toString() { + return JsonFactory.root().getConvert().convertTo(this); + } +} diff --git a/test/org/redkale/test/rest/HelloService.java b/src/test/java/org/redkale/test/rest/HelloService.java similarity index 97% rename from test/org/redkale/test/rest/HelloService.java rename to src/test/java/org/redkale/test/rest/HelloService.java index da7cd0037..acd240c3f 100644 --- a/test/org/redkale/test/rest/HelloService.java +++ b/src/test/java/org/redkale/test/rest/HelloService.java @@ -1,124 +1,124 @@ -package org.redkale.test.rest; - -import java.nio.channels.CompletionHandler; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Resource; -import org.redkale.net.http.*; - -import org.redkale.service.*; -import org.redkale.source.DataSource; -import org.redkale.source.Flipper; -import org.redkale.util.*; - -/** - * 类说明: - * Flipper : Source组件中的翻页对象 - * UserInfo :当前用户类 - * HelloEntity: Hello模块的实体类 - * HelloBean: Hello模块实现FilterBean的过滤Bean类 - * - */ -@RestService(automapping = true) -public class HelloService implements Service { - - private int nodeid; - - @Resource - private DataSource source; - - public HelloService() { - } - - public HelloService(int nodeid) { - this.nodeid = nodeid; - } - - //增加记录 - public RetResult createHello(UserInfo info, HelloEntity entity, @RestBody Map body) { - System.out.println("增加记录----------------" + nodeid + ": body =" + body + ", entity =" + entity); - entity.setCreator(info == null ? 0 : info.getUserid()); //设置当前用户ID - entity.setCreatetime(System.currentTimeMillis()); - if (source != null) source.insert(entity); - return new RetResult<>(entity); - } - - // - public HttpResult showHello(int id) { - return new HttpResult("a"); - } - - //删除记录 - public void deleteHello(int id) { //通过 /pipes/hello/delete/1234 删除对象 - source.delete(HelloEntity.class, id); - } - - //修改记录 - public void updateHello(@RestAddress String clientAddr, HelloEntity entity) { //通过 /pipes/hello/update?bean={...} 修改对象 - System.out.println("修改记录-" + nodeid + ": clientAddr = " + clientAddr + ", entity =" + entity); - if (entity != null) entity.setUpdatetime(System.currentTimeMillis()); - if (source != null) source.update(entity); - } - - //修改记录 - public void update2Hello(@RestAddress String clientAddr, @RestUploadFile byte[] fs) { //通过 /pipes/hello/update2?bean={...} 修改对象 - System.out.println("修改记录2-" + nodeid + ": clientAddr = " + clientAddr + ", fs =" + fs); - } - - //修改记录 - @RestMapping(name = "partupdate") - public void updateHello(HelloEntity entity, @RestParam(name = "cols") String[] columns) { //通过 /pipes/hello/partupdate?bean={...}&cols=... 修改对象 - entity.setUpdatetime(System.currentTimeMillis()); - source.updateColumn(entity, columns); - } - - //查询Sheet列表 - public Sheet queryHello(HelloBean bean, Flipper flipper) { //通过 /pipes/hello/query/offset:0/limit:20?bean={...} 查询Sheet列表 - return source.querySheet(HelloEntity.class, flipper, bean); - } - - //查询List列表 - @RestMapping(name = "list") - @RestConvert(type = HelloEntity.class, ignoreColumns = {"createtime"}) - public List queryHello(HelloBean bean) { //通过 /pipes/hello/list?bean={...} 查询List列表 - return source.queryList(HelloEntity.class, bean); - } - - //查询List列表 - @RestMapping(name = "listmap") - public List queryHello(HelloBean bean, @RestParam(name = "map") Map map) { //通过 /pipes/hello/list?bean={...} 查询List列表 - System.out.println("map参数: " + map); - if (source != null) return source.queryList(HelloEntity.class, bean); - return new ArrayList<>(); - } - - //查询单个 - @RestMapping(name = "find") - public HelloEntity findHello(@RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 - return source.find(HelloEntity.class, id); - } - - //异步查询单个 - @RestMapping(name = "asyncfind") - public CompletableFuture asyncFindHello(@RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 - if (source != null) source.findAsync(HelloEntity.class, id); - System.out.println("------------进入asyncfind1-------"); - return CompletableFuture.completedFuture(new HelloEntity()); - } - - //异步查询单个 - @RestMapping(name = "asyncfind2") - public void asyncFindHello(CompletionHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 - if (source != null) source.findAsync(HelloEntity.class, id); - System.out.println("-----------进入asyncfind2--------" + hander); - hander.completed(new HelloEntity(id), id); - } - - //异步查询单个 - @RestMapping(name = "asyncfind3") - public void asyncFindHello(HelloAsyncHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 - if (source != null) source.findAsync(HelloEntity.class, id); - System.out.println("-----------进入asyncfind3--------" + hander); - hander.completed(new HelloEntity(id), id); - } -} +package org.redkale.test.rest; + +import java.nio.channels.CompletionHandler; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Resource; +import org.redkale.net.http.*; + +import org.redkale.service.*; +import org.redkale.source.DataSource; +import org.redkale.source.Flipper; +import org.redkale.util.*; + +/** + * 类说明: + * Flipper : Source组件中的翻页对象 + * UserInfo :当前用户类 + * HelloEntity: Hello模块的实体类 + * HelloBean: Hello模块实现FilterBean的过滤Bean类 + * + */ +@RestService(automapping = true) +public class HelloService implements Service { + + private int nodeid; + + @Resource + private DataSource source; + + public HelloService() { + } + + public HelloService(int nodeid) { + this.nodeid = nodeid; + } + + //增加记录 + public RetResult createHello(UserInfo info, HelloEntity entity, @RestBody Map body) { + System.out.println("增加记录----------------" + nodeid + ": body =" + body + ", entity =" + entity); + entity.setCreator(info == null ? 0 : info.getUserid()); //设置当前用户ID + entity.setCreatetime(System.currentTimeMillis()); + if (source != null) source.insert(entity); + return new RetResult<>(entity); + } + + // + public HttpResult showHello(int id) { + return new HttpResult("a"); + } + + //删除记录 + public void deleteHello(int id) { //通过 /pipes/hello/delete/1234 删除对象 + source.delete(HelloEntity.class, id); + } + + //修改记录 + public void updateHello(@RestAddress String clientAddr, HelloEntity entity) { //通过 /pipes/hello/update?bean={...} 修改对象 + System.out.println("修改记录-" + nodeid + ": clientAddr = " + clientAddr + ", entity =" + entity); + if (entity != null) entity.setUpdatetime(System.currentTimeMillis()); + if (source != null) source.update(entity); + } + + //修改记录 + public void update2Hello(@RestAddress String clientAddr, @RestUploadFile byte[] fs) { //通过 /pipes/hello/update2?bean={...} 修改对象 + System.out.println("修改记录2-" + nodeid + ": clientAddr = " + clientAddr + ", fs =" + fs); + } + + //修改记录 + @RestMapping(name = "partupdate") + public void updateHello(HelloEntity entity, @RestParam(name = "cols") String[] columns) { //通过 /pipes/hello/partupdate?bean={...}&cols=... 修改对象 + entity.setUpdatetime(System.currentTimeMillis()); + source.updateColumn(entity, columns); + } + + //查询Sheet列表 + public Sheet queryHello(HelloBean bean, Flipper flipper) { //通过 /pipes/hello/query/offset:0/limit:20?bean={...} 查询Sheet列表 + return source.querySheet(HelloEntity.class, flipper, bean); + } + + //查询List列表 + @RestMapping(name = "list") + @RestConvert(type = HelloEntity.class, ignoreColumns = {"createtime"}) + public List queryHello(HelloBean bean) { //通过 /pipes/hello/list?bean={...} 查询List列表 + return source.queryList(HelloEntity.class, bean); + } + + //查询List列表 + @RestMapping(name = "listmap") + public List queryHello(HelloBean bean, @RestParam(name = "map") Map map) { //通过 /pipes/hello/list?bean={...} 查询List列表 + System.out.println("map参数: " + map); + if (source != null) return source.queryList(HelloEntity.class, bean); + return new ArrayList<>(); + } + + //查询单个 + @RestMapping(name = "find") + public HelloEntity findHello(@RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 + return source.find(HelloEntity.class, id); + } + + //异步查询单个 + @RestMapping(name = "asyncfind") + public CompletableFuture asyncFindHello(@RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 + if (source != null) source.findAsync(HelloEntity.class, id); + System.out.println("------------进入asyncfind1-------"); + return CompletableFuture.completedFuture(new HelloEntity()); + } + + //异步查询单个 + @RestMapping(name = "asyncfind2") + public void asyncFindHello(CompletionHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 + if (source != null) source.findAsync(HelloEntity.class, id); + System.out.println("-----------进入asyncfind2--------" + hander); + hander.completed(new HelloEntity(id), id); + } + + //异步查询单个 + @RestMapping(name = "asyncfind3") + public void asyncFindHello(HelloAsyncHandler hander, @RestParam(name = "#") int id) { //通过 /pipes/hello/find/1234、/pipes/hello/jsfind/1234 查询对象 + if (source != null) source.findAsync(HelloEntity.class, id); + System.out.println("-----------进入asyncfind3--------" + hander); + hander.completed(new HelloEntity(id), id); + } +} diff --git a/test/org/redkale/test/rest/HelloService2.java b/src/test/java/org/redkale/test/rest/HelloService2.java similarity index 97% rename from test/org/redkale/test/rest/HelloService2.java rename to src/test/java/org/redkale/test/rest/HelloService2.java index 0d89bb8e1..b7f313765 100644 --- a/test/org/redkale/test/rest/HelloService2.java +++ b/src/test/java/org/redkale/test/rest/HelloService2.java @@ -1,62 +1,62 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.rest; - -import javax.annotation.Resource; -import org.redkale.net.http.*; -import org.redkale.service.*; -import org.redkale.source.*; -import org.redkale.util.Sheet; - -/** - * 类说明: - * Flipper : Source组件中的翻页对象 - * UserInfo :当前用户类 - * HelloEntity: Hello模块的实体类 - * HelloBean: Hello模块实现FilterBean的过滤Bean类 - * - */ -@RestService(name = "hello", moduleid = 0, automapping = true, repair = true, ignore = false, comment = "Hello服务模块") -public class HelloService2 implements Service { - - @Resource - private DataSource source; - - //增加记录 - @RestMapping(name = "create", auth = false, comment = "创建Hello对象") - public RetResult createHello(UserInfo info, @RestParam(name = "bean", comment = "Hello对象") HelloEntity entity) { - entity.setCreator(info == null ? 0 : info.getUserid()); //设置当前用户ID - entity.setCreatetime(System.currentTimeMillis()); - source.insert(entity); - return new RetResult<>(entity); - } - - //删除记录 - @RestMapping(name = "delete", auth = false, comment = "根据id删除Hello对象") - public void deleteHello(@RestParam(name = "#", comment = "Hello对象id") int id) { //通过 /hello/delete/1234 删除对象 - source.delete(HelloEntity.class, id); - } - - //修改记录 - @RestMapping(name = "update", auth = false, comment = "修改Hello对象") - public void updateHello(@RestParam(name = "bean", comment = "Hello对象") HelloEntity entity) { //通过 /hello/update?bean={...} 修改对象 - entity.setUpdatetime(System.currentTimeMillis()); - source.update(entity); - } - - //查询列表 - @RestConvertCoder(type = HelloEntity.class, field = "createtime", coder = CreateTimeSimpleCoder.class) - @RestMapping(name = "query", auth = false, comment = "查询Hello对象列表") - public Sheet queryHello(@RestParam(name = "bean", comment = "过滤条件") HelloBean bean, Flipper flipper) { //通过 /hello/query/offset:0/limit:20?bean={...} 查询列表 - return source.querySheet(HelloEntity.class, flipper, bean); - } - - //查询单个 - @RestMapping(name = "find", auth = false, comment = "根据id查找单个Hello对象") - public HelloEntity findHello(@RestParam(name = "#", comment = "Hello对象id") int id) { //通过 /hello/find/1234 查询对象 - return source.find(HelloEntity.class, id); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import javax.annotation.Resource; +import org.redkale.net.http.*; +import org.redkale.service.*; +import org.redkale.source.*; +import org.redkale.util.Sheet; + +/** + * 类说明: + * Flipper : Source组件中的翻页对象 + * UserInfo :当前用户类 + * HelloEntity: Hello模块的实体类 + * HelloBean: Hello模块实现FilterBean的过滤Bean类 + * + */ +@RestService(name = "hello", moduleid = 0, automapping = true, repair = true, ignore = false, comment = "Hello服务模块") +public class HelloService2 implements Service { + + @Resource + private DataSource source; + + //增加记录 + @RestMapping(name = "create", auth = false, comment = "创建Hello对象") + public RetResult createHello(UserInfo info, @RestParam(name = "bean", comment = "Hello对象") HelloEntity entity) { + entity.setCreator(info == null ? 0 : info.getUserid()); //设置当前用户ID + entity.setCreatetime(System.currentTimeMillis()); + source.insert(entity); + return new RetResult<>(entity); + } + + //删除记录 + @RestMapping(name = "delete", auth = false, comment = "根据id删除Hello对象") + public void deleteHello(@RestParam(name = "#", comment = "Hello对象id") int id) { //通过 /hello/delete/1234 删除对象 + source.delete(HelloEntity.class, id); + } + + //修改记录 + @RestMapping(name = "update", auth = false, comment = "修改Hello对象") + public void updateHello(@RestParam(name = "bean", comment = "Hello对象") HelloEntity entity) { //通过 /hello/update?bean={...} 修改对象 + entity.setUpdatetime(System.currentTimeMillis()); + source.update(entity); + } + + //查询列表 + @RestConvertCoder(type = HelloEntity.class, field = "createtime", coder = CreateTimeSimpleCoder.class) + @RestMapping(name = "query", auth = false, comment = "查询Hello对象列表") + public Sheet queryHello(@RestParam(name = "bean", comment = "过滤条件") HelloBean bean, Flipper flipper) { //通过 /hello/query/offset:0/limit:20?bean={...} 查询列表 + return source.querySheet(HelloEntity.class, flipper, bean); + } + + //查询单个 + @RestMapping(name = "find", auth = false, comment = "根据id查找单个Hello对象") + public HelloEntity findHello(@RestParam(name = "#", comment = "Hello对象id") int id) { //通过 /hello/find/1234 查询对象 + return source.find(HelloEntity.class, id); + } +} diff --git a/test/org/redkale/test/rest/LoginBean.java b/src/test/java/org/redkale/test/rest/LoginBean.java similarity index 95% rename from test/org/redkale/test/rest/LoginBean.java rename to src/test/java/org/redkale/test/rest/LoginBean.java index d2ad11af9..9603ae467 100644 --- a/test/org/redkale/test/rest/LoginBean.java +++ b/src/test/java/org/redkale/test/rest/LoginBean.java @@ -1,40 +1,40 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.rest; - -import org.redkale.convert.json.JsonFactory; - -/** - * - * @author zhangjx - */ -public class LoginBean { - - private String account = ""; - - private String password = ""; - - public String getAccount() { - return account; - } - - public void setAccount(String account) { - this.account = account; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - @Override - public String toString() { - return JsonFactory.root().getConvert().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import org.redkale.convert.json.JsonFactory; + +/** + * + * @author zhangjx + */ +public class LoginBean { + + private String account = ""; + + private String password = ""; + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + return JsonFactory.root().getConvert().convertTo(this); + } +} diff --git a/test/org/redkale/test/rest/RetCodes.java b/src/test/java/org/redkale/test/rest/RetCodes.java similarity index 97% rename from test/org/redkale/test/rest/RetCodes.java rename to src/test/java/org/redkale/test/rest/RetCodes.java index 6f663c3c3..83fc9ec19 100644 --- a/test/org/redkale/test/rest/RetCodes.java +++ b/src/test/java/org/redkale/test/rest/RetCodes.java @@ -1,163 +1,163 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.rest; - -import java.lang.reflect.*; -import java.util.*; -import org.redkale.service.*; - -/** - * - * @author zhangjx - */ -public abstract class RetCodes { - - protected static final Map rets = new HashMap<>(); - - - protected RetCodes() { - } - //----------------------------------------------------------------------------------------------------------- - - protected static void load(Class clazz) { - for (Field field : clazz.getFields()) { - if (!Modifier.isStatic(field.getModifiers())) continue; - if (field.getType() != int.class) continue; - RetLabel info = field.getAnnotation(RetLabel.class); - if (info == null) continue; - int value; - try { - value = field.getInt(null); - } catch (Exception ex) { - ex.printStackTrace(); - continue; - } - rets.put(value, info.value()); - } - } - - public static RetResult retResult(int retcode) { - if (retcode == 0) return RetResult.success(); - return new RetResult(retcode, retInfo(retcode)); - } - - public static String retInfo(int retcode) { - if (retcode == 0) return "成功"; - return rets.getOrDefault(retcode, "未知错误"); - } - - //2000_0001 - 2999_9999 预留给 Redkale的扩展包redkalex使用 - //3000_0001 - 7999_9999 为平台系统使用 - //8000_0001 - 9999_9999 为OSS系统使用 - //------------------------------------- 通用模块 ----------------------------------------- - @RetLabel("参数无效") - public static final int RET_PARAMS_ILLEGAL = 30010001; - - @RetLabel("无上传文件") - public static final int RET_UPLOAD_NOFILE = 30010002; - - @RetLabel("上传文件过大") - public static final int RET_UPLOAD_FILETOOBIG = 30010003; - - @RetLabel("上传文件不是图片") - public static final int RET_UPLOAD_NOTIMAGE = 30010004; - - //------------------------------------- 用户模块 ----------------------------------------- - @RetLabel("未登录") - public static final int RET_USER_UNLOGIN = 30020001; - - @RetLabel("用户登录失败") - public static final int RET_USER_LOGIN_FAIL = 30020002; - - @RetLabel("用户或密码错误") - public static final int RET_USER_ACCOUNT_PWD_ILLEGAL = 30020003; - - @RetLabel("密码设置无效") - public static final int RET_USER_PASSWORD_ILLEGAL = 30020004; - - @RetLabel("用户被禁用") - public static final int RET_USER_FREEZED = 30020005; - - @RetLabel("用户权限不够") - public static final int RET_USER_AUTH_ILLEGAL = 30020006; - - @RetLabel("用户不存在") - public static final int RET_USER_NOTEXISTS = 30020007; - - @RetLabel("用户状态异常") - public static final int RET_USER_STATUS_ILLEGAL = 30020008; - - @RetLabel("用户注册参数无效") - public static final int RET_USER_SIGNUP_ILLEGAL = 30020009; - - @RetLabel("用户性别参数无效") - public static final int RET_USER_GENDER_ILLEGAL = 30020010; - - @RetLabel("用户名无效") - public static final int RET_USER_USERNAME_ILLEGAL = 30020011; - - @RetLabel("用户账号无效") - public static final int RET_USER_ACCOUNT_ILLEGAL = 30020012; - - @RetLabel("用户账号已存在") - public static final int RET_USER_ACCOUNT_EXISTS = 30020013; - - @RetLabel("手机号码无效") - public static final int RET_USER_MOBILE_ILLEGAL = 30020014; - - @RetLabel("手机号码已存在") - public static final int RET_USER_MOBILE_EXISTS = 30020015; - - @RetLabel("手机验证码发送过于频繁") - public static final int RET_USER_MOBILE_SMSFREQUENT = 30020016; - - @RetLabel("邮箱地址无效") - public static final int RET_USER_EMAIL_ILLEGAL = 30020017; - - @RetLabel("邮箱地址已存在") - public static final int RET_USER_EMAIL_EXISTS = 30020018; - - @RetLabel("微信绑定号无效") - public static final int RET_USER_WXID_ILLEGAL = 30020019; - - @RetLabel("微信绑定号已存在") - public static final int RET_USER_WXID_EXISTS = 30020020; - - @RetLabel("绑定微信号失败") - public static final int RET_USER_WXID_BIND_FAIL = 30020021; - - @RetLabel("QQ绑定号无效") - public static final int RET_USER_QQID_ILLEGAL = 30020022; - - @RetLabel("QQ绑定号已存在") - public static final int RET_USER_QQID_EXISTS = 30020023; - - @RetLabel("绑定QQ号失败") - public static final int RET_USER_QQID_BIND_FAIL = 30020024; - - @RetLabel("获取绑定QQ信息失败") - public static final int RET_USER_QQID_INFO_FAIL = 30020025; - - @RetLabel("验证码无效") - public static final int RET_USER_RANDCODE_ILLEGAL = 30020026; //邮件或者短信验证码 - - @RetLabel("验证码已过期") - public static final int RET_USER_RANDCODE_EXPIRED = 30020027; //邮件或者短信验证码 - - @RetLabel("验证码错误或失效") - public static final int RET_USER_CAPTCHA_ILLEGAL = 30020028; //图片验证码 - - @RetLabel("用户类型无效") - public static final int RET_USER_TYPE_ILLEGAL = 30020029; - - @RetLabel("用户设备ID无效") - public static final int RET_USER_APPTOKEN_ILLEGAL = 30020030; - - - static { - load(RetCodes.class); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import java.lang.reflect.*; +import java.util.*; +import org.redkale.service.*; + +/** + * + * @author zhangjx + */ +public abstract class RetCodes { + + protected static final Map rets = new HashMap<>(); + + + protected RetCodes() { + } + //----------------------------------------------------------------------------------------------------------- + + protected static void load(Class clazz) { + for (Field field : clazz.getFields()) { + if (!Modifier.isStatic(field.getModifiers())) continue; + if (field.getType() != int.class) continue; + RetLabel info = field.getAnnotation(RetLabel.class); + if (info == null) continue; + int value; + try { + value = field.getInt(null); + } catch (Exception ex) { + ex.printStackTrace(); + continue; + } + rets.put(value, info.value()); + } + } + + public static RetResult retResult(int retcode) { + if (retcode == 0) return RetResult.success(); + return new RetResult(retcode, retInfo(retcode)); + } + + public static String retInfo(int retcode) { + if (retcode == 0) return "成功"; + return rets.getOrDefault(retcode, "未知错误"); + } + + //2000_0001 - 2999_9999 预留给 Redkale的扩展包redkalex使用 + //3000_0001 - 7999_9999 为平台系统使用 + //8000_0001 - 9999_9999 为OSS系统使用 + //------------------------------------- 通用模块 ----------------------------------------- + @RetLabel("参数无效") + public static final int RET_PARAMS_ILLEGAL = 30010001; + + @RetLabel("无上传文件") + public static final int RET_UPLOAD_NOFILE = 30010002; + + @RetLabel("上传文件过大") + public static final int RET_UPLOAD_FILETOOBIG = 30010003; + + @RetLabel("上传文件不是图片") + public static final int RET_UPLOAD_NOTIMAGE = 30010004; + + //------------------------------------- 用户模块 ----------------------------------------- + @RetLabel("未登录") + public static final int RET_USER_UNLOGIN = 30020001; + + @RetLabel("用户登录失败") + public static final int RET_USER_LOGIN_FAIL = 30020002; + + @RetLabel("用户或密码错误") + public static final int RET_USER_ACCOUNT_PWD_ILLEGAL = 30020003; + + @RetLabel("密码设置无效") + public static final int RET_USER_PASSWORD_ILLEGAL = 30020004; + + @RetLabel("用户被禁用") + public static final int RET_USER_FREEZED = 30020005; + + @RetLabel("用户权限不够") + public static final int RET_USER_AUTH_ILLEGAL = 30020006; + + @RetLabel("用户不存在") + public static final int RET_USER_NOTEXISTS = 30020007; + + @RetLabel("用户状态异常") + public static final int RET_USER_STATUS_ILLEGAL = 30020008; + + @RetLabel("用户注册参数无效") + public static final int RET_USER_SIGNUP_ILLEGAL = 30020009; + + @RetLabel("用户性别参数无效") + public static final int RET_USER_GENDER_ILLEGAL = 30020010; + + @RetLabel("用户名无效") + public static final int RET_USER_USERNAME_ILLEGAL = 30020011; + + @RetLabel("用户账号无效") + public static final int RET_USER_ACCOUNT_ILLEGAL = 30020012; + + @RetLabel("用户账号已存在") + public static final int RET_USER_ACCOUNT_EXISTS = 30020013; + + @RetLabel("手机号码无效") + public static final int RET_USER_MOBILE_ILLEGAL = 30020014; + + @RetLabel("手机号码已存在") + public static final int RET_USER_MOBILE_EXISTS = 30020015; + + @RetLabel("手机验证码发送过于频繁") + public static final int RET_USER_MOBILE_SMSFREQUENT = 30020016; + + @RetLabel("邮箱地址无效") + public static final int RET_USER_EMAIL_ILLEGAL = 30020017; + + @RetLabel("邮箱地址已存在") + public static final int RET_USER_EMAIL_EXISTS = 30020018; + + @RetLabel("微信绑定号无效") + public static final int RET_USER_WXID_ILLEGAL = 30020019; + + @RetLabel("微信绑定号已存在") + public static final int RET_USER_WXID_EXISTS = 30020020; + + @RetLabel("绑定微信号失败") + public static final int RET_USER_WXID_BIND_FAIL = 30020021; + + @RetLabel("QQ绑定号无效") + public static final int RET_USER_QQID_ILLEGAL = 30020022; + + @RetLabel("QQ绑定号已存在") + public static final int RET_USER_QQID_EXISTS = 30020023; + + @RetLabel("绑定QQ号失败") + public static final int RET_USER_QQID_BIND_FAIL = 30020024; + + @RetLabel("获取绑定QQ信息失败") + public static final int RET_USER_QQID_INFO_FAIL = 30020025; + + @RetLabel("验证码无效") + public static final int RET_USER_RANDCODE_ILLEGAL = 30020026; //邮件或者短信验证码 + + @RetLabel("验证码已过期") + public static final int RET_USER_RANDCODE_EXPIRED = 30020027; //邮件或者短信验证码 + + @RetLabel("验证码错误或失效") + public static final int RET_USER_CAPTCHA_ILLEGAL = 30020028; //图片验证码 + + @RetLabel("用户类型无效") + public static final int RET_USER_TYPE_ILLEGAL = 30020029; + + @RetLabel("用户设备ID无效") + public static final int RET_USER_APPTOKEN_ILLEGAL = 30020030; + + + static { + load(RetCodes.class); + } +} diff --git a/test/org/redkale/test/rest/SimpleRestServlet.java b/src/test/java/org/redkale/test/rest/SimpleRestServlet.java similarity index 96% rename from test/org/redkale/test/rest/SimpleRestServlet.java rename to src/test/java/org/redkale/test/rest/SimpleRestServlet.java index 4c572b244..0a7ca82a8 100644 --- a/test/org/redkale/test/rest/SimpleRestServlet.java +++ b/src/test/java/org/redkale/test/rest/SimpleRestServlet.java @@ -1,39 +1,39 @@ -package org.redkale.test.rest; - -import java.io.IOException; -import javax.annotation.Resource; - -import org.redkale.net.http.*; -import org.redkale.service.RetResult; - -public class SimpleRestServlet extends HttpServlet { - - protected static final RetResult RET_UNLOGIN = RetCodes.retResult(RetCodes.RET_USER_UNLOGIN); - - protected static final RetResult RET_AUTHILLEGAL = RetCodes.retResult(RetCodes.RET_USER_AUTH_ILLEGAL); - - @Resource - private UserService userService = new UserService(); - - @Override - public void preExecute(HttpRequest request, HttpResponse response) throws IOException { - final String sessionid = request.getSessionid(true); - if (sessionid != null) { - UserInfo user = userService.current(sessionid); - if (user != null) request.setCurrentUserid(user.getUserid()); - } - response.nextEvent(); - } - - //普通鉴权 - @Override - public void authenticate(HttpRequest request, HttpResponse response) throws IOException { - int userid = request.currentUserid(int.class); - if (userid < 1) { - response.finishJson(RET_UNLOGIN); - return; - } - response.nextEvent(); - } - -} +package org.redkale.test.rest; + +import java.io.IOException; +import javax.annotation.Resource; + +import org.redkale.net.http.*; +import org.redkale.service.RetResult; + +public class SimpleRestServlet extends HttpServlet { + + protected static final RetResult RET_UNLOGIN = RetCodes.retResult(RetCodes.RET_USER_UNLOGIN); + + protected static final RetResult RET_AUTHILLEGAL = RetCodes.retResult(RetCodes.RET_USER_AUTH_ILLEGAL); + + @Resource + private UserService userService = new UserService(); + + @Override + public void preExecute(HttpRequest request, HttpResponse response) throws IOException { + final String sessionid = request.getSessionid(true); + if (sessionid != null) { + UserInfo user = userService.current(sessionid); + if (user != null) request.setCurrentUserid(user.getUserid()); + } + response.nextEvent(); + } + + //普通鉴权 + @Override + public void authenticate(HttpRequest request, HttpResponse response) throws IOException { + int userid = request.currentUserid(int.class); + if (userid < 1) { + response.finishJson(RET_UNLOGIN); + return; + } + response.nextEvent(); + } + +} diff --git a/test/org/redkale/test/rest/UserInfo.java b/src/test/java/org/redkale/test/rest/UserInfo.java similarity index 94% rename from test/org/redkale/test/rest/UserInfo.java rename to src/test/java/org/redkale/test/rest/UserInfo.java index 3f9123e2e..5ce449524 100644 --- a/test/org/redkale/test/rest/UserInfo.java +++ b/src/test/java/org/redkale/test/rest/UserInfo.java @@ -1,44 +1,44 @@ -package org.redkale.test.rest; - -import javax.persistence.Id; -import org.redkale.convert.json.JsonFactory; - -/** - * 当前用户对象 - * - * @author zhangjx - */ -public class UserInfo { - - @Id - private int userid; - - private String username = ""; - - public int getUserid() { - return userid; - } - - public boolean checkAuth(int moduleid, int actionid) { - if (moduleid == 0 || actionid == 0) return true; - //权限判断 - return true; - } - - public void setUserid(int userid) { - this.userid = userid; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - @Override - public String toString() { - return JsonFactory.root().getConvert().convertTo(this); - } -} +package org.redkale.test.rest; + +import javax.persistence.Id; +import org.redkale.convert.json.JsonFactory; + +/** + * 当前用户对象 + * + * @author zhangjx + */ +public class UserInfo { + + @Id + private int userid; + + private String username = ""; + + public int getUserid() { + return userid; + } + + public boolean checkAuth(int moduleid, int actionid) { + if (moduleid == 0 || actionid == 0) return true; + //权限判断 + return true; + } + + public void setUserid(int userid) { + this.userid = userid; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String toString() { + return JsonFactory.root().getConvert().convertTo(this); + } +} diff --git a/test/org/redkale/test/rest/UserService.java b/src/test/java/org/redkale/test/rest/UserService.java similarity index 96% rename from test/org/redkale/test/rest/UserService.java rename to src/test/java/org/redkale/test/rest/UserService.java index ed1154a42..ddad0d81e 100644 --- a/test/org/redkale/test/rest/UserService.java +++ b/src/test/java/org/redkale/test/rest/UserService.java @@ -1,25 +1,25 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.rest; - -import org.redkale.service.*; - -/** - * 简单的定义UserService接口 - * - * @author zhangjx - */ -public class UserService implements Service { - - //根据登录态获取当前用户信息 - public UserInfo current(String sessionid) { - return new UserInfo(); - } - - public RetResult login(LoginBean bean) { - return new RetResult<>(new UserInfo()); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import org.redkale.service.*; + +/** + * 简单的定义UserService接口 + * + * @author zhangjx + */ +public class UserService implements Service { + + //根据登录态获取当前用户信息 + public UserInfo current(String sessionid) { + return new UserInfo(); + } + + public RetResult login(LoginBean bean) { + return new RetResult<>(new UserInfo()); + } +} diff --git a/test/org/redkale/test/rest/_DynHelloRestServlet1.java b/src/test/java/org/redkale/test/rest/_DynHelloRestServlet1.java similarity index 97% rename from test/org/redkale/test/rest/_DynHelloRestServlet1.java rename to src/test/java/org/redkale/test/rest/_DynHelloRestServlet1.java index b48ac9bb0..b7593a78d 100644 --- a/test/org/redkale/test/rest/_DynHelloRestServlet1.java +++ b/src/test/java/org/redkale/test/rest/_DynHelloRestServlet1.java @@ -1,156 +1,156 @@ -package org.redkale.test.rest; - -import java.io.IOException; -import java.util.*; -import javax.annotation.Resource; -import org.redkale.net.http.*; -import org.redkale.service.RetResult; -import org.redkale.source.Flipper; -import org.redkale.util.*; -import org.redkale.util.AnyValue.DefaultAnyValue; - -@WebServlet(value = {"/hello/*"}, repair = true) -public class _DynHelloRestServlet1 extends SimpleRestServlet { - - @Resource - private HelloService _redkale_service; - - @Resource - private Map _redkale_servicemap; - - public static void main(String[] args) throws Throwable { - final int port = 8888; - HelloService service = new HelloService(); - HttpServer server = new HttpServer(); - - System.out.println(server.addRestServlet(null, service, null, SimpleRestServlet.class, "/pipes")); - System.out.println(server.addRestServlet(null, new HelloService(3), null, SimpleRestServlet.class, "/pipes")); - - DefaultAnyValue conf = DefaultAnyValue.create("port", "" + port); - server.init(conf); - server.start(null); - Thread.sleep(100); - - HelloEntity entity = new HelloEntity(); - entity.setHelloname("my name"); - Map headers = new HashMap<>(); - headers.put("hello-res", "my res"); - //headers.put(Rest.REST_HEADER_RESOURCE_NAME, "my-res"); - String url = "http://127.0.0.1:" + port + "/pipes/hello/update?entity={}&bean2={}"; - System.out.println(Utility.postHttpContent(url, headers, null)); - url = "http://127.0.0.1:" + port + "/pipes/hello/update2?entity={}&bean2={}"; - System.out.println(Utility.postHttpContent(url, headers, null)); - - url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind/1234"; - System.out.println("异步查找: " + Utility.postHttpContent(url, headers, null)); - - url = "http://127.0.0.1:" + port + "/pipes/hello/listmap?map={'a':5}"; - System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); - - url = "http://127.0.0.1:" + port + "/pipes/hello/create?entity={}"; - System.out.println("增加记录: " + Utility.postHttpContent(url, headers, "{'a':2,'b':3}")); - - url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind/111111"; - System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); - url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind2/22222"; - System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); - url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind3/333333"; - System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); - - } - - @HttpMapping(url = "/hello/create", auth = false) - public void create(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setResname(req.getHeader("hello-res")); - UserInfo user = req.currentUser(); - RetResult result = service.createHello(user, bean, req.getBodyJson(Map.class)); - resp.finishJson(result); - } - - @HttpMapping(url = "/hello/delete/", auth = false) - public void delete(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - int id = Integer.parseInt(req.getRequstURILastPath()); - service.deleteHello(id); - resp.finishJson(RetResult.success()); - } - - @HttpMapping(url = "/hello/update", auth = false) - public void update(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - String clientaddr = req.getRemoteAddr(); - HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setResname(req.getHeader("hello-res")); - service.updateHello(clientaddr, bean); - resp.finishJson(RetResult.success()); - } - - @HttpMapping(url = "/hello/partupdate", auth = false) - public void partupdate(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setResname(req.getHeader("hello-res")); - String[] cols = req.getJsonParameter(String[].class, "cols"); - service.updateHello(bean, cols); - resp.finishJson(RetResult.success()); - } - - @HttpMapping(url = "/hello/query", auth = false) - public void query(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setUseragent(req.getHeader("User-Agent")); - bean.setRescookie(req.getCookie("hello-cookie")); - bean.setSessionid(req.getSessionid(false)); - Flipper flipper = req.getFlipper(); - Sheet result = service.queryHello(bean, flipper); - resp.finishJson(result); - } - - @HttpMapping(url = "/hello/list", auth = false) - public void list(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setUseragent(req.getHeader("User-Agent")); - bean.setRescookie(req.getCookie("hello-cookie")); - bean.setSessionid(req.getSessionid(false)); - List result = service.queryHello(bean); - resp.finishJson(result); - } - - @HttpMapping(url = "/hello/find/", auth = false) - public void find(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - int id = Integer.parseInt(req.getRequstURILastPath()); - HelloEntity bean = service.findHello(id); - resp.finishJson(bean); - } - - @HttpMapping(url = "/hello/asyncfind/", auth = false) - public void asyncfind(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - int id = Integer.parseInt(req.getRequstURILastPath()); - resp.finishJson(service.asyncFindHello(id)); - } - - @HttpMapping(url = "/hello/asyncfind2/", auth = false) - public void asyncfind2(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - int id = Integer.parseInt(req.getRequstURILastPath()); - service.asyncFindHello(resp.createAsyncHandler(), id); - } - - @HttpMapping(url = "/hello/asyncfind3/", auth = false) - public void asyncfind3(HttpRequest req, HttpResponse resp) throws IOException { - HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - int id = Integer.parseInt(req.getRequstURILastPath()); - service.asyncFindHello(resp.createAsyncHandler(HelloAsyncHandler.class), id); - } -} +package org.redkale.test.rest; + +import java.io.IOException; +import java.util.*; +import javax.annotation.Resource; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import org.redkale.source.Flipper; +import org.redkale.util.*; +import org.redkale.util.AnyValue.DefaultAnyValue; + +@WebServlet(value = {"/hello/*"}, repair = true) +public class _DynHelloRestServlet1 extends SimpleRestServlet { + + @Resource + private HelloService _redkale_service; + + @Resource + private Map _redkale_servicemap; + + public static void main(String[] args) throws Throwable { + final int port = 8888; + HelloService service = new HelloService(); + HttpServer server = new HttpServer(); + + System.out.println(server.addRestServlet(null, service, null, SimpleRestServlet.class, "/pipes")); + System.out.println(server.addRestServlet(null, new HelloService(3), null, SimpleRestServlet.class, "/pipes")); + + DefaultAnyValue conf = DefaultAnyValue.create("port", "" + port); + server.init(conf); + server.start(); + Thread.sleep(100); + + HelloEntity entity = new HelloEntity(); + entity.setHelloname("my name"); + Map headers = new HashMap<>(); + headers.put("hello-res", "my res"); + //headers.put(Rest.REST_HEADER_RESOURCE_NAME, "my-res"); + String url = "http://127.0.0.1:" + port + "/pipes/hello/update?entity={}&bean2={}"; + System.out.println(Utility.postHttpContent(url, headers, null)); + url = "http://127.0.0.1:" + port + "/pipes/hello/update2?entity={}&bean2={}"; + System.out.println(Utility.postHttpContent(url, headers, null)); + + url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind/1234"; + System.out.println("异步查找: " + Utility.postHttpContent(url, headers, null)); + + url = "http://127.0.0.1:" + port + "/pipes/hello/listmap?map={'a':5}"; + System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + + url = "http://127.0.0.1:" + port + "/pipes/hello/create?entity={}"; + System.out.println("增加记录: " + Utility.postHttpContent(url, headers, "{'a':2,'b':3}")); + + url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind/111111"; + System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind2/22222"; + System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + url = "http://127.0.0.1:" + port + "/pipes/hello/asyncfind3/333333"; + System.out.println("listmap: " + Utility.postHttpContent(url, headers, null)); + + } + + @HttpMapping(url = "/hello/create", auth = false) + public void create(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setResname(req.getHeader("hello-res")); + UserInfo user = new UserInfo(); + RetResult result = service.createHello(user, bean, req.getBodyJson(Map.class)); + resp.finishJson(result); + } + + @HttpMapping(url = "/hello/delete/", auth = false) + public void delete(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + service.deleteHello(id); + resp.finishJson(RetResult.success()); + } + + @HttpMapping(url = "/hello/update", auth = false) + public void update(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + String clientaddr = req.getRemoteAddr(); + HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setResname(req.getHeader("hello-res")); + service.updateHello(clientaddr, bean); + resp.finishJson(RetResult.success()); + } + + @HttpMapping(url = "/hello/partupdate", auth = false) + public void partupdate(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setResname(req.getHeader("hello-res")); + String[] cols = req.getJsonParameter(String[].class, "cols"); + service.updateHello(bean, cols); + resp.finishJson(RetResult.success()); + } + + @HttpMapping(url = "/hello/query", auth = false) + public void query(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setUseragent(req.getHeader("User-Agent")); + bean.setRescookie(req.getCookie("hello-cookie")); + bean.setSessionid(req.getSessionid(false)); + Flipper flipper = req.getFlipper(); + Sheet result = service.queryHello(bean, flipper); + resp.finishJson(result); + } + + @HttpMapping(url = "/hello/list", auth = false) + public void list(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setUseragent(req.getHeader("User-Agent")); + bean.setRescookie(req.getCookie("hello-cookie")); + bean.setSessionid(req.getSessionid(false)); + List result = service.queryHello(bean); + resp.finishJson(result); + } + + @HttpMapping(url = "/hello/find/", auth = false) + public void find(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + HelloEntity bean = service.findHello(id); + resp.finishJson(bean); + } + + @HttpMapping(url = "/hello/asyncfind/", auth = false) + public void asyncfind(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + resp.finishJson(service.asyncFindHello(id)); + } + + @HttpMapping(url = "/hello/asyncfind2/", auth = false) + public void asyncfind2(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + service.asyncFindHello(resp.createAsyncHandler(), id); + } + + @HttpMapping(url = "/hello/asyncfind3/", auth = false) + public void asyncfind3(HttpRequest req, HttpResponse resp) throws IOException { + HelloService service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + service.asyncFindHello(resp.createAsyncHandler(HelloAsyncHandler.class), id); + } +} diff --git a/test/org/redkale/test/rest/_DynHelloRestServlet2.java b/src/test/java/org/redkale/test/rest/_DynHelloRestServlet2.java similarity index 96% rename from test/org/redkale/test/rest/_DynHelloRestServlet2.java rename to src/test/java/org/redkale/test/rest/_DynHelloRestServlet2.java index a99f78e69..1f0acc1b9 100644 --- a/test/org/redkale/test/rest/_DynHelloRestServlet2.java +++ b/src/test/java/org/redkale/test/rest/_DynHelloRestServlet2.java @@ -1,87 +1,87 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.rest; - -import java.io.IOException; -import java.util.Map; -import javax.annotation.Resource; -import org.redkale.net.http.*; -import org.redkale.service.RetResult; -import org.redkale.source.Flipper; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -@WebServlet(value = {"/hello/*"}, repair = true) -public class _DynHelloRestServlet2 extends SimpleRestServlet { - - @Resource - private HelloService2 _redkale_service; - - @Resource - private Map _redkale_servicemap; - - @HttpMapping(url = "/hello/create", auth = false, comment = "创建Hello对象") - @HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") - public void create(HttpRequest req, HttpResponse resp) throws IOException { - HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setResname(req.getHeader("hello-res")); - bean.setBodys(req.getBody()); - bean.setBodystr(req.getBodyUTF8()); - UserInfo user = req.currentUser(); - RetResult result = service.createHello(user, bean); - resp.finishJson(result); - } - - @HttpMapping(url = "/hello/delete/", auth = false, comment = "根据id删除Hello对象") - @HttpParam(name = "#", type = int.class, comment = "Hello对象id") - public void delete(HttpRequest req, HttpResponse resp) throws IOException { - HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - int id = Integer.parseInt(req.getRequstURILastPath()); - service.deleteHello(id); - resp.finishJson(RetResult.success()); - } - - @HttpMapping(url = "/hello/update", auth = false, comment = "修改Hello对象") - @HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") - public void update(HttpRequest req, HttpResponse resp) throws IOException { - HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setResname(req.getHeader("hello-res")); - bean.setBodys(req.getBody()); - bean.setBodystr(req.getBodyUTF8()); - service.updateHello(bean); - resp.finishJson(RetResult.success()); - } - - @HttpMapping(url = "/hello/query", auth = false, comment = "查询Hello对象列表") - @HttpParam(name = "bean", type = HelloBean.class, comment = "过滤条件") - public void query(HttpRequest req, HttpResponse resp) throws IOException { - HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); - bean.setClientaddr(req.getRemoteAddr()); - bean.setUseragent(req.getHeader("User-Agent")); - bean.setRescookie(req.getCookie("hello-cookie")); - bean.setSessionid(req.getSessionid(false)); - Flipper flipper = req.getFlipper(); - Sheet result = service.queryHello(bean, flipper); - resp.finishJson(result); - } - - @HttpMapping(url = "/hello/find/", auth = false, comment = "根据id删除Hello对象") - @HttpParam(name = "#", type = int.class, comment = "Hello对象id") - public void find(HttpRequest req, HttpResponse resp) throws IOException { - HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); - int id = Integer.parseInt(req.getRequstURILastPath()); - HelloEntity bean = service.findHello(id); - resp.finishJson(bean); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.rest; + +import java.io.IOException; +import java.util.Map; +import javax.annotation.Resource; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import org.redkale.source.Flipper; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +@WebServlet(value = {"/hello/*"}, repair = true) +public class _DynHelloRestServlet2 extends SimpleRestServlet { + + @Resource + private HelloService2 _redkale_service; + + @Resource + private Map _redkale_servicemap; + + @HttpMapping(url = "/hello/create", auth = false, comment = "创建Hello对象") + @HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") + public void create(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setResname(req.getHeader("hello-res")); + bean.setBodys(req.getBody()); + bean.setBodystr(req.getBodyUTF8()); + UserInfo user = new UserInfo(); + RetResult result = service.createHello(user, bean); + resp.finishJson(result); + } + + @HttpMapping(url = "/hello/delete/", auth = false, comment = "根据id删除Hello对象") + @HttpParam(name = "#", type = int.class, comment = "Hello对象id") + public void delete(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + service.deleteHello(id); + resp.finishJson(RetResult.success()); + } + + @HttpMapping(url = "/hello/update", auth = false, comment = "修改Hello对象") + @HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") + public void update(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setResname(req.getHeader("hello-res")); + bean.setBodys(req.getBody()); + bean.setBodystr(req.getBodyUTF8()); + service.updateHello(bean); + resp.finishJson(RetResult.success()); + } + + @HttpMapping(url = "/hello/query", auth = false, comment = "查询Hello对象列表") + @HttpParam(name = "bean", type = HelloBean.class, comment = "过滤条件") + public void query(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); + bean.setClientaddr(req.getRemoteAddr()); + bean.setUseragent(req.getHeader("User-Agent")); + bean.setRescookie(req.getCookie("hello-cookie")); + bean.setSessionid(req.getSessionid(false)); + Flipper flipper = req.getFlipper(); + Sheet result = service.queryHello(bean, flipper); + resp.finishJson(result); + } + + @HttpMapping(url = "/hello/find/", auth = false, comment = "根据id删除Hello对象") + @HttpParam(name = "#", type = int.class, comment = "Hello对象id") + public void find(HttpRequest req, HttpResponse resp) throws IOException { + HelloService2 service = _redkale_servicemap == null ? _redkale_service : _redkale_servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); + int id = Integer.parseInt(req.getRequstURILastPath()); + HelloEntity bean = service.findHello(id); + resp.finishJson(bean); + } +} diff --git a/test/org/redkale/test/service/ABMainService.java b/src/test/java/org/redkale/test/service/ABMainService.java similarity index 91% rename from test/org/redkale/test/service/ABMainService.java rename to src/test/java/org/redkale/test/service/ABMainService.java index e9231e130..f66e5f217 100644 --- a/test/org/redkale/test/service/ABMainService.java +++ b/src/test/java/org/redkale/test/service/ABMainService.java @@ -1,205 +1,205 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.service; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.logging.*; -import javax.annotation.Resource; -import org.redkale.convert.bson.BsonConvert; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.*; -import org.redkale.net.http.*; -import org.redkale.net.sncp.*; -import org.redkale.service.Service; -import org.redkale.util.AnyValue.DefaultAnyValue; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -@RestService(name = "abmain") -public class ABMainService implements Service { - - @Resource - private BCService bcService; - - public static void remotemain(String[] args) throws Throwable { - System.out.println("------------------- 远程模式调用 -----------------------------------"); - final int abport = 8888; - final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); - asyncGroup.start(); - ResourceFactory resFactory = ResourceFactory.root(); - ExecutorService executor = Executors.newSingleThreadExecutor(); - final TransportFactory transFactory = TransportFactory.create(asyncGroup, 0, 0); - transFactory.addGroupInfo("g77", new InetSocketAddress("127.0.0.1", 5577)); - transFactory.addGroupInfo("g88", new InetSocketAddress("127.0.0.1", 5588)); - transFactory.addGroupInfo("g99", new InetSocketAddress("127.0.0.1", 5599)); - - resFactory.register(JsonConvert.root()); - resFactory.register(BsonConvert.root()); - - //------------------------ 初始化 CService ------------------------------------ - CService cservice = Sncp.createSimpleLocalService(CService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 5577), "g77"); - SncpServer cserver = new SncpServer(); - cserver.getLogger().setLevel(Level.WARNING); - cserver.addSncpServlet(cservice); - cserver.init(DefaultAnyValue.create("port", 5577)); - cserver.start(null); - - //------------------------ 初始化 BCService ------------------------------------ - BCService bcservice = Sncp.createSimpleLocalService(BCService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 5588), "g88"); - CService remoteCService = Sncp.createSimpleRemoteService(CService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 5588), "g77"); - resFactory.inject(remoteCService); - resFactory.register("", remoteCService); - SncpServer bcserver = new SncpServer(); - bcserver.getLogger().setLevel(Level.WARNING); - bcserver.addSncpServlet(bcservice); - bcserver.init(DefaultAnyValue.create("port", 5588)); - bcserver.start(null); - - //------------------------ 初始化 ABMainService ------------------------------------ - ABMainService service = Sncp.createSimpleLocalService(ABMainService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 5599), "g99"); - BCService remoteBCService = Sncp.createSimpleRemoteService(BCService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 5599), "g88"); - resFactory.inject(remoteBCService); - resFactory.register("", remoteBCService); - - HttpServer server = new HttpServer(); - server.getLogger().setLevel(Level.WARNING); - - server.addRestServlet(null, service, null, HttpServlet.class, "/pipes"); - - resFactory.inject(cservice); - resFactory.inject(bcservice); - resFactory.inject(service); - - server.init(DefaultAnyValue.create("port", abport)); - server.start(null); - Thread.sleep(100); - - //同步方法 - String url = "http://127.0.0.1:" + abport + "/pipes/abmain/syncabtime/张先生"; - System.out.println(Utility.postHttpContent(url)); - - //异步方法 - url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime/张先生"; - System.out.println(Utility.postHttpContent(url)); - - //异步方法 - url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime2/张先生"; - System.out.println(Utility.postHttpContent(url)); - - server.shutdown(); - } - - public static void main(String[] args) throws Throwable { - System.out.println("------------------- 本地模式调用 -----------------------------------"); - final int abport = 8888; - ResourceFactory factory = ResourceFactory.root(); - - ABMainService service = new ABMainService(); - - BCService bcservice = new BCService(); - factory.register("", bcservice); - factory.register("", new CService()); - factory.inject(bcservice); - factory.inject(service); - - HttpServer server = new HttpServer(); - server.getLogger().setLevel(Level.WARNING); - - server.addRestServlet(null, service, null, HttpServlet.class, "/pipes"); - - server.init(DefaultAnyValue.create("port", "" + abport)); - server.start(null); - Thread.sleep(100); - - //同步方法 - String url = "http://127.0.0.1:" + abport + "/pipes/abmain/syncabtime/张先生"; - System.out.println(Utility.postHttpContent(url)); - - //异步方法 - url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime/张先生"; - System.out.println(Utility.postHttpContent(url)); - - //异步方法 - url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime2/张先生"; - System.out.println(Utility.postHttpContent(url)); - - server.shutdown(); - //远程模式 - remotemain(args); - } - - public static AsynchronousChannelGroup newChannelGroup() throws IOException { - final AtomicInteger counter = new AtomicInteger(); - ExecutorService transportExec = Executors.newFixedThreadPool(16, (Runnable r) -> { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("Transport-Thread-" + counter.incrementAndGet()); - return t; - }); - return AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1); - } - - public static ObjectPool newBufferPool() { - return ObjectPool.createSafePool(new AtomicLong(), new AtomicLong(), 16, - (Object... params) -> ByteBuffer.allocateDirect(8192), null, (e) -> { - if (e == null || e.isReadOnly() || e.capacity() != 8192) return false; - e.clear(); - return true; - }); - } - - @RestMapping(name = "syncabtime") - public String abCurrentTime(@RestParam(name = "#") final String name) { - String rs = "同步abCurrentTime: " + bcService.bcCurrentTime(name); - System.out.println("执行了 ABMainService.abCurrentTime++++同步方法"); - return rs; - } - - @RestMapping(name = "asyncabtime") - public void abCurrentTime(final CompletionHandler handler, @RestParam(name = "#") final String name) { - bcService.bcCurrentTime(Utility.createAsyncHandler((v, a) -> { - System.out.println("执行了 ABMainService.abCurrentTime----异步方法"); - String rs = "异步abCurrentTime: " + v; - if (handler != null) handler.completed(rs, a); - }, (t, a) -> { - if (handler != null) handler.failed(t, a); - }), name); - } - - @RestMapping(name = "asyncabtime2") - public void abCurrentTime(final MyAsyncHandler handler, @RestParam(name = "#") final String name) { - bcService.bcCurrentTime(new MyAsyncHandler() { - @Override - public int id() { - return 1; - } - - @Override - public void completed(String v, Void a) { - System.out.println("执行了 ABMainService.abCurrentTime----异步方法2"); - String rs = "异步abCurrentTime: " + v; - if (handler != null) handler.completed(rs, a); - } - - @Override - public void failed(Throwable exc, Void attachment) { - } - - @Override - public int id2() { - return 2; - } - }, name); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.service; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.logging.*; +import javax.annotation.Resource; +import org.redkale.convert.bson.BsonConvert; +import org.redkale.convert.json.JsonConvert; +import org.redkale.net.*; +import org.redkale.net.http.*; +import org.redkale.net.sncp.*; +import org.redkale.service.Service; +import org.redkale.util.AnyValue.DefaultAnyValue; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +@RestService(name = "abmain") +public class ABMainService implements Service { + + @Resource + private BCService bcService; + + public static void remotemain(String[] args) throws Throwable { + System.out.println("------------------- 远程模式调用 -----------------------------------"); + final int abport = 8888; + final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); + asyncGroup.start(); + ResourceFactory resFactory = ResourceFactory.create(); + ExecutorService executor = Executors.newSingleThreadExecutor(); + final TransportFactory transFactory = TransportFactory.create(asyncGroup, 0, 0); + transFactory.addGroupInfo("g77", new InetSocketAddress("127.0.0.1", 5577)); + transFactory.addGroupInfo("g88", new InetSocketAddress("127.0.0.1", 5588)); + transFactory.addGroupInfo("g99", new InetSocketAddress("127.0.0.1", 5599)); + + resFactory.register(JsonConvert.root()); + resFactory.register(BsonConvert.root()); + + //------------------------ 初始化 CService ------------------------------------ + CService cservice = Sncp.createSimpleLocalService(CService.class, null, resFactory, transFactory, new InetSocketAddress("127.0.0.1", 5577), "g77"); + SncpServer cserver = new SncpServer(); + cserver.getLogger().setLevel(Level.WARNING); + cserver.addSncpServlet(cservice); + cserver.init(DefaultAnyValue.create("port", 5577)); + cserver.start(); + + //------------------------ 初始化 BCService ------------------------------------ + BCService bcservice = Sncp.createSimpleLocalService(BCService.class, null, resFactory, transFactory, new InetSocketAddress("127.0.0.1", 5588), "g88"); + CService remoteCService = Sncp.createSimpleRemoteService(CService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 5588), "g77"); + resFactory.inject(remoteCService); + resFactory.register("", remoteCService); + SncpServer bcserver = new SncpServer(); + bcserver.getLogger().setLevel(Level.WARNING); + bcserver.addSncpServlet(bcservice); + bcserver.init(DefaultAnyValue.create("port", 5588)); + bcserver.start(); + + //------------------------ 初始化 ABMainService ------------------------------------ + ABMainService service = Sncp.createSimpleLocalService(ABMainService.class, null, resFactory, transFactory, new InetSocketAddress("127.0.0.1", 5599), "g99"); + BCService remoteBCService = Sncp.createSimpleRemoteService(BCService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 5599), "g88"); + resFactory.inject(remoteBCService); + resFactory.register("", remoteBCService); + + HttpServer server = new HttpServer(); + server.getLogger().setLevel(Level.WARNING); + + server.addRestServlet(null, service, null, HttpServlet.class, "/pipes"); + + resFactory.inject(cservice); + resFactory.inject(bcservice); + resFactory.inject(service); + + server.init(DefaultAnyValue.create("port", abport)); + server.start(); + Thread.sleep(100); + + //同步方法 + String url = "http://127.0.0.1:" + abport + "/pipes/abmain/syncabtime/张先生"; + System.out.println(Utility.postHttpContent(url)); + + //异步方法 + url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime/张先生"; + System.out.println(Utility.postHttpContent(url)); + + //异步方法 + url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime2/张先生"; + System.out.println(Utility.postHttpContent(url)); + + server.shutdown(); + } + + public static void main(String[] args) throws Throwable { + System.out.println("------------------- 本地模式调用 -----------------------------------"); + final int abport = 8888; + ResourceFactory factory = ResourceFactory.create(); + + ABMainService service = new ABMainService(); + + BCService bcservice = new BCService(); + factory.register("", bcservice); + factory.register("", new CService()); + factory.inject(bcservice); + factory.inject(service); + + HttpServer server = new HttpServer(); + server.getLogger().setLevel(Level.WARNING); + + server.addRestServlet(null, service, null, HttpServlet.class, "/pipes"); + + server.init(DefaultAnyValue.create("port", "" + abport)); + server.start(); + Thread.sleep(100); + + //同步方法 + String url = "http://127.0.0.1:" + abport + "/pipes/abmain/syncabtime/张先生"; + System.out.println(Utility.postHttpContent(url)); + + //异步方法 + url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime/张先生"; + System.out.println(Utility.postHttpContent(url)); + + //异步方法 + url = "http://127.0.0.1:" + abport + "/pipes/abmain/asyncabtime2/张先生"; + System.out.println(Utility.postHttpContent(url)); + + server.shutdown(); + //远程模式 + remotemain(args); + } + + public static AsynchronousChannelGroup newChannelGroup() throws IOException { + final AtomicInteger counter = new AtomicInteger(); + ExecutorService transportExec = Executors.newFixedThreadPool(16, (Runnable r) -> { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("Transport-Thread-" + counter.incrementAndGet()); + return t; + }); + return AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1); + } + + public static ObjectPool newBufferPool() { + return ObjectPool.createSafePool(new LongAdder(), new LongAdder(), 16, + (Object... params) -> ByteBuffer.allocateDirect(8192), null, (e) -> { + if (e == null || e.isReadOnly() || e.capacity() != 8192) return false; + e.clear(); + return true; + }); + } + + @RestMapping(name = "syncabtime") + public String abCurrentTime(@RestParam(name = "#") final String name) { + String rs = "同步abCurrentTime: " + bcService.bcCurrentTime(name); + System.out.println("执行了 ABMainService.abCurrentTime++++同步方法"); + return rs; + } + + @RestMapping(name = "asyncabtime") + public void abCurrentTime(final CompletionHandler handler, @RestParam(name = "#") final String name) { + bcService.bcCurrentTime(Utility.createAsyncHandler((v, a) -> { + System.out.println("执行了 ABMainService.abCurrentTime----异步方法"); + String rs = "异步abCurrentTime: " + v; + if (handler != null) handler.completed(rs, a); + }, (t, a) -> { + if (handler != null) handler.failed(t, a); + }), name); + } + + @RestMapping(name = "asyncabtime2") + public void abCurrentTime(final MyAsyncHandler handler, @RestParam(name = "#") final String name) { + bcService.bcCurrentTime(new MyAsyncHandler() { + @Override + public int id() { + return 1; + } + + @Override + public void completed(String v, Void a) { + System.out.println("执行了 ABMainService.abCurrentTime----异步方法2"); + String rs = "异步abCurrentTime: " + v; + if (handler != null) handler.completed(rs, a); + } + + @Override + public void failed(Throwable exc, Void attachment) { + } + + @Override + public int id2() { + return 2; + } + }, name); + } +} diff --git a/test/org/redkale/test/service/BCService.java b/src/test/java/org/redkale/test/service/BCService.java similarity index 97% rename from test/org/redkale/test/service/BCService.java rename to src/test/java/org/redkale/test/service/BCService.java index dd0d51df2..94d69d526 100644 --- a/test/org/redkale/test/service/BCService.java +++ b/src/test/java/org/redkale/test/service/BCService.java @@ -1,62 +1,62 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.service; - -import java.nio.channels.CompletionHandler; -import javax.annotation.Resource; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class BCService implements Service { - - @Resource - private CService cService; - - public String bcCurrentTime(final String name) { - String rs = "同步bcCurrentTime: " + cService.ccCurrentTime(name).getResult(); - System.out.println("执行了 BCService.bcCurrentTime++++同步方法"); - return rs; - } - - public void bcCurrentTime(final CompletionHandler handler, final String name) { - cService.ccCurrentTime(Utility.createAsyncHandler((v, a) -> { - System.out.println("执行了 BCService.bcCurrentTime----异步方法"); - String rs = "异步bcCurrentTime: " + (v == null ? null : v.getResult()); - if (handler != null) handler.completed(rs, null); - }, (t, a) -> { - if (handler != null) handler.failed(t, a); - }), name); - } - - public void bcCurrentTime(final MyAsyncHandler handler, final String name) { - cService.mcCurrentTime(new MyAsyncHandler, Void>() { - @Override - public int id() { - return 1; - } - - @Override - public void completed(RetResult v, Void a) { - System.out.println("执行了 BCService.bcCurrentTime----异步方法2"); - String rs = "异步bcCurrentTime: " + (v == null ? null : v.getResult()); - if (handler != null) handler.completed(rs, null); - } - - @Override - public void failed(Throwable exc, Void attachment) { - } - - @Override - public int id2() { - return 2; - } - }, name); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.service; + +import java.nio.channels.CompletionHandler; +import javax.annotation.Resource; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class BCService implements Service { + + @Resource + private CService cService; + + public String bcCurrentTime(final String name) { + String rs = "同步bcCurrentTime: " + cService.ccCurrentTime(name).getResult(); + System.out.println("执行了 BCService.bcCurrentTime++++同步方法"); + return rs; + } + + public void bcCurrentTime(final CompletionHandler handler, final String name) { + cService.ccCurrentTime(Utility.createAsyncHandler((v, a) -> { + System.out.println("执行了 BCService.bcCurrentTime----异步方法"); + String rs = "异步bcCurrentTime: " + (v == null ? null : v.getResult()); + if (handler != null) handler.completed(rs, null); + }, (t, a) -> { + if (handler != null) handler.failed(t, a); + }), name); + } + + public void bcCurrentTime(final MyAsyncHandler handler, final String name) { + cService.mcCurrentTime(new MyAsyncHandler, Void>() { + @Override + public int id() { + return 1; + } + + @Override + public void completed(RetResult v, Void a) { + System.out.println("执行了 BCService.bcCurrentTime----异步方法2"); + String rs = "异步bcCurrentTime: " + (v == null ? null : v.getResult()); + if (handler != null) handler.completed(rs, null); + } + + @Override + public void failed(Throwable exc, Void attachment) { + } + + @Override + public int id2() { + return 2; + } + }, name); + } +} diff --git a/test/org/redkale/test/service/CService.java b/src/test/java/org/redkale/test/service/CService.java similarity index 97% rename from test/org/redkale/test/service/CService.java rename to src/test/java/org/redkale/test/service/CService.java index 0be0455cd..6039a7ddf 100644 --- a/test/org/redkale/test/service/CService.java +++ b/src/test/java/org/redkale/test/service/CService.java @@ -1,35 +1,35 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.service; - -import java.nio.channels.CompletionHandler; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class CService implements Service { - - public RetResult ccCurrentTime(final String name) { - String rs = "同步ccCurrentTime: " + name + ": " + Utility.formatTime(System.currentTimeMillis()); - System.out.println("执行了 CService.ccCurrentTime++++同步方法"); - return new RetResult(rs); - } - - public void ccCurrentTime(final CompletionHandler, Void> handler, final String name) { - String rs = "异步ccCurrentTime: " + name + ": " + Utility.formatTime(System.currentTimeMillis()); - System.out.println("执行了 CService.ccCurrentTime----异步方法"); - if (handler != null) handler.completed(new RetResult(rs), null); - } - - public void mcCurrentTime(final MyAsyncHandler, Void> handler, final String name) { - String rs = "异步mcCurrentTime: " + name + ": " + Utility.formatTime(System.currentTimeMillis()); - System.out.println("执行了 CService.mcCurrentTime----异步方法2"); - if (handler != null) handler.completed(new RetResult(rs), null); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.service; + +import java.nio.channels.CompletionHandler; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class CService implements Service { + + public RetResult ccCurrentTime(final String name) { + String rs = "同步ccCurrentTime: " + name + ": " + Utility.formatTime(System.currentTimeMillis()); + System.out.println("执行了 CService.ccCurrentTime++++同步方法"); + return new RetResult(rs); + } + + public void ccCurrentTime(final CompletionHandler, Void> handler, final String name) { + String rs = "异步ccCurrentTime: " + name + ": " + Utility.formatTime(System.currentTimeMillis()); + System.out.println("执行了 CService.ccCurrentTime----异步方法"); + if (handler != null) handler.completed(new RetResult(rs), null); + } + + public void mcCurrentTime(final MyAsyncHandler, Void> handler, final String name) { + String rs = "异步mcCurrentTime: " + name + ": " + Utility.formatTime(System.currentTimeMillis()); + System.out.println("执行了 CService.mcCurrentTime----异步方法2"); + if (handler != null) handler.completed(new RetResult(rs), null); + } +} diff --git a/test/org/redkale/test/service/MyAsyncHandler.java b/src/test/java/org/redkale/test/service/MyAsyncHandler.java similarity index 95% rename from test/org/redkale/test/service/MyAsyncHandler.java rename to src/test/java/org/redkale/test/service/MyAsyncHandler.java index bad9729ea..61984b997 100644 --- a/test/org/redkale/test/service/MyAsyncHandler.java +++ b/src/test/java/org/redkale/test/service/MyAsyncHandler.java @@ -1,18 +1,18 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.service; - -/** - * - * @author zhangjx - * @param V - * @param
    A - */ -public abstract class MyAsyncHandler extends MyAsyncInnerHandler { - - public abstract int id(); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.service; + +/** + * + * @author zhangjx + * @param V + * @param A + */ +public abstract class MyAsyncHandler extends MyAsyncInnerHandler { + + public abstract int id(); + +} diff --git a/test/org/redkale/test/service/MyAsyncInnerHandler.java b/src/test/java/org/redkale/test/service/MyAsyncInnerHandler.java similarity index 95% rename from test/org/redkale/test/service/MyAsyncInnerHandler.java rename to src/test/java/org/redkale/test/service/MyAsyncInnerHandler.java index 671037220..ac337f0c8 100644 --- a/test/org/redkale/test/service/MyAsyncInnerHandler.java +++ b/src/test/java/org/redkale/test/service/MyAsyncInnerHandler.java @@ -1,18 +1,18 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.service; - -import java.nio.channels.CompletionHandler; - -/** - * - * @author zhangjx - */ -public abstract class MyAsyncInnerHandler implements CompletionHandler { - - protected abstract int id2(); - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.service; + +import java.nio.channels.CompletionHandler; + +/** + * + * @author zhangjx + */ +public abstract class MyAsyncInnerHandler implements CompletionHandler { + + protected abstract int id2(); + +} diff --git a/test/org/redkale/test/service/Person.java b/src/test/java/org/redkale/test/service/Person.java similarity index 94% rename from test/org/redkale/test/service/Person.java rename to src/test/java/org/redkale/test/service/Person.java index b0ed01586..6954ed0df 100644 --- a/test/org/redkale/test/service/Person.java +++ b/src/test/java/org/redkale/test/service/Person.java @@ -1,32 +1,32 @@ -package org.redkale.test.service; - -import java.io.Serializable; - -public class Person implements Serializable { - - private byte[] b = new byte[1024 * 2]; - - private String name; - - @Override - public String toString() { - return "{name=" + name + ", b =" + (b == null ? "null" : "[length=" + b.length + "]") + "}"; - } - - public byte[] getB() { - return b; - } - - public void setB(byte[] b) { - this.b = b; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - -} +package org.redkale.test.service; + +import java.io.Serializable; + +public class Person implements Serializable { + + private byte[] b = new byte[1024 * 2]; + + private String name; + + @Override + public String toString() { + return "{name=" + name + ", b =" + (b == null ? "null" : "[length=" + b.length + "]") + "}"; + } + + public byte[] getB() { + return b; + } + + public void setB(byte[] b) { + this.b = b; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/test/org/redkale/test/service/TestBean.java b/src/test/java/org/redkale/test/service/TestBean.java similarity index 95% rename from test/org/redkale/test/service/TestBean.java rename to src/test/java/org/redkale/test/service/TestBean.java index 4f7729d6e..0b09680c1 100644 --- a/test/org/redkale/test/service/TestBean.java +++ b/src/test/java/org/redkale/test/service/TestBean.java @@ -1,14 +1,14 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.service; - -/** - * - * @author zhangjx - */ -public class TestBean { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.service; + +/** + * + * @author zhangjx + */ +public class TestBean { + +} diff --git a/test/org/redkale/test/service/TestService.java b/src/test/java/org/redkale/test/service/TestService.java similarity index 96% rename from test/org/redkale/test/service/TestService.java rename to src/test/java/org/redkale/test/service/TestService.java index 58c89939a..1ac8d6029 100644 --- a/test/org/redkale/test/service/TestService.java +++ b/src/test/java/org/redkale/test/service/TestService.java @@ -1,32 +1,32 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.service; - -import java.nio.channels.CompletionHandler; -import org.redkale.net.sncp.*; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class TestService implements Service { - -// public boolean change(TestBean bean, String name, int id) { -// return false; -// } - - public void change(CompletionHandler handler, TestBean bean, String name, int id) { - - } - - public static void main(String[] args) throws Throwable { - SncpServer cserver = new SncpServer(); - cserver.addSncpServlet(new TestService()); - cserver.init(AnyValue.DefaultAnyValue.create("port", 5577)); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.service; + +import java.nio.channels.CompletionHandler; +import org.redkale.net.sncp.*; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class TestService implements Service { + +// public boolean change(TestBean bean, String name, int id) { +// return false; +// } + + public void change(CompletionHandler handler, TestBean bean, String name, int id) { + + } + + public static void main(String[] args) throws Throwable { + SncpServer cserver = new SncpServer(); + cserver.addSncpServlet(new TestService()); + cserver.init(AnyValue.DefaultAnyValue.create("port", 5577)); + } +} diff --git a/test/org/redkale/test/sncp/SncpTest.java b/src/test/java/org/redkale/test/sncp/SncpTest.java similarity index 91% rename from test/org/redkale/test/sncp/SncpTest.java rename to src/test/java/org/redkale/test/sncp/SncpTest.java index a2a49285e..319a2291b 100644 --- a/test/org/redkale/test/sncp/SncpTest.java +++ b/src/test/java/org/redkale/test/sncp/SncpTest.java @@ -1,223 +1,225 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.sncp; - -import java.io.*; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousChannelGroup; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.*; -import java.util.logging.LogManager; -import org.redkale.convert.bson.*; -import org.redkale.net.*; -import org.redkale.net.sncp.*; -import org.redkale.service.Service; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class SncpTest { - - private static final String serviceName = ""; - - private static final String myhost = Utility.localInetAddress().getHostAddress(); - - private static final int port = 4040; - - private static final int port2 = 4240; - - private static final String protocol = "SNCP.UDP"; - - public static void main(String[] args) throws Exception { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - final PrintStream ps = new PrintStream(out); - ps.println("handlers = java.util.logging.ConsoleHandler"); - ps.println(".handlers = java.util.logging.ConsoleHandler"); - ps.println(".level = FINEST"); - ps.println("java.util.logging.ConsoleHandler.level = FINEST"); - LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray())); - ResourceFactory.root().register("", BsonConvert.class, BsonFactory.root().getConvert()); - if (System.getProperty("client") == null) { - runServer(); - if (port2 > 0) runServer2(); - } - if (System.getProperty("server") == null) { - runClient(); - } - if (System.getProperty("server") != null) { - System.in.read(); - } - } - - public static AsynchronousChannelGroup newChannelGroup() throws IOException { - final AtomicInteger counter = new AtomicInteger(); - ExecutorService transportExec = Executors.newFixedThreadPool(16, (Runnable r) -> { - Thread t = new Thread(r); - t.setDaemon(true); - t.setName("Transport-Thread-" + counter.incrementAndGet()); - return t; - }); - return AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1); - } - - public static ObjectPool newBufferPool() { - return ObjectPool.createSafePool(new AtomicLong(), new AtomicLong(), 16, - (Object... params) -> ByteBuffer.allocateDirect(8192), null, (e) -> { - if (e == null || e.isReadOnly() || e.capacity() != 8192) return false; - e.clear(); - return true; - }); - } - - private static void runClient() throws Exception { - InetSocketAddress addr = new InetSocketAddress(myhost, port); - Set set = new LinkedHashSet<>(); - set.add(addr); - if (port2 > 0) set.add(new InetSocketAddress(myhost, port2)); - final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); - asyncGroup.start(); - final TransportFactory transFactory = TransportFactory.create(asyncGroup, protocol.endsWith(".UDP") ? "UDP" : "TCP", 0, 0); - transFactory.addGroupInfo("client", set); - final SncpTestIService service = Sncp.createSimpleRemoteService(SncpTestIService.class, null, transFactory, addr, "client"); - ResourceFactory.root().inject(service); - -// SncpTestBean bean = new SncpTestBean(); -// StringBuilder sb = new StringBuilder(); -// for (int i = 0; i < 2000; i++) { -// sb.append("_").append(i).append("_0123456789"); -// } -// bean.setContent(sb.toString()); -// bean.setContent("hello sncp"); - SncpTestBean callbean = new SncpTestBean(); - callbean.setId(1); - callbean.setContent("数据X"); - service.queryLongResult("f", 3, 33L); - - service.insert(callbean); - System.out.println("bean.id应该会被修改(id不会是1): " + callbean); - System.out.println("---------------------------------------------------"); - final int count = 10; - final CountDownLatch cld = new CountDownLatch(count); - final AtomicInteger ai = new AtomicInteger(); - for (int i = 0; i < count; i++) { - final int k = i + 1; - new Thread() { - @Override - public void run() { - try { - Thread.sleep(k); - SncpTestBean bean = new SncpTestBean(); - bean.setId(k); - bean.setContent("数据: " + (k < 10 ? "0" : "") + k); - StringBuilder sb = new StringBuilder(); - sb.append(k).append("------"); - for (int i = 0; i < 1200; i++) { - sb.append("_").append(i).append("_").append(k).append("_0123456789"); - } - bean.setContent(sb.toString()); - - service.queryResult(bean); - //service.updateBean(bean); - } catch (Exception e) { - e.printStackTrace(); - } finally { - long a = ai.incrementAndGet(); - System.out.println("运行了 " + (a == 100 ? "--------------------------------------------------" : "") + a); - cld.countDown(); - } - } - }.start(); - } - cld.await(); - final CountDownLatch cld2 = new CountDownLatch(1); - final CompletableFuture future = service.queryResultAsync(callbean); - future.whenComplete((v, e) -> { - cld2.countDown(); - System.out.println("异步执行完毕: " + v + ", 异常为: " + e); - }); - cld2.await(); - System.out.println("---全部运行完毕---"); - System.exit(0); - } - - private static void runServer() throws Exception { - InetSocketAddress addr = new InetSocketAddress(myhost, port); - final CountDownLatch cdl = new CountDownLatch(1); - final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); - asyncGroup.start(); - new Thread() { - { - setName("Thread-Server-01"); - } - - @Override - public void run() { - try { - AnyValue.DefaultAnyValue conf = new AnyValue.DefaultAnyValue(); - conf.addValue("host", "0.0.0.0"); - conf.addValue("port", "" + port); - conf.addValue("protocol", protocol); - SncpServer server = new SncpServer(null, System.currentTimeMillis(), conf, ResourceFactory.root()); - Set set = new LinkedHashSet<>(); - if (port2 > 0) set.add(new InetSocketAddress(myhost, port2)); - final TransportFactory transFactory = TransportFactory.create(asyncGroup, protocol.endsWith(".UDP") ? "UDP" : "TCP", 0, 0); - transFactory.addGroupInfo("server", set); - SncpTestIService service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, null, transFactory, addr, "server"); - ResourceFactory.root().inject(service); - server.addSncpServlet(service); - System.out.println(service); - server.init(conf); - server.start(null); - cdl.countDown(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }.start(); - cdl.await(); - } - - private static void runServer2() throws Exception { - InetSocketAddress addr = new InetSocketAddress(myhost, port2); - final CountDownLatch cdl = new CountDownLatch(1); - final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); - asyncGroup.start(); - new Thread() { - { - setName("Thread-Server-02"); - } - - @Override - public void run() { - try { - AnyValue.DefaultAnyValue conf = new AnyValue.DefaultAnyValue(); - conf.addValue("host", "0.0.0.0"); - conf.addValue("port", "" + port2); - conf.addValue("protocol", protocol); - SncpServer server = new SncpServer(null, System.currentTimeMillis(), conf, ResourceFactory.root()); - Set set = new LinkedHashSet<>(); - set.add(new InetSocketAddress(myhost, port)); - - final TransportFactory transFactory = TransportFactory.create(asyncGroup, protocol.endsWith(".UDP") ? "UDP" : "TCP", 0, 0); - transFactory.addGroupInfo("server", set); - Service service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, null, transFactory, addr, "server"); - server.addSncpServlet(service); - server.init(conf); - server.start(null); - cdl.countDown(); - } catch (Exception e) { - e.printStackTrace(); - } - } - }.start(); - cdl.await(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.sncp; + +import java.io.*; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousChannelGroup; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.util.logging.LogManager; +import org.redkale.convert.bson.*; +import org.redkale.net.*; +import org.redkale.net.sncp.*; +import org.redkale.service.Service; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class SncpTest { + + private static final String serviceName = ""; + + private static final String myhost = Utility.localInetAddress().getHostAddress(); + + private static final int port = 4040; + + private static final int port2 = 4240; + + private static final String protocol = "SNCP.UDP"; + + private static final ResourceFactory factory = ResourceFactory.create(); + + public static void main(String[] args) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + final PrintStream ps = new PrintStream(out); + ps.println("handlers = java.util.logging.ConsoleHandler"); + ps.println(".handlers = java.util.logging.ConsoleHandler"); + ps.println(".level = FINEST"); + ps.println("java.util.logging.ConsoleHandler.level = FINEST"); + LogManager.getLogManager().readConfiguration(new ByteArrayInputStream(out.toByteArray())); + factory.register("", BsonConvert.class, BsonFactory.root().getConvert()); + if (System.getProperty("client") == null) { + runServer(); + if (port2 > 0) runServer2(); + } + if (System.getProperty("server") == null) { + runClient(); + } + if (System.getProperty("server") != null) { + System.in.read(); + } + } + + public static AsynchronousChannelGroup newChannelGroup() throws IOException { + final AtomicInteger counter = new AtomicInteger(); + ExecutorService transportExec = Executors.newFixedThreadPool(16, (Runnable r) -> { + Thread t = new Thread(r); + t.setDaemon(true); + t.setName("Transport-Thread-" + counter.incrementAndGet()); + return t; + }); + return AsynchronousChannelGroup.withCachedThreadPool(transportExec, 1); + } + + public static ObjectPool newBufferPool() { + return ObjectPool.createSafePool(new LongAdder(), new LongAdder(), 16, + (Object... params) -> ByteBuffer.allocateDirect(8192), null, (e) -> { + if (e == null || e.isReadOnly() || e.capacity() != 8192) return false; + e.clear(); + return true; + }); + } + + private static void runClient() throws Exception { + InetSocketAddress addr = new InetSocketAddress(myhost, port); + Set set = new LinkedHashSet<>(); + set.add(addr); + if (port2 > 0) set.add(new InetSocketAddress(myhost, port2)); + final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); + asyncGroup.start(); + final TransportFactory transFactory = TransportFactory.create(asyncGroup, protocol.endsWith(".UDP") ? "UDP" : "TCP", 0, 0); + transFactory.addGroupInfo("client", set); + final SncpTestIService service = Sncp.createSimpleRemoteService(SncpTestIService.class, null, transFactory, addr, "client"); + factory.inject(service); + +// SncpTestBean bean = new SncpTestBean(); +// StringBuilder sb = new StringBuilder(); +// for (int i = 0; i < 2000; i++) { +// sb.append("_").append(i).append("_0123456789"); +// } +// bean.setContent(sb.toString()); +// bean.setContent("hello sncp"); + SncpTestBean callbean = new SncpTestBean(); + callbean.setId(1); + callbean.setContent("数据X"); + service.queryLongResult("f", 3, 33L); + + service.insert(callbean); + System.out.println("bean.id应该会被修改(id不会是1): " + callbean); + System.out.println("---------------------------------------------------"); + final int count = 10; + final CountDownLatch cld = new CountDownLatch(count); + final AtomicInteger ai = new AtomicInteger(); + for (int i = 0; i < count; i++) { + final int k = i + 1; + new Thread() { + @Override + public void run() { + try { + Thread.sleep(k); + SncpTestBean bean = new SncpTestBean(); + bean.setId(k); + bean.setContent("数据: " + (k < 10 ? "0" : "") + k); + StringBuilder sb = new StringBuilder(); + sb.append(k).append("------"); + for (int i = 0; i < 1200; i++) { + sb.append("_").append(i).append("_").append(k).append("_0123456789"); + } + bean.setContent(sb.toString()); + + service.queryResult(bean); + //service.updateBean(bean); + } catch (Exception e) { + e.printStackTrace(); + } finally { + long a = ai.incrementAndGet(); + System.out.println("运行了 " + (a == 100 ? "--------------------------------------------------" : "") + a); + cld.countDown(); + } + } + }.start(); + } + cld.await(); + final CountDownLatch cld2 = new CountDownLatch(1); + final CompletableFuture future = service.queryResultAsync(callbean); + future.whenComplete((v, e) -> { + cld2.countDown(); + System.out.println("异步执行完毕: " + v + ", 异常为: " + e); + }); + cld2.await(); + System.out.println("---全部运行完毕---"); + System.exit(0); + } + + private static void runServer() throws Exception { + InetSocketAddress addr = new InetSocketAddress(myhost, port); + final CountDownLatch cdl = new CountDownLatch(1); + final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); + asyncGroup.start(); + new Thread() { + { + setName("Thread-Server-01"); + } + + @Override + public void run() { + try { + AnyValue.DefaultAnyValue conf = new AnyValue.DefaultAnyValue(); + conf.addValue("host", "0.0.0.0"); + conf.addValue("port", "" + port); + conf.addValue("protocol", protocol); + SncpServer server = new SncpServer(null, System.currentTimeMillis(), conf, factory); + Set set = new LinkedHashSet<>(); + if (port2 > 0) set.add(new InetSocketAddress(myhost, port2)); + final TransportFactory transFactory = TransportFactory.create(asyncGroup, protocol.endsWith(".UDP") ? "UDP" : "TCP", 0, 0); + transFactory.addGroupInfo("server", set); + SncpTestIService service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, null, factory, transFactory, addr, "server"); + factory.inject(service); + server.addSncpServlet(service); + System.out.println(service); + server.init(conf); + server.start(); + cdl.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }.start(); + cdl.await(); + } + + private static void runServer2() throws Exception { + InetSocketAddress addr = new InetSocketAddress(myhost, port2); + final CountDownLatch cdl = new CountDownLatch(1); + final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); + asyncGroup.start(); + new Thread() { + { + setName("Thread-Server-02"); + } + + @Override + public void run() { + try { + AnyValue.DefaultAnyValue conf = new AnyValue.DefaultAnyValue(); + conf.addValue("host", "0.0.0.0"); + conf.addValue("port", "" + port2); + conf.addValue("protocol", protocol); + SncpServer server = new SncpServer(null, System.currentTimeMillis(), conf, factory); + Set set = new LinkedHashSet<>(); + set.add(new InetSocketAddress(myhost, port)); + + final TransportFactory transFactory = TransportFactory.create(asyncGroup, protocol.endsWith(".UDP") ? "UDP" : "TCP", 0, 0); + transFactory.addGroupInfo("server", set); + Service service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, null, factory, transFactory, addr, "server"); + server.addSncpServlet(service); + server.init(conf); + server.start(); + cdl.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + } + }.start(); + cdl.await(); + } + +} diff --git a/test/org/redkale/test/sncp/SncpTestBean.java b/src/test/java/org/redkale/test/sncp/SncpTestBean.java similarity index 96% rename from test/org/redkale/test/sncp/SncpTestBean.java rename to src/test/java/org/redkale/test/sncp/SncpTestBean.java index 77684df5e..275dc0684 100644 --- a/test/org/redkale/test/sncp/SncpTestBean.java +++ b/src/test/java/org/redkale/test/sncp/SncpTestBean.java @@ -1,54 +1,54 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.sncp; - -import org.redkale.convert.bson.BsonFactory; -import org.redkale.util.Utility; -import org.redkale.source.FilterBean; -import javax.persistence.*; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public class SncpTestBean implements FilterBean { - - @Id - private long id; - - private String content; - - public static void main(String[] args) throws Exception { - SncpTestBean bean = JsonConvert.root().convertFrom(SncpTestBean.class, "{\"content\":\"数据: 01\",\"id\":1}"); - System.out.println(bean); - byte[] bs = BsonFactory.root().getConvert().convertTo(bean); - Utility.println("---------", bs); - System.out.println(BsonFactory.root().getConvert().convertFrom(SncpTestBean.class, bs).toString()); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.sncp; + +import org.redkale.convert.bson.BsonFactory; +import org.redkale.util.Utility; +import org.redkale.source.FilterBean; +import javax.persistence.*; +import org.redkale.convert.json.*; + +/** + * + * @author zhangjx + */ +public class SncpTestBean implements FilterBean { + + @Id + private long id; + + private String content; + + public static void main(String[] args) throws Exception { + SncpTestBean bean = JsonConvert.root().convertFrom(SncpTestBean.class, "{\"content\":\"数据: 01\",\"id\":1}"); + System.out.println(bean); + byte[] bs = BsonFactory.root().getConvert().convertTo(bean); + Utility.println("---------", bs); + System.out.println(BsonFactory.root().getConvert().convertFrom(SncpTestBean.class, bs).toString()); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + +} diff --git a/test/org/redkale/test/sncp/SncpTestIService.java b/src/test/java/org/redkale/test/sncp/SncpTestIService.java similarity index 96% rename from test/org/redkale/test/sncp/SncpTestIService.java rename to src/test/java/org/redkale/test/sncp/SncpTestIService.java index dbbf9bbc1..737cb991a 100644 --- a/test/org/redkale/test/sncp/SncpTestIService.java +++ b/src/test/java/org/redkale/test/sncp/SncpTestIService.java @@ -1,28 +1,28 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.sncp; - -import java.util.concurrent.CompletableFuture; -import org.redkale.service.*; - -/** - * - * @author zhangjx - */ -public interface SncpTestIService extends Service { - - public String queryResult(SncpTestBean bean); - - public double queryDoubleResult(String a, int b, double value); - - public long queryLongResult(String a, int b, long value); - - public CompletableFuture queryResultAsync(SncpTestBean bean); - - public void insert(@RpcCall(RpcCallArrayAttribute.class) SncpTestBean... beans); - - public String updateBean(@RpcCall(SncpTestServiceImpl.CallAttribute.class) SncpTestBean bean); -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.sncp; + +import java.util.concurrent.CompletableFuture; +import org.redkale.service.*; + +/** + * + * @author zhangjx + */ +public interface SncpTestIService extends Service { + + public String queryResult(SncpTestBean bean); + + public double queryDoubleResult(String a, int b, double value); + + public long queryLongResult(String a, int b, long value); + + public CompletableFuture queryResultAsync(SncpTestBean bean); + + public void insert(@RpcCall(RpcCallArrayAttribute.class) SncpTestBean... beans); + + public String updateBean(@RpcCall(SncpTestServiceImpl.CallAttribute.class) SncpTestBean bean); +} diff --git a/test/org/redkale/test/sncp/SncpTestServiceImpl.java b/src/test/java/org/redkale/test/sncp/SncpTestServiceImpl.java similarity index 95% rename from test/org/redkale/test/sncp/SncpTestServiceImpl.java rename to src/test/java/org/redkale/test/sncp/SncpTestServiceImpl.java index f5254b374..e1af13da6 100644 --- a/test/org/redkale/test/sncp/SncpTestServiceImpl.java +++ b/src/test/java/org/redkale/test/sncp/SncpTestServiceImpl.java @@ -1,144 +1,144 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.sncp; - -import java.lang.reflect.Method; -import java.net.InetSocketAddress; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.*; -import org.redkale.net.*; -import org.redkale.net.sncp.*; -import org.redkale.service.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -@ResourceType(SncpTestIService.class) -public class SncpTestServiceImpl implements SncpTestIService { - - @Override - public CompletableFuture queryResultAsync(SncpTestBean bean) { - final CompletableFuture future = new CompletableFuture<>(); - new Thread() { - @Override - public void run() { - try { - Thread.sleep(1000); - System.out.println(Thread.currentThread().getName() + " 运行了异步方法-----------queryResultAsync方法"); - future.complete("异步result: " + bean); - } catch (Exception e) { - e.printStackTrace(); - } - } - }.start(); - return future; - - } - - @Override - public long queryLongResult(String a, int b, long value) { - return value + 1; - } - - @Override - public double queryDoubleResult(String a, int b, double value) { - return value + 1; - } - - public static class CallAttribute implements Attribute { - - @Override - public Class type() { - return long.class; - } - - @Override - public Class declaringClass() { - return SncpTestBean.class; - } - - @Override - public String field() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public Long get(SncpTestBean obj) { - System.out.println("返回ID: " + obj.getId()); - return obj.getId(); - } - - @Override - public void set(SncpTestBean obj, Long value) { - System.out.println("设置ID: " + value); - obj.setId(value); - } - - } - - @Override - public void insert(@RpcCall(RpcCallArrayAttribute.class) SncpTestBean... beans) { - for (SncpTestBean bean : beans) { - bean.setId(System.currentTimeMillis()); - } - } - - @Override - public String queryResult(SncpTestBean bean) { - System.out.println(Thread.currentThread().getName() + " 运行了queryResult方法"); - return "result: " + bean; - } - - public void queryResult(CompletionHandler handler, @RpcAttachment SncpTestBean bean) { - System.out.println(Thread.currentThread().getName() + " handler 运行了queryResult方法"); - if (handler != null) handler.completed("result: " + bean, bean); - } - - @Override - public String updateBean(@RpcCall(CallAttribute.class) SncpTestBean bean) { - bean.setId(System.currentTimeMillis()); - System.out.println(Thread.currentThread().getName() + " 运行了updateBean方法"); - return "result: " + bean; - } - - public static void main(String[] args) throws Exception { - - final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); - asyncGroup.start(); - final TransportFactory transFactory = TransportFactory.create(asyncGroup, 0, 0); - - transFactory.addGroupInfo("g70", new InetSocketAddress("127.0.0.1", 7070)); - Service service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, null, transFactory, new InetSocketAddress("127.0.0.1", 7070), "g70"); - for (Method method : service.getClass().getDeclaredMethods()) { - System.out.println(method); - } - System.out.println("-----------------------------------"); - for (Method method : SncpClient.parseMethod(service.getClass())) { - System.out.println(method); - } - System.out.println("-----------------------------------"); - service = Sncp.createSimpleRemoteService(SncpTestServiceImpl.class, null, transFactory, new InetSocketAddress("127.0.0.1", 7070), "g70"); - for (Method method : service.getClass().getDeclaredMethods()) { - System.out.println(method); - } - System.out.println("-----------------------------------"); - for (Method method : SncpClient.parseMethod(service.getClass())) { - System.out.println(method); - } - System.out.println("-----------------------------------"); - service = Sncp.createSimpleRemoteService(SncpTestIService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 7070), "g70"); - for (Method method : service.getClass().getDeclaredMethods()) { - System.out.println(method); - } - System.out.println("-----------------------------------"); - for (Method method : SncpClient.parseMethod(service.getClass())) { - System.out.println(method); - } - System.out.println("-----------------------------------"); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.sncp; + +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.*; +import org.redkale.net.*; +import org.redkale.net.sncp.*; +import org.redkale.service.*; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +@ResourceType(SncpTestIService.class) +public class SncpTestServiceImpl implements SncpTestIService { + + @Override + public CompletableFuture queryResultAsync(SncpTestBean bean) { + final CompletableFuture future = new CompletableFuture<>(); + new Thread() { + @Override + public void run() { + try { + Thread.sleep(1000); + System.out.println(Thread.currentThread().getName() + " 运行了异步方法-----------queryResultAsync方法"); + future.complete("异步result: " + bean); + } catch (Exception e) { + e.printStackTrace(); + } + } + }.start(); + return future; + + } + + @Override + public long queryLongResult(String a, int b, long value) { + return value + 1; + } + + @Override + public double queryDoubleResult(String a, int b, double value) { + return value + 1; + } + + public static class CallAttribute implements Attribute { + + @Override + public Class type() { + return long.class; + } + + @Override + public Class declaringClass() { + return SncpTestBean.class; + } + + @Override + public String field() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Long get(SncpTestBean obj) { + System.out.println("返回ID: " + obj.getId()); + return obj.getId(); + } + + @Override + public void set(SncpTestBean obj, Long value) { + System.out.println("设置ID: " + value); + obj.setId(value); + } + + } + + @Override + public void insert(@RpcCall(RpcCallArrayAttribute.class) SncpTestBean... beans) { + for (SncpTestBean bean : beans) { + bean.setId(System.currentTimeMillis()); + } + } + + @Override + public String queryResult(SncpTestBean bean) { + System.out.println(Thread.currentThread().getName() + " 运行了queryResult方法"); + return "result: " + bean; + } + + public void queryResult(CompletionHandler handler, @RpcAttachment SncpTestBean bean) { + System.out.println(Thread.currentThread().getName() + " handler 运行了queryResult方法"); + if (handler != null) handler.completed("result: " + bean, bean); + } + + @Override + public String updateBean(@RpcCall(CallAttribute.class) SncpTestBean bean) { + bean.setId(System.currentTimeMillis()); + System.out.println(Thread.currentThread().getName() + " 运行了updateBean方法"); + return "result: " + bean; + } + + public static void main(String[] args) throws Exception { + + final AsyncIOGroup asyncGroup = new AsyncIOGroup(8192, 16); + asyncGroup.start(); + final TransportFactory transFactory = TransportFactory.create(asyncGroup, 0, 0); + + transFactory.addGroupInfo("g70", new InetSocketAddress("127.0.0.1", 7070)); + Service service = Sncp.createSimpleLocalService(SncpTestServiceImpl.class, null, ResourceFactory.create(), transFactory, new InetSocketAddress("127.0.0.1", 7070), "g70"); + for (Method method : service.getClass().getDeclaredMethods()) { + System.out.println(method); + } + System.out.println("-----------------------------------"); + for (Method method : SncpClient.parseMethod(service.getClass())) { + System.out.println(method); + } + System.out.println("-----------------------------------"); + service = Sncp.createSimpleRemoteService(SncpTestServiceImpl.class, null, transFactory, new InetSocketAddress("127.0.0.1", 7070), "g70"); + for (Method method : service.getClass().getDeclaredMethods()) { + System.out.println(method); + } + System.out.println("-----------------------------------"); + for (Method method : SncpClient.parseMethod(service.getClass())) { + System.out.println(method); + } + System.out.println("-----------------------------------"); + service = Sncp.createSimpleRemoteService(SncpTestIService.class, null, transFactory, new InetSocketAddress("127.0.0.1", 7070), "g70"); + for (Method method : service.getClass().getDeclaredMethods()) { + System.out.println(method); + } + System.out.println("-----------------------------------"); + for (Method method : SncpClient.parseMethod(service.getClass())) { + System.out.println(method); + } + System.out.println("-----------------------------------"); + } +} diff --git a/test/org/redkale/test/sncp/_DynLocalSncpTestService.java b/src/test/java/org/redkale/test/sncp/_DynLocalSncpTestService.java similarity index 95% rename from test/org/redkale/test/sncp/_DynLocalSncpTestService.java rename to src/test/java/org/redkale/test/sncp/_DynLocalSncpTestService.java index 27a8596da..1659e2be0 100644 --- a/test/org/redkale/test/sncp/_DynLocalSncpTestService.java +++ b/src/test/java/org/redkale/test/sncp/_DynLocalSncpTestService.java @@ -1,20 +1,20 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.sncp; - -import org.redkale.net.sncp.*; -import org.redkale.util.ResourceType; - -/** - * - * @author zhangjx - */ -@ResourceType(SncpTestIService.class) -public class _DynLocalSncpTestService extends SncpTestServiceImpl { - - private SncpClient _redkale_client; - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.sncp; + +import org.redkale.net.sncp.*; +import org.redkale.util.ResourceType; + +/** + * + * @author zhangjx + */ +@ResourceType(SncpTestIService.class) +public class _DynLocalSncpTestService extends SncpTestServiceImpl { + + private SncpClient _redkale_client; + +} diff --git a/test/org/redkale/test/source/BaseEntity.java b/src/test/java/org/redkale/test/source/BaseEntity.java similarity index 95% rename from test/org/redkale/test/source/BaseEntity.java rename to src/test/java/org/redkale/test/source/BaseEntity.java index ee78d0676..8aac3d93c 100644 --- a/test/org/redkale/test/source/BaseEntity.java +++ b/src/test/java/org/redkale/test/source/BaseEntity.java @@ -1,22 +1,22 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.io.Serializable; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public abstract class BaseEntity implements Serializable { - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.io.Serializable; +import org.redkale.convert.json.*; + +/** + * + * @author zhangjx + */ +public abstract class BaseEntity implements Serializable { + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + +} diff --git a/test/org/redkale/test/source/CacheTestBean.java b/src/test/java/org/redkale/test/source/CacheTestBean.java similarity index 97% rename from test/org/redkale/test/source/CacheTestBean.java rename to src/test/java/org/redkale/test/source/CacheTestBean.java index 74025ea2a..40b7abcb8 100644 --- a/test/org/redkale/test/source/CacheTestBean.java +++ b/src/test/java/org/redkale/test/source/CacheTestBean.java @@ -1,100 +1,100 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.lang.reflect.Method; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; -import javax.persistence.Id; -import org.redkale.convert.json.JsonConvert; -import org.redkale.source.*; - -/** - * - * @author zhangjx - */ -@VirtualEntity(loader = CacheTestBean.DefaultBeanLoader.class) -public class CacheTestBean { - - @Id - private long pkgid; - - private String name; - - private long price; - - public static void main(String[] args) throws Exception { - Method method = EntityInfo.class.getDeclaredMethod("load", Class.class, boolean.class, Properties.class, DataSource.class, BiFunction.class); - method.setAccessible(true); - final EntityInfo info = (EntityInfo) method.invoke(null, CacheTestBean.class, true, new Properties(), null, new CacheTestBean.DefaultBeanLoader()); - EntityCache cache = new EntityCache(info, null); - cache.fullLoadAsync(); - - System.out.println(cache.queryColumnMap("pkgid", FilterFunc.COUNT, "name", null)); - System.out.println(cache.queryColumnMap("pkgid", FilterFunc.DISTINCTCOUNT, "name", null)); - System.out.println(cache.queryColumnMap("pkgid", FilterFunc.AVG, "price", null)); - System.out.println(cache.queryColumnMap("pkgid", FilterFunc.SUM, "price", null)); - System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MAX, "price", null)); - System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MIN, "price", null)); - - System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.EQUAL, "BB"))); - System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.IGNORECASEEQUAL, "BB"))); - System.out.println(cache.querySheet(null, null, FilterNode.create("name", FilterExpress.IGNORECASENOTLIKE, "B"))); - } - - public CacheTestBean() { - } - - public CacheTestBean(long pkgid, String name, long price) { - this.pkgid = pkgid; - this.name = name; - this.price = price; - } - - public long getPkgid() { - return pkgid; - } - - public void setPkgid(long pkgid) { - this.pkgid = pkgid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public long getPrice() { - return price; - } - - public void setPrice(long price) { - this.price = price; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public static class DefaultBeanLoader implements BiFunction> { - - @Override - public CompletableFuture apply(DataSource t, EntityInfo u) { - final List list = new ArrayList<>(); - list.add(new CacheTestBean(1, "a", 12)); - list.add(new CacheTestBean(1, "a", 18)); - list.add(new CacheTestBean(2, "b", 20)); - list.add(new CacheTestBean(2, "bb", 60)); - return CompletableFuture.completedFuture(list); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.BiFunction; +import javax.persistence.Id; +import org.redkale.convert.json.JsonConvert; +import org.redkale.source.*; + +/** + * + * @author zhangjx + */ +@VirtualEntity(loader = CacheTestBean.DefaultBeanLoader.class) +public class CacheTestBean { + + @Id + private long pkgid; + + private String name; + + private long price; + + public static void main(String[] args) throws Exception { + Method method = EntityInfo.class.getDeclaredMethod("load", Class.class, boolean.class, Properties.class, DataSource.class, BiFunction.class); + method.setAccessible(true); + final EntityInfo info = (EntityInfo) method.invoke(null, CacheTestBean.class, true, new Properties(), null, new CacheTestBean.DefaultBeanLoader()); + EntityCache cache = new EntityCache(info, null); + cache.fullLoadAsync(); + + System.out.println(cache.queryColumnMap("pkgid", FilterFunc.COUNT, "name", null)); + System.out.println(cache.queryColumnMap("pkgid", FilterFunc.DISTINCTCOUNT, "name", null)); + System.out.println(cache.queryColumnMap("pkgid", FilterFunc.AVG, "price", null)); + System.out.println(cache.queryColumnMap("pkgid", FilterFunc.SUM, "price", null)); + System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MAX, "price", null)); + System.out.println(cache.queryColumnMap("pkgid", FilterFunc.MIN, "price", null)); + + System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.EQUAL, "BB"))); + System.out.println(cache.find(null, FilterNode.create("name", FilterExpress.IGNORECASEEQUAL, "BB"))); + System.out.println(cache.querySheet(null, null, FilterNode.create("name", FilterExpress.IGNORECASENOTLIKE, "B"))); + } + + public CacheTestBean() { + } + + public CacheTestBean(long pkgid, String name, long price) { + this.pkgid = pkgid; + this.name = name; + this.price = price; + } + + public long getPkgid() { + return pkgid; + } + + public void setPkgid(long pkgid) { + this.pkgid = pkgid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getPrice() { + return price; + } + + public void setPrice(long price) { + this.price = price; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public static class DefaultBeanLoader implements BiFunction> { + + @Override + public CompletableFuture apply(DataSource t, EntityInfo u) { + final List list = new ArrayList<>(); + list.add(new CacheTestBean(1, "a", 12)); + list.add(new CacheTestBean(1, "a", 18)); + list.add(new CacheTestBean(2, "b", 20)); + list.add(new CacheTestBean(2, "bb", 60)); + return CompletableFuture.completedFuture(list); + } + + } +} diff --git a/test/org/redkale/source/FilterNodeTest.java b/src/test/java/org/redkale/test/source/FilterNodeTest.java similarity index 62% rename from test/org/redkale/source/FilterNodeTest.java rename to src/test/java/org/redkale/test/source/FilterNodeTest.java index 6a47f1b73..0a7532fe0 100644 --- a/test/org/redkale/source/FilterNodeTest.java +++ b/src/test/java/org/redkale/test/source/FilterNodeTest.java @@ -1,307 +1,389 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.source; - -import org.redkale.util.AutoLoad; -import static org.redkale.source.FilterExpress.*; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.*; -import javax.persistence.*; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public class FilterNodeTest { - - public static void main(String[] args) throws Exception { - final Properties props = new Properties(); - final BiFunction> fullloader = (s, t) -> CompletableFuture.completedFuture(new ArrayList()); - final Function func = (Class t) -> EntityInfo.load(t, false, props, null, fullloader); - final EntityInfo carEntity = EntityInfo.load(CarTestTable.class, false, props, null, (s, t) -> CompletableFuture.completedFuture(CarTestTable.createList())); - final EntityInfo userEntity = EntityInfo.load(UserTestTable.class, false, props, null, (s, t) -> CompletableFuture.completedFuture(UserTestTable.createList())); - final EntityInfo typeEntity = EntityInfo.load(CarTypeTestTable.class, false, props, null, (s, t) -> CompletableFuture.completedFuture(CarTypeTestTable.createList())); - - final CarTestBean bean = new CarTestBean(); - bean.carid = 70002; - bean.username = "用户1"; - bean.createtime = 500; - bean.typename = "法拉利"; - FilterNode joinNode1 = FilterJoinNode.create(UserTestTable.class, new String[]{"userid", "username"}, "username", LIKE, bean.username) - .or(FilterJoinNode.create(UserTestTable.class, new String[]{"userid", "username"}, "createtime", GREATERTHAN, bean.createtime)); - FilterNode joinNode2 = FilterJoinNode.create(CarTypeTestTable.class, "cartype", "typename", LIKE, bean.typename); - final FilterNode node = CarTestBean.caridTransient() ? (joinNode2.or(joinNode1)) : FilterNode.create("carid", GREATERTHAN, bean.carid).and(joinNode1).or(joinNode2); - final FilterNode beanNode = FilterNodeBean.createFilterNode(bean); - System.out.println("node.string = " + node); - System.out.println("bean.string = " + beanNode); - Map nodeJoinTabalis = node.getJoinTabalis(); - Map beanJoinTabalis = beanNode.getJoinTabalis(); - CharSequence nodeJoinsql = node.createSQLJoin(func, false, nodeJoinTabalis, new HashSet<>(), carEntity); - CharSequence beanJoinsql = beanNode.createSQLJoin(func, false, beanJoinTabalis, new HashSet<>(), carEntity); - CharSequence nodeWhere = node.createSQLExpress(carEntity, nodeJoinTabalis); - CharSequence beanWhere = beanNode.createSQLExpress(carEntity, beanJoinTabalis); - System.out.println("node.sql = SELECT a.* FROM " + CarTestTable.class.getSimpleName().toLowerCase() + " a" + (nodeJoinsql == null ? "" : nodeJoinsql) + " WHERE " + nodeWhere); - System.out.println("bean.sql = SELECT a.* FROM " + CarTestTable.class.getSimpleName().toLowerCase() + " a" + (beanJoinsql == null ? "" : beanJoinsql) + " WHERE " + beanWhere); - boolean r1 = node.isCacheUseable(func); - if (!r1) System.err.println("node.isCacheUseable 应该是true"); - boolean r2 = beanNode.isCacheUseable(func); - if (!r2) System.err.println("beanNode.isCacheUseable 应该是true"); - - System.out.println("node.Predicate = " + node.createPredicate(carEntity.getCache())); - System.out.println("bean.Predicate = " + beanNode.createPredicate(carEntity.getCache())); - System.out.println("node.sheet = " + carEntity.getCache().querySheet(null, new Flipper(), node)); - System.out.println("bean.sheet = " + carEntity.getCache().querySheet(null, new Flipper(), beanNode)); - } - - public static class CarTestBean implements FilterBean { - - @FilterGroup("[OR].[AND]a") - @FilterColumn(express = GREATERTHAN) - @Transient - public long carid; - - @FilterGroup("[OR].[AND]a.[OR]c") - @FilterColumn(express = LIKE) - @FilterJoinColumn(table = UserTestTable.class, columns = {"userid", "username"}) - public String username; - - @FilterGroup("[OR].[AND]a.[OR]c") - @FilterColumn(express = GREATERTHAN) - @FilterJoinColumn(table = UserTestTable.class, columns = {"userid", "username"}) - public long createtime; - - @FilterGroup("[OR]") - @FilterColumn(express = LIKE) - @FilterJoinColumn(table = CarTypeTestTable.class, columns = {"cartype"}) - public String typename; - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public static boolean caridTransient() { - try { - return CarTestBean.class.getDeclaredField("carid").getAnnotation(Transient.class) != null; - } catch (Exception e) { - e.printStackTrace(); - return true; - } - } - } - - @AutoLoad - @Cacheable - public static class CarTestTable { - - public static List createList() { - List list = new ArrayList<>(); - - list.add(new CarTestTable(70001, 101, 1000011, "我的车")); - list.add(new CarTestTable(70002, 102, 1000012, "我的车")); - list.add(new CarTestTable(70003, 103, 1000013, "我的车")); - list.add(new CarTestTable(70004, 104, 1000014, "我的车")); - list.add(new CarTestTable(70005, 105, 1000015, "我的车")); - - list.add(new CarTestTable(70201, 201, 1000031, "我的车")); - list.add(new CarTestTable(70202, 202, 1000032, "我的车")); - list.add(new CarTestTable(70203, 203, 1000033, "我的车")); - list.add(new CarTestTable(70204, 204, 1000034, "我的车")); - list.add(new CarTestTable(70205, 205, 1000035, "我的车")); - list.add(new CarTestTable(70505, 301, 1008000, "我的车")); - - return list; - } - - @Id - private long carid; - - private int cartype; - - private int userid; - - private String username; - - private String cartitle; - - public CarTestTable() { - - } - - public CarTestTable(long carid, int cartype, int userid, String cartitle) { - this.carid = carid; - this.cartype = cartype; - this.userid = userid; - this.username = "用户" + userid % 1000; - this.cartitle = cartitle; - } - - public long getCarid() { - return carid; - } - - public void setCarid(long carid) { - this.carid = carid; - } - - public int getUserid() { - return userid; - } - - public void setUserid(int userid) { - this.userid = userid; - } - - public String getCartitle() { - return cartitle; - } - - public void setCartitle(String cartitle) { - this.cartitle = cartitle; - } - - public int getCartype() { - return cartype; - } - - public void setCartype(int cartype) { - this.cartype = cartype; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - } - - @AutoLoad - @Cacheable - public static class CarTypeTestTable { - - public static List createList() { - List list = new ArrayList<>(); - list.add(new CarTypeTestTable(101, "奥迪A1")); - list.add(new CarTypeTestTable(102, "奥迪A2")); - list.add(new CarTypeTestTable(103, "奥迪A3")); - list.add(new CarTypeTestTable(104, "奥迪A4")); - list.add(new CarTypeTestTable(105, "奥迪A5")); - list.add(new CarTypeTestTable(201, "奔驰S1")); - list.add(new CarTypeTestTable(202, "奔驰S2")); - list.add(new CarTypeTestTable(203, "奔驰S3")); - list.add(new CarTypeTestTable(204, "奔驰S4")); - list.add(new CarTypeTestTable(205, "奔驰S5")); - list.add(new CarTypeTestTable(301, "法拉利")); - return list; - } - - @Id - private int cartype; - - private String typename; - - public CarTypeTestTable() { - - } - - public CarTypeTestTable(int cartype, String typename) { - this.cartype = cartype; - this.typename = typename; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public int getCartype() { - return cartype; - } - - public void setCartype(int cartype) { - this.cartype = cartype; - } - - public String getTypename() { - return typename; - } - - public void setTypename(String typename) { - this.typename = typename; - } - - } - - @AutoLoad - @Cacheable - public static class UserTestTable { - - public static List createList() { - List list = new ArrayList<>(); - for (int i = 11; i <= 50; i++) { - list.add(new UserTestTable(1000000 + i, "用户" + i, i * 20)); - } - list.add(new UserTestTable(1008000, "车主A", 20)); - return list; - } - - @Id - private int userid; - - private String username; - - private long createtime; - - public UserTestTable() { - } - - public UserTestTable(int userid, String username, long createtime) { - this.userid = userid; - this.username = username; - this.createtime = createtime; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public int getUserid() { - return userid; - } - - public void setUserid(int userid) { - this.userid = userid; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public long getCreatetime() { - return createtime; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import org.redkale.source.*; +import org.redkale.util.AutoLoad; + +import static org.redkale.source.FilterExpress.*; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.function.*; +import javax.persistence.*; + +import org.redkale.convert.json.*; + +import org.junit.jupiter.api.*; + +/** + * @author zhangjx + */ +public class FilterNodeTest { + + private static Function func; + + private static EntityInfo carEntity; + + @BeforeAll + public static void init() throws Exception { + final Properties props = new Properties(); + final BiFunction> fullloader = (s, t) -> CompletableFuture.completedFuture(new ArrayList()); + func = (Class t) -> loadEntityInfo(t, false, props, null, fullloader); + carEntity = loadEntityInfo(CarTestTable.class, false, props, null, (s, t) -> CompletableFuture.completedFuture(CarTestTable.createList())); + final EntityInfo userEntity = loadEntityInfo(UserTestTable.class, false, props, null, (s, t) -> CompletableFuture.completedFuture(UserTestTable.createList())); + final EntityInfo typeEntity = loadEntityInfo(CarTypeTestTable.class, false, props, null, (s, t) -> CompletableFuture.completedFuture(CarTypeTestTable.createList())); + } + + private static EntityInfo loadEntityInfo(Class clazz, final boolean cacheForbidden, final Properties conf, DataSource source, BiFunction> fullloader) { + try { + Method loadMethod = EntityInfo.class.getDeclaredMethod("load", Class.class, boolean.class, Properties.class, DataSource.class, BiFunction.class); + loadMethod.setAccessible(true); + return (EntityInfo) loadMethod.invoke(null, clazz, cacheForbidden, conf, source, fullloader); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Map getJoinTabalis(FilterNode node) { + try { + Method method = FilterNode.class.getDeclaredMethod("getJoinTabalis"); + method.setAccessible(true); + return (Map) method.invoke(node); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static CharSequence createSQLJoin(FilterNode node, Function func, final boolean update, final Map joinTabalis, final Set haset, final EntityInfo info) { + try { + Method method = FilterNode.class.getDeclaredMethod("createSQLJoin", Function.class, boolean.class, Map.class, Set.class, EntityInfo.class); + method.setAccessible(true); + return (CharSequence) method.invoke(node, func, update, joinTabalis, haset, info); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static CharSequence createSQLExpress(FilterNode node, DataSqlSource source, final EntityInfo info, final Map joinTabalis) { + try { + Method method = FilterNode.class.getDeclaredMethod("createSQLExpress", DataSqlSource.class, EntityInfo.class, Map.class); + method.setAccessible(true); + return (CharSequence) method.invoke(node, source, info, joinTabalis); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static boolean isCacheUseable(FilterNode node, final Function entityApplyer) { + try { + Method method = FilterNode.class.getDeclaredMethod("isCacheUseable", Function.class); + method.setAccessible(true); + return (Boolean) method.invoke(node, entityApplyer); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static Predicate createPredicate(FilterNode node, final EntityCache cache) { + try { + Method method = FilterNode.class.getDeclaredMethod("createPredicate", EntityCache.class); + method.setAccessible(true); + return (Predicate) method.invoke(node, cache); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Test + public void run() throws Exception { + final CarTestBean bean = CarTestBean.create(); + FilterNode joinNode1 = FilterJoinNode.create(UserTestTable.class, new String[]{"userid", "username"}, "username", LIKE, bean.username) + .or(FilterJoinNode.create(UserTestTable.class, new String[]{"userid", "username"}, "createtime", GREATERTHAN, bean.createtime)); + FilterNode joinNode2 = FilterJoinNode.create(CarTypeTestTable.class, "cartype", "typename", LIKE, bean.typename); + final FilterNode node = CarTestBean.caridTransient() ? (joinNode2.or(joinNode1)) : FilterNode.create("carid", GREATERTHAN, bean.carid).and(joinNode1).or(joinNode2); + final FilterNode beanNode = FilterNodeBean.createFilterNode(bean); + System.out.println("node.string = " + node); + System.out.println("bean.string = " + beanNode); + Assertions.assertEquals("(CarTypeTestTable.typename LIKE '%法拉利%' OR (UserTestTable.username LIKE '%用户1%' OR UserTestTable.createtime > 500))", node.toString()); + Assertions.assertEquals(node.toString(), beanNode.toString()); + Map nodeJoinTabalis = getJoinTabalis(node); + Map beanJoinTabalis = getJoinTabalis(beanNode); + CharSequence nodeJoinsql = createSQLJoin(node, func, false, nodeJoinTabalis, new HashSet<>(), carEntity); + CharSequence beanJoinsql = createSQLJoin(beanNode, func, false, beanJoinTabalis, new HashSet<>(), carEntity); + CharSequence nodeWhere = createSQLExpress(node, null, carEntity, nodeJoinTabalis); + CharSequence beanWhere = createSQLExpress(beanNode, null, carEntity, beanJoinTabalis); + System.out.println("node.sql = SELECT a.* FROM " + CarTestTable.class.getSimpleName().toLowerCase() + " a" + (nodeJoinsql == null ? "" : nodeJoinsql) + " WHERE " + nodeWhere); + System.out.println("bean.sql = SELECT a.* FROM " + CarTestTable.class.getSimpleName().toLowerCase() + " a" + (beanJoinsql == null ? "" : beanJoinsql) + " WHERE " + beanWhere); + boolean r1 = isCacheUseable(node, func); + Assertions.assertTrue(r1); + if (!r1) System.err.println("node.isCacheUseable 应该是true"); + boolean r2 = isCacheUseable(beanNode, func); + Assertions.assertTrue(r2); + + System.out.println("node.Predicate = " + createPredicate(node, carEntity.getCache())); + System.out.println("bean.Predicate = " + createPredicate(beanNode, carEntity.getCache())); + System.out.println("node.sheet = " + carEntity.getCache().querySheet(null, new Flipper(), node)); + System.out.println("bean.sheet = " + carEntity.getCache().querySheet(null, new Flipper(), beanNode)); + } + + public static class CarTestBean implements FilterBean { + + @FilterGroup("[OR].[AND]a") + @FilterColumn(express = GREATERTHAN) + @Transient + public long carid; + + @FilterGroup("[OR].[AND]a.[OR]c") + @FilterColumn(express = LIKE) + @FilterJoinColumn(table = UserTestTable.class, columns = {"userid", "username"}) + public String username; + + @FilterGroup("[OR].[AND]a.[OR]c") + @FilterColumn(express = GREATERTHAN) + @FilterJoinColumn(table = UserTestTable.class, columns = {"userid", "username"}) + public long createtime; + + @FilterGroup("[OR]") + @FilterColumn(express = LIKE) + @FilterJoinColumn(table = CarTypeTestTable.class, columns = {"cartype"}) + public String typename; + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public static boolean caridTransient() { + try { + return CarTestBean.class.getDeclaredField("carid").getAnnotation(Transient.class) != null; + } catch (Exception e) { + e.printStackTrace(); + return true; + } + } + + public static CarTestBean create() { + final CarTestBean bean = new CarTestBean(); + bean.carid = 70002; + bean.username = "用户1"; + bean.createtime = 500; + bean.typename = "法拉利"; + return bean; + } + } + + @AutoLoad + @Cacheable + public static class CarTestTable { + + public static List createList() { + List list = new ArrayList<>(); + + list.add(new CarTestTable(70001, 101, 1000011, "我的车")); + list.add(new CarTestTable(70002, 102, 1000012, "我的车")); + list.add(new CarTestTable(70003, 103, 1000013, "我的车")); + list.add(new CarTestTable(70004, 104, 1000014, "我的车")); + list.add(new CarTestTable(70005, 105, 1000015, "我的车")); + + list.add(new CarTestTable(70201, 201, 1000031, "我的车")); + list.add(new CarTestTable(70202, 202, 1000032, "我的车")); + list.add(new CarTestTable(70203, 203, 1000033, "我的车")); + list.add(new CarTestTable(70204, 204, 1000034, "我的车")); + list.add(new CarTestTable(70205, 205, 1000035, "我的车")); + list.add(new CarTestTable(70505, 301, 1008000, "我的车")); + + return list; + } + + @Id + private long carid; + + private int cartype; + + private int userid; + + private String username; + + private String cartitle; + + public CarTestTable() { + + } + + public CarTestTable(long carid, int cartype, int userid, String cartitle) { + this.carid = carid; + this.cartype = cartype; + this.userid = userid; + this.username = "用户" + userid % 1000; + this.cartitle = cartitle; + } + + public long getCarid() { + return carid; + } + + public void setCarid(long carid) { + this.carid = carid; + } + + public int getUserid() { + return userid; + } + + public void setUserid(int userid) { + this.userid = userid; + } + + public String getCartitle() { + return cartitle; + } + + public void setCartitle(String cartitle) { + this.cartitle = cartitle; + } + + public int getCartype() { + return cartype; + } + + public void setCartype(int cartype) { + this.cartype = cartype; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + } + + @AutoLoad + @Cacheable + public static class CarTypeTestTable { + + public static List createList() { + List list = new ArrayList<>(); + list.add(new CarTypeTestTable(101, "奥迪A1")); + list.add(new CarTypeTestTable(102, "奥迪A2")); + list.add(new CarTypeTestTable(103, "奥迪A3")); + list.add(new CarTypeTestTable(104, "奥迪A4")); + list.add(new CarTypeTestTable(105, "奥迪A5")); + list.add(new CarTypeTestTable(201, "奔驰S1")); + list.add(new CarTypeTestTable(202, "奔驰S2")); + list.add(new CarTypeTestTable(203, "奔驰S3")); + list.add(new CarTypeTestTable(204, "奔驰S4")); + list.add(new CarTypeTestTable(205, "奔驰S5")); + list.add(new CarTypeTestTable(301, "法拉利")); + return list; + } + + @Id + private int cartype; + + private String typename; + + public CarTypeTestTable() { + + } + + public CarTypeTestTable(int cartype, String typename) { + this.cartype = cartype; + this.typename = typename; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public int getCartype() { + return cartype; + } + + public void setCartype(int cartype) { + this.cartype = cartype; + } + + public String getTypename() { + return typename; + } + + public void setTypename(String typename) { + this.typename = typename; + } + + } + + @AutoLoad + @Cacheable + public static class UserTestTable { + + public static List createList() { + List list = new ArrayList<>(); + for (int i = 11; i <= 50; i++) { + list.add(new UserTestTable(1000000 + i, "用户" + i, i * 20)); + } + list.add(new UserTestTable(1008000, "车主A", 20)); + return list; + } + + @Id + private int userid; + + private String username; + + private long createtime; + + public UserTestTable() { + } + + public UserTestTable(int userid, String username, long createtime) { + this.userid = userid; + this.username = username; + this.createtime = createtime; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public int getUserid() { + return userid; + } + + public void setUserid(int userid) { + this.userid = userid; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public long getCreatetime() { + return createtime; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } + + } +} diff --git a/test/org/redkale/test/source/JDBCTest.java b/src/test/java/org/redkale/test/source/JDBCTest.java similarity index 90% rename from test/org/redkale/test/source/JDBCTest.java rename to src/test/java/org/redkale/test/source/JDBCTest.java index 835df0f6d..08d7152c7 100644 --- a/test/org/redkale/test/source/JDBCTest.java +++ b/src/test/java/org/redkale/test/source/JDBCTest.java @@ -1,41 +1,41 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import org.redkale.source.*; - -/** - * - * @author zhangjx - */ -public class JDBCTest { - - public static void main(String[] args) throws Exception { - DataSource source = DataSources.createDataSource(""); //耗时:37415 - int count = 1000; - LoginTestRecord last = null; - long s = System.currentTimeMillis(); - int c = 0; - try { - for (int i = 0; i < count; i++) { - LoginTestRecord record = new LoginTestRecord(); - record.setSessionid(Long.toHexString(System.nanoTime())); - record.setLoginagent("win7"); - record.setLogintime(System.currentTimeMillis()); - record.setLoginip("127.0.0.1"); - record.setUserid(i); - source.insert(record); - last = record; - c = i; - } - } catch (Exception e) { - System.out.println("异常了: " + c); - e.printStackTrace(); - } - long e = System.currentTimeMillis() - s; - System.out.println("耗时:" + e); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import org.redkale.source.*; + +/** + * + * @author zhangjx + */ +public class JDBCTest { + + public static void main(String[] args) throws Exception { + DataSource source = DataSources.createDataSource(null, ""); //耗时:37415 + int count = 1000; + LoginTestRecord last = null; + long s = System.currentTimeMillis(); + int c = 0; + try { + for (int i = 0; i < count; i++) { + LoginTestRecord record = new LoginTestRecord(); + record.setSessionid(Long.toHexString(System.nanoTime())); + record.setLoginagent("win7"); + record.setLogintime(System.currentTimeMillis()); + record.setLoginip("127.0.0.1"); + record.setUserid(i); + source.insert(record); + last = record; + c = i; + } + } catch (Exception e) { + System.out.println("异常了: " + c); + e.printStackTrace(); + } + long e = System.currentTimeMillis() - s; + System.out.println("耗时:" + e); + } +} diff --git a/test/org/redkale/test/source/JsonRecord.java b/src/test/java/org/redkale/test/source/JsonRecord.java similarity index 96% rename from test/org/redkale/test/source/JsonRecord.java rename to src/test/java/org/redkale/test/source/JsonRecord.java index 9db9341b2..f5b1defbf 100644 --- a/test/org/redkale/test/source/JsonRecord.java +++ b/src/test/java/org/redkale/test/source/JsonRecord.java @@ -1,122 +1,122 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.io.Serializable; -import java.util.*; -import javax.persistence.*; -import org.redkale.convert.json.JsonConvert; -import org.redkale.source.*; - -/** - * - * @author zhangjx - */ -//@Cacheable -public class JsonRecord { - - @SourceConvert - private static JsonConvert createConvert() { - return JsonConvert.root(); - } - - @Id - @Column(comment = "主键ID;") - private long recordid; - - @Column(comment = ";") - private String recordname = ""; - - @Column(comment = ";") - private Map rmap; - - @Column(comment = ";") - private List rlist; - - @Column(comment = ";") - private Set rset; - - public static JsonRecord create() { - JsonRecord record = new JsonRecord(); - record.setRecordid(System.currentTimeMillis()); - record.setRecordname("my name"); - Map map = new HashMap<>(); - map.put("str111", 10000); - map.put("str222", 20000); - record.setRmap(map); - List list = new ArrayList<>(); - list.add("item11"); - list.add("item22"); - list.add("item11"); - record.setRlist(list); - Set set = new HashSet<>(); - set.add("r1"); - set.add("r2"); - record.setRset(set); - return record; - } - - public static void main(String[] args) throws Throwable { - Properties properties = new Properties(); - properties.put("javax.persistence.jdbc.url", "jdbc:mysql://localhost:3306/center?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"); - properties.put("javax.persistence.jdbc.user", "root"); - properties.put("javax.persistence.jdbc.password", ""); - DataSource source = DataSources.createDataSource("", properties); - JsonRecord record = JsonRecord.create(); - source.insert(record); - source.updateColumn(JsonRecord.class, record.getRecordid(), ColumnValue.mov("recordname", "my name 2")); - record.getRmap().put("haha", 2222); - source.updateColumn(JsonRecord.class, record.getRecordid(), ColumnValue.mov("rmap", (Serializable) (Object) record.getRmap())); - System.out.println(source.find(JsonRecord.class, record.getRecordid())); - System.out.println(source.findColumn(JsonRecord.class, "rmap", record.getRecordid())); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public long getRecordid() { - return recordid; - } - - public void setRecordid(long recordid) { - this.recordid = recordid; - } - - public String getRecordname() { - return recordname; - } - - public void setRecordname(String recordname) { - this.recordname = recordname; - } - - public Map getRmap() { - return rmap; - } - - public void setRmap(Map rmap) { - this.rmap = rmap; - } - - public List getRlist() { - return rlist; - } - - public void setRlist(List rlist) { - this.rlist = rlist; - } - - public Set getRset() { - return rset; - } - - public void setRset(Set rset) { - this.rset = rset; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.io.Serializable; +import java.util.*; +import javax.persistence.*; +import org.redkale.convert.json.JsonConvert; +import org.redkale.source.*; + +/** + * + * @author zhangjx + */ +//@Cacheable +public class JsonRecord { + + @SourceConvert + private static JsonConvert createConvert() { + return JsonConvert.root(); + } + + @Id + @Column(comment = "主键ID;") + private long recordid; + + @Column(comment = ";") + private String recordname = ""; + + @Column(comment = ";") + private Map rmap; + + @Column(comment = ";") + private List rlist; + + @Column(comment = ";") + private Set rset; + + public static JsonRecord create() { + JsonRecord record = new JsonRecord(); + record.setRecordid(System.currentTimeMillis()); + record.setRecordname("my name"); + Map map = new HashMap<>(); + map.put("str111", 10000); + map.put("str222", 20000); + record.setRmap(map); + List list = new ArrayList<>(); + list.add("item11"); + list.add("item22"); + list.add("item11"); + record.setRlist(list); + Set set = new HashSet<>(); + set.add("r1"); + set.add("r2"); + record.setRset(set); + return record; + } + + public static void main(String[] args) throws Throwable { + Properties properties = new Properties(); + properties.put("javax.persistence.jdbc.url", "jdbc:mysql://localhost:3306/center?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true"); + properties.put("javax.persistence.jdbc.user", "root"); + properties.put("javax.persistence.jdbc.password", ""); + DataSource source = DataSources.createDataSource("", properties); + JsonRecord record = JsonRecord.create(); + source.insert(record); + source.updateColumn(JsonRecord.class, record.getRecordid(), ColumnValue.mov("recordname", "my name 2")); + record.getRmap().put("haha", 2222); + source.updateColumn(JsonRecord.class, record.getRecordid(), ColumnValue.mov("rmap", (Serializable) (Object) record.getRmap())); + System.out.println(source.find(JsonRecord.class, record.getRecordid())); + System.out.println(source.findColumn(JsonRecord.class, "rmap", record.getRecordid())); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public long getRecordid() { + return recordid; + } + + public void setRecordid(long recordid) { + this.recordid = recordid; + } + + public String getRecordname() { + return recordname; + } + + public void setRecordname(String recordname) { + this.recordname = recordname; + } + + public Map getRmap() { + return rmap; + } + + public void setRmap(Map rmap) { + this.rmap = rmap; + } + + public List getRlist() { + return rlist; + } + + public void setRlist(List rlist) { + this.rlist = rlist; + } + + public Set getRset() { + return rset; + } + + public void setRset(Set rset) { + this.rset = rset; + } + +} diff --git a/test/org/redkale/test/source/LoginRecord.java b/src/test/java/org/redkale/test/source/LoginRecord.java similarity index 97% rename from test/org/redkale/test/source/LoginRecord.java rename to src/test/java/org/redkale/test/source/LoginRecord.java index 3f9fe85d1..c8498bfdd 100644 --- a/test/org/redkale/test/source/LoginRecord.java +++ b/src/test/java/org/redkale/test/source/LoginRecord.java @@ -1,157 +1,157 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.io.Serializable; -import javax.persistence.*; -import org.redkale.source.*; -import org.redkale.util.Utility; - -/** - * - * @author zhangjx - */ -@DistributeTable(strategy = LoginRecord.TableStrategy.class) -public class LoginRecord extends BaseEntity { - - @Id - @Column(comment = "主键ID; 值=create36time(9位)+'-'+UUID(32位)") - private String loginid = ""; //主键ID; 值=create36time(9位)+'-'+UUID(32位) - - @Column(updatable = false, comment = "C端用户ID") - private long userid; //C端用户ID - - @Column(updatable = false, comment = "登录网络类型; wifi/4g/3g") - private String netmode = ""; //登录网络类型; wifi/4g/3g - - @Column(updatable = false, comment = "APP版本信息") - private String appversion = ""; //APP版本信息 - - @Column(updatable = false, comment = "APP操作系统信息") - private String appos = ""; //APP操作系统信息 - - @Column(updatable = false, comment = "登录时客户端信息") - private String loginagent = ""; //登录时客户端信息 - - @Column(updatable = false, comment = "登录时的IP") - private String loginaddr = ""; //登录时的IP - - @Column(updatable = false, comment = "创建时间") - private long createtime; //创建时间 - - /** 以下省略getter setter方法 */ - // - public void setLoginid(String loginid) { - this.loginid = loginid; - } - - public String getLoginid() { - return this.loginid; - } - - public void setUserid(long userid) { - this.userid = userid; - } - - public long getUserid() { - return this.userid; - } - - public void setNetmode(String netmode) { - this.netmode = netmode; - } - - public String getNetmode() { - return this.netmode; - } - - public String getAppversion() { - return appversion; - } - - public void setAppversion(String appversion) { - this.appversion = appversion; - } - - public String getAppos() { - return appos; - } - - public void setAppos(String appos) { - this.appos = appos; - } - - public void setLoginagent(String loginagent) { - this.loginagent = loginagent; - } - - public String getLoginagent() { - return this.loginagent; - } - - public void setLoginaddr(String loginaddr) { - this.loginaddr = loginaddr; - } - - public String getLoginaddr() { - return this.loginaddr; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } - - public long getCreatetime() { - return this.createtime; - } - - private static DataSource source; - - //创建对象 - public static void main(String[] args) throws Throwable { - LoginRecord record = new LoginRecord(); - long now = System.currentTimeMillis(); - record.setCreatetime(now); //设置创建时间 - record.setLoginid(Utility.format36time(now) + "-" + Utility.uuid()); //主键的生成规则 - //.... 填充其他字段 - source.insert(record); - } - - public static class TableStrategy implements DistributeTableStrategy { - - private static final String dayformat = "%1$tY%1$tm%1$td"; //一天一个表 - - private static final String yearformat = "%1$tY"; //一年一个库 - - //过滤查询时调用本方法 - @Override - public String getTable(String table, FilterNode node) { - Serializable day = node.findValue("#day"); //LoginRecord没有day字段,所以前面要加#,表示虚拟字段, 值为yyyyMMdd格式 - if (day != null) getTable(table, (Integer) day, 0L); //存在#day参数则直接使用day值 - Serializable time = node.findValue("createtime"); //存在createtime则使用最小时间,且createtime的范围必须在一天内,因为本表以天为单位建表 - return getTable(table, 0, (time == null ? 0L : (time instanceof Range ? ((Range.LongRange) time).getMin() : (Long) time))); - } - - //创建或单个查询时调用本方法 - @Override - public String getTable(String table, LoginRecord bean) { - return getTable(table, 0, bean.getCreatetime()); - } - - //根据主键ID查询单个记录时调用本方法 - @Override - public String getTable(String table, Serializable primary) { - String id = (String) primary; - return getTable(table, 0, Long.parseLong(id.substring(0, 9), 36)); - } - - private String getTable(String table, int day, long createtime) { //day为0或yyyyMMdd格式数据 - int pos = table.indexOf('.'); - String year = day > 0 ? String.valueOf(day / 10000) : String.format(yearformat, createtime); //没有day取createtime - return "platf_login_" + year + "." + table.substring(pos + 1) + "_" + (day > 0 ? day : String.format(dayformat, createtime)); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.io.Serializable; +import javax.persistence.*; +import org.redkale.source.*; +import org.redkale.util.Utility; + +/** + * + * @author zhangjx + */ +@DistributeTable(strategy = LoginRecord.TableStrategy.class) +public class LoginRecord extends BaseEntity { + + @Id + @Column(comment = "主键ID; 值=create36time(9位)+'-'+UUID(32位)") + private String loginid = ""; //主键ID; 值=create36time(9位)+'-'+UUID(32位) + + @Column(updatable = false, comment = "C端用户ID") + private long userid; //C端用户ID + + @Column(updatable = false, comment = "登录网络类型; wifi/4g/3g") + private String netmode = ""; //登录网络类型; wifi/4g/3g + + @Column(updatable = false, comment = "APP版本信息") + private String appversion = ""; //APP版本信息 + + @Column(updatable = false, comment = "APP操作系统信息") + private String appos = ""; //APP操作系统信息 + + @Column(updatable = false, comment = "登录时客户端信息") + private String loginagent = ""; //登录时客户端信息 + + @Column(updatable = false, comment = "登录时的IP") + private String loginaddr = ""; //登录时的IP + + @Column(updatable = false, comment = "创建时间") + private long createtime; //创建时间 + + /** 以下省略getter setter方法 */ + // + public void setLoginid(String loginid) { + this.loginid = loginid; + } + + public String getLoginid() { + return this.loginid; + } + + public void setUserid(long userid) { + this.userid = userid; + } + + public long getUserid() { + return this.userid; + } + + public void setNetmode(String netmode) { + this.netmode = netmode; + } + + public String getNetmode() { + return this.netmode; + } + + public String getAppversion() { + return appversion; + } + + public void setAppversion(String appversion) { + this.appversion = appversion; + } + + public String getAppos() { + return appos; + } + + public void setAppos(String appos) { + this.appos = appos; + } + + public void setLoginagent(String loginagent) { + this.loginagent = loginagent; + } + + public String getLoginagent() { + return this.loginagent; + } + + public void setLoginaddr(String loginaddr) { + this.loginaddr = loginaddr; + } + + public String getLoginaddr() { + return this.loginaddr; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } + + public long getCreatetime() { + return this.createtime; + } + + private static DataSource source; + + //创建对象 + public static void main(String[] args) throws Throwable { + LoginRecord record = new LoginRecord(); + long now = System.currentTimeMillis(); + record.setCreatetime(now); //设置创建时间 + record.setLoginid(Utility.format36time(now) + "-" + Utility.uuid()); //主键的生成规则 + //.... 填充其他字段 + source.insert(record); + } + + public static class TableStrategy implements DistributeTableStrategy { + + private static final String dayformat = "%1$tY%1$tm%1$td"; //一天一个表 + + private static final String yearformat = "%1$tY"; //一年一个库 + + //过滤查询时调用本方法 + @Override + public String getTable(String table, FilterNode node) { + Serializable day = node.findValue("#day"); //LoginRecord没有day字段,所以前面要加#,表示虚拟字段, 值为yyyyMMdd格式 + if (day != null) getTable(table, (Integer) day, 0L); //存在#day参数则直接使用day值 + Serializable time = node.findValue("createtime"); //存在createtime则使用最小时间,且createtime的范围必须在一天内,因为本表以天为单位建表 + return getTable(table, 0, (time == null ? 0L : (time instanceof Range ? ((Range.LongRange) time).getMin() : (Long) time))); + } + + //创建或单个查询时调用本方法 + @Override + public String getTable(String table, LoginRecord bean) { + return getTable(table, 0, bean.getCreatetime()); + } + + //根据主键ID查询单个记录时调用本方法 + @Override + public String getTable(String table, Serializable primary) { + String id = (String) primary; + return getTable(table, 0, Long.parseLong(id.substring(0, 9), 36)); + } + + private String getTable(String table, int day, long createtime) { //day为0或yyyyMMdd格式数据 + int pos = table.indexOf('.'); + String year = day > 0 ? String.valueOf(day / 10000) : String.format(yearformat, createtime); //没有day取createtime + return "platf_login_" + year + "." + table.substring(pos + 1) + "_" + (day > 0 ? day : String.format(dayformat, createtime)); + } + } +} diff --git a/test/org/redkale/test/source/LoginTestBean.java b/src/test/java/org/redkale/test/source/LoginTestBean.java similarity index 97% rename from test/org/redkale/test/source/LoginTestBean.java rename to src/test/java/org/redkale/test/source/LoginTestBean.java index b7dde106c..fbae30d93 100644 --- a/test/org/redkale/test/source/LoginTestBean.java +++ b/src/test/java/org/redkale/test/source/LoginTestBean.java @@ -1,55 +1,55 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.lang.reflect.Method; -import java.util.*; -import java.util.function.BiFunction; -import org.redkale.source.*; - -/** - * - * @author zhangjx - */ -public class LoginTestBean implements FilterBean { - - private String sessionid; - - private int sid; - public static void main(String[] args) throws Throwable { - LoginTestBean bean = new LoginTestBean(); - bean.setSessionid("xxx"); - bean.setSid(23333); - BiFunction fullloader = (s, z) -> new ArrayList(); - Method method = EntityInfo.class.getDeclaredMethod("load", Class.class, boolean.class, Properties.class, - DataSource.class, BiFunction.class); - method.setAccessible(true); - final EntityInfo info = (EntityInfo) method.invoke(null, CacheTestBean.class, true, new Properties(), null, fullloader); - EntityCache cache = new EntityCache(info, null); - FilterNode node = FilterNodeBean.createFilterNode(bean); - System.out.println("cache = " + cache + ", node = " + node); - Method pre = FilterNode.class.getDeclaredMethod("createPredicate", EntityCache.class); - pre.setAccessible(true); - //为null是因为CacheTestBean 没有sid和sessionid这两个字段 - System.out.println(pre.invoke(node,cache)); - } - public String getSessionid() { - return sessionid; - } - - public void setSessionid(String sessionid) { - this.sessionid = sessionid; - } - - public int getSid() { - return sid; - } - - public void setSid(int sid) { - this.sid = sid; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.function.BiFunction; +import org.redkale.source.*; + +/** + * + * @author zhangjx + */ +public class LoginTestBean implements FilterBean { + + private String sessionid; + + private int sid; + public static void main(String[] args) throws Throwable { + LoginTestBean bean = new LoginTestBean(); + bean.setSessionid("xxx"); + bean.setSid(23333); + BiFunction fullloader = (s, z) -> new ArrayList(); + Method method = EntityInfo.class.getDeclaredMethod("load", Class.class, boolean.class, Properties.class, + DataSource.class, BiFunction.class); + method.setAccessible(true); + final EntityInfo info = (EntityInfo) method.invoke(null, CacheTestBean.class, true, new Properties(), null, fullloader); + EntityCache cache = new EntityCache(info, null); + FilterNode node = FilterNodeBean.createFilterNode(bean); + System.out.println("cache = " + cache + ", node = " + node); + Method pre = FilterNode.class.getDeclaredMethod("createPredicate", EntityCache.class); + pre.setAccessible(true); + //为null是因为CacheTestBean 没有sid和sessionid这两个字段 + System.out.println(pre.invoke(node,cache)); + } + public String getSessionid() { + return sessionid; + } + + public void setSessionid(String sessionid) { + this.sessionid = sessionid; + } + + public int getSid() { + return sid; + } + + public void setSid(int sid) { + this.sid = sid; + } + +} diff --git a/test/org/redkale/test/source/LoginTestRecord.java b/src/test/java/org/redkale/test/source/LoginTestRecord.java similarity index 95% rename from test/org/redkale/test/source/LoginTestRecord.java rename to src/test/java/org/redkale/test/source/LoginTestRecord.java index 3b86e208d..0bc1067a3 100644 --- a/test/org/redkale/test/source/LoginTestRecord.java +++ b/src/test/java/org/redkale/test/source/LoginTestRecord.java @@ -1,92 +1,92 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import javax.persistence.*; -import org.redkale.convert.json.*; - -/** - * CREATE TABLE `LoginTestRecord` ( - * `sessionid` VARCHAR(64) NOT NULL COMMENT '登录会话ID', - * `userid` INT(11) NOT NULL COMMENT '登录用户ID', - * `loginagent` VARCHAR(128) NOT NULL COMMENT '登录端信息', - * `loginip` VARCHAR(255) NOT NULL COMMENT '登录IP', - * `logintime` BIGINT(20) NOT NULL COMMENT '登录时间', - * `logouttime` BIGINT(20) NOT NULL COMMENT '注销时间', - * PRIMARY KEY (`sessionid`) - * ) ENGINE=INNODB DEFAULT CHARSET=utf8; - * - * @author zhangjx - */ -public class LoginTestRecord { - - @Id - private String sessionid; - - private int userid; - - private String loginagent; - - private String loginip; - - private long logintime; - - private long logouttime; - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public String getSessionid() { - return sessionid; - } - - public void setSessionid(String sessionid) { - this.sessionid = sessionid; - } - - public int getUserid() { - return userid; - } - - public void setUserid(int userid) { - this.userid = userid; - } - - public String getLoginagent() { - return loginagent; - } - - public void setLoginagent(String loginagent) { - this.loginagent = loginagent; - } - - public String getLoginip() { - return loginip; - } - - public void setLoginip(String loginip) { - this.loginip = loginip; - } - - public long getLogintime() { - return logintime; - } - - public void setLogintime(long logintime) { - this.logintime = logintime; - } - - public long getLogouttime() { - return logouttime; - } - - public void setLogouttime(long logouttime) { - this.logouttime = logouttime; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import javax.persistence.*; +import org.redkale.convert.json.*; + +/** + * CREATE TABLE `LoginTestRecord` ( + * `sessionid` VARCHAR(64) NOT NULL COMMENT '登录会话ID', + * `userid` INT(11) NOT NULL COMMENT '登录用户ID', + * `loginagent` VARCHAR(128) NOT NULL COMMENT '登录端信息', + * `loginip` VARCHAR(255) NOT NULL COMMENT '登录IP', + * `logintime` BIGINT(20) NOT NULL COMMENT '登录时间', + * `logouttime` BIGINT(20) NOT NULL COMMENT '注销时间', + * PRIMARY KEY (`sessionid`) + * ) ENGINE=INNODB DEFAULT CHARSET=utf8; + * + * @author zhangjx + */ +public class LoginTestRecord { + + @Id + private String sessionid; + + private int userid; + + private String loginagent; + + private String loginip; + + private long logintime; + + private long logouttime; + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public String getSessionid() { + return sessionid; + } + + public void setSessionid(String sessionid) { + this.sessionid = sessionid; + } + + public int getUserid() { + return userid; + } + + public void setUserid(int userid) { + this.userid = userid; + } + + public String getLoginagent() { + return loginagent; + } + + public void setLoginagent(String loginagent) { + this.loginagent = loginagent; + } + + public String getLoginip() { + return loginip; + } + + public void setLoginip(String loginip) { + this.loginip = loginip; + } + + public long getLogintime() { + return logintime; + } + + public void setLogintime(long logintime) { + this.logintime = logintime; + } + + public long getLogouttime() { + return logouttime; + } + + public void setLogouttime(long logouttime) { + this.logouttime = logouttime; + } + +} diff --git a/test/org/redkale/test/source/LoginUserRecord.java b/src/test/java/org/redkale/test/source/LoginUserRecord.java similarity index 96% rename from test/org/redkale/test/source/LoginUserRecord.java rename to src/test/java/org/redkale/test/source/LoginUserRecord.java index cdf0f32fa..8ea9006d2 100644 --- a/test/org/redkale/test/source/LoginUserRecord.java +++ b/src/test/java/org/redkale/test/source/LoginUserRecord.java @@ -1,92 +1,92 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.io.Serializable; -import javax.persistence.*; -import org.redkale.source.*; - -/** - * - * @author zhangjx - */ -@DistributeTable(strategy = LoginUserRecord.TableStrategy.class) -public class LoginUserRecord extends BaseEntity { - - @Id - @Column(comment = "记录ID; 值=userid+'-'+UUID") - private String seqid = ""; //记录ID; 值=userid+'-'+UUID - - @Column(updatable = false, comment = "C端用户ID") - private long userid; //C端用户ID - - @Column(comment = "LoginRecord主键") - private String loginid = ""; //LoginRecord主键 - - @Column(updatable = false, comment = "创建时间") - private long createtime; //创建时间 - - /** 以下省略getter setter方法 */ - // - public String getSeqid() { - return seqid; - } - - public void setSeqid(String seqid) { - this.seqid = seqid; - } - - public long getUserid() { - return userid; - } - - public void setUserid(long userid) { - this.userid = userid; - } - - public String getLoginid() { - return loginid; - } - - public void setLoginid(String loginid) { - this.loginid = loginid; - } - - public long getCreatetime() { - return createtime; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } - - public static class TableStrategy implements DistributeTableStrategy { - - @Override - public String getTable(String table, LoginUserRecord bean) { - return getTable(table, bean.getUserid()); - } - - @Override - public String getTable(String table, FilterNode node) { - Serializable id = node.findValue("userid"); - if (id != null) return getTable(table, id); - return getHashTable(table, (Integer) node.findValue("#hash")); - } - - @Override - public String getTable(String table, Serializable primary) { - String id = (String) primary; - return getHashTable(table, (int) (Long.parseLong(id.substring(0, id.indexOf('-'))) % 100)); - } - - private String getHashTable(String table, int hash) { - int pos = table.indexOf('.'); - return "platf_login." + table.substring(pos + 1) + "_" + (hash > 9 ? hash : ("0" + hash)); - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.io.Serializable; +import javax.persistence.*; +import org.redkale.source.*; + +/** + * + * @author zhangjx + */ +@DistributeTable(strategy = LoginUserRecord.TableStrategy.class) +public class LoginUserRecord extends BaseEntity { + + @Id + @Column(comment = "记录ID; 值=userid+'-'+UUID") + private String seqid = ""; //记录ID; 值=userid+'-'+UUID + + @Column(updatable = false, comment = "C端用户ID") + private long userid; //C端用户ID + + @Column(comment = "LoginRecord主键") + private String loginid = ""; //LoginRecord主键 + + @Column(updatable = false, comment = "创建时间") + private long createtime; //创建时间 + + /** 以下省略getter setter方法 */ + // + public String getSeqid() { + return seqid; + } + + public void setSeqid(String seqid) { + this.seqid = seqid; + } + + public long getUserid() { + return userid; + } + + public void setUserid(long userid) { + this.userid = userid; + } + + public String getLoginid() { + return loginid; + } + + public void setLoginid(String loginid) { + this.loginid = loginid; + } + + public long getCreatetime() { + return createtime; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } + + public static class TableStrategy implements DistributeTableStrategy { + + @Override + public String getTable(String table, LoginUserRecord bean) { + return getTable(table, bean.getUserid()); + } + + @Override + public String getTable(String table, FilterNode node) { + Serializable id = node.findValue("userid"); + if (id != null) return getTable(table, id); + return getHashTable(table, (Integer) node.findValue("#hash")); + } + + @Override + public String getTable(String table, Serializable primary) { + String id = (String) primary; + return getHashTable(table, (int) (Long.parseLong(id.substring(0, id.indexOf('-'))) % 100)); + } + + private String getHashTable(String table, int hash) { + int pos = table.indexOf('.'); + return "platf_login." + table.substring(pos + 1) + "_" + (hash > 9 ? hash : ("0" + hash)); + } + + } +} diff --git a/test/org/redkale/test/source/TestSourceCache.java b/src/test/java/org/redkale/test/source/TestSourceCache.java similarity index 97% rename from test/org/redkale/test/source/TestSourceCache.java rename to src/test/java/org/redkale/test/source/TestSourceCache.java index 8cfc56661..d77e53fec 100644 --- a/test/org/redkale/test/source/TestSourceCache.java +++ b/src/test/java/org/redkale/test/source/TestSourceCache.java @@ -1,141 +1,141 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.lang.reflect.Method; -import java.util.*; -import org.redkale.source.VirtualEntity; -import org.redkale.source.FilterNodeBean; -import org.redkale.source.FilterExpress; -import org.redkale.source.FilterColumn; -import org.redkale.util.Sheet; -import org.redkale.source.FilterBean; -import org.redkale.source.Flipper; -import org.redkale.source.EntityInfo; -import org.redkale.source.FilterNode; -import java.util.concurrent.*; -import java.util.function.BiFunction; -import javax.persistence.*; -import org.redkale.convert.json.*; -import org.redkale.source.*; - -/** - * - * @author zhangjx - */ -public class TestSourceCache { - - public static class TestEntityBean implements FilterBean { - - @FilterColumn(express = FilterExpress.GREATERTHAN) - public int userid; - - @FilterColumn(express = FilterExpress.LIKE) - public String username; - - public TestEntityBean(int userid, String username) { - this.userid = userid; - this.username = username; - } - } - - public static void main(String[] args) throws Exception { - final BiFunction fullloader = (DataSource t, Class u) -> null; - Method method = EntityInfo.class.getDeclaredMethod("load", Class.class, boolean.class, Properties.class, - DataSource.class, BiFunction.class); - method.setAccessible(true); - final EntityInfo info = (EntityInfo) method.invoke(null, TestEntity.class, false, new Properties(), null, fullloader); - TestEntity[] entitys = new TestEntity[10_0000]; - for (int i = 0; i < entitys.length; i++) { - entitys[i] = new TestEntity(i + 1, "用户_" + (i + 1)); - } - long s = System.currentTimeMillis(); - for (TestEntity en : entitys) { - info.getCache().insert(en); - } - long e = System.currentTimeMillis() - s; - System.out.println("插入十万条记录耗时: " + e / 1000.0 + " 秒"); - - s = System.currentTimeMillis(); - TestEntity one = info.getCache().find(9999); - e = System.currentTimeMillis() - s; - System.out.println("十万条数据中查询一条记录耗时: " + e / 1000.0 + " 秒 " + one); - - final Flipper flipper = new Flipper(2); - flipper.setSort("userid DESC, createtime DESC"); - final FilterNode node = FilterNode.create("userid", FilterExpress.GREATERTHAN, 1000).and("username", FilterExpress.LIKE, "用户"); - System.out.println("node = " + node); - Sheet sheet = info.getCache().querySheet(null, flipper, node); - System.out.println(sheet); - System.out.println(info.getCache().querySheet(null, flipper, FilterNodeBean.createFilterNode(new TestEntityBean(1000, "用户")))); - final CountDownLatch cdl = new CountDownLatch(100); - s = System.currentTimeMillis(); - for (int i = 0; i < 100; i++) { - new Thread() { - @Override - public void run() { - for (int k = 0; k < 10; k++) { - info.getCache().querySheet(true, false, null, flipper, node); - } - cdl.countDown(); - } - }.start(); - } - cdl.await(); - e = System.currentTimeMillis() - s; - System.out.println("十万条数据中100并发查询一页循环10次记录耗时: " + e / 1000.0 + " 秒 " + sheet); // CopyOnWriteArrayList 0.798 ConcurrentLinkedQueue 1.063 - } - - @VirtualEntity - @Cacheable - public static class TestEntity { - - @Id - private int userid; - - private String username; - - private long createtime = System.currentTimeMillis(); - - public TestEntity() { - - } - - public TestEntity(int userid, String username) { - this.userid = userid; - this.username = username; - } - - public int getUserid() { - return userid; - } - - public void setUserid(int userid) { - this.userid = userid; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public long getCreatetime() { - return createtime; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.lang.reflect.Method; +import java.util.*; +import org.redkale.source.VirtualEntity; +import org.redkale.source.FilterNodeBean; +import org.redkale.source.FilterExpress; +import org.redkale.source.FilterColumn; +import org.redkale.util.Sheet; +import org.redkale.source.FilterBean; +import org.redkale.source.Flipper; +import org.redkale.source.EntityInfo; +import org.redkale.source.FilterNode; +import java.util.concurrent.*; +import java.util.function.BiFunction; +import javax.persistence.*; +import org.redkale.convert.json.*; +import org.redkale.source.*; + +/** + * + * @author zhangjx + */ +public class TestSourceCache { + + public static class TestEntityBean implements FilterBean { + + @FilterColumn(express = FilterExpress.GREATERTHAN) + public int userid; + + @FilterColumn(express = FilterExpress.LIKE) + public String username; + + public TestEntityBean(int userid, String username) { + this.userid = userid; + this.username = username; + } + } + + public static void main(String[] args) throws Exception { + final BiFunction fullloader = (DataSource t, Class u) -> null; + Method method = EntityInfo.class.getDeclaredMethod("load", Class.class, boolean.class, Properties.class, + DataSource.class, BiFunction.class); + method.setAccessible(true); + final EntityInfo info = (EntityInfo) method.invoke(null, TestEntity.class, false, new Properties(), null, fullloader); + TestEntity[] entitys = new TestEntity[10_0000]; + for (int i = 0; i < entitys.length; i++) { + entitys[i] = new TestEntity(i + 1, "用户_" + (i + 1)); + } + long s = System.currentTimeMillis(); + for (TestEntity en : entitys) { + info.getCache().insert(en); + } + long e = System.currentTimeMillis() - s; + System.out.println("插入十万条记录耗时: " + e / 1000.0 + " 秒"); + + s = System.currentTimeMillis(); + TestEntity one = info.getCache().find(9999); + e = System.currentTimeMillis() - s; + System.out.println("十万条数据中查询一条记录耗时: " + e / 1000.0 + " 秒 " + one); + + final Flipper flipper = new Flipper(2); + flipper.setSort("userid DESC, createtime DESC"); + final FilterNode node = FilterNode.create("userid", FilterExpress.GREATERTHAN, 1000).and("username", FilterExpress.LIKE, "用户"); + System.out.println("node = " + node); + Sheet sheet = info.getCache().querySheet(null, flipper, node); + System.out.println(sheet); + System.out.println(info.getCache().querySheet(null, flipper, FilterNodeBean.createFilterNode(new TestEntityBean(1000, "用户")))); + final CountDownLatch cdl = new CountDownLatch(100); + s = System.currentTimeMillis(); + for (int i = 0; i < 100; i++) { + new Thread() { + @Override + public void run() { + for (int k = 0; k < 10; k++) { + info.getCache().querySheet(true, false, null, flipper, node); + } + cdl.countDown(); + } + }.start(); + } + cdl.await(); + e = System.currentTimeMillis() - s; + System.out.println("十万条数据中100并发查询一页循环10次记录耗时: " + e / 1000.0 + " 秒 " + sheet); // CopyOnWriteArrayList 0.798 ConcurrentLinkedQueue 1.063 + } + + @VirtualEntity + @Cacheable + public static class TestEntity { + + @Id + private int userid; + + private String username; + + private long createtime = System.currentTimeMillis(); + + public TestEntity() { + + } + + public TestEntity(int userid, String username) { + this.userid = userid; + this.username = username; + } + + public int getUserid() { + return userid; + } + + public void setUserid(int userid) { + this.userid = userid; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public long getCreatetime() { + return createtime; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } +} diff --git a/test/org/redkale/test/source/UserDetail.java b/src/test/java/org/redkale/test/source/UserDetail.java similarity index 96% rename from test/org/redkale/test/source/UserDetail.java rename to src/test/java/org/redkale/test/source/UserDetail.java index 22dc7f4ac..3bdd5c6f5 100644 --- a/test/org/redkale/test/source/UserDetail.java +++ b/src/test/java/org/redkale/test/source/UserDetail.java @@ -1,113 +1,113 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.source; - -import java.io.Serializable; -import javax.persistence.*; -import org.redkale.convert.*; -import org.redkale.source.*; - -/** - * - * @author zhangjx - */ -@DistributeTable(strategy = UserDetail.TableStrategy.class) -public class UserDetail extends BaseEntity { - - public static class TableStrategy implements DistributeTableStrategy { - - @Override - public String getTable(String table, UserDetail bean) { - return getTable(table, bean.getUserid()); - } - - @Override - public String getTable(String table, FilterNode node) { - Serializable id = node.findValue("userid"); - if (id != null) return getTable(table, id); - return getHashTable(table, (Integer) node.findValue("#hash")); - } - - @Override - public String getTable(String table, Serializable userid) { - return getHashTable(table, (int) (((Long) userid) % 100)); - } - - private String getHashTable(String table, int hash) { - int pos = table.indexOf('.'); - return "platf_user." + table.substring(pos + 1) + "_" + (hash > 9 ? hash : ("0" + hash)); - } - - } - - @Id - private long userid; //用户ID - - @Column(length = 64, comment = "用户昵称") - private String username = ""; //用户昵称 - - @Column(length = 32, comment = "手机号码") - private String mobile = ""; //手机号码 - - @Column(length = 64, comment = "密码") - @ConvertColumn(ignore = true, type = ConvertType.ALL) - private String password = ""; //密码 - - @Column(length = 128, comment = "备注") - private String remark = ""; //备注 - - @Column(updatable = false, comment = "创建时间") - private long createtime; //创建时间 - - /** 以下省略getter setter方法 */ - public long getUserid() { - return userid; - } - - public void setUserid(long userid) { - this.userid = userid; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getMobile() { - return mobile; - } - - public void setMobile(String mobile) { - this.mobile = mobile; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public long getCreatetime() { - return createtime; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.source; + +import java.io.Serializable; +import javax.persistence.*; +import org.redkale.convert.*; +import org.redkale.source.*; + +/** + * + * @author zhangjx + */ +@DistributeTable(strategy = UserDetail.TableStrategy.class) +public class UserDetail extends BaseEntity { + + public static class TableStrategy implements DistributeTableStrategy { + + @Override + public String getTable(String table, UserDetail bean) { + return getTable(table, bean.getUserid()); + } + + @Override + public String getTable(String table, FilterNode node) { + Serializable id = node.findValue("userid"); + if (id != null) return getTable(table, id); + return getHashTable(table, (Integer) node.findValue("#hash")); + } + + @Override + public String getTable(String table, Serializable userid) { + return getHashTable(table, (int) (((Long) userid) % 100)); + } + + private String getHashTable(String table, int hash) { + int pos = table.indexOf('.'); + return "platf_user." + table.substring(pos + 1) + "_" + (hash > 9 ? hash : ("0" + hash)); + } + + } + + @Id + private long userid; //用户ID + + @Column(length = 64, comment = "用户昵称") + private String username = ""; //用户昵称 + + @Column(length = 32, comment = "手机号码") + private String mobile = ""; //手机号码 + + @Column(length = 64, comment = "密码") + @ConvertColumn(ignore = true, type = ConvertType.ALL) + private String password = ""; //密码 + + @Column(length = 128, comment = "备注") + private String remark = ""; //备注 + + @Column(updatable = false, comment = "创建时间") + private long createtime; //创建时间 + + /** 以下省略getter setter方法 */ + public long getUserid() { + return userid; + } + + public void setUserid(long userid) { + this.userid = userid; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public long getCreatetime() { + return createtime; + } + + public void setCreatetime(long createtime) { + this.createtime = createtime; + } +} diff --git a/test/org/redkale/test/type/OneBean.java b/src/test/java/org/redkale/test/type/OneBean.java similarity index 95% rename from test/org/redkale/test/type/OneBean.java rename to src/test/java/org/redkale/test/type/OneBean.java index b8ec19c49..421e765ff 100644 --- a/test/org/redkale/test/type/OneBean.java +++ b/src/test/java/org/redkale/test/type/OneBean.java @@ -1,15 +1,15 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - */ -public class OneBean { - - public int id; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + */ +public class OneBean { + + public int id; +} diff --git a/test/org/redkale/test/type/OneRound.java b/src/test/java/org/redkale/test/type/OneRound.java similarity index 95% rename from test/org/redkale/test/type/OneRound.java rename to src/test/java/org/redkale/test/type/OneRound.java index a90a680c8..fc62e808d 100644 --- a/test/org/redkale/test/type/OneRound.java +++ b/src/test/java/org/redkale/test/type/OneRound.java @@ -1,14 +1,14 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - */ -public class OneRound { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + */ +public class OneRound { + +} diff --git a/test/org/redkale/test/type/OneService.java b/src/test/java/org/redkale/test/type/OneService.java similarity index 95% rename from test/org/redkale/test/type/OneService.java rename to src/test/java/org/redkale/test/type/OneService.java index 88f6b9e40..2d04a92e3 100644 --- a/test/org/redkale/test/type/OneService.java +++ b/src/test/java/org/redkale/test/type/OneService.java @@ -1,22 +1,22 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -import org.redkale.service.RetResult; - -/** - * - * @author zhangjx - * @param - * @param - */ -public class OneService { - - public RetResult run(OR round, OB bean) { - return RetResult.success(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +import org.redkale.service.RetResult; + +/** + * + * @author zhangjx + * @param + * @param + */ +public class OneService { + + public RetResult run(OR round, OB bean) { + return RetResult.success(); + } + +} diff --git a/test/org/redkale/test/type/ThreeBean.java b/src/test/java/org/redkale/test/type/ThreeBean.java similarity index 95% rename from test/org/redkale/test/type/ThreeBean.java rename to src/test/java/org/redkale/test/type/ThreeBean.java index b17ec09de..39c93d3fd 100644 --- a/test/org/redkale/test/type/ThreeBean.java +++ b/src/test/java/org/redkale/test/type/ThreeBean.java @@ -1,15 +1,15 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - */ -public class ThreeBean extends TwoBean { - - public String desc; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + */ +public class ThreeBean extends TwoBean { + + public String desc; +} diff --git a/test/org/redkale/test/type/ThreeRound.java b/src/test/java/org/redkale/test/type/ThreeRound.java similarity index 95% rename from test/org/redkale/test/type/ThreeRound.java rename to src/test/java/org/redkale/test/type/ThreeRound.java index a8487aec0..4b3b90a4b 100644 --- a/test/org/redkale/test/type/ThreeRound.java +++ b/src/test/java/org/redkale/test/type/ThreeRound.java @@ -1,14 +1,14 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - */ -public class ThreeRound extends TwoRound { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + */ +public class ThreeRound extends TwoRound { + +} diff --git a/test/org/redkale/test/type/ThreeService.java b/src/test/java/org/redkale/test/type/ThreeService.java similarity index 96% rename from test/org/redkale/test/type/ThreeService.java rename to src/test/java/org/redkale/test/type/ThreeService.java index 777a6e688..838117c1b 100644 --- a/test/org/redkale/test/type/ThreeService.java +++ b/src/test/java/org/redkale/test/type/ThreeService.java @@ -1,20 +1,20 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - * @param - * @param - * @param - */ -public class ThreeService extends OneService { - - public String key(K key) { - return "" + key; - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + * @param + * @param + * @param + */ +public class ThreeService extends OneService { + + public String key(K key) { + return "" + key; + } +} diff --git a/test/org/redkale/test/type/TwoBean.java b/src/test/java/org/redkale/test/type/TwoBean.java similarity index 95% rename from test/org/redkale/test/type/TwoBean.java rename to src/test/java/org/redkale/test/type/TwoBean.java index 0797ecbc0..44a46893e 100644 --- a/test/org/redkale/test/type/TwoBean.java +++ b/src/test/java/org/redkale/test/type/TwoBean.java @@ -1,15 +1,15 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - */ -public class TwoBean extends OneBean { - - public String name; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + */ +public class TwoBean extends OneBean { + + public String name; +} diff --git a/test/org/redkale/test/type/TwoRound.java b/src/test/java/org/redkale/test/type/TwoRound.java similarity index 95% rename from test/org/redkale/test/type/TwoRound.java rename to src/test/java/org/redkale/test/type/TwoRound.java index 6ed43badf..7330bba2c 100644 --- a/test/org/redkale/test/type/TwoRound.java +++ b/src/test/java/org/redkale/test/type/TwoRound.java @@ -1,14 +1,14 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - */ -public class TwoRound extends OneRound { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + */ +public class TwoRound extends OneRound { + +} diff --git a/test/org/redkale/test/type/TwoService.java b/src/test/java/org/redkale/test/type/TwoService.java similarity index 95% rename from test/org/redkale/test/type/TwoService.java rename to src/test/java/org/redkale/test/type/TwoService.java index 40e0f251e..292905569 100644 --- a/test/org/redkale/test/type/TwoService.java +++ b/src/test/java/org/redkale/test/type/TwoService.java @@ -1,16 +1,16 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -/** - * - * @author zhangjx - * @param - * @param - */ -public class TwoService extends OneService { - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +/** + * + * @author zhangjx + * @param + * @param + */ +public class TwoService extends OneService { + +} diff --git a/test/org/redkale/test/type/TypeTokenTest.java b/src/test/java/org/redkale/test/type/TypeTokenTest.java similarity index 97% rename from test/org/redkale/test/type/TypeTokenTest.java rename to src/test/java/org/redkale/test/type/TypeTokenTest.java index 87508315d..79fc4e864 100644 --- a/test/org/redkale/test/type/TypeTokenTest.java +++ b/src/test/java/org/redkale/test/type/TypeTokenTest.java @@ -1,32 +1,32 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.type; - -import java.lang.reflect.*; -import org.redkale.util.TypeToken; - -/** - * - * @author zhangjx - */ -public class TypeTokenTest { - - public static void main(String[] args) throws Throwable { - Class declaringClass = ThreeService.class; - ParameterizedType declaringType = (ParameterizedType) declaringClass.getGenericSuperclass(); - System.out.println("getRawType:" + declaringType.getRawType()); - TypeVariable argType0 = (TypeVariable)declaringType.getActualTypeArguments()[0]; - System.out.println("argType0.getBounds[0]:" + argType0.getBounds()[0]); - - for (Method method : declaringClass.getMethods()) { - if (!"run".equals(method.getName())) continue; - System.out.println("返回值应该是: " + ThreeRound.class); - System.out.println("返回值结果是: " + TypeToken.getGenericType(method.getGenericParameterTypes()[0], declaringClass)); - break; - } - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.type; + +import java.lang.reflect.*; +import org.redkale.util.TypeToken; + +/** + * + * @author zhangjx + */ +public class TypeTokenTest { + + public static void main(String[] args) throws Throwable { + Class declaringClass = ThreeService.class; + ParameterizedType declaringType = (ParameterizedType) declaringClass.getGenericSuperclass(); + System.out.println("getRawType:" + declaringType.getRawType()); + TypeVariable argType0 = (TypeVariable)declaringType.getActualTypeArguments()[0]; + System.out.println("argType0.getBounds[0]:" + argType0.getBounds()[0]); + + for (Method method : declaringClass.getMethods()) { + if (!"run".equals(method.getName())) continue; + System.out.println("返回值应该是: " + ThreeRound.class); + System.out.println("返回值结果是: " + TypeToken.getGenericType(method.getGenericParameterTypes()[0], declaringClass)); + break; + } + } + +} diff --git a/test/org/redkale/test/util/CreatorRecord.java b/src/test/java/org/redkale/test/util/CreatorRecord.java similarity index 95% rename from test/org/redkale/test/util/CreatorRecord.java rename to src/test/java/org/redkale/test/util/CreatorRecord.java index 3e1c76da5..3b691e35e 100644 --- a/test/org/redkale/test/util/CreatorRecord.java +++ b/src/test/java/org/redkale/test/util/CreatorRecord.java @@ -1,96 +1,96 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.util; - -import org.redkale.convert.json.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class CreatorRecord { - - private int id = -1; - - private String name; - - private long lval; - - private boolean tval; - - private byte bval; - - private short sval; - - private char cval; - - private float fval; - - private double dval; - - @ConstructorParameters({"id", "name", "lval", "tval", "bval", "sval", "cval", "fval", "dval"}) - CreatorRecord(int id, String name, long lval, boolean tval, byte bval, short sval, char cval, float fval, double dval) { - this.id = id; - this.name = name; - this.lval = lval; - this.tval = tval; - this.bval = bval; - this.sval = sval; - this.cval = cval; - this.fval = fval; - this.dval = dval; - } - - public static void main(String[] args) throws Exception { - CreatorRecord record = Creator.create(CreatorRecord.class).create(new Object[]{null, "ss", null, true, null, (short) 45, null, 4.3f, null}); - String json = record.toString(); - System.out.println(json); - System.out.println(JsonConvert.root().convertFrom(CreatorRecord.class, json).toString()); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - - public int getId() { - return id; - } - - public String getName() { - return name; - } - - public long getLval() { - return lval; - } - - public boolean isTval() { - return tval; - } - - public byte getBval() { - return bval; - } - - public short getSval() { - return sval; - } - - public char getCval() { - return cval; - } - - public float getFval() { - return fval; - } - - public double getDval() { - return dval; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.util; + +import org.redkale.convert.json.*; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class CreatorRecord { + + private int id = -1; + + private String name; + + private long lval; + + private boolean tval; + + private byte bval; + + private short sval; + + private char cval; + + private float fval; + + private double dval; + + @ConstructorParameters({"id", "name", "lval", "tval", "bval", "sval", "cval", "fval", "dval"}) + CreatorRecord(int id, String name, long lval, boolean tval, byte bval, short sval, char cval, float fval, double dval) { + this.id = id; + this.name = name; + this.lval = lval; + this.tval = tval; + this.bval = bval; + this.sval = sval; + this.cval = cval; + this.fval = fval; + this.dval = dval; + } + + public static void main(String[] args) throws Exception { + CreatorRecord record = Creator.create(CreatorRecord.class).create(new Object[]{null, "ss", null, true, null, (short) 45, null, 4.3f, null}); + String json = record.toString(); + System.out.println(json); + System.out.println(JsonConvert.root().convertFrom(CreatorRecord.class, json).toString()); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + + public int getId() { + return id; + } + + public String getName() { + return name; + } + + public long getLval() { + return lval; + } + + public boolean isTval() { + return tval; + } + + public byte getBval() { + return bval; + } + + public short getSval() { + return sval; + } + + public char getCval() { + return cval; + } + + public float getFval() { + return fval; + } + + public double getDval() { + return dval; + } + +} diff --git a/src/test/java/org/redkale/test/util/InvokerTest.java b/src/test/java/org/redkale/test/util/InvokerTest.java new file mode 100644 index 000000000..6716ed67d --- /dev/null +++ b/src/test/java/org/redkale/test/util/InvokerTest.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.util; + +import org.redkale.util.Invoker; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * + * @author zhangjx + */ +public class InvokerTest { + + @Test + public void run1() { + Invoker invoker = Invoker.create(String.class, "toLowerCase"); + Assertions.assertEquals("aaa", invoker.invoke("AAA")); + } + + @Test + public void run2() { + Invoker invoker = Invoker.create(String.class, "equals", Object.class); + Assertions.assertTrue(invoker.invoke("AAA", "AAA")); + } + + @Test + public void run3() { + Invoker invoker = Invoker.create(java.sql.Date.class, "valueOf", String.class); + String str = new java.sql.Date(System.currentTimeMillis()).toString(); + System.out.println(str); + Assertions.assertEquals(str, invoker.invoke(null, str).toString()); + } +} diff --git a/test/org/redkale/test/util/ResourceInjectMain.java b/src/test/java/org/redkale/test/util/ResourceInjectMain.java similarity index 93% rename from test/org/redkale/test/util/ResourceInjectMain.java rename to src/test/java/org/redkale/test/util/ResourceInjectMain.java index 8e8beab47..78083a60a 100644 --- a/test/org/redkale/test/util/ResourceInjectMain.java +++ b/src/test/java/org/redkale/test/util/ResourceInjectMain.java @@ -1,67 +1,67 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.util; - -import java.io.File; -import java.lang.annotation.*; -import static java.lang.annotation.ElementType.FIELD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; -import java.lang.reflect.Field; -import org.redkale.convert.json.JsonConvert; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class ResourceInjectMain { - - public static void main(String[] args) throws Throwable { - ResourceFactory factory = ResourceFactory.root(); - factory.register(new CustomConfLoader()); - InjectBean bean = new InjectBean(); - factory.inject(bean); - } - - public static class CustomConfLoader implements ResourceInjectLoader { - - @Override - public void load(ResourceFactory factory, Object src, CustomConf annotation, Field field, Object attachment) { - try { - field.set(src, new File(annotation.path())); - } catch (Exception e) { - e.printStackTrace(); - } - System.out.println("对象是 src =" + src + ", path=" + annotation.path()); - } - - @Override - public Class annotationType() { - return CustomConf.class; - } - - } - - public static class InjectBean { - - @CustomConf(path = "conf/test.xml") - public File conf; - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } - - @Documented - @Target({FIELD}) - @Retention(RUNTIME) - public static @interface CustomConf { - - String path(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.util; + +import java.io.File; +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import java.lang.reflect.Field; +import org.redkale.convert.json.JsonConvert; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class ResourceInjectMain { + + public static void main(String[] args) throws Throwable { + ResourceFactory factory = ResourceFactory.create(); + factory.register(new CustomConfLoader()); + InjectBean bean = new InjectBean(); + factory.inject(bean); + } + + public static class CustomConfLoader implements ResourceInjectLoader { + + @Override + public void load(ResourceFactory factory, Object src, CustomConf annotation, Field field, Object attachment) { + try { + field.set(src, new File(annotation.path())); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println("对象是 src =" + src + ", path=" + annotation.path()); + } + + @Override + public Class annotationType() { + return CustomConf.class; + } + + } + + public static class InjectBean { + + @CustomConf(path = "conf/test.xml") + public File conf; + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } + + @Documented + @Target({FIELD}) + @Retention(RUNTIME) + public static @interface CustomConf { + + String path(); + } + +} diff --git a/test/org/redkale/test/util/ResourceTest.java b/src/test/java/org/redkale/test/util/ResourceTest.java similarity index 95% rename from test/org/redkale/test/util/ResourceTest.java rename to src/test/java/org/redkale/test/util/ResourceTest.java index 8983d2b93..69a5cae06 100644 --- a/test/org/redkale/test/util/ResourceTest.java +++ b/src/test/java/org/redkale/test/util/ResourceTest.java @@ -1,158 +1,158 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template bigint, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.util; - -import java.math.*; -import javax.annotation.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class ResourceTest { - - public static void main(String[] args) throws Exception { - ResourceFactory factory = ResourceFactory.root(); - factory.register("property.id", "2345"); //注入String类型的property.id - AService aservice = new AService(); - BService bservice = new BService("eeeee"); - - factory.register(aservice); //放进Resource池内,默认的资源名name为"" - factory.register(bservice); //放进Resource池内,默认的资源名name为"" - - factory.inject(aservice); //给aservice注入id、bservice,bigint没有资源,所以为null - factory.inject(bservice); //给bservice注入id、aservice - System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}} - System.out.println(bservice); //输出结果为:{name:"eeeee", id: 2345, aserivce:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}}} - - factory.register("seqid", 200); //放进Resource池内, 同时ResourceFactory会自动更新aservice的seqid值 - System.out.println(factory.find("seqid", int.class)); //输出结果为:200 - factory.register("bigint", new BigInteger("666666666666666")); //放进Resource池内, 同时ResourceFactory会自动更新aservice对象的bigint值 - System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:666666666666666, bservice:{name:eeeee}} 可以看出seqid与bigint值都已自动更新 - - factory.register("property.id", "6789"); //更新Resource池内的id资源值, 同时ResourceFactory会自动更新aservice、bservice的id值 - System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}} - System.out.println(bservice); //输出结果为:{name:"eeeee", id: 6789, aserivce:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}}} - - bservice = new BService("ffff"); - factory.register(bservice); //更新Resource池内name=""的BService资源, 同时ResourceFactory会自动更新aservice的bservice对象 - factory.inject(bservice); - System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:ffff}} - - } - -} - -class BService { - - @Resource(name = "property.id") - private String id; - - @Resource - private AService aservice; - - private String name = ""; - - @ResourceListener - private void changeResource(String name, Object newVal, Object oldVal) { - System.out.println("@Resource = " + name + " 资源变更: newVal = " + newVal + ", oldVal = " + oldVal); - } - - @ConstructorParameters({"name"}) - public BService(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public AService getAservice() { - return aservice; - } - - public void setAservice(AService aservice) { - this.aservice = aservice; - } - - @Override - public String toString() { - return "{name:\"" + name + "\", id: " + id + ", aserivce:" + aservice + "}"; - } -} - -class AService { - - @Resource(name = "property.id") - private String id; - - @Resource(name = "property.id") //property.开头的资源名允许String自动转换成primitive数值类型 - private int intid; - - @Resource(name = "bigint") - private BigInteger bigint; - - @Resource(name = "seqid") - private int seqid; - - @Resource - private BService bservice; - - @Override - public String toString() { - return "{id:\"" + id + "\", intid: " + intid + ", bigint:" + bigint + ", bservice:" + (bservice == null ? null : ("{name:" + bservice.getName() + "}")) + "}"; - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public int getIntid() { - return intid; - } - - public void setIntid(int intid) { - this.intid = intid; - } - - public int getSeqid() { - return seqid; - } - - public void setSeqid(int seqid) { - this.seqid = seqid; - } - - public BigInteger getBigint() { - return bigint; - } - - public void setBigint(BigInteger bigint) { - this.bigint = bigint; - } - - public void setBservice(BService bservice) { - this.bservice = bservice; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template bigint, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.util; + +import java.math.*; +import javax.annotation.*; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +public class ResourceTest { + + public static void main(String[] args) throws Exception { + ResourceFactory factory = ResourceFactory.create(); + factory.register("property.id", "2345"); //注入String类型的property.id + AService aservice = new AService(); + BService bservice = new BService("eeeee"); + + factory.register(aservice); //放进Resource池内,默认的资源名name为"" + factory.register(bservice); //放进Resource池内,默认的资源名name为"" + + factory.inject(aservice); //给aservice注入id、bservice,bigint没有资源,所以为null + factory.inject(bservice); //给bservice注入id、aservice + System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}} + System.out.println(bservice); //输出结果为:{name:"eeeee", id: 2345, aserivce:{id:"2345", intid: 2345, bigint:null, bservice:{name:eeeee}}} + + factory.register("seqid", 200); //放进Resource池内, 同时ResourceFactory会自动更新aservice的seqid值 + System.out.println(factory.find("seqid", int.class)); //输出结果为:200 + factory.register("bigint", new BigInteger("666666666666666")); //放进Resource池内, 同时ResourceFactory会自动更新aservice对象的bigint值 + System.out.println(aservice); //输出结果为:{id:"2345", intid: 2345, bigint:666666666666666, bservice:{name:eeeee}} 可以看出seqid与bigint值都已自动更新 + + factory.register("property.id", "6789"); //更新Resource池内的id资源值, 同时ResourceFactory会自动更新aservice、bservice的id值 + System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}} + System.out.println(bservice); //输出结果为:{name:"eeeee", id: 6789, aserivce:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:eeeee}}} + + bservice = new BService("ffff"); + factory.register(bservice); //更新Resource池内name=""的BService资源, 同时ResourceFactory会自动更新aservice的bservice对象 + factory.inject(bservice); + System.out.println(aservice); //输出结果为:{id:"6789", intid: 6789, bigint:666666666666666, bservice:{name:ffff}} + + } + +} + +class BService { + + @Resource(name = "property.id") + private String id; + + @Resource + private AService aservice; + + private String name = ""; + + @ResourceListener + private void changeResource(String name, Object newVal, Object oldVal) { + System.out.println("@Resource = " + name + " 资源变更: newVal = " + newVal + ", oldVal = " + oldVal); + } + + @ConstructorParameters({"name"}) + public BService(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public AService getAservice() { + return aservice; + } + + public void setAservice(AService aservice) { + this.aservice = aservice; + } + + @Override + public String toString() { + return "{name:\"" + name + "\", id: " + id + ", aserivce:" + aservice + "}"; + } +} + +class AService { + + @Resource(name = "property.id") + private String id; + + @Resource(name = "property.id") //property.开头的资源名允许String自动转换成primitive数值类型 + private int intid; + + @Resource(name = "bigint") + private BigInteger bigint; + + @Resource(name = "seqid") + private int seqid; + + @Resource + private BService bservice; + + @Override + public String toString() { + return "{id:\"" + id + "\", intid: " + intid + ", bigint:" + bigint + ", bservice:" + (bservice == null ? null : ("{name:" + bservice.getName() + "}")) + "}"; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getIntid() { + return intid; + } + + public void setIntid(int intid) { + this.intid = intid; + } + + public int getSeqid() { + return seqid; + } + + public void setSeqid(int seqid) { + this.seqid = seqid; + } + + public BigInteger getBigint() { + return bigint; + } + + public void setBigint(BigInteger bigint) { + this.bigint = bigint; + } + + public void setBservice(BService bservice) { + this.bservice = bservice; + } + +} diff --git a/test/org/redkale/test/util/TestABean.java b/src/test/java/org/redkale/test/util/TestABean.java similarity index 95% rename from test/org/redkale/test/util/TestABean.java rename to src/test/java/org/redkale/test/util/TestABean.java index 9e594f310..7a884976b 100644 --- a/test/org/redkale/test/util/TestABean.java +++ b/src/test/java/org/redkale/test/util/TestABean.java @@ -1,14 +1,14 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.util; - -/** - * - * @author zhangjx - */ -public class TestABean { - public long time; -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.util; + +/** + * + * @author zhangjx + */ +public class TestABean { + public long time; +} diff --git a/test/org/redkale/test/util/TestBean.java b/src/test/java/org/redkale/test/util/TestBean.java similarity index 94% rename from test/org/redkale/test/util/TestBean.java rename to src/test/java/org/redkale/test/util/TestBean.java index 9f9f77c93..e9efd1eb6 100644 --- a/test/org/redkale/test/util/TestBean.java +++ b/src/test/java/org/redkale/test/util/TestBean.java @@ -1,46 +1,46 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.util; - -import java.util.Map; - -/** - * - * @author zhangjx - */ -public class TestBean extends TestABean { - - private String name; - - private int id; - - private Map map; - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public Map getMap() { - return map; - } - - public void setMap(Map map) { - this.map = map; - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.util; + +import java.util.Map; + +/** + * + * @author zhangjx + */ +public class TestBean extends TestABean { + + private String name; + + private int id; + + private Map map; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public Map getMap() { + return map; + } + + public void setMap(Map map) { + this.map = map; + } + +} diff --git a/test/org/redkale/test/util/TestXBean.java b/src/test/java/org/redkale/test/util/TestXBean.java similarity index 95% rename from test/org/redkale/test/util/TestXBean.java rename to src/test/java/org/redkale/test/util/TestXBean.java index d7fe82282..eddd61f86 100644 --- a/test/org/redkale/test/util/TestXBean.java +++ b/src/test/java/org/redkale/test/util/TestXBean.java @@ -1,15 +1,15 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ - -package org.redkale.test.util; - -/** - * - * @author zhangjx - */ -public class TestXBean extends TestBean{ - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.redkale.test.util; + +/** + * + * @author zhangjx + */ +public class TestXBean extends TestBean{ + +} diff --git a/test/org/redkale/test/util/UntilTestMain.java b/src/test/java/org/redkale/test/util/UntilTestMain.java similarity index 96% rename from test/org/redkale/test/util/UntilTestMain.java rename to src/test/java/org/redkale/test/util/UntilTestMain.java index 98ed7c8d8..f5faf6fe1 100644 --- a/test/org/redkale/test/util/UntilTestMain.java +++ b/src/test/java/org/redkale/test/util/UntilTestMain.java @@ -1,104 +1,104 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.util; - -import org.redkale.util.Reproduce; -import org.redkale.util.Attribute; - -/** - * - * @author zhangjx - */ -public class UntilTestMain { - - public static void main(String[] args) throws Throwable { - reproduce(args); - attribute(args); - } - - public static void reproduce(String[] args) throws Throwable { - final TestBean bean = new TestBean(); - bean.setId(123456); - bean.setName("zhangjx"); - bean.time = 2000; - final TestXBean beanx = new TestXBean(); - Reproduce action1 = Reproduce.create(TestXBean.class, TestBean.class); - Reproduce action2 = new Reproduce() { - - @Override - public TestXBean apply(TestXBean dest, TestBean src) { - dest.time = src.time; - dest.setId(src.getId()); - dest.setName(src.getName()); - dest.setMap(src.getMap()); - return dest; - } - }; - final int count = 1_000_000; - long s = System.nanoTime(); - for (int i = 0; i < count; i++) { - action2.apply(beanx, bean); - } - long e = System.nanoTime() - s; - System.out.println("静态Reproduce耗时: " + e); - s = System.nanoTime(); - for (int i = 0; i < count; i++) { - action1.apply(beanx, bean); - } - e = System.nanoTime() - s; - System.out.println("动态Reproduce耗时: " + e); - System.out.println(); - } - - public static void attribute(String[] args) throws Throwable { - final TestBean bean = new TestBean(); - bean.setId(123456); - bean.setName("zhangjx"); - Attribute action1 = Attribute.create(TestBean.class.getDeclaredField("name")); - Attribute action2 = new Attribute() { - - @Override - public String field() { - return "name"; - } - - @Override - public String get(TestBean obj) { - return obj.getName(); - } - - @Override - public void set(TestBean obj, String value) { - obj.setName(value); - } - - @Override - public Class type() { - return String.class; - } - - @Override - public Class declaringClass() { - return TestBean.class; - } - }; - final int count = 1_000_000; - long s = System.nanoTime(); - for (int i = 0; i < count; i++) { - action2.set(bean, "zhangjx2"); - } - long e = System.nanoTime() - s; - System.out.println("静态Attribute耗时: " + e); - s = System.nanoTime(); - for (int i = 0; i < count; i++) { - action1.set(bean, "zhangjx2"); - } - e = System.nanoTime() - s; - System.out.println("动态Attribute耗时: " + e); - System.out.println(); - System.out.println("TestBean.map: " + Attribute.create(TestBean.class.getDeclaredField("map")).genericType()); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.util; + +import org.redkale.util.Reproduce; +import org.redkale.util.Attribute; + +/** + * + * @author zhangjx + */ +public class UntilTestMain { + + public static void main(String[] args) throws Throwable { + reproduce(args); + attribute(args); + } + + public static void reproduce(String[] args) throws Throwable { + final TestBean bean = new TestBean(); + bean.setId(123456); + bean.setName("zhangjx"); + bean.time = 2000; + final TestXBean beanx = new TestXBean(); + Reproduce action1 = Reproduce.create(TestXBean.class, TestBean.class); + Reproduce action2 = new Reproduce() { + + @Override + public TestXBean apply(TestXBean dest, TestBean src) { + dest.time = src.time; + dest.setId(src.getId()); + dest.setName(src.getName()); + dest.setMap(src.getMap()); + return dest; + } + }; + final int count = 1_000_000; + long s = System.nanoTime(); + for (int i = 0; i < count; i++) { + action2.apply(beanx, bean); + } + long e = System.nanoTime() - s; + System.out.println("静态Reproduce耗时: " + e); + s = System.nanoTime(); + for (int i = 0; i < count; i++) { + action1.apply(beanx, bean); + } + e = System.nanoTime() - s; + System.out.println("动态Reproduce耗时: " + e); + System.out.println(); + } + + public static void attribute(String[] args) throws Throwable { + final TestBean bean = new TestBean(); + bean.setId(123456); + bean.setName("zhangjx"); + Attribute action1 = Attribute.create(TestBean.class.getDeclaredField("name")); + Attribute action2 = new Attribute() { + + @Override + public String field() { + return "name"; + } + + @Override + public String get(TestBean obj) { + return obj.getName(); + } + + @Override + public void set(TestBean obj, String value) { + obj.setName(value); + } + + @Override + public Class type() { + return String.class; + } + + @Override + public Class declaringClass() { + return TestBean.class; + } + }; + final int count = 1_000_000; + long s = System.nanoTime(); + for (int i = 0; i < count; i++) { + action2.set(bean, "zhangjx2"); + } + long e = System.nanoTime() - s; + System.out.println("静态Attribute耗时: " + e); + s = System.nanoTime(); + for (int i = 0; i < count; i++) { + action1.set(bean, "zhangjx2"); + } + e = System.nanoTime() - s; + System.out.println("动态Attribute耗时: " + e); + System.out.println(); + System.out.println("TestBean.map: " + Attribute.create(TestBean.class.getDeclaredField("map")).genericType()); + } +} diff --git a/test/org/redkale/test/websocket/ChatWebSocketServlet.java b/src/test/java/org/redkale/test/websocket/ChatWebSocketServlet.java similarity index 97% rename from test/org/redkale/test/websocket/ChatWebSocketServlet.java rename to src/test/java/org/redkale/test/websocket/ChatWebSocketServlet.java index 7d74fc3fe..0bb999e25 100644 --- a/test/org/redkale/test/websocket/ChatWebSocketServlet.java +++ b/src/test/java/org/redkale/test/websocket/ChatWebSocketServlet.java @@ -1,76 +1,76 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.websocket; - -import org.redkale.net.http.WebServlet; -import org.redkale.net.http.WebSocketServlet; -import org.redkale.net.http.WebSocket; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Resource; -import org.redkale.net.http.*; -import org.redkale.test.rest.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -@WebServlet("/ws/chat") -public class ChatWebSocketServlet extends WebSocketServlet { - - @Resource - private UserService userService; - - @Override - public void init(HttpContext context, AnyValue conf) { - System.out.println("本实例的WebSocketNode: " + super.node); - } - - @Override - public void destroy(HttpContext context, AnyValue conf) { - System.out.println("关闭了ChatWebSocketServlet"); - } - - @Override - protected WebSocket createWebSocket() { - - return new WebSocket() { - - private UserInfo user; - - @Override - public void onMessage(ChatMessage message, boolean last) { // text 接收的格式: {"receiveid":200000001, "content":"Hi Redkale!"} - message.sendid = user.getUserid(); //将当前用户设为消息的发送方 - message.sendtime = System.currentTimeMillis(); //设置消息发送时间 - //给接收方发送消息, 即使接收方在其他WebSocket进程节点上有链接,Redkale也会自动发送到其他链接进程节点上。 - super.sendMessage(message, message.receiveid); - } - - @Override - protected CompletableFuture createUserid() { //创建用户ID - this.user = userService.current(String.valueOf(super.getSessionid())); - return CompletableFuture.completedFuture(this.user == null ? null : this.user.getUserid()); - } - - @Override - public CompletableFuture onOpen(HttpRequest request) { - return CompletableFuture.completedFuture(request.getSessionid(false)); //以request中的sessionid字符串作为WebSocket的sessionid - } - - }; - } - - public static class ChatMessage { - - public int sendid; //发送方用户ID - - public int receiveid; //接收方用户ID - - public String content; //文本消息内容 - - public long sendtime; //消息发送时间 - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.websocket; + +import org.redkale.net.http.WebServlet; +import org.redkale.net.http.WebSocketServlet; +import org.redkale.net.http.WebSocket; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Resource; +import org.redkale.net.http.*; +import org.redkale.test.rest.*; +import org.redkale.util.*; + +/** + * + * @author zhangjx + */ +@WebServlet("/ws/chat") +public class ChatWebSocketServlet extends WebSocketServlet { + + @Resource + private UserService userService; + + @Override + public void init(HttpContext context, AnyValue conf) { + System.out.println("本实例的WebSocketNode: " + super.node); + } + + @Override + public void destroy(HttpContext context, AnyValue conf) { + System.out.println("关闭了ChatWebSocketServlet"); + } + + @Override + protected WebSocket createWebSocket() { + + return new WebSocket() { + + private UserInfo user; + + @Override + public void onMessage(ChatMessage message, boolean last) { // text 接收的格式: {"receiveid":200000001, "content":"Hi Redkale!"} + message.sendid = user.getUserid(); //将当前用户设为消息的发送方 + message.sendtime = System.currentTimeMillis(); //设置消息发送时间 + //给接收方发送消息, 即使接收方在其他WebSocket进程节点上有链接,Redkale也会自动发送到其他链接进程节点上。 + super.sendMessage(message, message.receiveid); + } + + @Override + protected CompletableFuture createUserid() { //创建用户ID + this.user = userService.current(String.valueOf(super.getSessionid())); + return CompletableFuture.completedFuture(this.user == null ? null : this.user.getUserid()); + } + + @Override + public CompletableFuture onOpen(HttpRequest request) { + return CompletableFuture.completedFuture(request.getSessionid(false)); //以request中的sessionid字符串作为WebSocket的sessionid + } + + }; + } + + public static class ChatMessage { + + public int sendid; //发送方用户ID + + public int receiveid; //接收方用户ID + + public String content; //文本消息内容 + + public long sendtime; //消息发送时间 + } +} diff --git a/test/org/redkale/test/websocket/Flash843.java b/src/test/java/org/redkale/test/websocket/Flash843.java similarity index 96% rename from test/org/redkale/test/websocket/Flash843.java rename to src/test/java/org/redkale/test/websocket/Flash843.java index bd29f8ddf..86e3c909f 100644 --- a/test/org/redkale/test/websocket/Flash843.java +++ b/src/test/java/org/redkale/test/websocket/Flash843.java @@ -1,29 +1,29 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.websocket; - -import java.io.ByteArrayOutputStream; -import java.net.Socket; - -/** - * - * @author zhangjx - */ -public class Flash843 { - - public static void main(String[] args) throws Exception { - Socket socket = new Socket("113.105.88.229", 843); - socket.getOutputStream().write("".getBytes()); - socket.getOutputStream().flush(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] bytes = new byte[1024]; - int pos; - while ((pos = socket.getInputStream().read(bytes)) != -1) { - out.write(bytes, 0, pos); - } - System.out.println(out.toString()); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.websocket; + +import java.io.ByteArrayOutputStream; +import java.net.Socket; + +/** + * + * @author zhangjx + */ +public class Flash843 { + + public static void main(String[] args) throws Exception { + Socket socket = new Socket("113.105.88.229", 843); + socket.getOutputStream().write("".getBytes()); + socket.getOutputStream().flush(); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] bytes = new byte[1024]; + int pos; + while ((pos = socket.getInputStream().read(bytes)) != -1) { + out.write(bytes, 0, pos); + } + System.out.println(out.toString()); + } +} diff --git a/test/org/redkale/test/websocket/VideoWebSocketServlet.java b/src/test/java/org/redkale/test/websocket/VideoWebSocketServlet.java similarity index 96% rename from test/org/redkale/test/websocket/VideoWebSocketServlet.java rename to src/test/java/org/redkale/test/websocket/VideoWebSocketServlet.java index 7701dc86a..81f6694ed 100644 --- a/test/org/redkale/test/websocket/VideoWebSocketServlet.java +++ b/src/test/java/org/redkale/test/websocket/VideoWebSocketServlet.java @@ -1,125 +1,125 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.websocket; - -import org.redkale.net.http.WebServlet; -import org.redkale.net.http.WebSocketServlet; -import org.redkale.net.http.HttpRequest; -import org.redkale.net.http.WebSocket; -import org.redkale.net.http.HttpServer; -import org.redkale.util.TypeToken; -import org.redkale.util.AnyValue; -import java.io.*; -import java.util.*; -import java.util.concurrent.*; - -/** - * - * @author zhangjx - */ -@WebServlet({"/ws/listen"}) -public class VideoWebSocketServlet extends WebSocketServlet { - - private final Map sessions = new java.util.concurrent.ConcurrentHashMap<>(); - - private final Map users = new HashMap<>(); - - private static final class Entry { - - public WebSocket socket; - - public String username; - - public Serializable userid; - - } - - public VideoWebSocketServlet() { - super(); - users.put("zhangjx", "xxxx"); - } - - @Override - protected WebSocket createWebSocket() { - WebSocket socket = new WebSocket() { - - private final TypeToken> mapToken = new TypeToken>() { - }; - - private boolean repeat = false; - - @Override - public CompletableFuture onOpen(final HttpRequest request) { - String uri = request.getRequestURI(); - int pos = uri.indexOf("/listen/"); - uri = uri.substring(pos + "/listen/".length()); - this.repeat = sessions.get(uri) != null; - if (!this.repeat) this.repeat = users.get(uri) == null; - String sessionid = Long.toString(System.nanoTime()); - if (uri.indexOf('\'') >= 0 || uri.indexOf('"') >= 0) return null; - if (!repeat) sessionid = uri; - return CompletableFuture.completedFuture(sessionid); - } - - @Override - public CompletableFuture onConnected() { - if (repeat) { - super.close(); - } else { - Entry entry = new Entry(); - entry.userid = this.getSessionid(); - entry.username = users.get(entry.userid); - sessions.put(this.getSessionid(), entry); - StringBuilder sb = new StringBuilder(); - for (Map.Entry en : sessions.entrySet()) { - if (sb.length() > 0) sb.append(','); - sb.append("{'userid':'").append(en.getKey()).append("','username':'").append(en.getValue().username).append("'}"); - } - super.send(("{'type':'user_list','users':[" + sb + "]}").replace('\'', '"')); - String msg = ("{'type':'discover_user','user':{'userid':'" + this.getSessionid() + "','username':'" + users.get(this.getSessionid()) + "'}}").replace('\'', '"'); - super.broadcastMessage(msg); - } - return null; - } - - @Override - public void onMessage(Object text, boolean last) { - //System.out.println("接收到消息: " + text); - super.broadcastMessage(text, last); - } - - @Override - public CompletableFuture onClose(int code, String reason) { - sessions.remove(this.getSessionid()); - String msg = ("{'type':'remove_user','user':{'userid':'" + this.getSessionid() + "','username':'" + users.get(this.getSessionid()) + "'}}").replace('\'', '"'); - return super.broadcastMessage(msg); - } - - @Override - protected CompletableFuture createUserid() { - return CompletableFuture.completedFuture("2"); - } - }; - return socket; - } - - public static void main(String[] args) throws Throwable { - CountDownLatch cdl = new CountDownLatch(1); - AnyValue.DefaultAnyValue config = AnyValue.create() - .addValue("threads", System.getProperty("threads")) - .addValue("bufferPoolSize", System.getProperty("bufferPoolSize")) - .addValue("responsePoolSize", System.getProperty("responsePoolSize")) - .addValue("host", System.getProperty("host", "0.0.0.0")) - .addValue("port", System.getProperty("port", "8070")) - .addValue("root", System.getProperty("root", "./root3/")); - HttpServer server = new HttpServer(); - server.addHttpServlet("/pipes", new VideoWebSocketServlet(), "/listen/*"); - server.init(config); - server.start(null); - cdl.await(); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.websocket; + +import org.redkale.net.http.WebServlet; +import org.redkale.net.http.WebSocketServlet; +import org.redkale.net.http.HttpRequest; +import org.redkale.net.http.WebSocket; +import org.redkale.net.http.HttpServer; +import org.redkale.util.TypeToken; +import org.redkale.util.AnyValue; +import java.io.*; +import java.util.*; +import java.util.concurrent.*; + +/** + * + * @author zhangjx + */ +@WebServlet({"/ws/listen"}) +public class VideoWebSocketServlet extends WebSocketServlet { + + private final Map sessions = new java.util.concurrent.ConcurrentHashMap<>(); + + private final Map users = new HashMap<>(); + + private static final class Entry { + + public WebSocket socket; + + public String username; + + public Serializable userid; + + } + + public VideoWebSocketServlet() { + super(); + users.put("zhangjx", "xxxx"); + } + + @Override + protected WebSocket createWebSocket() { + WebSocket socket = new WebSocket() { + + private final TypeToken> mapToken = new TypeToken>() { + }; + + private boolean repeat = false; + + @Override + public CompletableFuture onOpen(final HttpRequest request) { + String uri = request.getRequestURI(); + int pos = uri.indexOf("/listen/"); + uri = uri.substring(pos + "/listen/".length()); + this.repeat = sessions.get(uri) != null; + if (!this.repeat) this.repeat = users.get(uri) == null; + String sessionid = Long.toString(System.nanoTime()); + if (uri.indexOf('\'') >= 0 || uri.indexOf('"') >= 0) return null; + if (!repeat) sessionid = uri; + return CompletableFuture.completedFuture(sessionid); + } + + @Override + public CompletableFuture onConnected() { + if (repeat) { + super.close(); + } else { + Entry entry = new Entry(); + entry.userid = this.getSessionid(); + entry.username = users.get(entry.userid); + sessions.put(this.getSessionid(), entry); + StringBuilder sb = new StringBuilder(); + for (Map.Entry en : sessions.entrySet()) { + if (sb.length() > 0) sb.append(','); + sb.append("{'userid':'").append(en.getKey()).append("','username':'").append(en.getValue().username).append("'}"); + } + super.send(("{'type':'user_list','users':[" + sb + "]}").replace('\'', '"')); + String msg = ("{'type':'discover_user','user':{'userid':'" + this.getSessionid() + "','username':'" + users.get(this.getSessionid()) + "'}}").replace('\'', '"'); + super.broadcastMessage(msg); + } + return null; + } + + @Override + public void onMessage(Object text, boolean last) { + //System.out.println("接收到消息: " + text); + super.broadcastMessage(text, last); + } + + @Override + public CompletableFuture onClose(int code, String reason) { + sessions.remove(this.getSessionid()); + String msg = ("{'type':'remove_user','user':{'userid':'" + this.getSessionid() + "','username':'" + users.get(this.getSessionid()) + "'}}").replace('\'', '"'); + return super.broadcastMessage(msg); + } + + @Override + protected CompletableFuture createUserid() { + return CompletableFuture.completedFuture("2"); + } + }; + return socket; + } + + public static void main(String[] args) throws Throwable { + CountDownLatch cdl = new CountDownLatch(1); + AnyValue.DefaultAnyValue config = AnyValue.create() + .addValue("threads", System.getProperty("threads")) + .addValue("bufferPoolSize", System.getProperty("bufferPoolSize")) + .addValue("responsePoolSize", System.getProperty("responsePoolSize")) + .addValue("host", System.getProperty("host", "0.0.0.0")) + .addValue("port", System.getProperty("port", "8070")) + .addValue("root", System.getProperty("root", "./root3/")); + HttpServer server = new HttpServer(); + server.addHttpServlet("/pipes", new VideoWebSocketServlet(), "/listen/*"); + server.init(config); + server.start(); + cdl.await(); + } + +} diff --git a/test/org/redkale/test/ws/ChatMessage.java b/src/test/java/org/redkale/test/ws/ChatMessage.java similarity index 95% rename from test/org/redkale/test/ws/ChatMessage.java rename to src/test/java/org/redkale/test/ws/ChatMessage.java index 398c8e302..b2d1ec065 100644 --- a/test/org/redkale/test/ws/ChatMessage.java +++ b/src/test/java/org/redkale/test/ws/ChatMessage.java @@ -1,27 +1,27 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.ws; - -import org.redkale.convert.json.JsonConvert; - -/** - * - * @author zhangjx - */ -public class ChatMessage { - - public int fromuserid; - - public int touserid; - - public String fromusername; - - public String content; - - public String toString() { - return JsonConvert.root().convertTo(this); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.ws; + +import org.redkale.convert.json.JsonConvert; + +/** + * + * @author zhangjx + */ +public class ChatMessage { + + public int fromuserid; + + public int touserid; + + public String fromusername; + + public String content; + + public String toString() { + return JsonConvert.root().convertTo(this); + } +} diff --git a/test/org/redkale/test/ws/ChatService.java b/src/test/java/org/redkale/test/ws/ChatService.java similarity index 96% rename from test/org/redkale/test/ws/ChatService.java rename to src/test/java/org/redkale/test/ws/ChatService.java index 68636ab28..45762f3ef 100644 --- a/test/org/redkale/test/ws/ChatService.java +++ b/src/test/java/org/redkale/test/ws/ChatService.java @@ -1,49 +1,49 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.ws; - -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; -import javax.annotation.Resource; -import org.redkale.net.http.WebSocketNode; -import org.redkale.service.*; -import org.redkale.util.Comment; - -/** - * - * @author zhangjx - */ -public class ChatService implements Service { - - @Comment("key=用户ID,value=房间ID") - private final Map userToRooms = new ConcurrentHashMap<>(); - - @Comment("key=房间ID,value=用户ID列表") - private final Map> roomToUsers = new ConcurrentHashMap<>(); - - protected final AtomicInteger idcreator = new AtomicInteger(10000); - - @Resource(name = "chat") - protected WebSocketNode wsnode; - - @Comment("创建一个用户ID") - public int createUserid() { - return idcreator.incrementAndGet(); - } - - @Comment("用户加入指定房间") - public boolean joinRoom(int userid, int roomid) { - userToRooms.put(userid, roomid); - roomToUsers.computeIfAbsent(roomid, (id) -> new CopyOnWriteArrayList()).add(userid); - System.out.println("加入房间: roomid: " + roomid); - return true; - } - - public void chatMessage(ChatMessage message) { - wsnode.broadcastMessage(message); - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.ws; + +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.Resource; +import org.redkale.net.http.WebSocketNode; +import org.redkale.service.*; +import org.redkale.util.Comment; + +/** + * + * @author zhangjx + */ +public class ChatService implements Service { + + @Comment("key=用户ID,value=房间ID") + private final Map userToRooms = new ConcurrentHashMap<>(); + + @Comment("key=房间ID,value=用户ID列表") + private final Map> roomToUsers = new ConcurrentHashMap<>(); + + protected final AtomicInteger idcreator = new AtomicInteger(10000); + + @Resource(name = "chat") + protected WebSocketNode wsnode; + + @Comment("创建一个用户ID") + public int createUserid() { + return idcreator.incrementAndGet(); + } + + @Comment("用户加入指定房间") + public boolean joinRoom(int userid, int roomid) { + userToRooms.put(userid, roomid); + roomToUsers.computeIfAbsent(roomid, (id) -> new CopyOnWriteArrayList()).add(userid); + System.out.println("加入房间: roomid: " + roomid); + return true; + } + + public void chatMessage(ChatMessage message) { + wsnode.broadcastMessage(message); + } +} diff --git a/test/org/redkale/test/ws/ChatWebSocket.java b/src/test/java/org/redkale/test/ws/ChatWebSocket.java similarity index 96% rename from test/org/redkale/test/ws/ChatWebSocket.java rename to src/test/java/org/redkale/test/ws/ChatWebSocket.java index df1f4c8e1..5d2037bc6 100644 --- a/test/org/redkale/test/ws/ChatWebSocket.java +++ b/src/test/java/org/redkale/test/ws/ChatWebSocket.java @@ -1,94 +1,94 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.ws; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import javax.annotation.Resource; -import org.redkale.net.http.*; -import org.redkale.service.RetResult; -import org.redkale.test.rest.*; - -/** - * - * @author zhangjx - */ -//anyuser = true 表示WebSocket.createUserid返回的值不表示用户登录态 -@RestWebSocket(name = "chat", catalog = "ws", comment = "文字聊天", anyuser = true) -public class ChatWebSocket extends WebSocket { - - //@Resource标记的Field只能被修饰为public或protected - @Resource - protected ChatService service; - - @Resource - protected UserService userService; - - protected UserInfo user; - - @Override - protected CompletableFuture onOpen(final HttpRequest request) { - LoginBean bean = request.getJsonParameter(LoginBean.class, "bean"); - RetResult ret = userService.login(bean); - if (ret.isSuccess()) { //登录成功 - user = ret.getResult(); - //随机创建一个sessionid - return CompletableFuture.completedFuture(request.getSessionid(true)); - } else { //登录失败, 返回null - return send("{\"onLoginFailMessage\":" + ret + "}").thenApply(x -> null); - } - } - - @Override - protected CompletableFuture createUserid() { - return CompletableFuture.completedFuture(user.getUserid()); - } - - /** - * 浏览器WebSocket请求: - *
    -     * websocket.send(JSON.stringify({
    -     *      sendmessage:{
    -     *          message:{
    -     *              content : "这是聊天内容"
    -     *          },
    -     *          extmap:{
    -     *              "a":1,
    -     *              "b":"haha"
    -     *          }
    -     *      }
    -     * }));
    -     * 
    - * - * @param message 参数1 - * @param extmap 参数2 - */ - @RestOnMessage(name = "sendmessage") - public void onChatMessage(ChatMessage message, Map extmap) { - message.fromuserid = getUserid(); - message.fromusername = "用户" + getUserid(); - System.out.println("获取消息: message: " + message + ", map: " + extmap); - service.chatMessage(message); - } - - /** - * 浏览器WebSocket请求: - *
    -     * websocket.send(JSON.stringify({
    -     *      joinroom:{
    -     *          roomid: 10212
    -     *      }
    -     * }));
    -     * 
    - * - * @param roomid 参数1 - */ - @RestOnMessage(name = "joinroom") - public void onJoinRoom(int roomid) { - service.joinRoom(getUserid(), roomid); - } - -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.ws; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import javax.annotation.Resource; +import org.redkale.net.http.*; +import org.redkale.service.RetResult; +import org.redkale.test.rest.*; + +/** + * + * @author zhangjx + */ +//anyuser = true 表示WebSocket.createUserid返回的值不表示用户登录态 +@RestWebSocket(name = "chat", catalog = "ws", comment = "文字聊天", anyuser = true) +public class ChatWebSocket extends WebSocket { + + //@Resource标记的Field只能被修饰为public或protected + @Resource + protected ChatService service; + + @Resource + protected UserService userService; + + protected UserInfo user; + + @Override + protected CompletableFuture onOpen(final HttpRequest request) { + LoginBean bean = request.getJsonParameter(LoginBean.class, "bean"); + RetResult ret = userService.login(bean); + if (ret.isSuccess()) { //登录成功 + user = ret.getResult(); + //随机创建一个sessionid + return CompletableFuture.completedFuture(request.getSessionid(true)); + } else { //登录失败, 返回null + return send("{\"onLoginFailMessage\":" + ret + "}").thenApply(x -> null); + } + } + + @Override + protected CompletableFuture createUserid() { + return CompletableFuture.completedFuture(user.getUserid()); + } + + /** + * 浏览器WebSocket请求: + *
    +     * websocket.send(JSON.stringify({
    +     *      sendmessage:{
    +     *          message:{
    +     *              content : "这是聊天内容"
    +     *          },
    +     *          extmap:{
    +     *              "a":1,
    +     *              "b":"haha"
    +     *          }
    +     *      }
    +     * }));
    +     * 
    + * + * @param message 参数1 + * @param extmap 参数2 + */ + @RestOnMessage(name = "sendmessage") + public void onChatMessage(ChatMessage message, Map extmap) { + message.fromuserid = getUserid(); + message.fromusername = "用户" + getUserid(); + System.out.println("获取消息: message: " + message + ", map: " + extmap); + service.chatMessage(message); + } + + /** + * 浏览器WebSocket请求: + *
    +     * websocket.send(JSON.stringify({
    +     *      joinroom:{
    +     *          roomid: 10212
    +     *      }
    +     * }));
    +     * 
    + * + * @param roomid 参数1 + */ + @RestOnMessage(name = "joinroom") + public void onJoinRoom(int roomid) { + service.joinRoom(getUserid(), roomid); + } + +} diff --git a/test/org/redkale/test/wsdync/_DyncChatWebSocketServlet.java b/src/test/java/org/redkale/test/wsdync/_DyncChatWebSocketServlet.java similarity index 96% rename from test/org/redkale/test/wsdync/_DyncChatWebSocketServlet.java rename to src/test/java/org/redkale/test/wsdync/_DyncChatWebSocketServlet.java index 4804ce981..b5017ae72 100644 --- a/test/org/redkale/test/wsdync/_DyncChatWebSocketServlet.java +++ b/src/test/java/org/redkale/test/wsdync/_DyncChatWebSocketServlet.java @@ -1,169 +1,169 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.wsdync; - -import java.io.Serializable; -import java.lang.annotation.Annotation; -import java.util.*; -import java.util.function.BiConsumer; -import javax.annotation.Resource; -import org.redkale.convert.ConvertDisabled; -import org.redkale.convert.json.JsonConvert; -import org.redkale.net.http.*; -import org.redkale.test.ws.ChatMessage; -import org.redkale.test.ws.ChatService; -import org.redkale.test.ws.ChatWebSocket; - -/** - * - * @author zhangjx - */ -//@WebServlet("/ws/chat") -public final class _DyncChatWebSocketServlet extends WebSocketServlet { - - @Resource - private ChatService _redkale_resource_0; - - public static Map _redkale_annotations; - - public _DyncChatWebSocketServlet() { - super(); - this.messageTextType = _DyncChatWebSocketMessage.class; - } - - @Override - protected WebSocket createWebSocket() { - return (WebSocket) new _DyncChatWebSocket(_redkale_resource_0); - } - - @Override - protected BiConsumer createRestOnMessageConsumer() { - return new _DynRestOnMessageConsumer(); - } - - public static class _DyncChatWebSocket extends ChatWebSocket { - - public _DyncChatWebSocket(ChatService service) { - super(); - this.service = service; - } - } - - public static class _DyncChatWebSocketMessage { - - public _DyncChatWebSocketMessage_sendmessagee_00 sendmessage; - - public _DyncChatWebSocketMessage_joinroom_01 joinroom; - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } - - public static class _DyncChatWebSocketMessage_sendmessagee_00 implements WebSocketParam, Runnable { - - public ChatMessage message; - - public Map extmap; - - @ConvertDisabled - public _DyncChatWebSocket _redkale_websocket; - - @Override - public String[] getNames() { - return new String[]{"message", "extmap"}; - } - - @Override - public T getValue(String name) { - if ("message".equals(name)) return (T) message; - if ("extmap".equals(name)) return (T) extmap; - return null; - } - - @Override - public Annotation[] getAnnotations() { - Annotation[] annotations = _redkale_annotations.get("org/redkale/test/wsdync/_DyncChatWebSocketServlet$_DyncChatWebSocketMessage_sendmessagee_00"); - if (annotations == null) return new Annotation[0]; - return Arrays.copyOf(annotations, annotations.length); - } - - public void execute(_DyncChatWebSocket websocket) { - this._redkale_websocket = websocket; - websocket.preOnMessage("sendmessage", this, this); - } - - @Override - public void run() { - _redkale_websocket.onChatMessage(this.message, this.extmap); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } - - public static class _DyncChatWebSocketMessage_joinroom_01 implements WebSocketParam, Runnable { - - public int roomid; - - @ConvertDisabled - public _DyncChatWebSocket _redkale_websocket; - - @Override - public String[] getNames() { - return new String[]{"roomid"}; - } - - @Override - public T getValue(String name) { - if ("roomid".equals(name)) return (T) (Integer) roomid; - return null; - } - - @Override - public Annotation[] getAnnotations() { - Annotation[] annotations = _redkale_annotations.get("org/redkale/test/wsdync/_DyncChatWebSocketServlet$_DyncChatWebSocketMessage_joinroom_01"); - if (annotations == null) return new Annotation[0]; - return Arrays.copyOf(annotations, annotations.length); - } - - public void execute(_DyncChatWebSocket websocket) { - this._redkale_websocket = websocket; - websocket.preOnMessage("joinroom", this, this); - } - - @Override - public void run() { - _redkale_websocket.onJoinRoom(this.roomid); - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } - } - - public static class _DynRestOnMessageConsumer implements BiConsumer { - - @Override - public void accept(WebSocket websocket0, Object message0) { - _DyncChatWebSocket websocket = (_DyncChatWebSocket) websocket0; - _DyncChatWebSocketMessage message = (_DyncChatWebSocketMessage) message0; - if (message.sendmessage != null) { - message.sendmessage.execute(websocket); - return; - } - if (message.joinroom != null) { - message.joinroom.execute(websocket); - return; - } - } - - } -} +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.test.wsdync; + +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.util.*; +import java.util.function.BiConsumer; +import javax.annotation.Resource; +import org.redkale.convert.ConvertDisabled; +import org.redkale.convert.json.JsonConvert; +import org.redkale.net.http.*; +import org.redkale.test.ws.ChatMessage; +import org.redkale.test.ws.ChatService; +import org.redkale.test.ws.ChatWebSocket; + +/** + * + * @author zhangjx + */ +//@WebServlet("/ws/chat") +public final class _DyncChatWebSocketServlet extends WebSocketServlet { + + @Resource + private ChatService _redkale_resource_0; + + public static Map _redkale_annotations; + + public _DyncChatWebSocketServlet() { + super(); + this.messageTextType = _DyncChatWebSocketMessage.class; + } + + @Override + protected WebSocket createWebSocket() { + return (WebSocket) new _DyncChatWebSocket(_redkale_resource_0); + } + + @Override + protected BiConsumer createRestOnMessageConsumer() { + return new _DynRestOnMessageConsumer(); + } + + public static class _DyncChatWebSocket extends ChatWebSocket { + + public _DyncChatWebSocket(ChatService service) { + super(); + this.service = service; + } + } + + public static class _DyncChatWebSocketMessage { + + public _DyncChatWebSocketMessage_sendmessagee_00 sendmessage; + + public _DyncChatWebSocketMessage_joinroom_01 joinroom; + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } + + public static class _DyncChatWebSocketMessage_sendmessagee_00 implements WebSocketParam, Runnable { + + public ChatMessage message; + + public Map extmap; + + @ConvertDisabled + public _DyncChatWebSocket _redkale_websocket; + + @Override + public String[] getNames() { + return new String[]{"message", "extmap"}; + } + + @Override + public T getValue(String name) { + if ("message".equals(name)) return (T) message; + if ("extmap".equals(name)) return (T) extmap; + return null; + } + + @Override + public Annotation[] getAnnotations() { + Annotation[] annotations = _redkale_annotations.get("org/redkale/test/wsdync/_DyncChatWebSocketServlet$_DyncChatWebSocketMessage_sendmessagee_00"); + if (annotations == null) return new Annotation[0]; + return Arrays.copyOf(annotations, annotations.length); + } + + public void execute(_DyncChatWebSocket websocket) { + this._redkale_websocket = websocket; + websocket.preOnMessage("sendmessage", this, this); + } + + @Override + public void run() { + _redkale_websocket.onChatMessage(this.message, this.extmap); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } + + public static class _DyncChatWebSocketMessage_joinroom_01 implements WebSocketParam, Runnable { + + public int roomid; + + @ConvertDisabled + public _DyncChatWebSocket _redkale_websocket; + + @Override + public String[] getNames() { + return new String[]{"roomid"}; + } + + @Override + public T getValue(String name) { + if ("roomid".equals(name)) return (T) (Integer) roomid; + return null; + } + + @Override + public Annotation[] getAnnotations() { + Annotation[] annotations = _redkale_annotations.get("org/redkale/test/wsdync/_DyncChatWebSocketServlet$_DyncChatWebSocketMessage_joinroom_01"); + if (annotations == null) return new Annotation[0]; + return Arrays.copyOf(annotations, annotations.length); + } + + public void execute(_DyncChatWebSocket websocket) { + this._redkale_websocket = websocket; + websocket.preOnMessage("joinroom", this, this); + } + + @Override + public void run() { + _redkale_websocket.onJoinRoom(this.roomid); + } + + @Override + public String toString() { + return JsonConvert.root().convertTo(this); + } + } + + public static class _DynRestOnMessageConsumer implements BiConsumer { + + @Override + public void accept(WebSocket websocket0, Object message0) { + _DyncChatWebSocket websocket = (_DyncChatWebSocket) websocket0; + _DyncChatWebSocketMessage message = (_DyncChatWebSocketMessage) message0; + if (message.sendmessage != null) { + message.sendmessage.execute(websocket); + return; + } + if (message.joinroom != null) { + message.joinroom.execute(websocket); + return; + } + } + + } +} diff --git a/test/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml similarity index 91% rename from test/META-INF/persistence.xml rename to src/test/resources/META-INF/persistence.xml index eba70637b..76bf00936 100644 --- a/test/META-INF/persistence.xml +++ b/src/test/resources/META-INF/persistence.xml @@ -6,7 +6,6 @@ NONE - diff --git a/test/org/redkale/test/convert/BasedEntity.java b/test/org/redkale/test/convert/BasedEntity.java deleted file mode 100644 index d1dc3867c..000000000 --- a/test/org/redkale/test/convert/BasedEntity.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.io.Serializable; -import org.redkale.convert.json.*; - -/** - * - * @author zhangjx - */ -public abstract class BasedEntity implements Serializable { - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -} diff --git a/test/org/redkale/test/convert/BsonTestMain.java b/test/org/redkale/test/convert/BsonTestMain.java deleted file mode 100644 index c8e89afa9..000000000 --- a/test/org/redkale/test/convert/BsonTestMain.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.io.*; -import org.redkale.convert.bson.BsonByteBufferWriter; -import org.redkale.convert.bson.BsonFactory; -import org.redkale.util.Utility; -import org.redkale.convert.bson.BsonConvert; -import java.nio.*; -import java.util.*; -import org.redkale.convert.json.*; -import org.redkale.util.*; - -/** - * - * @author zhangjx - */ -public class BsonTestMain { - - public static void main(String[] args) throws Throwable { - Serializable[] sers = new Serializable[]{"aaa", 4}; - final BsonConvert convert = BsonFactory.root().getConvert(); - byte[] bytes = convert.convertTo(sers); - Utility.println("---", bytes); - Serializable[] a = convert.convertFrom(Serializable[].class, bytes); - System.out.println(Arrays.toString(a)); - Two.main(args); - main2(args); - main3(args); - main4(args); - main5(args); - main6(args); - } - - public static void main2(String[] args) throws Exception { - final BsonConvert convert = BsonFactory.root().getConvert(); - SimpleChildEntity entry = SimpleChildEntity.create(); - byte[] bytes = convert.convertTo(SimpleEntity.class, entry); - System.out.println("长度: " + bytes.length); - BsonByteBufferWriter writer = convert.pollBsonWriter(() -> ByteBuffer.allocate(1)); - convert.convertTo(writer, SimpleEntity.class, entry); - ByteBuffer[] buffers = writer.toBuffers(); - int len = 0; - ByteArrayOutputStream out = new ByteArrayOutputStream(); - for (ByteBuffer b : buffers) { - len += b.remaining(); - byte[] ts = new byte[b.remaining()]; - b.get(ts); - out.write(ts); - b.flip(); - } - System.out.println("长度: " + len); - SimpleChildEntity entry2 = convert.convertFrom(SimpleChildEntity.class, buffers); - System.out.println(entry); - System.out.println(entry2); - } - - public static void main3(String[] args) throws Exception { - final BsonConvert convert = BsonFactory.root().getConvert(); - SimpleChildEntity entry = SimpleChildEntity.create(); - byte[] bytes = convert.convertTo(SimpleEntity.class, entry); - Utility.println(null, bytes); - System.out.println(JsonConvert.root().convertTo(entry)); - SimpleEntity rs = convert.convertFrom(SimpleEntity.class, bytes); - System.out.println(rs.toString()); - System.out.println(JsonConvert.root().convertTo(rs)); - - ComplextEntity bean = new ComplextEntity(); - byte[] bytes2 = convert.convertTo(Object.class, bean); - final int len = bytes2.length; - BsonByteBufferWriter writer = convert.pollBsonWriter(() -> ByteBuffer.allocate(len / 2)); - convert.convertTo(writer, bean); - bytes2 = writer.toArray(); - System.out.println(convert.convertFrom(ComplextEntity.class, bytes2).toString()); - } - - public static void main4(String[] args) throws Exception { - final BsonConvert convert = BsonFactory.root().getConvert(); - SimpleChildEntity entry = SimpleChildEntity.create(); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - convert.convertTo(out, SimpleEntity.class, entry); - byte[] bytes = out.toByteArray(); - Utility.println(null, bytes); - System.out.println(JsonConvert.root().convertTo(entry)); - SimpleEntity rs = convert.convertFrom(SimpleEntity.class, new ByteArrayInputStream(bytes)); - System.out.println(rs.toString()); - - } - - public static void main5(String[] args) throws Exception { - final BsonConvert convert = BsonFactory.root().getConvert(); - - LinkedHashMap map = new LinkedHashMap(); - map.put("1", 1); - map.put("2", "a2"); - byte[] bs = convert.convertTo(Object.class, map); - Object mapobj = convert.convertFrom(Object.class, bs); - System.out.println(mapobj); - } - - public static void main6(String[] args) throws Exception { - final BsonConvert convert = BsonFactory.root().getConvert(); - - Optional val = Optional.ofNullable("haha"); - byte[] bs = convert.convertTo(val); - Object obj = convert.convertFrom(Optional.class, bs); - System.out.println(obj); - bs = convert.convertTo(Object.class, val); - obj = convert.convertFrom(Object.class, bs); - System.out.println(obj); - bs = convert.convertTo(new TypeToken>(){}.getType(), val); - obj = convert.convertFrom(new TypeToken>(){}.getType(), bs); - System.out.println(obj); - System.out.println(JsonConvert.root().convertTo(val)); - } -} diff --git a/test/org/redkale/test/convert/ComplextEntity.java b/test/org/redkale/test/convert/ComplextEntity.java deleted file mode 100644 index 14f4fe11f..000000000 --- a/test/org/redkale/test/convert/ComplextEntity.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import java.util.List; -import javax.persistence.*; - -/** - * - * @author zhangjx - */ -public class ComplextEntity extends BasedEntity { - - @Id - private int userid; - - private String chname = ""; - - @Transient - private boolean flag = true; - - @Transient - private List children; - - @Transient - private SimpleEntity user; - - public int getUserid() { - return userid; - } - - public void setUserid(int userid) { - this.userid = userid; - } - - public String getChname() { - return chname; - } - - public void setChname(String chname) { - this.chname = chname; - } - - public boolean isFlag() { - return flag; - } - - public void setFlag(boolean flag) { - this.flag = flag; - } - - public List getChildren() { - return children; - } - - public void setChildren(List children) { - this.children = children; - } - - public SimpleEntity getUser() { - return user; - } - - public void setUser(SimpleEntity user) { - this.user = user; - } - -} diff --git a/test/org/redkale/test/convert/ConstructorArgsEntity.java b/test/org/redkale/test/convert/ConstructorArgsEntity.java deleted file mode 100644 index 77b86096c..000000000 --- a/test/org/redkale/test/convert/ConstructorArgsEntity.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.redkale.test.convert; - -import org.redkale.convert.bson.*; -import org.redkale.convert.json.*; -import org.redkale.util.ConstructorParameters; - -/** - * 测试不存在无参数的构造函数的bean类解析 - * - * @author zhangjx - */ -public class ConstructorArgsEntity { - - private final int userid; - - private String name; - - private long createtime; - - @ConstructorParameters({"userid", "name"}) - public ConstructorArgsEntity(int userid, String name) { - this.userid = userid; - this.name = name; - } - - public static void main(String[] args) throws Exception { - final JsonConvert jsonConvert = JsonConvert.root(); - final BsonConvert bsonConvert = BsonFactory.root().getConvert(); - ConstructorArgsEntity bean = new ConstructorArgsEntity(12345678, "哈哈"); - bean.setCreatetime(System.currentTimeMillis()); - String json = jsonConvert.convertTo(bean); - System.out.println(json); - System.out.println(jsonConvert.convertFrom(ConstructorArgsEntity.class, json).toString()); - byte[] bytes = bsonConvert.convertTo(bean); - System.out.println(bsonConvert.convertFrom(ConstructorArgsEntity.class, bytes).toString()); - } - - public int getUserid() { - return userid; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public long getCreatetime() { - return createtime; - } - - public void setCreatetime(long createtime) { - this.createtime = createtime; - } - - @Override - public String toString() { - return JsonConvert.root().convertTo(this); - } -}

    - * 详情见: https://redkale.org - * - * @author zhangjx - * @param 结果对象的泛型 - * @param 附件对象的泛型 - */ -public interface SncpAsyncHandler extends CompletionHandler { - - public Object[] sncp_getParams(); - - public void sncp_setParams(Object... params); - - public void sncp_setFuture(CompletableFuture future); - - public CompletableFuture sncp_getFuture(); - - static class Factory { - - /** - *