From 4d2daa035efaa37b66ed7493a5b82f960b384ca3 Mon Sep 17 00:00:00 2001 From: luanjia Date: Wed, 8 Nov 2017 23:05:41 +0800 Subject: [PATCH] release_0.0.1.c --- README.md | 66 +++++---- bin/sandbox-packages.sh | 2 +- bin/sandbox.properties | 20 ++- bin/sandbox.sh | 19 ++- pom.xml | 39 ++++- .../jvm/sandbox/agent/AgentLauncher.java | 8 +- sandbox-api/pom.xml | 6 +- .../alibaba/jvm/sandbox/api/event/Event.java | 26 ++-- .../jvm/sandbox/api/event/ThrowsEvent.java | 2 +- .../sandbox/api/resource/EventMonitor.java | 60 ++++++++ sandbox-common-api/pom.xml | 6 +- .../jvm/sandbox/api/resource/ConfigInfo.java | 45 ++++++ sandbox-core/pom.xml | 4 + .../jvm/sandbox/core/CoreConfigure.java | 118 ++++++++++++--- .../enhance/weaver/EventListenerHandlers.java | 16 +- .../core/enhance/weaver/asm/AsmMethods.java | 3 +- .../core/enhance/weaver/asm/AsmTypes.java | 3 +- .../core/enhance/weaver/asm/EventWeaver.java | 2 +- .../core/manager/ModuleLifeCycleEventBus.java | 2 +- .../core/manager/impl/DefaultConfigInfo.java | 34 ++++- .../impl/DefaultCoreModuleManager.java | 140 +++++++++++------- .../manager/impl/DefaultEventMonitor.java | 41 +++++ .../manager/impl/DefaultModuleController.java | 2 +- .../core/manager/impl/ModuleJarLoader.java | 2 +- .../impl/SandboxClassFileTransformer.java | 13 +- .../core/server/jetty/JettyCoreServer.java | 37 ++++- .../jvm/sandbox/core/util/EventPool.java | 43 +++++- .../jvm/sandbox/core/util/NetworkUtils.java | 36 +++++ .../jvm/sandbox/core/util/ObjectIDs.java | 2 +- .../resources/com/alibaba/jvm/sandbox/version | 2 +- .../sandbox/core/enhance/BaseTestCase.java | 4 +- sandbox-debug-module/pom.xml | 2 +- .../jvm/sandbox/module/debug/DebugModule.java | 48 +++++- .../module/debug/NamePatternFilter.java | 1 + .../module/debug/PerformanceModule.java | 6 +- .../sandbox/module/debug/ProgressPrinter.java | 4 +- .../module/debug/WatchEventListener.java | 4 +- .../module/debug/util/GaEnumUtils.java | 2 +- .../jvm/sandbox/module/mgr/ControlModule.java | 140 ++++++++++++++++++ .../jvm/sandbox/module/mgr/InfoModule.java | 55 ++++--- .../sandbox/module/mgr/ModuleMgrModule.java | 5 +- .../com.alibaba.jvm.sandbox.api.Module | 3 +- .../mgr/EmptyModuleJarLoadingChain.java | 1 + sandbox-provider-api/pom.xml | 6 +- .../jvm/sandbox/util/SandboxStringUtils.java | 1 - 45 files changed, 863 insertions(+), 218 deletions(-) create mode 100644 sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/EventMonitor.java create mode 100644 sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultEventMonitor.java create mode 100644 sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/NetworkUtils.java create mode 100644 sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ControlModule.java diff --git a/README.md b/README.md index 54916323..c22299d7 100755 --- a/README.md +++ b/README.md @@ -1,37 +1,42 @@ # JVM-SANDBOX ->基于JVM的实时无侵入AOP框架容器 +> 基于JVM的实时无侵入AOP框架容器 ## 项目简介 -#### JVM-SANDBOX的核心功能是什么? +### JVM-SANDBOX的核心功能是什么? + +#### 实时无侵入AOP框架 -##### 1.实时无侵入AOP框架 在常见的AOP框架实现方案中,有静态编织和动态编织两种。 -###### 静态编织 -静态编织发生在字节码生成时根据一定框架的规则提前将AOP字节码插入到目标类和方法中,实现AOP; -###### 动态编织 -动态编织则允许在JVM运行过程中完成指定方法的AOP字节码增强. -常见的动态编织方案大多采用重命名原有方法,再新建一个同签名的方法来做代理的工作模式来完成AOP的功能(常见的实现方案如CgLib),但这种方式存在一些应用边界: -- 侵入性 - 对被代理的目标类需要进行侵入式改造。比如:在Spring中必须是托管于Spring容器中的Bean。 -- 固化性 - 目标代理方法在启动之后即固化,无法重新对一个已有方法进行AOP增强。 - -###### 热部署 +1. **静态编织** + 静态编织发生在字节码生成时根据一定框架的规则提前将AOP字节码插入到目标类和方法中,实现AOP; + +1. **动态编织** + 动态编织则允许在JVM运行过程中完成指定方法的AOP字节码增强.常见的动态编织方案大多采用重命名原有方法,再新建一个同签名的方法来做代理的工作模式来完成AOP的功能(常见的实现方案如CgLib),但这种方式存在一些应用边界: + + - **侵入性** + 对被代理的目标类需要进行侵入式改造。比如:在Spring中必须是托管于Spring容器中的Bean。 + + - **固化性** + 目标代理方法在启动之后即固化,无法重新对一个已有方法进行AOP增强。 + +#### 热部署 + 还有一些实现AOP的方式是通过类似热部署的方式完成,但现有的热部署实现方案也存在一些应用边界: -- 性能折损巨大 -- 对JVM存在侵入性 -- 必须启动时显式开启 +1. 性能折损巨大 +1. 对JVM存在侵入性 +1. 必须启动时显式开启 基于此我通过JDK6所提供的Instrumentation-API实现了利用HotSwap技术在不重启JVM的情况下实现对任意方法的AOP增强。而且性能开销还在可以接受的范围之内。 -#####2. 动态可插拔容器 +#### 动态可插拔容器 + 为了实现沙箱模块的动态热插拔,容器客户端和沙箱动态可插拔容器采用HTTP协议进行通讯,底层用Jetty6作为HTTP服务器。 -#### What can the JVM-SANDBOX do? +### JVM-SANDBOX能做什么? 在JVM沙箱(以下简称沙箱)的世界观中,任何一个Java方法的调用都可以分解为`BEFORE`、`RETURN`和`THROWS`三个环节,由此在三个环节上引申出对应环节的事件探测和流程控制机制。 @@ -51,7 +56,6 @@ try { } ``` - 基于`BEFORE`、`RETURN`和`THROWS`三个环节事件,可以完成很多类AOP的操作。 1. 可以感知和改变方法调用的入参 @@ -62,7 +66,7 @@ try { - 在方法体返回之前重新构造新的结果对象,甚至可以改变为抛出异常 - 在方法体抛出异常之后重新抛出新的异常,甚至可以改变为正常返回 -#### JVM沙箱都有哪些可能的应用场景 +### JVM沙箱都有哪些可能的应用场景 - 线上故障定位 - 线上系统流控 @@ -73,24 +77,24 @@ try { JVM沙箱还能帮助你做很多很多,取决于你的脑洞有多大了。 -# For English +# FOR ENGLISH # JVM-SANDBOX ->Real - time non-invasive AOP framework container based on JVM +> Real - time non-invasive AOP framework container based on JVM ## Foreword -#### What is the core function of the JVM-SANDBOX? +### What is the core function of the JVM-SANDBOX? -##### Real-time non-invasive AOP framework +#### Real-time non-invasive AOP framework In the common AOP framework to achieve the program, there are two kinds of static weaving and dynamic weaving. -###### Static weaving +##### Static weaving Static weaving occurs in the bytecode generation according to a certain framework of the rules in advance AOP byte code into the target class and method to achieve AOP. -###### Dynamic weaving +##### Dynamic weaving Dynamic weaving allows AOP bytecode enhancement of the specified method to be completed during the execution of the JVM. @@ -104,7 +108,7 @@ Common dynamic weaving programs are mostly used to rename the original method, a The target agent method is solidified after startup and can not re-validate an existing method -###### Hot deployment +##### Hot deployment There are some ways to implement AOP is done through a similar hot deployment, but there are some application boundaries for existing hot deployment implementations: - Performance damage huge @@ -114,10 +118,10 @@ There are some ways to implement AOP is done through a similar hot deployment, b Based on this I am through the JDK6 provided Instrumentation-API implementation of the use of HotSwap technology without restarting the JVM in the case of any method to achieve AOP enhancements. And performance overhead is still within acceptable limits -##### Provides a plug-and-play module management container +#### Provides a plug-and-play module management container In order to realize the dynamic hot-swapping of the sandbox module, the container client and the sandbox dynamic pluggable container communicate with the HTTP protocol. The bottom layer uses Jetty6 as the HTTP server. -#### What can the JVM-SANDBOX do? +### What can the JVM-SANDBOX do? In the JVM-SANDBOX (hereinafter referred to as the sandbox) world view, any one of the Java method calls can be broken down into `BEFORE`,` RETURN` and `THROWS` three links, which in three links on the corresponding link Event detection and process control mechanisms. @@ -148,7 +152,7 @@ Based on the `BEFORE`,` RETURN` and `THROWS` three events, you can do a lot of A - Throws a new exception after throwing an exception in the method body, and can even change to a normal return -#### What are the possible scenarios for the JVM-SANDBOX? +### What are the possible scenarios for the JVM-SANDBOX? - Online fault location - Online system flow control diff --git a/bin/sandbox-packages.sh b/bin/sandbox-packages.sh index 4a574725..cc2ad445 100755 --- a/bin/sandbox-packages.sh +++ b/bin/sandbox-packages.sh @@ -40,7 +40,7 @@ echo "${SANDBOX_VERSION}" > ${SANDBOX_TARGET_DIR}/cfg/version # for test ## cp ../sandbox-debug-module/target/sandbox-debug-module-*-jar-with-dependencies.jar\ -## ${SANDBOX_TARGET_DIR}/module/sandbox-debug-module.jar +## ${SANDBOX_TARGET_DIR}/module/sandbox-debug-module.jar # for mgr cp ../sandbox-mgr-module/target/sandbox-mgr-module-*-jar-with-dependencies.jar\ diff --git a/bin/sandbox.properties b/bin/sandbox.properties index ea75c023..a1e9f2fe 100755 --- a/bin/sandbox.properties +++ b/bin/sandbox.properties @@ -7,18 +7,26 @@ # define the sandbox's ${SYSTEM_MODULE} dir ## system_module=../module -# define the sandbox's ${USER_MODULE} dir -## user_module=~/.sandbox-module +# define the sandbox's ${USER_MODULE} dir, multi values, use ',' split +## user_module=~/.sandbox-module;~/.sandbox-module-1;~/.sandbox-module-2;~/.sandbox-module-n; +user_module=~/.sandbox-module; +#user_module=/home/staragent/plugins/jvm-sandbox-module/sandbox-module;/home/staragent/plugins/monkeyking; # define the sandbox's ${PROVIDER_LIB} dir ## provider=../provider +# define the network interface +## server.ip=0.0.0.0 + +# define the network port +## server.port=4769 + # switch the sandbox can enhance system class unsafe.enable=true # define the sandbox event pool arguments(min/max/total) event.pool.enable=true -event.pool.key.min=100 -event.pool.key.max=2000 -event.pool.total=3000 - +event.pool.max.total=6000 +event.pool.min.idle.per.event=50 +event.pool.max.idle.per.event=100 +event.pool.max.total.per.event=2000 diff --git a/bin/sandbox.sh b/bin/sandbox.sh index 8a1589f4..716fcd28 100755 --- a/bin/sandbox.sh +++ b/bin/sandbox.sh @@ -150,6 +150,9 @@ usage: ${0} [h] [ [vlRFfu:a:A:d:m:I:P:C:]] EXAMPLE: ${0} -C -I 192.168.0.1 -P 3658 -m debug + -S : Shutdown server + Shutdown jvm-sandbox\` server + ' } @@ -174,7 +177,10 @@ reset_for_env() # if env define the JAVA_HOME, use it first # if is alibaba opts, use alibaba ops's default JAVA_HOME - [ -z ${JAVA_HOME} ] && JAVA_HOME=/opt/taobao/java + # [ -z ${JAVA_HOME} ] && JAVA_HOME=/opt/taobao/java + if [[ -z ${JAVA_HOME} ]]; then + JAVA_HOME=$(ps aux|grep ${TARGET_JVM_PID}|grep java|awk '{print $11}'|xargs ls -l|awk '{if($1~/^l/){print $11}else{print $9}}'|sed 's/\/bin\/java//g') + fi # check the jvm version, we need 1.6+ local JAVA_VERSION=$(${JAVA_HOME}/bin/java -version 2>&1|awk -F '"' '/version/&&$2>"1.5"{print $2}') @@ -236,9 +242,8 @@ function sandbox_debug_curl() { function main() { check_permission - reset_for_env - while getopts "hp:vFfRu:a:A:d:m:I:P:Cl" ARG + while getopts "hp:vFfRu:a:A:d:m:I:P:ClS" ARG do case ${ARG} in h) usage;exit;; @@ -256,13 +261,15 @@ function main() { I) TARGET_SERVER_IP=${OPTARG};; P) TARGET_SERVER_PORT=${OPTARG};; C) OP_CONNECT_ONLY=1;; + S) OP_SHUTDOWN=1;; ?) usage;exit_on_err 1;; esac done + reset_for_env # reset IP - [ -z ${TARGET_SERVER_IP} ] && TARGET_SERVER_IP="127.0.0.1"; + [ -z ${TARGET_SERVER_IP} ] && TARGET_SERVER_IP="0.0.0.0"; # reset PORT [ -z ${TARGET_SERVER_PORT} ] && TARGET_SERVER_PORT=0; @@ -315,6 +322,10 @@ function main() { [[ ! -z ${OP_MODULE_DETAIL} ]] \ && sandbox_curl_with_exit "module-mgr/detail" "&id=${ARG_MODULE_DETAIL}" + # -S shutdown + [[ ! -z ${OP_SHUTDOWN} ]] \ + && sandbox_curl_with_exit "control/shutdown" + # -d debug if [[ ! -z ${OP_DEBUG} ]]; then sandbox_debug_curl "module/http/${ARG_DEBUG}" diff --git a/pom.xml b/pom.xml index 88387e52..0e4bd8f1 100755 --- a/pom.xml +++ b/pom.xml @@ -1,12 +1,15 @@ - + 4.0.0 + com.alibaba.jvm.sandbox sandbox @@ -17,9 +20,9 @@ UTF-8 - 1.0.8 - 1.0.0 - 1.0.0 + 1.0.9 + 1.0.1 + 1.0.1 @@ -146,6 +149,14 @@ commons-pool2 2.4.2 + + + + ant + ant + 1.7.0 + + @@ -160,6 +171,20 @@ sandbox-mgr-module sandbox-provider-api sandbox-common-api - sandbox-mgr-provider - - \ No newline at end of file + sandbox-mgr-provider + + + + + diff --git a/sandbox-agent/src/main/java/com/alibaba/jvm/sandbox/agent/AgentLauncher.java b/sandbox-agent/src/main/java/com/alibaba/jvm/sandbox/agent/AgentLauncher.java index 213b68f9..7e1a6c96 100755 --- a/sandbox-agent/src/main/java/com/alibaba/jvm/sandbox/agent/AgentLauncher.java +++ b/sandbox-agent/src/main/java/com/alibaba/jvm/sandbox/agent/AgentLauncher.java @@ -66,16 +66,16 @@ private static String substringBeforeLast(String str, String separator) { private static volatile ClassLoader sandboxClassLoader; - private static boolean isBlankString(final String string) { + private static boolean isNotBlankString(final String string) { return null != string && string.length() > 0 && !string.matches("^\\s*$"); } private static String getDefaultString(final String string, final String defaultString) { - return isBlankString(string) - ? defaultString - : string; + return isNotBlankString(string) + ? string + : defaultString; } /** diff --git a/sandbox-api/pom.xml b/sandbox-api/pom.xml index 5994b97e..9ddb839d 100755 --- a/sandbox-api/pom.xml +++ b/sandbox-api/pom.xml @@ -16,10 +16,12 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.2 + 3.0.0-M1 + attach-javadocs @@ -42,7 +44,7 @@ org.apache.maven.plugins maven-source-plugin - 2.1.2 + 3.0.1 diff --git a/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/Event.java b/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/Event.java index dfd66da3..6d826f66 100755 --- a/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/Event.java +++ b/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/Event.java @@ -37,23 +37,11 @@ public enum Type { */ RETURN, - /** - * 立即调用:RETURN - * 由{@link com.alibaba.jvm.sandbox.api.ProcessControlException#throwReturnImmediately(Object)}触发 - */ - IMMEDIATELY_RETURN, - /** * 调用:THROWS */ THROWS, - /** - * 立即调用:THROWS - * 由{@link com.alibaba.jvm.sandbox.api.ProcessControlException#throwThrowsImmediately(Throwable)}触发 - */ - IMMEDIATELY_THROWS, - /** * 调用:LINE * 一行被调用了 @@ -92,6 +80,20 @@ public enum Type { * 一个方法被调用抛出异常之后 */ CALL_THROWS, + + + /** + * 立即调用:RETURN + * 由{@link com.alibaba.jvm.sandbox.api.ProcessControlException#throwReturnImmediately(Object)}触发 + */ + IMMEDIATELY_RETURN, + + /** + * 立即调用:THROWS + * 由{@link com.alibaba.jvm.sandbox.api.ProcessControlException#throwThrowsImmediately(Throwable)}触发 + */ + IMMEDIATELY_THROWS, + } } diff --git a/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/ThrowsEvent.java b/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/ThrowsEvent.java index 67d74bc0..fe501a40 100755 --- a/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/ThrowsEvent.java +++ b/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/event/ThrowsEvent.java @@ -39,7 +39,7 @@ public ThrowsEvent(final int processId, final int processId, final int invokeId, final Throwable throwable) { - super(processId, invokeId, Type.THROWS); + super(processId, invokeId, type); this.throwable = throwable; // 对入参进行校验 diff --git a/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/EventMonitor.java b/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/EventMonitor.java new file mode 100644 index 00000000..48943dc6 --- /dev/null +++ b/sandbox-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/EventMonitor.java @@ -0,0 +1,60 @@ +package com.alibaba.jvm.sandbox.api.resource; + +import com.alibaba.jvm.sandbox.api.event.Event; + +/** + * 事件监控器 + * + * @author luanjia@taobao.com + * @since {@code sandbox-api:1.0.9} + */ +public interface EventMonitor { + + /** + * 事件池信息 + * + * @author luanjia@taobao.com + * @since {@code sandbox-api:1.0.9} + */ + interface EventPoolInfo { + + /** + * 获取已分配事件对象总数量 + * + * @return 已分配事件对象总数量 + */ + int getNumActive(); + + /** + * 获取指定事件类型已分配对象数量 + * + * @param type 指定事件类型 + * @return 指定事件类型已分配数量 + */ + int getNumActive(Event.Type type); + + /** + * 获取事件对象总空闲数量 + * + * @return 事件对象总空闲数量 + */ + int getNumIdle(); + + /** + * 获取指定事件类型总空闲对象数量 + * + * @param type 指定事件类型 + * @return 指定事件类型总空闲对象数量 + */ + int getNumIdle(Event.Type type); + + } + + /** + * 获取事件池信息 + * + * @return 获取事件池信息 + */ + EventPoolInfo getEventPoolInfo(); + +} diff --git a/sandbox-common-api/pom.xml b/sandbox-common-api/pom.xml index e1498d75..1b1795c3 100644 --- a/sandbox-common-api/pom.xml +++ b/sandbox-common-api/pom.xml @@ -16,10 +16,12 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.2 + 3.0.0-M1 + attach-javadocs @@ -42,7 +44,7 @@ org.apache.maven.plugins maven-source-plugin - 2.1.2 + 3.0.1 diff --git a/sandbox-common-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/ConfigInfo.java b/sandbox-common-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/ConfigInfo.java index cd06d6ca..a1b4b1b7 100755 --- a/sandbox-common-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/ConfigInfo.java +++ b/sandbox-common-api/src/main/java/com/alibaba/jvm/sandbox/api/resource/ConfigInfo.java @@ -66,9 +66,18 @@ public interface ConfigInfo { *

固定在${HOME}/.sandbox-module目录下

* * @return 用户模块目录地址 + * @deprecated 已经废弃,因为用户地址允许配置多条,可以通过{@link #getUserModuleLibPaths()}来获取所有的用户模块地址 */ + @Deprecated String getUserModuleLibPath(); + /** + * 获取沙箱的用户模块目录地址(集合) + * + * @return 用户模块目录地址(集合) + */ + String[] getUserModuleLibPaths(); + /** * 判断沙箱是否启用了事件对象池 *

启用事件对象池之后将会极大降低沙箱对JVM新生代的压力,但同时会带来一定的调用开销

@@ -83,6 +92,7 @@ public interface ConfigInfo { * 沙箱事件对象池单个事件类型缓存最小数量,{@link #isEnableEventPool()}==true时候有意义 * * @return 单个事件类型缓存最小数量 + * @deprecated 已经被废弃,推荐使用{@link #getEventPoolMaxIdlePerEvent()} */ int getEventPoolKeyMin(); @@ -90,6 +100,7 @@ public interface ConfigInfo { * 沙箱事件对象池单个事件类型缓存最大数量,{@link #isEnableEventPool()}==true时候有意义 * * @return 单个事件类型缓存最大数量 + * @deprecated 已被废弃,推荐使用{@link #getEventPoolMaxTotalPerEvent()} */ int getEventPoolKeyMax(); @@ -97,9 +108,43 @@ public interface ConfigInfo { * 沙箱事件对象池所有事件类型缓存最大总数量,{@link #isEnableEventPool()}==true时候有意义 * * @return 所有事件类型缓存最大总数量 + * @deprecated 已被废弃,推荐使用{@link #getEventPoolMaxTotal()} */ int getEventPoolTotal(); + /** + * 获取事件池最大容量 + * + * @return 事件池最大容量 + * @since {@code sandbox-common-api:1.0.1} + */ + int getEventPoolMaxTotal(); + + /** + * 获取事件池每个事件最小空闲容量 + * + * @return 事件池每个事件最小空闲容量 + * @since {@code sandbox-common-api:1.0.1} + */ + int getEventPoolMinIdlePerEvent(); + + /** + * 获取事件池每个事件最大空闲容量 + * + * @return 事件池每个事件最大空闲容量 + * @since {@code sandbox-common-api:1.0.1} + */ + int getEventPoolMaxIdlePerEvent(); + + /** + * 获取事件池每个事件最大容量 + * + * @return 事件池每个事件最大容量 + * @since {@code sandbox-common-api:1.0.1} + */ + int getEventPoolMaxTotalPerEvent(); + + /** * 获取沙箱HTTP服务侦听地址 * 如果服务器未能完成端口的绑定,则返回("0.0.0.0:0") diff --git a/sandbox-core/pom.xml b/sandbox-core/pom.xml index 44c1a165..64e95858 100755 --- a/sandbox-core/pom.xml +++ b/sandbox-core/pom.xml @@ -129,6 +129,10 @@ javax.servlet javax.servlet-api + + ant + ant + UTF-8 diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/CoreConfigure.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/CoreConfigure.java index 60fc90d7..b659928e 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/CoreConfigure.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/CoreConfigure.java @@ -1,6 +1,7 @@ package com.alibaba.jvm.sandbox.core; import com.alibaba.jvm.sandbox.api.Information; +import com.alibaba.jvm.sandbox.api.event.Event; import com.alibaba.jvm.sandbox.core.util.FeatureCodec; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; @@ -8,6 +9,7 @@ import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; +import org.apache.tools.ant.DirectoryScanner; import java.io.File; import java.io.InputStream; @@ -33,15 +35,23 @@ public class CoreConfigure { private static final String VAL_LAUNCH_MODE_AGENT = "agent"; private static final String VAL_LAUNCH_MODE_ATTACH = "attach"; private static final String KEY_EVENT_POOL_ENABLE = "event.pool.enable"; - private static final String KEY_EVENT_POOL_KEY_MIN = "event.pool.key.min"; - private static final String KEY_EVENT_POOL_KEY_MAX = "event.pool.key.max"; - private static final String KEY_EVENT_POOL_TAL = "event.pool.total"; + + // ------------------------------------- 事件池配置 ------------------------------------- + private static final String KEY_EVENT_POOL_MAX_TOTAL = "event.pool.max.total"; + private static final String KEY_EVENT_POOL_MIN_IDLE_PER_EVENT = "event.pool.min.idle.per.event"; + private static final int DEFAULT_VAL_EVENT_POOL_MIN_IDLE_PER_EVENT = 50; + private static final String KEY_EVENT_POOL_MAX_IDLE_PER_EVENT = "event.pool.max.idle.per.event"; + private static final int DEFAULT_VAL_EVENT_POOL_MAX_IDLE_PER_EVENT = 100; + private static final String KEY_EVENT_POOL_MAX_TOTAL_PER_EVENT = "event.pool.max.total.per.event"; + private static final int DEFAULT_VAL_EVENT_POOL_MAX_TOTAL_PER_EVENT = 2000; + private static final String KEY_UNSAFE_ENABLE = "unsafe.enable"; // 受保护key数组,在保护key范围之内,如果前端已经传递过参数了,只能认前端,后端无法修改 private static final String[] PROTECT_KEY_ARRAY = {KEY_SANDBOX_HOME, KEY_LAUNCH_MODE, KEY_SERVER_IP, KEY_SERVER_PORT}; private static final FeatureCodec codec = new FeatureCodec(';', '='); + private final Map featureMap; private CoreConfigure(final String featureString) { @@ -105,6 +115,7 @@ public String getSystemModuleLibPath() { return featureMap.get(KEY_SYSTEM_MODULE_LIB_PATH); } + /** * 获取用户模块加载路径 * @@ -114,6 +125,63 @@ public String getUserModuleLibPath() { return featureMap.get(KEY_USER_MODULE_LIB_PATH); } + /** + * 获取用户模块加载路径(集合) + * + * @return 用户模块加载路径(集合) + */ + public String[] getUserModuleLibPaths() { + return replaceWithSysPropUserHome(codec.toCollection(featureMap.get(KEY_USER_MODULE_LIB_PATH)).toArray(new String[]{})); + } + + private static String[] replaceWithSysPropUserHome(final String[] pathArray) { + if (ArrayUtils.isEmpty(pathArray)) { + return pathArray; + } + final String SYS_PROP_USER_HOME = System.getProperty("user.home"); + for (int index = 0; index < pathArray.length; index++) { + if (StringUtils.startsWith(pathArray[index], "~")) { + pathArray[index] = StringUtils.replaceOnce(pathArray[index], "~", SYS_PROP_USER_HOME); + } + } + return pathArray; + } + + /** + * 获取用户模块加载文件/目录(集合) + * + * @return 用户模块加载文件/目录(集合) + */ + public synchronized File[] getUserModuleLibFiles() { + final DirectoryScanner scanner = new DirectoryScanner(); + scanner.setIncludes(getUserModuleLibPaths()); + scanner.setCaseSensitive(false); + scanner.scan(); + final String[] filePaths = scanner.getIncludedDirectories(); + final File[] files = new File[filePaths.length]; + for (int index = 0; index < filePaths.length; index++) { + files[index] = new File(filePaths[index]); + } + return GET_USER_MODULE_LIB_FILES_CACHE = files; + } + + // 用户模块加载文件/目录缓存集合 + private volatile File[] GET_USER_MODULE_LIB_FILES_CACHE = null; + + /** + * 从缓存中获取用户模块加载文件/目录 + * + * @return 用户模块加载文件/目录 + */ + public File[] getUserModuleLibFilesWithCache() { + if (null != GET_USER_MODULE_LIB_FILES_CACHE) { + return GET_USER_MODULE_LIB_FILES_CACHE; + } else { + return getUserModuleLibFiles(); + } + } + + /** * 获取配置文件加载路径 * @@ -163,33 +231,35 @@ public Information.Mode getLaunchMode() { } - /** - * 获取事件池最小值 - * - * @return event.pool.min - */ - public int getEventPoolKeyMin() { - return NumberUtils.toInt(featureMap.get(KEY_EVENT_POOL_KEY_MIN), Runtime.getRuntime().availableProcessors() * 64); + public int getEventPoolMaxTotal() { + return NumberUtils.toInt( + featureMap.get(KEY_EVENT_POOL_MAX_TOTAL), + getEventPoolMaxTotalPerEvent() * Event.Type.values().length + ); } - /** - * 获取事件池最大值 - * - * @return event.pool.max - */ - public int getEventPoolKeyMax() { - return NumberUtils.toInt(featureMap.get(KEY_EVENT_POOL_KEY_MAX), Runtime.getRuntime().availableProcessors() * 128); + public int getEventPoolMinIdlePerEvent() { + return NumberUtils.toInt( + featureMap.get(KEY_EVENT_POOL_MIN_IDLE_PER_EVENT), + DEFAULT_VAL_EVENT_POOL_MIN_IDLE_PER_EVENT + ); } - /** - * 获取事件池总体最大值 - * - * @return event.pool.total - */ - public int getEventPoolTotal() { - return NumberUtils.toInt(featureMap.get(KEY_EVENT_POOL_TAL), getEventPoolKeyMax() * 5/*numbers(Event.Type)==5*/); + public int getEventPoolMaxIdlePerEvent() { + return NumberUtils.toInt( + featureMap.get(KEY_EVENT_POOL_MAX_IDLE_PER_EVENT), + DEFAULT_VAL_EVENT_POOL_MAX_IDLE_PER_EVENT + ); } + public int getEventPoolMaxTotalPerEvent() { + return NumberUtils.toInt( + featureMap.get(KEY_EVENT_POOL_MAX_TOTAL_PER_EVENT), + DEFAULT_VAL_EVENT_POOL_MAX_TOTAL_PER_EVENT + ); + } + + /** * 是否启用事件池 * diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/EventListenerHandlers.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/EventListenerHandlers.java index f664b5ff..6a39ecb4 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/EventListenerHandlers.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/EventListenerHandlers.java @@ -12,10 +12,10 @@ import com.alibaba.jvm.sandbox.core.util.Sequencer; import com.alibaba.jvm.sandbox.core.util.collection.GaStack; import com.alibaba.jvm.sandbox.core.util.collection.ThreadUnsafeGaStack; -import java.com.alibaba.jvm.sandbox.spy.Spy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.com.alibaba.jvm.sandbox.spy.Spy; import java.lang.reflect.Method; import java.util.Map; import java.util.WeakHashMap; @@ -25,7 +25,8 @@ /** * 事件处理 - * Created by luanjia@taobao.com on 16/7/12. + * + * @author luanjia@taobao.com */ public class EventListenerHandlers { @@ -41,6 +42,15 @@ public class EventListenerHandlers { // 事件对象池 private final EventPool eventPool = new EventPool(); + /** + * 获取事件对象池 + * + * @return 事件对象池 + */ + public EventPool getEventPool() { + return eventPool; + } + /** * 注册事件处理器 * @@ -545,7 +555,7 @@ public static Object onBefore(final int listenerId, final Object[] argumentArray) throws Throwable { return singleton.handleOnBeforeWithTargetClassLoaderSpyRet( listenerId, - (ClassLoader)ObjectIDs.instance.getObject(targetClassLoaderObjectID), + (ClassLoader) ObjectIDs.instance.getObject(targetClassLoaderObjectID), spyRetClassInTargetClassLoader, javaClassName, javaMethodName, diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmMethods.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmMethods.java index c4639e62..ab6ab138 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmMethods.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmMethods.java @@ -1,8 +1,9 @@ package com.alibaba.jvm.sandbox.core.enhance.weaver.asm; -import java.com.alibaba.jvm.sandbox.spy.Spy; import org.objectweb.asm.commons.Method; +import java.com.alibaba.jvm.sandbox.spy.Spy; + import static com.alibaba.jvm.sandbox.core.enhance.weaver.asm.AsmMethods.InnerHelper.getAsmMethod; import static com.alibaba.jvm.sandbox.core.util.SandboxReflectUtils.unCaughtGetClassDeclaredJavaMethod; diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmTypes.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmTypes.java index 718bc176..4db46644 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmTypes.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/AsmTypes.java @@ -1,8 +1,9 @@ package com.alibaba.jvm.sandbox.core.enhance.weaver.asm; -import java.com.alibaba.jvm.sandbox.spy.Spy; import org.objectweb.asm.Type; +import java.com.alibaba.jvm.sandbox.spy.Spy; + /** * 常用的ASM type集合
* 省得我到处声明 diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/EventWeaver.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/EventWeaver.java index b8e5886b..f27f2c54 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/EventWeaver.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/enhance/weaver/asm/EventWeaver.java @@ -4,7 +4,6 @@ import com.alibaba.jvm.sandbox.api.filter.Filter; import com.alibaba.jvm.sandbox.core.enhance.weaver.CodeLock; import com.alibaba.jvm.sandbox.core.util.BitUtils; -import java.com.alibaba.jvm.sandbox.spy.Spy; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.objectweb.asm.*; @@ -13,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.com.alibaba.jvm.sandbox.spy.Spy; import java.util.ArrayList; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/ModuleLifeCycleEventBus.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/ModuleLifeCycleEventBus.java index b08faa60..3ddb286e 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/ModuleLifeCycleEventBus.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/ModuleLifeCycleEventBus.java @@ -66,7 +66,7 @@ interface ModuleLifeCycleEventListener { * @param coreModule 被通知的沙箱模块 * @param event 通知事件类型 * @return TRUE : 继续保持监听,下次有事件通知时继续接收消息 - * FALSE : 放弃后续的监听 + * FALSE : 放弃后续的监听 */ boolean onFire(CoreModule coreModule, Event event); diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultConfigInfo.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultConfigInfo.java index 3f7c5ac8..5691c73e 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultConfigInfo.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultConfigInfo.java @@ -57,24 +57,52 @@ public String getUserModuleLibPath() { return cfg.getUserModuleLibPath(); } + @Override + public String[] getUserModuleLibPaths() { + return cfg.getUserModuleLibPaths(); + } + @Override public boolean isEnableEventPool() { return cfg.isEventPoolEnable(); } + @Deprecated @Override public int getEventPoolKeyMin() { - return cfg.getEventPoolKeyMin(); + return getEventPoolMaxIdlePerEvent(); } + @Deprecated @Override public int getEventPoolKeyMax() { - return cfg.getEventPoolKeyMax(); + return getEventPoolMaxTotal(); } + @Deprecated @Override public int getEventPoolTotal() { - return cfg.getEventPoolTotal(); + return getEventPoolMaxTotal(); + } + + @Override + public int getEventPoolMaxTotal() { + return cfg.getEventPoolMaxTotal(); + } + + @Override + public int getEventPoolMinIdlePerEvent() { + return cfg.getEventPoolMinIdlePerEvent(); + } + + @Override + public int getEventPoolMaxIdlePerEvent() { + return cfg.getEventPoolMaxIdlePerEvent(); + } + + @Override + public int getEventPoolMaxTotalPerEvent() { + return cfg.getEventPoolMaxTotalPerEvent(); } @Override diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultCoreModuleManager.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultCoreModuleManager.java index fa46450a..09df0740 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultCoreModuleManager.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultCoreModuleManager.java @@ -26,6 +26,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -76,10 +77,10 @@ public DefaultCoreModuleManager(final Instrumentation inst, this.providerManager = providerManager; // 初始化模块目录 - this.moduleLibDirArray = new File[]{ - new File(cfg.getSystemModuleLibPath()), - new File(cfg.getUserModuleLibPath()) - }; + this.moduleLibDirArray = mergeFileArray( + new File[]{new File(cfg.getSystemModuleLibPath())}, + cfg.getUserModuleLibFilesWithCache() + ); // 初始化加载所有的模块 try { @@ -89,6 +90,17 @@ public DefaultCoreModuleManager(final Instrumentation inst, } } + private File[] mergeFileArray(File[] aFileArray, File[] bFileArray) { + final List _r = new ArrayList(); + for (final File aFile : aFileArray) { + _r.add(aFile); + } + for (final File bFile : bFileArray) { + _r.add(bFile); + } + return _r.toArray(new File[]{}); + } + /* * 通知模块生命周期 */ @@ -197,6 +209,11 @@ else if (ConfigInfo.class.isAssignableFrom(fieldType)) { FieldUtils.writeField(resourceField, module, configInfo, true); } + // EventMonitor注入 + else if (EventMonitor.class.isAssignableFrom(fieldType)) { + FieldUtils.writeField(resourceField, module, new DefaultEventMonitor(), true); + } + // 其他情况需要输出日志警告 else { logger.warn("inject required @Resource field[name={};] into module[id={};class={};] failed, type={}; was not support yet.", @@ -409,6 +426,10 @@ private static boolean isOptimisticDirectoryContainsFile(final File directory, } } + private boolean isSystemModule(final File child) { + return isOptimisticDirectoryContainsFile(new File(cfg.getSystemModuleLibPath()), child); + } + /** * 用户模块文件加载回调 */ @@ -537,51 +558,60 @@ private boolean isChecksumCRC32Existed(long checksumCRC32) { */ private void softFlush() throws ModuleException { - final File userModuleLibDir = new File(cfg.getUserModuleLibPath()); - final ArrayList appendJarFiles = new ArrayList(); - final ArrayList removeCoreModules = new ArrayList(); - final ArrayList checksumCRC32s = new ArrayList(); + final File[] userModuleLibDirArray = cfg.getUserModuleLibFilesWithCache(); + for (final File userModuleLibDir : userModuleLibDirArray) { - // 1. 找出所有有变动的文件(add/remove) - for (final File jarFile : listFiles(userModuleLibDir, new String[]{"jar"}, false)) { - final long checksumCRC32; try { - checksumCRC32 = FileUtils.checksumCRC32(jarFile); - } catch (IOException e) { - logger.warn("soft flush {} failed, ignore this file.", jarFile, e); - continue; - } - checksumCRC32s.add(checksumCRC32); - // 如果CRC32已经在已加载的模块集合中存在,则说明这个文件没有变动,忽略 - if (isChecksumCRC32Existed(checksumCRC32)) { - continue; - } - appendJarFiles.add(jarFile); - } + // final File userModuleLibDir = new File(cfg.getUserModuleLibPath()); + final ArrayList appendJarFiles = new ArrayList(); + final ArrayList removeCoreModules = new ArrayList(); + final ArrayList checksumCRC32s = new ArrayList(); + + // 1. 找出所有有变动的文件(add/remove) + for (final File jarFile : listFiles(userModuleLibDir, new String[]{"jar"}, false)) { + final long checksumCRC32; + try { + checksumCRC32 = FileUtils.checksumCRC32(jarFile); + } catch (IOException e) { + logger.warn("soft flush {} failed, ignore this file.", jarFile, e); + continue; + } + checksumCRC32s.add(checksumCRC32); + // 如果CRC32已经在已加载的模块集合中存在,则说明这个文件没有变动,忽略 + if (isChecksumCRC32Existed(checksumCRC32)) { + continue; + } + appendJarFiles.add(jarFile); + } - // 2. 找出所有待卸载的已加载用户模块 - for (final CoreModule coreModule : loadedModuleBOMap.values()) { - final ModuleClassLoader moduleClassLoader = coreModule.getLoader(); - // 如果不是用户模块目录,忽略 - if (!isOptimisticDirectoryContainsFile(userModuleLibDir, coreModule.getJarFile())) { - continue; - } - // 如果CRC32已经在这次待加载的集合中,则说明这个文件没有变动,忽略 - if (checksumCRC32s.contains(moduleClassLoader.getChecksumCRC32())) { - continue; - } - removeCoreModules.add(coreModule); - } + // 2. 找出所有待卸载的已加载用户模块 + for (final CoreModule coreModule : loadedModuleBOMap.values()) { + final ModuleClassLoader moduleClassLoader = coreModule.getLoader(); + // 如果不是用户模块目录,忽略 + if (!isOptimisticDirectoryContainsFile(userModuleLibDir, coreModule.getJarFile())) { + continue; + } + // 如果CRC32已经在这次待加载的集合中,则说明这个文件没有变动,忽略 + if (checksumCRC32s.contains(moduleClassLoader.getChecksumCRC32())) { + continue; + } + removeCoreModules.add(coreModule); + } - // 3. 删除remove - for (final CoreModule coreModule : removeCoreModules) { - unload(coreModule, true); - } + // 3. 删除remove + for (final CoreModule coreModule : removeCoreModules) { + unload(coreModule, true); + } + + // 4. 加载add + for (final File jarFile : appendJarFiles) { + new ModuleJarLoader(jarFile, cfg.getLaunchMode(), sandboxClassLoader) + .load(new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback()); + } + } catch (Throwable cause) { + logger.warn("flushing USER_LIB_MODULE[{}] failed.", userModuleLibDir, cause); + } - // 4. 加载add - for (final File jarFile : appendJarFiles) { - new ModuleJarLoader(jarFile, cfg.getLaunchMode(), sandboxClassLoader) - .load(new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback()); } } @@ -593,8 +623,6 @@ private void softFlush() throws ModuleException { * @throws ModuleException 模块操作失败 */ private void forceFlush() throws ModuleException { - // 用户模块目录 - final File userModuleLibDir = new File(cfg.getUserModuleLibPath()); // 1. 卸载模块 // 等待卸载的模块集合 @@ -603,7 +631,7 @@ private void forceFlush() throws ModuleException { // 找出所有USER的模块,所以这些模块都卸载了 for (final CoreModule coreModule : loadedModuleBOMap.values()) { // 如果判断是属于USER模块目录下的模块,则加入到待卸载模块集合,稍后统一进行卸载 - if (isOptimisticDirectoryContainsFile(userModuleLibDir, coreModule.getJarFile())) { + if (!isSystemModule(coreModule.getJarFile())) { waitingUnloadCoreModules.add(coreModule); } } @@ -613,16 +641,22 @@ private void forceFlush() throws ModuleException { unload(coreModule, true); } + // 2. 加载模块 // 用户模块加载目录,加载用户模块目录下的所有模块 // 对模块访问权限进行校验 - if (userModuleLibDir.exists() - && userModuleLibDir.canRead()) { - new ModuleJarLoader(userModuleLibDir, cfg.getLaunchMode(), sandboxClassLoader) - .load(new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback()); - } else { - logger.warn("MODULE-LIB[{}] can not access, ignore flush load this lib.", userModuleLibDir); + // 用户模块目录 + final File[] userModuleLibFileArray = cfg.getUserModuleLibFiles(); + for (final File userModuleLibDir : userModuleLibFileArray) { + if (userModuleLibDir.exists() + && userModuleLibDir.canRead()) { + new ModuleJarLoader(userModuleLibDir, cfg.getLaunchMode(), sandboxClassLoader) + .load(new InnerModuleJarLoadCallback(), new InnerModuleLoadCallback()); + } else { + logger.warn("MODULE-LIB[{}] can not access, ignore flush load this lib.", userModuleLibDir); + } } + } } diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultEventMonitor.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultEventMonitor.java new file mode 100644 index 00000000..cf938031 --- /dev/null +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultEventMonitor.java @@ -0,0 +1,41 @@ +package com.alibaba.jvm.sandbox.core.manager.impl; + +import com.alibaba.jvm.sandbox.api.event.Event; +import com.alibaba.jvm.sandbox.api.resource.EventMonitor; +import com.alibaba.jvm.sandbox.core.enhance.weaver.EventListenerHandlers; +import com.alibaba.jvm.sandbox.core.util.EventPool; + +/** + * 事件监控器实现 + */ +class DefaultEventMonitor implements EventMonitor { + + @Override + public EventPoolInfo getEventPoolInfo() { + + final EventPool pool = EventListenerHandlers.getSingleton().getEventPool(); + + return new EventPoolInfo() { + @Override + public int getNumActive() { + return pool.getNumActive(); + } + + @Override + public int getNumActive(Event.Type type) { + return pool.getNumActive(type); + } + + @Override + public int getNumIdle() { + return pool.getNumIdle(); + } + + @Override + public int getNumIdle(Event.Type type) { + return pool.getNumIdle(type); + } + }; + } + +} diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultModuleController.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultModuleController.java index 69d32cf3..bd6c26b0 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultModuleController.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/DefaultModuleController.java @@ -1,7 +1,7 @@ package com.alibaba.jvm.sandbox.core.manager.impl; -import com.alibaba.jvm.sandbox.api.resource.ModuleController; import com.alibaba.jvm.sandbox.api.ModuleException; +import com.alibaba.jvm.sandbox.api.resource.ModuleController; import com.alibaba.jvm.sandbox.core.domain.CoreModule; import com.alibaba.jvm.sandbox.core.manager.CoreModuleManager; diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/ModuleJarLoader.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/ModuleJarLoader.java index 6cd4996f..774366fa 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/ModuleJarLoader.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/ModuleJarLoader.java @@ -81,7 +81,7 @@ public void load(final ModuleJarLoadCallback mjCb, // 是否有模块加载成功 boolean hasModuleLoadedSuccessFlag = false; - if(null != mjCb) { + if (null != mjCb) { mjCb.onLoad(moduleJarFile); } diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/SandboxClassFileTransformer.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/SandboxClassFileTransformer.java index 052c7857..ec1bc2b9 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/SandboxClassFileTransformer.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/manager/impl/SandboxClassFileTransformer.java @@ -90,7 +90,8 @@ public byte[] transform(final ClassLoader loader, return toByteCodeArray; } catch (Throwable cause) { logger.warn("enhancer toByteCode failed, module[id={}];class={};loader={};", uniqueId, javaClassName, loader, cause); - throw new IllegalClassFormatException(cause.getMessage()); + // throw new IllegalClassFormatException(cause.getMessage()); + return null; } } @@ -99,7 +100,7 @@ public byte[] transform(final ClassLoader loader, * * @return 观察ID */ - public int getWatchId() { + int getWatchId() { return watchId; } @@ -108,7 +109,7 @@ public int getWatchId() { * * @return 事件监听器 */ - public EventListener getEventListener() { + EventListener getEventListener() { return eventListener; } @@ -117,7 +118,7 @@ public EventListener getEventListener() { * * @return 事件监听器ID */ - public int getListenerId() { + int getListenerId() { return listenerId; } @@ -126,7 +127,7 @@ public int getListenerId() { * * @return 类和方法过滤器 */ - public Filter getFilter() { + Filter getFilter() { return filter; } @@ -135,7 +136,7 @@ public Filter getFilter() { * * @return 本次监听事件类型数组 */ - public Event.Type[] getEventTypeArray() { + Event.Type[] getEventTypeArray() { return eventTypeArray; } diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/server/jetty/JettyCoreServer.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/server/jetty/JettyCoreServer.java index e0b0b21f..aa3d37bf 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/server/jetty/JettyCoreServer.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/server/jetty/JettyCoreServer.java @@ -14,6 +14,7 @@ import com.alibaba.jvm.sandbox.core.server.jetty.servlet.ModuleHttpServlet; import com.alibaba.jvm.sandbox.core.server.jetty.servlet.WebSocketAcceptorServlet; import com.alibaba.jvm.sandbox.core.util.Initializer; +import com.alibaba.jvm.sandbox.core.util.NetworkUtils; import org.apache.commons.io.IOUtils; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Server; @@ -35,7 +36,8 @@ /** * Jetty实现的Http服务器 - * Created by luanjia@taobao.com on 16/10/2. + * + * @author luanjia@taobao.com */ public class JettyCoreServer implements CoreServer { @@ -80,14 +82,20 @@ public void process() throws Throwable { // stop http server httpServer.stop(); +// while (!httpServer.isStopped()) { +// logger.info("server is stopping...."); +// Thread.sleep(1000); +// } logger.info("server was stop."); - // destroy http server - httpServer.destroy(); - logger.info("server was destroyed."); } } }); + + // destroy http server + httpServer.destroy(); + logger.info("server was destroyed."); + } catch (Throwable cause) { logger.debug("unBind failed.", cause); throw new IOException("unBind failed.", cause); @@ -157,6 +165,16 @@ private void initLogback(final CoreConfigure cfg) throws Throwable { } private void initHttpServer(final CoreConfigure cfg) { + + // 如果IP:PORT已经被占用,则无法继续被绑定 + // 这里说明下为什么要这么无聊加个这个判断,让Jetty的Server.bind()抛出异常不是更好么? + // 比较郁闷的是,如果这个端口的绑定是"SO_REUSEADDR"端口可重用的模式,那么这个server是能正常启动,但无法正常工作的 + // 所以这里必须先主动检查一次端口占用情况,当然了,这里也会存在一定的并发问题,BUT,我认为这种概率事件我可以选择暂时忽略 + if (NetworkUtils.isPortInUsing(cfg.getServerIp(), cfg.getServerPort())) { + throw new IllegalStateException(String.format("server[ip=%s;port=%s;] already in using, server bind failed.", + cfg.getServerIp(), cfg.getServerPort())); + } + httpServer = new Server(new InetSocketAddress(cfg.getServerIp(), cfg.getServerPort())); if (httpServer.getThreadPool() instanceof QueuedThreadPool) { final QueuedThreadPool qtp = (QueuedThreadPool) httpServer.getThreadPool(); @@ -188,7 +206,7 @@ private void initManager(final Instrumentation inst, } @Override - public void bind(final CoreConfigure cfg, final Instrumentation inst) throws IOException { + public synchronized void bind(final CoreConfigure cfg, final Instrumentation inst) throws IOException { try { initializer.initProcess(new Initializer.Processor() { @Override @@ -198,7 +216,7 @@ public void process() throws Throwable { initLogback(cfg); logger.debug("init logback finished."); - logger.info("cfg={}",cfg.toString()); + logger.info("cfg={}", cfg.toString()); initManager(inst, cfg); logger.debug("init resource finished."); @@ -217,8 +235,11 @@ public void process() throws Throwable { } }); } catch (Throwable cause) { - logger.debug("server bind failed. cfg={}", cfg); - throw new IOException("server bind failed.", cause); + logger.warn("server bind failed. cfg={}", cfg, cause); + throw new IOException( + String.format("server bind to %s:%s failed.", cfg.getServerIp(), cfg.getServerPort()), + cause + ); } logger.info("server bind to {} success. cfg={}", getLocal(), cfg); diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/EventPool.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/EventPool.java index 7732bb4d..54b17838 100755 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/EventPool.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/EventPool.java @@ -14,7 +14,8 @@ /** * 事件对象池 - * Created by luanjia on 16/10/15. + * + * @author luanjia@taobao.com */ public class EventPool { @@ -31,13 +32,15 @@ private KeyedObjectPool createEventPool() { final CoreConfigure cfg = CoreConfigure.getInstance(); if (cfg.isEventPoolEnable()) { final GenericKeyedObjectPoolConfig poolConfig = new GenericKeyedObjectPoolConfig(); - poolConfig.setMaxTotalPerKey(cfg.getEventPoolKeyMax()); - poolConfig.setMinIdlePerKey(cfg.getEventPoolKeyMin()); - poolConfig.setMaxTotal(cfg.getEventPoolTotal()); - logger.info("enable event-pool[key-min={};key-max={};total={};]", - cfg.getEventPoolKeyMin(), - cfg.getEventPoolKeyMax(), - cfg.getEventPoolTotal() + poolConfig.setMaxTotalPerKey(cfg.getEventPoolMaxTotalPerEvent()); + poolConfig.setMinIdlePerKey(cfg.getEventPoolMinIdlePerEvent()); + poolConfig.setMaxIdlePerKey(cfg.getEventPoolMaxIdlePerEvent()); + poolConfig.setMaxTotal(cfg.getEventPoolMaxTotal()); + logger.info("enable event-pool[per-key-idle-min={};per-key-idle-max={};per-key-max={};total={};]", + cfg.getEventPoolMinIdlePerEvent(), + cfg.getEventPoolMaxIdlePerEvent(), + cfg.getEventPoolMaxTotalPerEvent(), + cfg.getEventPoolMaxTotal() ); return new GenericKeyedObjectPool(new EventFactory(), poolConfig); } else { @@ -46,6 +49,30 @@ private KeyedObjectPool createEventPool() { } } + public int getNumActive() { + return isEnable + ? pool.getNumActive() + : -1; + } + + public int getNumActive(Event.Type type) { + return isEnable + ? pool.getNumActive(type) + : -1; + } + + public int getNumIdle() { + return isEnable + ? pool.getNumIdle() + : -1; + } + + public int getNumIdle(Event.Type type) { + return isEnable + ? pool.getNumIdle(type) + : -1; + } + public BeforeEvent borrowBeforeEvent(final int processId, final int invokeId, final ClassLoader javaClassLoader, diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/NetworkUtils.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/NetworkUtils.java new file mode 100644 index 00000000..0dc80087 --- /dev/null +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/NetworkUtils.java @@ -0,0 +1,36 @@ +package com.alibaba.jvm.sandbox.core.util; + +import org.apache.commons.io.IOUtils; + +import java.net.InetAddress; +import java.net.Socket; + +/** + * 用于网络通讯的工具类 + * + * @author luanjia@taobao.com + */ +public class NetworkUtils { + + /*** + * 测试主机Host的port端口是否被使用 + * @param host 指定IP + * @param port 指定端口 + */ + public static boolean isPortInUsing(String host, int port) { + Socket socket = null; + try { + final InetAddress Address = InetAddress.getByName(host); + socket = new Socket(Address, port); //建立一个Socket连接 + return socket.isConnected(); + } catch (Throwable cause) { + // ignore + } finally { + if (null != socket) { + IOUtils.closeQuietly(socket); + } + } + return false; + } + +} diff --git a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/ObjectIDs.java b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/ObjectIDs.java index c3cafc11..427eae31 100644 --- a/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/ObjectIDs.java +++ b/sandbox-core/src/main/java/com/alibaba/jvm/sandbox/core/util/ObjectIDs.java @@ -153,7 +153,7 @@ public T getObject(final int objectID) { final IdentityWeakReference reference = identityObjectMapping.get(objectID); if (null != reference && null != (object = reference.get())) { - return (T)object; + return (T) object; } else { return null; } diff --git a/sandbox-core/src/main/resources/com/alibaba/jvm/sandbox/version b/sandbox-core/src/main/resources/com/alibaba/jvm/sandbox/version index 1b8df667..f0fafe2a 100755 --- a/sandbox-core/src/main/resources/com/alibaba/jvm/sandbox/version +++ b/sandbox-core/src/main/resources/com/alibaba/jvm/sandbox/version @@ -1 +1 @@ -0.0.0.v \ No newline at end of file +0.0.1.c \ No newline at end of file diff --git a/sandbox-core/src/test/java/test/com/alibaba/jvm/sandbox/core/enhance/BaseTestCase.java b/sandbox-core/src/test/java/test/com/alibaba/jvm/sandbox/core/enhance/BaseTestCase.java index b1b7eac6..40b6b7ec 100755 --- a/sandbox-core/src/test/java/test/com/alibaba/jvm/sandbox/core/enhance/BaseTestCase.java +++ b/sandbox-core/src/test/java/test/com/alibaba/jvm/sandbox/core/enhance/BaseTestCase.java @@ -86,7 +86,7 @@ public boolean doMethodFilter(final int access, @BeforeClass public static void testBeforeClass() { - CoreConfigure.toConfigure("",""); + CoreConfigure.toConfigure("", ""); } @Ignore @@ -98,7 +98,7 @@ public void test_sum() throws IOException, InvocationTargetException, IllegalAcc new EventListener() { @Override public void onEvent(Event event) throws Throwable { - final BeforeEvent beforeEvent = (BeforeEvent)event; + final BeforeEvent beforeEvent = (BeforeEvent) event; final int[] numberArray = (int[]) beforeEvent.argumentArray[0]; numberArray[0] = 40; numberArray[1] = 60; diff --git a/sandbox-debug-module/pom.xml b/sandbox-debug-module/pom.xml index 167e31ea..d40cf50c 100755 --- a/sandbox-debug-module/pom.xml +++ b/sandbox-debug-module/pom.xml @@ -11,7 +11,7 @@ sandbox-debug-module 1.0.0-SNAPSHOT sandbox-debug-module - http://maven.apache.org + http://maven.apache.org diff --git a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugModule.java b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugModule.java index 4d732480..165166ee 100755 --- a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugModule.java +++ b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/DebugModule.java @@ -2,6 +2,7 @@ import com.alibaba.jvm.sandbox.api.Information; import com.alibaba.jvm.sandbox.api.Module; +import com.alibaba.jvm.sandbox.api.ProcessControlException; import com.alibaba.jvm.sandbox.api.event.CallBeforeEvent; import com.alibaba.jvm.sandbox.api.event.CallReturnEvent; import com.alibaba.jvm.sandbox.api.event.CallThrowsEvent; @@ -38,6 +39,7 @@ /** * 测试用模块 + * * @author luanjia@taobao.com */ @Information(id = "debug", version = "0.0.0.5", author = "luanjia@taobao.com") @@ -193,7 +195,7 @@ public void watchCompleted() throws Throwable { } }, null, - RETURN,BEFORE, THROWS + RETURN, BEFORE, THROWS ); } finally { @@ -290,4 +292,48 @@ public void watchCompleted() throws Throwable { } + @Http("/broken") + public void broken(final HttpServletRequest req, + final HttpServletResponse resp) throws Throwable { + + final String classNamePattern = req.getParameter("class"); + if (StringUtils.isBlank(classNamePattern)) { + resp.sendError(SC_BAD_REQUEST, "class parameter was required."); + return; + } + + final String methodNamePattern = req.getParameter("method"); + if (StringUtils.isBlank(methodNamePattern)) { + resp.sendError(SC_BAD_REQUEST, "method parameter was required."); + return; + } + + final Printer printer = new ConcurrentLinkedQueuePrinter(resp.getWriter(), 20, 20, 2000); + + try { + + moduleEventWatcher.watching( + new NamePatternFilter(classNamePattern, methodNamePattern), + new EventListener() { + @Override + public void onEvent(final Event event) throws Throwable { + printer.println(String.format("RECEIVE EVENT=%s;", event)); + ProcessControlException.throwThrowsImmediately(new RuntimeException("TEST FOR JVM-SANDBOX!")); + } + }, + new ModuleEventWatcher.WatchCallback() { + @Override + public void watchCompleted() throws Throwable { + printer.waitingForBroken(); + } + }, + Event.Type.RETURN, Event.Type.THROWS + ); + } finally { + printer.close(); + } + + } + + } diff --git a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/NamePatternFilter.java b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/NamePatternFilter.java index 4325711a..d1bdde11 100755 --- a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/NamePatternFilter.java +++ b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/NamePatternFilter.java @@ -5,6 +5,7 @@ /** * 名称模版匹配过滤器 + * * @author launajia@taobao.com */ public class NamePatternFilter implements Filter { diff --git a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/PerformanceModule.java b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/PerformanceModule.java index 2cf89695..be47e538 100644 --- a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/PerformanceModule.java +++ b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/PerformanceModule.java @@ -159,7 +159,7 @@ public void run() { } outputSB .append("TOTAL:").append(total).append("\n") - .append("RATE:").append(df.format(total/(INTERVAL_MS/1000))).append("/sec\n"); + .append("RATE:").append(df.format(total / (INTERVAL_MS / 1000))).append("/sec\n"); // 统计输出 @@ -198,10 +198,10 @@ public void onEvent(Event event) throws Throwable { try { AtomicInteger countRef = eventCountMap.get(event.type); - if(null == countRef) { + if (null == countRef) { countRef = new AtomicInteger(0); final AtomicInteger tempCountRef = eventCountMap.putIfAbsent(event.type, countRef); - if(null != tempCountRef) { + if (null != tempCountRef) { countRef = tempCountRef; } } diff --git a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/ProgressPrinter.java b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/ProgressPrinter.java index befb522d..e6f7cefe 100755 --- a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/ProgressPrinter.java +++ b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/ProgressPrinter.java @@ -1,7 +1,7 @@ package com.alibaba.jvm.sandbox.module.debug; -import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher; import com.alibaba.jvm.sandbox.api.http.printer.Printer; +import com.alibaba.jvm.sandbox.api.resource.ModuleEventWatcher; import org.apache.commons.lang3.StringUtils; /** @@ -38,7 +38,7 @@ public void progressOnFailed(Class clazz, int index, Throwable cause) { } private void progress(int index) { - if(printer.isBroken()) { + if (printer.isBroken()) { return; } final int rate = computeRate(index); diff --git a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/WatchEventListener.java b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/WatchEventListener.java index 22d05384..2b3a5719 100755 --- a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/WatchEventListener.java +++ b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/WatchEventListener.java @@ -1,13 +1,13 @@ package com.alibaba.jvm.sandbox.module.debug; -import com.alibaba.jvm.sandbox.api.listener.EventListener; import com.alibaba.jvm.sandbox.api.event.BeforeEvent; import com.alibaba.jvm.sandbox.api.event.Event; import com.alibaba.jvm.sandbox.api.event.ReturnEvent; import com.alibaba.jvm.sandbox.api.event.ThrowsEvent; +import com.alibaba.jvm.sandbox.api.http.printer.Printer; +import com.alibaba.jvm.sandbox.api.listener.EventListener; import com.alibaba.jvm.sandbox.module.debug.util.Express; import com.alibaba.jvm.sandbox.module.debug.util.GaStringUtils; -import com.alibaba.jvm.sandbox.api.http.printer.Printer; import java.util.HashMap; import java.util.Map; diff --git a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/util/GaEnumUtils.java b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/util/GaEnumUtils.java index da720b4d..e7249428 100755 --- a/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/util/GaEnumUtils.java +++ b/sandbox-debug-module/src/main/java/com/alibaba/jvm/sandbox/module/debug/util/GaEnumUtils.java @@ -16,7 +16,7 @@ public class GaEnumUtils { public static > Set valuesOf(Class enumClass, String[] enumNameArray, T[] defaultEnumArray) { final Set enumSet = new LinkedHashSet(); - if(ArrayUtils.isNotEmpty(enumNameArray)) { + if (ArrayUtils.isNotEmpty(enumNameArray)) { for (final String enumName : enumNameArray) { final T enumValue = EnumUtils.getEnum(enumClass, enumName); if (null != enumValue) { diff --git a/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ControlModule.java b/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ControlModule.java new file mode 100644 index 00000000..624f184c --- /dev/null +++ b/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ControlModule.java @@ -0,0 +1,140 @@ +package com.albaba.jvm.sandbox.module.mgr; + +import com.alibaba.jvm.sandbox.api.Information; +import com.alibaba.jvm.sandbox.api.Module; +import com.alibaba.jvm.sandbox.api.ModuleException; +import com.alibaba.jvm.sandbox.api.http.Http; +import com.alibaba.jvm.sandbox.api.resource.ModuleManager; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +@Information(id = "control", version = "0.0.1", author = "luanjia@taobao.com") +public class ControlModule implements Module { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Resource + private ModuleManager moduleManager; + + // 清道夫,清理SandboxClassLoader和Spy中对Method的引用 + private void cleanRef(final Class classOfAgentLauncher) throws IllegalAccessException, ClassNotFoundException { + + // 清理AgentLauncher.sandboxClassLoader + FieldUtils.writeDeclaredStaticField( + classOfAgentLauncher, + "sandboxClassLoader", + null, + true + ); + logger.info("clean jvm-sandbox ClassLoader success, for jvm-sandbox shutdown."); + + // 清理Spy中的所有方法 + final Class classOfSpy = getClass().getClassLoader() + .loadClass("java.com.alibaba.jvm.sandbox.spy.Spy"); + for (final Field waitingCleanField : classOfSpy.getDeclaredFields()) { + if (Method.class.isAssignableFrom(waitingCleanField.getType())) { + FieldUtils.writeDeclaredStaticField( + classOfSpy, + waitingCleanField.getName(), + null, + true + ); + } + } + logger.info("clean Spy's method success, for jvm-sandbox shutdown."); + } + + // 卸载所有模块 + // 从这里开始只允许调用JDK自带的反射方法,因为ControlModule已经完成卸载,你找不到apache的包了 + private void unloadModules() throws ModuleException, IOException { + + for (final Module module : moduleManager.list()) { + final Information information = module.getClass().getAnnotation(Information.class); + if (null == information + || StringUtils.isBlank(information.id())) { + continue; + } + // 如果遇到自己,需要最后才卸载 + if (module == this) { + continue; + } + moduleManager.unload(information.id()); + logger.info("unload module={} success, for shutdown jvm-sandbox.", information.id()); + } + + } + + // 卸载自己 + private void unloadSelf() throws ModuleException { + // 卸载自己 + final String self = getClass().getAnnotation(Information.class).id(); + moduleManager.unload(self); + logger.info("unload module={} success, for shutdown jvm-sandbox.", self); + } + + // 关闭HTTP服务器 + private void shutdownServer(final ClassLoader sandboxClassLoader) + throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException { + + final Class classOfJettyCoreServer = sandboxClassLoader + .loadClass("com.alibaba.jvm.sandbox.core.server.jetty.JettyCoreServer"); + final Object objectOfJettyCoreServer = classOfJettyCoreServer.getMethod("getInstance").invoke(null); + final Method methodOfDestroy = classOfJettyCoreServer.getMethod("destroy"); + methodOfDestroy.invoke(objectOfJettyCoreServer, null); + logger.info("shutdown http-server success, for shutdown jvm-sandbox."); + } + + @Http("/shutdown") + public void shutdown(final HttpServletResponse resp) throws Exception { + + logger.info("prepare to shutdown jvm-sandbox."); + + final Class classOfAgentLauncher = getClass().getClassLoader() + .loadClass("com.alibaba.jvm.sandbox.agent.AgentLauncher"); + + final ClassLoader sandboxClassLoader = (ClassLoader) FieldUtils.getDeclaredField( + classOfAgentLauncher, + "sandboxClassLoader", + true + ).get(null); + + // 清理引用 + cleanRef(classOfAgentLauncher); + + // 卸载模块 + unloadModules(); + + // 关闭HTTP服务器 + final Thread shutdownJvmSandboxHook = new Thread(new Runnable() { + @Override + public void run() { + try { + unloadSelf(); + shutdownServer(sandboxClassLoader); + logger.info("shutdown jvm-sandbox finished."); + } catch (Throwable cause) { + logger.warn("shutdown jvm-sandbox failed.", cause); + } + } + }, "shutdown-jvm-sandbox-hook"); + shutdownJvmSandboxHook.setDaemon(true); + + // 在卸载自己之前,先向这个世界发出最后的呐喊吧! + resp.getWriter().println("jvm-sandbox shutdown finished."); + resp.getWriter().flush(); + resp.getWriter().close(); + + shutdownJvmSandboxHook.start(); + + } + +} diff --git a/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/InfoModule.java b/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/InfoModule.java index 784edec5..6eff6dea 100755 --- a/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/InfoModule.java +++ b/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/InfoModule.java @@ -2,8 +2,10 @@ import com.alibaba.jvm.sandbox.api.Information; import com.alibaba.jvm.sandbox.api.Module; +import com.alibaba.jvm.sandbox.api.event.Event; import com.alibaba.jvm.sandbox.api.http.Http; import com.alibaba.jvm.sandbox.api.resource.ConfigInfo; +import com.alibaba.jvm.sandbox.api.resource.EventMonitor; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; @@ -11,36 +13,41 @@ /** * 沙箱信息模块 - * Created by luanjia@taobao.com on 2017/2/9. + * + * @author luanjia@taobao.com */ -@Information(id = "info", version = "0.0.0.1", author = "luanjia@taobao.com") +@Information(id = "info", version = "0.0.2", author = "luanjia@taobao.com") public class InfoModule implements Module { @Resource private ConfigInfo configInfo; + @Resource + private EventMonitor eventMonitor; + @Http("/version") public void version(final HttpServletResponse resp) throws IOException { final StringBuilder versionSB = new StringBuilder() - .append(" VERSION : ").append(configInfo.getVersion()).append("\n") - .append(" MODE : ").append(configInfo.getMode()).append("\n") - .append(" SERVER_ADDR : ").append(configInfo.getServerAddress().getHostName()).append("\n") - .append(" SERVER_PORT : ").append(configInfo.getServerAddress().getPort()).append("\n") - .append(" UNSAFE_SUPPORT : ").append(configInfo.isEnableUnsafe() ? "ENABLE" : "DISABLE").append("\n") - .append(" SANDBOX_HOME : ").append(configInfo.getHome()).append("\n") - .append(" SYSTEM_MODULE_LIB : ").append(configInfo.getSystemModuleLibPath()).append("\n") - .append(" USER_MODULE_LIB : ").append(configInfo.getUserModuleLibPath()).append("\n") - .append("SYSTEM_PROVIDER_LIB : ").append(configInfo.getSystemProviderLibPath()).append("\n") - .append(" EVENT_POOL_SUPPORT : ").append(configInfo.isEnableEventPool() ? "ENABLE" : "DISABLE"); - /*################### : */ + .append(" VERSION : ").append(configInfo.getVersion()).append("\n") + .append(" MODE : ").append(configInfo.getMode()).append("\n") + .append(" SERVER_ADDR : ").append(configInfo.getServerAddress().getHostName()).append("\n") + .append(" SERVER_PORT : ").append(configInfo.getServerAddress().getPort()).append("\n") + .append(" UNSAFE_SUPPORT : ").append(configInfo.isEnableUnsafe() ? "ENABLE" : "DISABLE").append("\n") + .append(" SANDBOX_HOME : ").append(configInfo.getHome()).append("\n") + .append(" SYSTEM_MODULE_LIB : ").append(configInfo.getSystemModuleLibPath()).append("\n") + .append(" USER_MODULE_LIB : ").append(configInfo.getUserModuleLibPath()).append("\n") + .append(" SYSTEM_PROVIDER_LIB : ").append(configInfo.getSystemProviderLibPath()).append("\n") + .append(" EVENT_POOL_SUPPORT : ").append(configInfo.isEnableEventPool() ? "ENABLE" : "DISABLE"); + /*############################# : */ if (configInfo.isEnableEventPool()) { versionSB .append("\n") - /*################### : */ - .append(" EVENT_POOL_KEY_MIN : ").append(configInfo.getEventPoolKeyMin()).append("\n") - .append(" EVENT_POOL_KEY_MAX : ").append(configInfo.getEventPoolKeyMax()).append("\n") - .append(" EVENT_POOL_TOTAL : ").append(configInfo.getEventPoolTotal()) + /*############################# : */ + .append(" EVENT_POOL_PER_KEY_IDLE_MIN : ").append(configInfo.getEventPoolMinIdlePerEvent()).append("\n") + .append(" EVENT_POOL_PER_KEY_IDLE_MAX : ").append(configInfo.getEventPoolMaxIdlePerEvent()).append("\n") + .append(" EVENT_POOL_PER_KEY_TOTAL_MAX : ").append(configInfo.getEventPoolMaxTotalPerEvent()).append("\n") + .append(" EVENT_POOL_TOTAL : ").append(configInfo.getEventPoolMaxTotal()) ; } @@ -48,4 +55,18 @@ public void version(final HttpServletResponse resp) throws IOException { } + @Http("/event-pool") + public void eventPool(final HttpServletResponse resp) throws IOException { + + for (Event.Type type : Event.Type.values()) { + resp.getWriter().println(String.format( + "%18s : %d / %d", + type, + eventMonitor.getEventPoolInfo().getNumActive(type), + eventMonitor.getEventPoolInfo().getNumIdle(type) + )); + } + + } + } diff --git a/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ModuleMgrModule.java b/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ModuleMgrModule.java index 618d52a5..7d1afe00 100755 --- a/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ModuleMgrModule.java +++ b/sandbox-mgr-module/src/main/java/com/albaba/jvm/sandbox/module/mgr/ModuleMgrModule.java @@ -27,9 +27,10 @@ /** * 沙箱模块管理模块 - * Created by luanjia@taobao.com on 2017/2/9. + * + * @author luanjia@taobao.com */ -@Information(id = "module-mgr", author = "luanjia@taobao.com", version = "0.0.0.1") +@Information(id = "module-mgr", author = "luanjia@taobao.com", version = "0.0.1") public class ModuleMgrModule implements Module { private final Logger logger = LoggerFactory.getLogger(getClass()); diff --git a/sandbox-mgr-module/src/main/resources/META-INF/services/com.alibaba.jvm.sandbox.api.Module b/sandbox-mgr-module/src/main/resources/META-INF/services/com.alibaba.jvm.sandbox.api.Module index 6e630ae9..cfc01fac 100755 --- a/sandbox-mgr-module/src/main/resources/META-INF/services/com.alibaba.jvm.sandbox.api.Module +++ b/sandbox-mgr-module/src/main/resources/META-INF/services/com.alibaba.jvm.sandbox.api.Module @@ -1,2 +1,3 @@ com.albaba.jvm.sandbox.module.mgr.ModuleMgrModule -com.albaba.jvm.sandbox.module.mgr.InfoModule \ No newline at end of file +com.albaba.jvm.sandbox.module.mgr.InfoModule +com.albaba.jvm.sandbox.module.mgr.ControlModule \ No newline at end of file diff --git a/sandbox-mgr-provider/src/main/java/com/alibaba/jvm/sandbox/provider/mgr/EmptyModuleJarLoadingChain.java b/sandbox-mgr-provider/src/main/java/com/alibaba/jvm/sandbox/provider/mgr/EmptyModuleJarLoadingChain.java index 954bb7e3..2b00a604 100644 --- a/sandbox-mgr-provider/src/main/java/com/alibaba/jvm/sandbox/provider/mgr/EmptyModuleJarLoadingChain.java +++ b/sandbox-mgr-provider/src/main/java/com/alibaba/jvm/sandbox/provider/mgr/EmptyModuleJarLoadingChain.java @@ -6,6 +6,7 @@ /** * 空实现 + * * @author luanjia@taobao.com */ public class EmptyModuleJarLoadingChain implements ModuleJarLoadingChain { diff --git a/sandbox-provider-api/pom.xml b/sandbox-provider-api/pom.xml index b1f82ee6..fa08069c 100644 --- a/sandbox-provider-api/pom.xml +++ b/sandbox-provider-api/pom.xml @@ -19,10 +19,12 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.2 + 3.0.0-M1 + attach-javadocs @@ -45,7 +47,7 @@ org.apache.maven.plugins maven-source-plugin - 2.1.2 + 3.0.1 diff --git a/sandbox-util/src/main/java/com/alibaba/jvm/sandbox/util/SandboxStringUtils.java b/sandbox-util/src/main/java/com/alibaba/jvm/sandbox/util/SandboxStringUtils.java index f6047719..6e1ea933 100755 --- a/sandbox-util/src/main/java/com/alibaba/jvm/sandbox/util/SandboxStringUtils.java +++ b/sandbox-util/src/main/java/com/alibaba/jvm/sandbox/util/SandboxStringUtils.java @@ -54,7 +54,6 @@ public static String getCauseMessage(Throwable t) { } - /** * 通配符表达式匹配 *