Giter Site home page Giter Site logo

jvm-sandbox's Introduction

BANNER

Build Status codecov Average time to resolve an issue Percentage of issues still open

JVM沙箱容器,一种JVM的非侵入式运行期AOP解决方案
Real - time non-invasive AOP framework container based on JVM

目标群体

  • BTRACE好强大,也曾技痒想做一个更便捷、更适合自己的问题定位工具,既可支持线上链路监控排查,也可支持单机版问题定位。
  • 有时候突然一个问题反馈上来,需要入参才能完成定位,但恰恰没有任何日志,甚至出现在别人的代码里,好想开发一个工具可以根据需要动态添加日志,最好还能按照业务ID进行过滤。
  • 系统间的异常模拟可以使用的工具很多,可是系统内的异常模拟怎么办,加开关或是用AOP在开发系统中实现,好想开发一个更优雅的异常模拟工具,既能模拟系统间的异常,又能模拟系统内的异常。
  • 好想获取行调用链路数据,可以用它识别场景、覆盖率统计等等,覆盖率统计工具不能原生支持,统计链路数据不准确。想自己开发一个工具获取行链路数据。
  • 我想开发录制回放、故障模拟、动态日志、行链路获取等等工具,就算我开发完成了,这些工具底层实现原理相同,同时使用,要怎么消除这些工具之间的影响,怎么保证这些工具动态加载,怎么保证动态加载/卸载之后不会影响其他工具,怎么保证在工具有问题的时候,快速消除影响,代码还原

如果你有以上研发诉求,那么你就是JVM-SANDBOX(以下简称沙箱容器)的潜在客户。沙箱容器提供

  1. 动态增强类你所指定的类,获取你想要的参数和行信息甚至改变方法执行
  2. 动态可插拔容器框架

项目简介

JVM-SANDBOX(沙箱)实现了一种在不重启、不侵入目标JVM应用的AOP解决方案。

沙箱的特性

  1. 无侵入:目标应用无需重启也无需感知沙箱的存在
  2. 类隔离:沙箱以及沙箱的模块不会和目标应用的类相互干扰
  3. 可插拔:沙箱以及沙箱的模块可以随时加载和卸载,不会在目标应用留下痕迹
  4. 多租户:目标应用可以同时挂载不同租户下的沙箱并独立控制
  5. 高兼容:支持JDK[6,11]

沙箱常见应用场景

  • 线上故障定位
  • 线上系统流控
  • 线上故障模拟
  • 方法请求录制和结果回放
  • 动态日志打印
  • 安全信息监测和脱敏

JVM-SANDBOX还能帮助你做很多很多,取决于你的脑洞有多大了。

实时无侵入AOP框架

在常见的AOP框架实现方案中,有静态编织和动态编织两种。

  1. 静态编织:静态编织发生在字节码生成时根据一定框架的规则提前将AOP字节码插入到目标类和方法中,实现AOP;
  2. 动态编织:动态编织则允许在JVM运行过程中完成指定方法的AOP字节码增强.常见的动态编织方案大多采用重命名原有方法,再新建一个同签名的方法来做代理的工作模式来完成AOP的功能(常见的实现方案如CgLib),但这种方式存在一些应用边界:
    • 侵入性:对被代理的目标类需要进行侵入式改造。比如:在Spring中必须是托管于Spring容器中的Bean
    • 固化性:目标代理方法在启动之后即固化,无法重新对一个已有方法进行AOP增强

要解决无侵入的特性需要AOP框架具备 在运行时完成目标方法的增强和替换。在JDK的规范中运行期重定义一个类必须准循以下原则

  1. 不允许新增、修改和删除成员变量
  2. 不允许新增和删除方法
  3. 不允许修改方法签名

JVM-SANDBOX属于基于Instrumentation的动态编织类的AOP框架,通过精心构造了字节码增强逻辑,使得沙箱的模块能在不违反JDK约束情况下实现对目标应用方法的无侵入运行时AOP拦截

核心原理

事件驱动

在沙箱的世界观中,任何一个Java方法的调用都可以分解为BEFORERETURNTHROWS三个环节,由此在三个环节上引申出对应环节的事件探测和流程控制机制。

// BEFORE
try {

   /*
    * do something...
    */

    // RETURN
    return;

} catch (Throwable cause) {
    // THROWS
}

基于BEFORERETURNTHROWS三个环节事件分离,沙箱的模块可以完成很多类AOP的操作。

  1. 可以感知和改变方法调用的入参
  2. 可以感知和改变方法调用返回值和抛出的异常
  3. 可以改变方法执行的流程
    • 在方法体执行之前直接返回自定义结果对象,原有方法代码将不会被执行
    • 在方法体返回之前重新构造新的结果对象,甚至可以改变为抛出异常
    • 在方法体抛出异常之后重新抛出新的异常,甚至可以改变为正常返回

类隔离策略

沙箱通过自定义的SandboxClassLoader破坏了双亲委派的约定,实现了和目标应用的类隔离。所以不用担心加载沙箱会引起应用的类污染、冲突。各模块之间类通过ModuleJarClassLoader实现了各自的独立,达到模块之间、模块和沙箱之间、模块和应用之间互不干扰。

jvm-sandbox-classloader

类增强策略

沙箱通过在BootstrapClassLoader中埋藏的Spy类完成目标类和沙箱内核的通讯

jvm-sandbox-enhance-class

整体架构

jvm-sandbox-architecture

快速安装

  • 下载并安装或自行打包

    # 下载最新版本的JVM-SANDBOX,oss已到期,或者oss链接不可访问时,可选择自行打包
    wget https://ompc.oss-cn-hangzhou.aliyuncs.com/jvm-sandbox/release/sandbox-1.3.3-bin.zip
    
    # 解压
    unzip sandbox-1.3.3-bin.zip
    #自行打包
     cd bin
     ./sandbox-packages.sh
     #target路径下有多种构建件类型,选择一个合适的使用
     cd ../target
  • 挂载目标应用

    # 进入沙箱执行脚本
    cd sandbox/bin
    
    # 目标JVM进程33342
    ./sandbox.sh -p 33342
  • 挂载成功后会提示

    ./sandbox.sh -p 33342
               NAMESPACE : default
                 VERSION : 1.2.0
                    MODE : ATTACH
             SERVER_ADDR : 0.0.0.0
             SERVER_PORT : 55756
          UNSAFE_SUPPORT : ENABLE
            SANDBOX_HOME : /Users/vlinux/opt/sandbox
       SYSTEM_MODULE_LIB : /Users/vlinux/opt/sandbox/module
         USER_MODULE_LIB : ~/.sandbox-module;
     SYSTEM_PROVIDER_LIB : /Users/vlinux/opt/sandbox/provider
      EVENT_POOL_SUPPORT : DISABLE
  • 卸载沙箱

    ./sandbox.sh -p 33342 -S
    jvm-sandbox[default] shutdown finished.

项目构建

当你修改了sandbox的代码后,想打包成自己需要的发行版,可以执行以下命令

脚本执行目录默认为项目主目录,后续不在另外说明

cd bin
./sandbox-package.sh

命令执行成功后会在target目录下生成sandbox-<版本号>-bin.zip文件

构建注意事项

  1. 必须用JDK1.8进行构建,工程自身和maven插件中使用了tools.jar
  2. 必须在Linux/Mac/Unix下进行构建,有部分测试用例没有考虑好$USER_HOME的目录路径在windows下的特殊性,会导致测试用例跑不通过。

修改sandbox版本号

sandbox的版本号需要修改所有的pom文件以及.//sandbox-core/src/main/resources/com/alibaba/jvm/sandbox/version,这里有一个脚本方便执行

cd bin
./set-version.sh -s 1.4.0

脚本第一个参数是[s|r]

  • s : SNAPSHOT版,会自动在版本号后边追加-SNAPSHOT
  • r : 正式版

本地仓库安装api包

如果本次你修改了sandbox-api、sandbox-common-api、sandbox-module-starter等本应该发布到**仓库的包,但你需要本地测试验证,可以执行以下命令

mvn clean install

以下四个包将会安装到本地manven仓库

  • sandbox
  • sandbox-api
  • sandbox-common-api
  • sandbox-module-starter
  • sandbox-provider-api

项目背景

2014年GREYS第一版正式发布,一路看着他从无到有,并不断优化强大,感慨羡慕之余,也在想GREYS是不是只能做问题定位。

2015年开始根据GREYS的底层代码完成了人生的第一个字节码增强工具——动态日志。之后又萌生了将其拆解成录制回放故障模拟等工具的想法。扪心自问,我是想以一人一个团队的力量建立大而全的工具平台,还是做一个底层中台,让每一位技术人员都可以在它的基础上快速的实现业务功能。我选择了后者。

相关文档

jvm-sandbox's People

Contributors

caoenergy avatar cuishuang avatar dadiyang avatar dayjun avatar dependabot[bot] avatar dongchenxu avatar erichetti avatar feshfans avatar hejack0207 avatar jackychee avatar jbachorik avatar li-fujian avatar oldmanpushcart avatar qinfred2003 avatar qxo avatar sdhjl2000 avatar shoothzj avatar stigkj avatar tmtbe avatar winjeg avatar wongoo avatar wrightxyy avatar xrayw avatar yangqiju avatar zhaoyb1990 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jvm-sandbox's Issues

希望支持命名空间

在实际使用过程中,一开始你很难统一大家都用同一个jvm-sandbox来部署、管理所有的moudle。大家都希望能有更多独立的控制权。(容器版本、部署路径、加载策略)

所以希望能支持命名空间,大家通过命名空间隔离。不同命名空间的容器能同时加载且相互之间不会被干扰。

provider目录是做什么目的的?

看到Sandbox有两个项目和"provider"相关,好像也是一种用SPI进行插件加载的架构。
${SANDBOX_TARGET_DIR}/provider/sandbox-mgr-provider.jar
但是实现是空的
com.alibaba.jvm.sandbox.provider.mgr.EmptyModuleJarLoadingChain
com.alibaba.jvm.sandbox.provider.mgr.EmptyModuleLoadingChain

另外在DefaultCoreModuleManager里面看到下面这个片段
/**
* 用户模块文件加载回调
*/
final private class InnerModuleJarLoadCallback implements ModuleJarLoader.ModuleJarLoadCallback {
@OverRide
public void onLoad(File moduleJarFile) throws Throwable {
providerManager.loading(moduleJarFile);
}
}

这个是不是说去掉这块也不影响主要功能, 只是说给用户一个口子在每个模块文件加载时候可以有一定的方式可以影响?

在时钟测试程序中遇到空指针异常

import javax.annotation.Resource; 
@Resource
private ModuleEventWatcher moduleEventWatcher;

moduleEventWatcher这个对象一直获取报空指针异常,请问这个对象是如何初始化到spring中的,在spring中的id,或者如何获取。

waitingReTransformClassSet is empty,一切成功也不报错,就是没法拦截

2018-01-23 08:37:35 INFO frozen module[id=broken-clock-tinker;] finish.
2018-01-23 08:37:35 INFO all module unload, ModuleClassLoader[crc32=2282037844;file=/root/.sandbox-module/clock-tinker-0.0.1-SNAPSHOT-jar-with-dependencies.jar;] was release.
2018-01-23 08:37:35 INFO module loaded found 1 jar in /root/.sandbox-module
2018-01-23 08:37:35 INFO append ModuleLifeCycleEventListener[listener=com.alibaba.jvm.sandbox.core.manager.impl.DefaultModuleEventWatcher@35c815bd;].
2018-01-23 08:37:35 INFO active module[id=broken-clock-tinker;] finish.
2018-01-23 08:37:35 INFO loaded module[id=broken-clock-tinker;class=class com.sandbox.BrokenClockTinkerModule;] success, loader=ModuleClassLoader[crc32=2282037844;file=/root/.sandbox-module/clock-tinker-0.0.1-SNAPSHOT-jar-with-dependencies.jar;]
2018-01-23 08:37:57 INFO waitingReTransformClassSet is empty, ignore reTransformClasses. module[id=broken-clock-tinker;];watchId=1000;
2018-01-23 08:37:57 INFO active listener success. listener-id=7;listener=com.sandbox.BrokenClockTinkerModule$2@5f1f480;

Windows 无法启动解决

AgentLauncher.java#L24
小改一下就OK了, 原因.getCodeSource().getLocation()返回的URl,路径分隔符一定是/,用File.separator去substring自然在Win下会出问题

SANDBOX_HOME
            = substringBeforeLast(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile(), File.separator)
            + File.separator + "..";
			
SANDBOX_HOME = new File(AgentLauncher.class.getProtectionDomain().getCodeSource().getLocation().getFile()).getParentFile().getParent();

call before 为何对调用两次?

moduleEventWatcher.watch(

            // 匹配到Clock$BrokenClock#checkState()
            new NameRegexFilter("TestLambda", "main1"),

            // 监听THROWS事件并且改变原有方法抛出异常为正常返回
            new EventListener() {
                @Override
                public void onEvent(Event event) throws Throwable {
                    // 立即返回
                    System.out.println("aaaaaaaaaaaaaaaaaaaaaa");

                }
            },
            // 指定监听的事件为抛出异常
            Event.Type.CALL_BEFORE

把例子稍微修改了一下,为何调用一次main1之前会调用两次这个call before的内容,有遇到过的吗

自研模块如何添加第三方依赖jar

看例子里面的maven文件只添加了sandbox和servlet-api的provide依赖,如果想添加一些compile的依赖jar怎么打包模块,是用jarjar么? 还是能有一个classpath的位置放这些第三方的包?

不支持JDK9

jvm-sandbox容器在JDK9运行的时候会报错,需要对

  1. asm5.0太老了,需要升级到6.0
  2. 找不到javax.annotation.Resource

方法功能增强问题

希望在支持Spring框架的情况下,能够进一步改进方法的增强方式,比如一个方法原来没有抛出异常,我现在改为让它抛出异常的话,我还需要同时加上@transactional注解事务,让它开启事务,以验证一些问题,这方面的功能增强仍然有提升空间。还有一个应用场景:

我写了一段代码,想要在指定方法的before之前拦截住,不让它执行原来有问题的代码,而改为执行我现在的代码,那么,一般情况下这段代码,是有很强的针对性的,我写的这段代码竟然能匹配很多类的很多个方法,个人感觉这种情况一般不会发生,真遇到这种情况,最初写工程的时候,我就要把它提取出来做一定的抽象和封装,这样才能减少代码冗余,降低维护成本。所以,正则匹配类名和方法名的这个问题,我觉得还可以再讨论一下。

一个方法同时watch,会对字节码进行重复增强

  public java.lang.String checkPreload();
    descriptor: ()Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=8, locals=1, args_size=1
         0: iconst_2
         1: pop
         2: iconst_0
         3: anewarray     #5                  // class java/lang/Object
         6: dup
         7: bipush        21
         9: iconst_2
        10: ldc           #35                 // String com.alibaba.middleware.MainController
        12: ldc           #36                 // String checkPreload
        14: ldc           #37                 // String ()Ljava/lang/String;
        16: aload_0
        17: invokestatic  #43                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnBefore:([Ljava/lang/Object;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
        20: swap
        21: pop
        22: dup
        23: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
        26: dup
        27: iconst_1
        28: if_icmpeq     38
        31: iconst_2
        32: if_icmpeq     49
        35: goto          56
        38: pop
        39: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
        42: checkcast     #55                 // class java/lang/String
        45: areturn
        46: nop
        47: nop
        48: athrow
        49: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
        52: checkcast     #33                 // class java/lang/Throwable
        55: athrow
        56: pop
        57: iconst_3
        58: pop
        59: iconst_2
        60: pop
        61: iconst_0
        62: anewarray     #5                  // class java/lang/Object
        65: dup
        66: bipush        18
        68: iconst_2
        69: ldc           #35                 // String com.alibaba.middleware.MainController
        71: ldc           #36                 // String checkPreload
        73: ldc           #37                 // String ()Ljava/lang/String;
        75: aload_0
        76: invokestatic  #43                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnBefore:([Ljava/lang/Object;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
        79: swap
        80: pop
        81: dup
        82: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
        85: dup
        86: iconst_1
        87: if_icmpeq     97
        90: iconst_2
        91: if_icmpeq     153
        94: goto          160
        97: pop
        98: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       101: checkcast     #55                 // class java/lang/String
       104: iconst_2
       105: pop
       106: dup
       107: bipush        21
       109: invokestatic  #59                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnReturn:(Ljava/lang/Object;I)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
       112: dup
       113: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
       116: dup
       117: iconst_1
       118: if_icmpeq     128
       121: iconst_2
       122: if_icmpeq     139
       125: goto          146
       128: pop
       129: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       132: checkcast     #55                 // class java/lang/String
       135: areturn
       136: nop
       137: nop
       138: athrow
       139: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       142: checkcast     #33                 // class java/lang/Throwable
       145: athrow
       146: pop
       147: iconst_3
       148: pop
       149: areturn
       150: nop
       151: nop
       152: athrow
       153: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       156: checkcast     #33                 // class java/lang/Throwable
       159: athrow
       160: pop
       161: iconst_3
       162: pop
       163: ldc           #3                  // String success
       165: iconst_2
       166: pop
       167: dup
       168: bipush        18
       170: invokestatic  #59                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnReturn:(Ljava/lang/Object;I)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
       173: dup
       174: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
       177: dup
       178: iconst_1
       179: if_icmpeq     189
       182: iconst_2
       183: if_icmpeq     245
       186: goto          252
       189: pop
       190: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       193: checkcast     #55                 // class java/lang/String
       196: iconst_2
       197: pop
       198: dup
       199: bipush        21
       201: invokestatic  #59                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnReturn:(Ljava/lang/Object;I)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
       204: dup
       205: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
       208: dup
       209: iconst_1
       210: if_icmpeq     220
       213: iconst_2
       214: if_icmpeq     231
       217: goto          238
       220: pop
       221: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       224: checkcast     #55                 // class java/lang/String
       227: areturn
       228: nop
       229: nop
       230: athrow
       231: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       234: checkcast     #33                 // class java/lang/Throwable
       237: athrow
       238: pop
       239: iconst_3
       240: pop
       241: areturn
       242: nop
       243: nop
       244: athrow
       245: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       248: checkcast     #33                 // class java/lang/Throwable
       251: athrow
       252: pop
       253: iconst_3
       254: pop
       255: iconst_2
       256: pop
       257: dup
       258: bipush        21
       260: invokestatic  #59                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnReturn:(Ljava/lang/Object;I)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
       263: dup
       264: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
       267: dup
       268: iconst_1
       269: if_icmpeq     279
       272: iconst_2
       273: if_icmpeq     290
       276: goto          297
       279: pop
       280: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       283: checkcast     #55                 // class java/lang/String
       286: areturn
       287: nop
       288: nop
       289: athrow
       290: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       293: checkcast     #33                 // class java/lang/Throwable
       296: athrow
       297: pop
       298: iconst_3
       299: pop
       300: areturn
       301: iconst_2
       302: pop
       303: dup
       304: bipush        18
       306: invokestatic  #63                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnThrows:(Ljava/lang/Throwable;I)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
       309: dup
       310: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
       313: dup
       314: iconst_1
       315: if_icmpeq     325
       318: iconst_2
       319: if_icmpeq     381
       322: goto          388
       325: pop
       326: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       329: checkcast     #55                 // class java/lang/String
       332: iconst_2
       333: pop
       334: dup
       335: bipush        21
       337: invokestatic  #59                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnReturn:(Ljava/lang/Object;I)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
       340: dup
       341: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
       344: dup
       345: iconst_1
       346: if_icmpeq     356
       349: iconst_2
       350: if_icmpeq     367
       353: goto          374
       356: pop
       357: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       360: checkcast     #55                 // class java/lang/String
       363: areturn
       364: nop
       365: nop
       366: athrow
       367: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       370: checkcast     #33                 // class java/lang/Throwable
       373: athrow
       374: pop
       375: iconst_3
       376: pop
       377: areturn
       378: nop
       379: nop
       380: athrow
       381: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       384: checkcast     #33                 // class java/lang/Throwable
       387: athrow
       388: pop
       389: iconst_3
       390: pop
       391: athrow
       392: iconst_2
       393: pop
       394: dup
       395: bipush        21
       397: invokestatic  #63                 // Method java/com/alibaba/jvm/sandbox/spy/Spy.spyMethodOnThrows:(Ljava/lang/Throwable;I)Ljava/com/alibaba/jvm/sandbox/spy/Spy$Ret;
       400: dup
       401: getfield      #49                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.state:I
       404: dup
       405: iconst_1
       406: if_icmpeq     416
       409: iconst_2
       410: if_icmpeq     427
       413: goto          434
       416: pop
       417: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       420: checkcast     #55                 // class java/lang/String
       423: areturn
       424: nop
       425: nop
       426: athrow
       427: getfield      #53                 // Field java/com/alibaba/jvm/sandbox/spy/Spy$Ret.respond:Ljava/lang/Object;
       430: checkcast     #33                 // class java/lang/Throwable
       433: athrow
       434: pop
       435: iconst_3
       436: pop
       437: athrow
      Exception table:
         from    to  target type
            59   136   301   Class java/lang/Throwable
           139   150   301   Class java/lang/Throwable
           153   228   301   Class java/lang/Throwable
           231   242   301   Class java/lang/Throwable
           245   287   301   Class java/lang/Throwable
           290   301   301   Class java/lang/Throwable
             0    46   392   Class java/lang/Throwable
            49   136   392   Class java/lang/Throwable
           139   150   392   Class java/lang/Throwable
           153   228   392   Class java/lang/Throwable
           231   242   392   Class java/lang/Throwable
           245   287   392   Class java/lang/Throwable
           290   364   392   Class java/lang/Throwable
           367   378   392   Class java/lang/Throwable
           381   392   392   Class java/lang/Throwable
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
          163     138     0  this   Lcom/alibaba/middleware/MainController;
      LineNumberTable:
        line 31: 163
      StackMapTable: number_of_entries = 38
        frame_type = 255 /* full_frame */
          offset_delta = 38
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 7
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 70 /* same_locals_1_stack_item */
          stack = [ class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 40
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 30
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 7
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 3
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 70 /* same_locals_1_stack_item */
          stack = [ class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 28
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 30
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 7
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 3
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 26
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 7
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 67 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 23
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 30
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 7
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/lang/String, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 3
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 67 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 23
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret, int ]
        frame_type = 255 /* full_frame */
          offset_delta = 7
          locals = []
          stack = [ class java/lang/Throwable ]
        frame_type = 255 /* full_frame */
          offset_delta = 2
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
        frame_type = 255 /* full_frame */
          offset_delta = 6
          locals = [ class com/alibaba/middleware/MainController ]
          stack = [ class java/lang/Throwable, class java/com/alibaba/jvm/sandbox/spy/Spy$Ret ]
    RuntimeVisibleAnnotations:
      0: #16(#17=[s#22])
      1: #23()
}

有没有办法做成一个ListenerDispatcher,只增强一次。

SandboxClassLoader的parent为AppClassLoader导致的logback加载问题

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/zhuyong/.m2/repository/ch/qos/logback/logback-classic/1.1.11/logback-classic-1.1.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/zhuyong/github/jvm-sandbox/target/sandbox/lib/../lib/sandbox-core.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/zhuyong/.m2/repository/ch/qos/logback/logback-classic/1.1.11/logback-classic-1.1.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/var/folders/_8/t39d5h3s6ql89cqwy7j470sc0000gn/T/sandbox_module_jar_5526050970864565436.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [ch.qos.logback.classic.util.ContextSelectorStaticBinder]

当应用AppClassLoader中有logback时,sandbox启动时,会在应用的控制台日志中输出如上的信息,原因是SandboxClassLoader的parent为AppClassLoader,Slf4j在初始化时会扫描classpath下的实现类,这时会加载到应用中的资源;

建议是将SandboxClassLoader的parent设置为ext classloader。

执行-R命令时sandbox.log会有异常抛出

虽然不影响功能,但毕竟不优雅,需要再下次发布时解决

2017-12-19 17:39:54 WARN  http request value=/module-mgr/reset invoke module[id=module-mgr;] class com.albaba.jvm.sandbox.module.mgr.ModuleMgrModule#reset failed.
org.eclipse.jetty.io.UncheckedIOException: java.io.IOException: Closed
	at org.eclipse.jetty.io.UncheckedPrintWriter.setError(UncheckedPrintWriter.java:110)
	at org.eclipse.jetty.io.UncheckedPrintWriter.write(UncheckedPrintWriter.java:283)
	at org.eclipse.jetty.io.UncheckedPrintWriter.write(UncheckedPrintWriter.java:298)
	at org.eclipse.jetty.io.UncheckedPrintWriter.print(UncheckedPrintWriter.java:463)
	at org.eclipse.jetty.io.UncheckedPrintWriter.println(UncheckedPrintWriter.java:645)
	at com.albaba.jvm.sandbox.module.mgr.ModuleMgrModule.output(ModuleMgrModule.java:64)
	at com.albaba.jvm.sandbox.module.mgr.ModuleMgrModule.reset(ModuleMgrModule.java:119)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at com.alibaba.jvm.sandbox.core.server.jetty.servlet.ModuleHttpServlet.doMethod(ModuleHttpServlet.java:119)
	at com.alibaba.jvm.sandbox.core.server.jetty.servlet.ModuleHttpServlet.doGet(ModuleHttpServlet.java:44)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:734)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
	at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:558)
	at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:488)
	at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:973)
	at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:417)
	at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:907)
	at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:117)
	at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:110)
	at org.eclipse.jetty.server.Server.handle(Server.java:350)
	at org.eclipse.jetty.server.HttpConnection.handleRequest(HttpConnection.java:442)
	at org.eclipse.jetty.server.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:924)
	at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:582)
	at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:218)
	at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:51)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:586)
	at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:44)
	at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:598)
	at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:533)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.io.IOException: Closed
	at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:152)
	at org.eclipse.jetty.server.HttpOutput.write(HttpOutput.java:101)
	at java.io.ByteArrayOutputStream.writeTo(ByteArrayOutputStream.java:167)
	at org.eclipse.jetty.server.HttpWriter.write(HttpWriter.java:283)
	at org.eclipse.jetty.server.HttpWriter.write(HttpWriter.java:107)
	at org.eclipse.jetty.io.UncheckedPrintWriter.write(UncheckedPrintWriter.java:274)
	... 31 common frames omitted

[问题]sandbox server是和目标进程公用一个JVM?

简单的了解了下启动部分,看到jetty的启动是在AgentLauncher里进行的,所以最后外部访问的server是放在目标的JVM里吗?如果是这样,这么做的考量是什么呢?为什么不单独放在外部呢?

如果有理解错的地方,望指正。

启动时报这个错误,但jar包里肯定有这个类的。

启动时报这个错误,
java.lang.NoClassDefFoundError: com/alibaba/jvm/sandbox/api/Module
但jar包里肯定有这个类的。并且用 ./sandbox.sh -p xxxx -l 显示模块已加载出来了

2018-01-29 06:55:40 INFO cfg=;port=0;event.pool.enable=true;event.pool.max.total.per.event=2000;event.pool.max.total=6000;provider=/usr/local/sandbox/lib/../provider;sandbox_home=/usr/local/sandbox/lib/..;event.pool.min.idle.per.event=50;event.pool.max.idle.per.event=100;mode=attach;user_module=/.sandbox-module;;namespace=default;ip=0.0.0.0;system_module=/usr/local/sandbox/lib/../module;unsafe.enable=true;cfg=/usr/local/sandbox/lib/../cfg;
2018-01-29 06:55:40 INFO enable event-pool[per-key-idle-min=50;per-key-idle-max=100;per-key-max=2000;total=6000;]
2018-01-29 06:55:40 INFO append ModuleLifeCycleEventListener[listener=com.alibaba.jvm.sandbox.core.manager.impl.DefaultModuleResourceManager@64a26054;].
2018-01-29 06:55:40 INFO loading provider[com.alibaba.jvm.sandbox.provider.api.ModuleJarLoadingChain] was success from provider-jar[/usr/local/sandbox/lib/../provider/sandbox-mgr-provider.jar], impl=com.alibaba.jvm.sandbox.provider.mgr.EmptyModuleJarLoadingChain
2018-01-29 06:55:40 INFO loading provider[com.alibaba.jvm.sandbox.provider.api.ModuleLoadingChain] was success from provider-jar[/usr/local/sandbox/lib/../provider/sandbox-mgr-provider.jar], impl=com.alibaba.jvm.sandbox.provider.mgr.EmptyModuleLoadingChain
2018-01-29 06:55:40 INFO loading provider-jar[/usr/local/sandbox/lib/../provider/sandbox-mgr-provider.jar] was success.
2018-01-29 06:55:40 INFO module loaded found 1 jar in /usr/local/sandbox/lib/../module
2018-01-29 06:55:40 WARN module SPI new instance failed, ignore this SPI.
java.lang.NoClassDefFoundError: com/alibaba/jvm/sandbox/api/Module
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:800)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:449)
at java.net.URLClassLoader.access$100(URLClassLoader.java:71)
at java.net.URLClassLoader$1.run(URLClassLoader.java:361)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:412)
at com.alibaba.jvm.sandbox.core.classloader.RoutingURLClassLoader.loadClass(RoutingURLClassLoader.java:69)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:274)
at java.util.ServiceLoader$LazyIterator.next(ServiceLoader.java:363)
at java.util.ServiceLoader$1.next(ServiceLoader.java:445)
at com.alibaba.jvm.sandbox.core.manager.impl.ModuleJarLoader.load(ModuleJarLoader.java:97)
at com.alibaba.jvm.sandbox.core.manager.impl.DefaultCoreModuleManager.reset(DefaultCoreModuleManager.java:507)
at com.alibaba.jvm.sandbox.core.manager.impl.DefaultCoreModuleManager.(DefaultCoreModuleManager.java:87)
at com.alibaba.jvm.sandbox.core.server.jetty.JettyCoreServer.initManager(JettyCoreServer.java:204)
at com.alibaba.jvm.sandbox.core.server.jetty.JettyCoreServer.access$300(JettyCoreServer.java:43)
at com.alibaba.jvm.sandbox.core.server.jetty.JettyCoreServer$2.process(JettyCoreServer.java:222)
at com.alibaba.jvm.sandbox.core.util.Initializer.initProcess(Initializer.java:91)
at com.alibaba.jvm.sandbox.core.server.jetty.JettyCoreServer.bind(JettyCoreServer.java:212)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at com.alibaba.jvm.sandbox.agent.AgentLauncher.main(AgentLauncher.java:221)
at com.alibaba.jvm.sandbox.agent.AgentLauncher.agentmain(AgentLauncher.java:101)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.instrument.InstrumentationImpl.loadClassAndStartAgent(InstrumentationImpl.java:382)
at sun.instrument.InstrumentationImpl.loadClassAndCallAgentmain(InstrumentationImpl.java:407)
Caused by: java.lang.ClassNotFoundException: com.alibaba.jvm.sandbox.api.Module
at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
... 37 common frames omitted
2018-01-29 06:55:41 INFO active module[id=module-mgr;] finish.
2018-01-29 06:55:41 INFO loaded module[id=module-mgr;class=class com.albaba.jvm.sandbox.module.mgr.ModuleMgrModule;] success, loader=ModuleClassLoader[crc32=893661261;file=/usr/local/sandbox/lib/../module/sandbox-mgr-module.jar;]
2018-01-29 06:55:41 INFO active module[id=info;] finish.
2018-01-29 06:55:41 INFO loaded module[id=info;class=class com.albaba.jvm.sandbox.module.mgr.InfoModule;] success, loader=ModuleClassLoader[crc32=893661261;file=/usr/local/sandbox/lib/../module/sandbox-mgr-module.jar;]
2018-01-29 06:55:41 INFO active module[id=control;] finish.
2018-01-29 06:55:41 INFO loaded module[id=control;class=class com.albaba.jvm.sandbox.module.mgr.ControlModule;] success, loader=ModuleClassLoader[crc32=893661261;file=/usr/local/sandbox/lib/../module/sandbox-mgr-module.jar;]
2018-01-29 06:55:41 INFO module loaded found 1 jar in /root/.sandbox-module
2018-01-29 06:55:41 INFO append ModuleLifeCycleEventListener[listener=com.alibaba.jvm.sandbox.core.manager.impl.DefaultModuleEventWatcher@4ceae360;].
2018-01-29 06:55:41 INFO active module[id=broken-clock-tinker;] finish.
2018-01-29 06:55:41 INFO loaded module[id=broken-clock-tinker;class=class com.vbgps.sandbox.BrokenClockTinkerModule;] success, loader=ModuleClassLoader[crc32=2311267895;file=/root/.sandbox-module/sandbox-clock-0.0.1-SNAPSHOT-jar-with-dependencies.jar;]
2018-01-29 06:55:41 INFO jetty-8.0.y.z-SNAPSHOT
2018-01-29 06:55:41 INFO Started SelectChannelConnector@localhost:44628 STARTING
2018-01-29 06:55:41 INFO sandbox start finished.
2018-01-29 06:55:41 INFO server bind to localhost/127.0.0.1:44628 success. cfg=;port=0;event.pool.enable=true;event.pool.max.total.per.event=2000;event.pool.max.total=6000;provider=/usr/local/sandbox/lib/../provider;sandbox_home=/usr/local/sandbox/lib/..;event.pool.min.idle.per.event=50;event.pool.max.idle.per.event=100;mode=attach;user_module=/.sandbox-module;;namespace=default;ip=0.0.0.0;system_module=/usr/local/sandbox/lib/../module;unsafe.enable=true;cfg=/usr/local/sandbox/lib/../cfg;

加载不到jar包,见内容

2018-01-23 07:30:50 INFO module loaded found 1 jar in /root/.sandbox-module
2018-01-23 07:30:50 WARN load sandbox module JAR[file=/root/.sandbox-module/clock-tinker-0.0.1-SNAPSHOT-jar-with-dependencies.jar], but none module loaded, close this ModuleClassLoader.

可以attach远程JVM吗

你好,我想用sandbox客户端在本地操作多台远程服务器上的JVM,可以吗?

SelfCallBarrier#delete(Node node)

节点删除后,虽然没有外部引用它,但它却引用了top节点和next节点,虽然可被回收,但相对gc不友好,
不知道理解的对不对?

模块加载成功后未生效

按照开发示例中代码进行测试,本地安装成功,attach成功,但是加载模块后,执行./sandbox.sh -p 18083 -d 'broken-clock-tinker/repairCheckState'后未生效。
运行环境:
JDK 1.7
Ubuntu 16.04

sandbox日志:
2018-01-30 13:28:15 INFO cfg=;port=0;event.pool.enable=true;event.pool.max.total.per.event=2000;event.pool.max.total=6000;provider=/home/sunshined/.opt/sandbox/lib/../provider;sandbox_home=/home/sunshined/.opt/sandbox/lib/..;event.pool.min.idle.per.event=50;event.pool.max.idle.per.event=100;mode=attach;user_module=~/.sandbox-module\;;namespace=default;ip=0.0.0.0;system_module=/home/sunshined/.opt/sandbox/lib/../module;unsafe.enable=true;cfg=/home/sunshined/.opt/sandbox/lib/../cfg; 2018-01-30 13:28:15 INFO enable event-pool[per-key-idle-min=50;per-key-idle-max=100;per-key-max=2000;total=6000;] 2018-01-30 13:28:15 INFO append ModuleLifeCycleEventListener[listener=com.alibaba.jvm.sandbox.core.manager.impl.DefaultModuleResourceManager@2d7f2fae;]. 2018-01-30 13:28:15 INFO loading provider[com.alibaba.jvm.sandbox.provider.api.ModuleJarLoadingChain] was success from provider-jar[/home/sunshined/.opt/sandbox/lib/../provider/sandbox-mgr-provider.jar], impl=com.alibaba.jvm.sandbox.provider.mgr.EmptyModuleJarLoadingChain 2018-01-30 13:28:15 INFO loading provider[com.alibaba.jvm.sandbox.provider.api.ModuleLoadingChain] was success from provider-jar[/home/sunshined/.opt/sandbox/lib/../provider/sandbox-mgr-provider.jar], impl=com.alibaba.jvm.sandbox.provider.mgr.EmptyModuleLoadingChain 2018-01-30 13:28:15 INFO loading provider-jar[/home/sunshined/.opt/sandbox/lib/../provider/sandbox-mgr-provider.jar] was success. 2018-01-30 13:28:15 INFO module loaded found 1 jar in /home/sunshined/.opt/sandbox/lib/../module 2018-01-30 13:28:15 INFO active module[id=module-mgr;] finish. 2018-01-30 13:28:15 INFO loaded module[id=module-mgr;class=class com.albaba.jvm.sandbox.module.mgr.ModuleMgrModule;] success, loader=ModuleClassLoader[crc32=893661261;file=/home/sunshined/.opt/sandbox/lib/../module/sandbox-mgr-module.jar;] 2018-01-30 13:28:15 INFO active module[id=info;] finish. 2018-01-30 13:28:15 INFO loaded module[id=info;class=class com.albaba.jvm.sandbox.module.mgr.InfoModule;] success, loader=ModuleClassLoader[crc32=893661261;file=/home/sunshined/.opt/sandbox/lib/../module/sandbox-mgr-module.jar;] 2018-01-30 13:28:15 INFO active module[id=control;] finish. 2018-01-30 13:28:15 INFO loaded module[id=control;class=class com.albaba.jvm.sandbox.module.mgr.ControlModule;] success, loader=ModuleClassLoader[crc32=893661261;file=/home/sunshined/.opt/sandbox/lib/../module/sandbox-mgr-module.jar;] 2018-01-30 13:28:15 INFO module loaded found 1 jar in /home/sunshined/.sandbox-module 2018-01-30 13:28:15 INFO append ModuleLifeCycleEventListener[listener=com.alibaba.jvm.sandbox.core.manager.impl.DefaultModuleEventWatcher@7486a276;]. 2018-01-30 13:28:15 INFO active module[id=broken-clock-tinker;] finish. 2018-01-30 13:28:15 INFO loaded module[id=broken-clock-tinker;class=class com.cmos.clock.tinker.BrokenClockTinkerModule;] success, loader=ModuleClassLoader[crc32=1822121259;file=/home/sunshined/.sandbox-module/clock-tinker-1.0-SNAPSHOT-jar-with-dependencies.jar;] 2018-01-30 13:28:15 INFO jetty-8.0.y.z-SNAPSHOT 2018-01-30 13:28:15 INFO Started SelectChannelConnector@localhost:42993 STARTING 2018-01-30 13:28:15 INFO sandbox start finished. 2018-01-30 13:28:15 INFO server bind to localhost/127.0.0.1:42993 success. cfg=;port=0;event.pool.enable=true;event.pool.max.total.per.event=2000;event.pool.max.total=6000;provider=/home/sunshined/.opt/sandbox/lib/../provider;sandbox_home=/home/sunshined/.opt/sandbox/lib/..;event.pool.min.idle.per.event=50;event.pool.max.idle.per.event=100;mode=attach;user_module=~/.sandbox-module\;;namespace=default;ip=0.0.0.0;system_module=/home/sunshined/.opt/sandbox/lib/../module;unsafe.enable=true;cfg=/home/sunshined/.opt/sandbox/lib/../cfg; 2018-01-30 13:29:23 INFO waitingReTransformClassSet is empty, ignore reTransformClasses. module[id=broken-clock-tinker;];watchId=1000; 2018-01-30 13:29:23 INFO active listener success. listener-id=1;listener=com.cmos.clock.tinker.BrokenClockTinkerModule$1@1044ec1;

新版“脊柱”发布公告

新版“脊柱”发布公告

同步群主的消息

年后SANDBOX将会发布新版本,代号:脊柱

核心特性有

  1. 插桩点增强

    • 增加AspectJ Pointcut expression支持

      可能会跳票

      将会精简实现Aspject的部分表达式语法,以后写埋点的时候将不用那么痛苦去实现Filter,直接拼写表达式即可。表达式可以参考:Spring AOP AspectJ Pointcut Expressions With Examples

    • 增加Wildcard Pointcut expression支持

      进一步降低类匹配门槛

    • 增加对实现类、子类的支持

      当前Filter只能过滤普通的Class,但实际在使用过程中往往实现类是匿名内部类或通过工厂模式所生产,无法拿到确定的类。

  2. API优化

    相信各位已经在实现Sandbox-Module的时候被Event的处理折腾疯了,我也看到很多人在写例子的时候会踩到线程安全,递归调用等常见的坑,这些都是API不够友好所导致。

    很多人也私聊推荐我们采用bytebuddy的优美API,我们收到了。正在考虑中,希望集思广益~

  3. 模块消息通讯

    开源的最大价值在于给JVM-SANDOBX沉淀更多的模块。但沉淀的模块如果没有被复用的价值,那是不会有人期待公共模块的诞生,没有买卖就没有市场。

    最近也在看Pinpoint的代码,里面对我触动最大的就是沉淀了市面上常见的中间件的埋点代码。如何让更多的埋点事件沉淀到JVM-SANDBOX生态圈上,并进一步降低大家的门槛。

    我们的解决方案是增加模块之间的消息通讯,敬请期待~

  4. 增加更多的Example

    这个似乎是更多人的呼声了,但你希望增加那些EXAMPLE呢?时间毕竟有限,我可以选择投票最高的2个来实现。

    • 线上故障定位
    • 线上系统流控
    • 线上故障模拟
    • 方法请求录制和结果回放
    • 动态日志打印
    • 安全信息监测和脱敏
  5. 合并大家的PR

    以下PR将会被合并,非常感谢大家的努力参与

    1. Code enhancement for #20 Author:caoenergy
    2. sandbox.log时区问题 Author: duxiaokang
    3. 获取参数的方式错误 Author: yangqiju
    4. fix SANDBOX_HOME on Windews #52 Author: Xlongshu

关于 watch 使用时候的一点疑问

我看到文档中只描述了 BEFORE、RETURN、THROWS,但是翻看代码时候发现,com.alibaba.jvm.sandbox.api.event.EVENT 里面有 LINE 事件,请问一下这个有调用实例的 demo 吗,我们可以做到监控某行代码被调用到的情况吗

增强的类出现循环依赖错误

异常信息

java.lang.ClassCircularityError: com/alibaba/jvm/sandbox/module/trace/cache/ThreadLocalHolder
	at com.alibaba.jvm.sandbox.module.trace.TraceMetaQListener.onEvent(TraceMetaQListener.java:31)
	at com.alibaba.jvm.sandbox.core.enhance.weaver.SeparateImmediatelyEventListener.onEvent(SeparateImmediatelyEventListener.java:73)
	at com.alibaba.jvm.sandbox.core.enhance.weaver.EventListenerHandlers.handleEvent(EventListenerHandlers.java:106)
	at com.alibaba.jvm.sandbox.core.enhance.weaver.EventListenerHandlers.handleOnBefore(EventListenerHandlers.java:332)
	at com.alibaba.jvm.sandbox.core.enhance.weaver.EventListenerHandlers.handleOnBeforeWithTargetClassLoaderSpyRet(EventListenerHandlers.java:385)
	at com.alibaba.jvm.sandbox.core.enhance.weaver.EventListenerHandlers.onBefore(EventListenerHandlers.java:558)
	at sun.reflect.GeneratedMethodAccessor140.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at java.com.alibaba.jvm.sandbox.spy.Spy.spyMethodOnBefore(Spy.java:84)
	at com.alibaba.security.jam.framework.JamSecurityManager.checkPropertyAccess(JamSecurityManager.java)
	at java.lang.System.getProperty(System.java:708)
	at com.laiwang.rpc.server.lwp.LwpServiceStatus.status(LwpServiceStatus.java:17)
	at com.laiwang.rpc.server.lwp.LwpServiceListen.replyOK(LwpServiceListen.java:34)
	at sun.reflect.GeneratedMethodAccessor49.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:606)
	at com.laiwang.protocol.ListenDriver$ToInvoker$1.invoke(ListenDriver.java:86)
	at com.laiwang.protocol.ListenDriver$2.received(ListenDriver.java:31)
	at com.laiwang.protocol.transport.MessageHandler.channelRead(MessageHandler.java:67)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at com.laiwang.protocol.transport.HeartbeatHandler.channelRead(HeartbeatHandler.java:55)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at com.laiwang.protocol.transport.GracefullOfflineHandler.channelRead(GracefullOfflineHandler.java:43)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at com.laiwang.protocol.transport.ViaHandler.channelRead(ViaHandler.java:73)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at com.laiwang.protocol.transport.CompressHandler.channelRead(CompressHandler.java:36)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at com.laiwang.protocol.transport.WireLoggingHandler.channelRead(WireLoggingHandler.java:198)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at com.laiwang.protocol.transport.LogAccessHandler.channelRead(LogAccessHandler.java:57)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:293)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:267)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:351)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1334)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:373)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:926)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:129)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:651)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:574)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:488)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:450)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:873)
	at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:144)
	at java.lang.Thread.run(Thread.java:744)

执行.sandbox.sh时报错

安装成功,但是执行./sandbox.sh报错,具体报错信息如下,可能是什么原因了...
20180208230143

想问一下怎么访问被观测方法的入参?

问题:
想观察一下系统所有的对外发起HTTP的requestBody (基于Apache HTTPClient),记录到log文件。
@Override
public CloseableHttpResponse execute(
final HttpUriRequest request,
final HttpContext context) throws IOException, ClientProtocolException
在观察的事件里面需要调用request.getURI()之类的方法

这里的HttpUriRequest, HttpContext, CloseableHttpResponse 类在应用系统里面(Tomcat)均是被WebappClassLoader载入的, 在沙箱的模块代码里面如何访问这些应用的Class的?

我看了下沙箱项目的ClassLoader的设计和Class的隔离的管理,下面是我的理解,不知道是否有误:

  1. 沙箱项目的Spy类在最顶级的BootstrapClassLoader被加载
  2. 被观察的系统的类按照其自己的设计和部署(比如Tomcat), 用合适的ClassLoader载入自己相关目录资源下面的class.
  3. 沙箱模块自己的Class由沙箱自己的SandboxClassLoader载入,包括用户使用的EventWatcher
    4.在被观察模块里面注入对Spy类的调用(触发调用用户提供的Handler)

用户在自己的handler里面怎么才能访问到入参的Class呢?这里是要用ContextClassLoader来反射调用吗?

有同学反馈原声的EventListener很难用

集中在

  1. 并发场景需要处理
  2. ReturnEvent需要拿到BeforeEvent中的信息需要处理
  3. 各种Event是事件池管控,如若处理不当会引起Event污染的问题

JVM-SANDBOX暴露出EventListener是出于性能上的考虑,所以这个原生的Listener仍然还会留着,只是提供给对性能非常敏感的场景下进行高级使用。

另外将会提供出更多便于使用的封装API给大家

时钟的例子成功执行但时钟没修复

你好,

我在尝试时钟修复的例子的时候,植入成功,两个修复命令都执行了没有报错,但是时钟却没有被修复,会是什么原因呢?

root@fde2808ec2a0:/home/xxxxx/.opt/sandbox/bin# ./sandbox.sh -p 2524
NAMESPACE : default
VERSION : 0.1.0.2
MODE : ATTACH
SERVER_ADDR : localhost
SERVER_PORT : 33617
UNSAFE_SUPPORT : ENABLE
SANDBOX_HOME : /home/xxxxx/.opt/sandbox/lib/..
SYSTEM_MODULE_LIB : /home/xxxxx/.opt/sandbox/lib/../module
USER_MODULE_LIB : ~/.sandbox-module;
SYSTEM_PROVIDER_LIB : /home/xxxxx/.opt/sandbox/lib/../provider
EVENT_POOL_SUPPORT : ENABLE
EVENT_POOL_PER_KEY_IDLE_MIN : 50
EVENT_POOL_PER_KEY_IDLE_MAX : 100
EVENT_POOL_PER_KEY_TOTAL_MAX : 2000
EVENT_POOL_TOTAL : 6000
root@fde2808ec2a0:/home/xxxxx/.opt/sandbox/bin# ./sandbox.sh -p 2524 -d 'broken-clock-tinker/repairCheckState'
root@fde2808ec2a0:/home/xxxxx/.opt/sandbox/bin# ./sandbox.sh -p 2524 -d 'broken-clock-tinker/repairDelay'
root@fde2808ec2a0:/home/xxxxx/.opt/sandbox/bin#

SANDBOX提供感知业务容器的HOOK

在很多场景中需要拿到目标对象的实例,而SANDBOX的框架机制是不允许你能拿到对象实例的。所以需要提供一个HOOK,侵入到业务代码中拿到这个实例。

需求:SANDBOX需要提供一个hook-api系列,以便与大家能通过SANDBOX容器通过这个HOOK拿到目标对象的实例

用例过少

JVM Sandbox现在的用例非常少,可否供一些典型场景的用例供学习使用。这样子也方便项目推广。

clock用例没有执行

attach之后得到:

./sandbox.sh -p 48630 -F
module flush finished, total=3;

这个是什么意思呢?已经把package结果复制到 ~/.sandbox-module/下面了;attach也能attach成功,但是看不到active的module?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.