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 extends NodeServer> type = entry.getType();
- NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
- String p = pros.value().toUpperCase();
- if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
- final Class extends NodeServer> 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 extends NodeServer> 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 extends Service>... extServiceClasses) throws Exception {
- return singleton("", serviceClass, extServiceClasses);
- }
-
- public static T singleton(String name, Class serviceClass, Class extends Service>... 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 extends NodeServer> type = entry.getType();
+ NodeProtocol pros = type.getAnnotation(NodeProtocol.class);
+ String p = pros.value().toUpperCase();
+ if ("SNCP".equals(p) || "HTTP".equals(p)) continue;
+ final Class extends NodeServer> 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 extends NodeServer> 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 extends Service>... extServiceClasses) throws Exception {
+ return singleton("", serviceClass, extServiceClasses);
+ }
+
+ public static T singleton(String name, Class serviceClass, Class extends Service>... 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 extends Annotation> 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 extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
- this(classLoader, annotationClass, superClass, excludeSuperClasses, null);
- }
-
- public ClassFilter(ClassLoader classLoader, Class extends Annotation> 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 extends Annotation> 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 extends Annotation> 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 extends Annotation> 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 extends Annotation> annotationClass, Class superClass, Class[] excludeSuperClasses) {
+ this(classLoader, annotationClass, superClass, excludeSuperClasses, null);
+ }
+
+ public ClassFilter(RedkaleClassLoader classLoader, Class extends Annotation> 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 extends Annotation> 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 extends Annotation> 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 extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
- super.loadService(serviceFilter, otherFilter);
- initWebSocketService();
- }
-
- @Override
- protected void loadFilter(ClassFilter extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
- if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- protected void loadServlet(ClassFilter extends Servlet> 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 extends Filter> 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 extends Filter> 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 extends Servlet> servletFilter, ClassFilter extends WebSocket> 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 extends Servlet> o1, FilterEntry extends Servlet> 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 extends Servlet> 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 extends WebSocket> 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 extends WebSocket> 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 extends WebSocket> 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 extends Service> serviceFilter, ClassFilter otherFilter) throws Exception {
+ super.loadService(serviceFilter, otherFilter);
+ initWebSocketService();
+ }
+
+ @Override
+ protected void loadFilter(ClassFilter extends Filter> filterFilter, ClassFilter otherFilter) throws Exception {
+ if (httpServer != null) loadHttpFilter(this.serverConf.getAnyValue("filters"), filterFilter);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void loadServlet(ClassFilter extends Servlet> 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 extends Filter> 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 extends Filter> 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 extends Servlet> servletFilter, ClassFilter extends WebSocket> 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 extends Servlet> o1, FilterEntry extends Servlet> 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 extends Servlet> en : list) {
+ Class