Zeppelin 使用JShell实现java解释器,从此用notebook写java

REPL

交互式解释器环境
Read(取值)-> Evaluation(求值)-> Print(打印)-> Loop(循环)
python,scala都提供原生的REPL ,例如在scala命令行内,键入scala代码,会直接返回结果
既可以作为一个独立的程序运行,也可以包含在其他程序中作为整体程序的一部分使用

Zeppelin0.7.2目前不支持java的原因

当前spark解释器只支持scala,虽然提供了javaSparkContext的一个实例jsc,但并没有为它提供初始化,
我对spark解释器的源码进行了阅读,通过调用scala的REPL执行scala代码

scala调用REPL的关键代码:使用反射,将代码扔到repl中执行

/**
 * intp - org.apache.spark.repl.SparkIMain (scala 2.10)
 * intp - scala.tools.nsc.interpreter.IMain; (scala 2.11)
 */
private Results.Result interpret(String line) {
  return (Results.Result) Utils.invokeMethod(
      intp,
      "interpret",
      new Class[] {String.class},//方法的参数列表
      new Object[] {line});//方法执行时要用的实参
}

代码的执行结果直接通过控制台输出,在这之前做了输出重定向,所以直接重定向到InterpreterOutputStream输出(提供了write方法,以字节数组的形式输出)。并不做拦截或返回,这也是为什么dataset.show的输出没有直接提供Zeppelin强大的可视化功能,不过提供了其他解决办法:zeppelincontext类封装了各种输出形式的实现,包括input,chexkbox等表单,show方法封装了dataframe的可视化图表形式输出等,如果想让dataset拥有和select语句在Zeppelin中一样强大的可视化效果,自己调用z.show方法即可

Scala REPL

scala REPL文档
http://docs.scala-lang.org/overviews/repl/overview.html

IMain
http://www.scala-lang.org/api/2.12.1/scala-compiler/scala/tools/nsc/interpreter/IMain.html
scala代码解释器
compile()加载一个完整的Scala文件
interpret()根据用户的请求执行一行Scala代码
bind()将对象绑定到一个变量,然后可以被稍后解释的代码使用
整体方法基于:编译所请求的代码,然后使用Java类加载器和Java反射来运行代码并访问其结果

细节:一个单独的编译器实例用于累积所有成功编译或解释的Scala代码
为了“解释”一行代码,编译器将生成一个新对象,其中包含代码,和公共成员(以导出由该代码定义的所有变量)
要提取解释的结果显示给用户,将创建第二个“结果对象”,导入由上述对象导出的变量,然后导出名为“ eval print”的成员
优缺点
主要的优点是解释代码的行为与编译代码完全一样,包括全速运行
主要的缺点是重新定义类和方法不能正确处理,因为在Java级别的重新绑定在技术上是困难的
Zeppelin 使用JShell实现java解释器,从此用notebook写java_第1张图片

Zeppelin 使用JShell实现java解释器,从此用notebook写java_第2张图片

JShell

从Java9开始,java也可以原生支持repl,这就是JShell

目前java9还未正式发布,不过功能已经相对完善,可以在这里下载与体验http://blog.csdn.net/nougats/article/details/76219357

可以直接在bin目录下启动JShell,体验强大功能

Zeppelin 使用JShell实现java解释器,从此用notebook写java_第3张图片

JShell为我们提供了良好API,可以实现我们自己的解释器
http://download.java.net/java/jdk9/docs/api/jdk/jshell/package-summary.html
简单概括:把代码丢进JShell里,JShell会生成一系列snippet流,每一个snippet都有自己的状态标记,eval方法会执行一句代码,并返回该snippet的状态和初始化的变量值等信息,实际功能十分强大,还需自己看API文档
Zeppelin 使用JShell实现java解释器,从此用notebook写java_第4张图片

为Zeppelin0.7.2实现java解释器

重点在open,interpret方法,关键点有输出重定向,代码完整判断,source,remaining的使用

public class JavaInterpreter extends Interpreter {
  public static Logger logger = LoggerFactory.getLogger(JavaInterpreter.class);

  private JShell j;

  private InterpreterOutputStream outputStream;//zeppelin的输出流,目的是重定向JShell向控制台输出为向web页面输出

  public JavaInterpreter(Properties property) {
    super(property);
  }

  public void open() {
    //输出重定向第一步,JShell中System.out这类输出会默认输出到控制台,在zeppelin上显示不出,需要重定向JShell输出到zeppelin的输出流
    outputStream = new InterpreterOutputStream(logger);
    PrintStream ps = new PrintStream(outputStream);
    //此处out为更改JShell输出流,err为更改错误信息输出流,但并没有得到想要的错误信息,问题暂未解决
    j = JShell.builder().err(ps).out(ps).build();
  }

  public void close() {

  }

  public InterpreterResult interpret(String input, InterpreterContext interpreterContext) {
    //这里真正结束了重定向,interpreterContext.out为当前段落的输出流,将outputStream流的输出定向为interpreterContext.out
    outputStream.setInterpreterOutput(interpreterContext.out);

    InterpreterResult.Code code = InterpreterResult.Code.SUCCESS;
    StringBuffer sb = new StringBuffer();
    while (!input.isEmpty()) {
      SourceCodeAnalysis.CompletionInfo c =
          j.sourceCodeAnalysis().analyzeCompletion(input);
      //source返回代码的第一个Snippet,比如以第一个分号为界,eval一次只会执行一个Snippet
      List events = j.eval(c.source());
      for (SnippetEvent e : events) {
        sb.append(e.value() + "\n");
        if (e.causeSnippet() == null) {
          if (e.status() == Snippet.Status.REJECTED) {
            try {
            //向输出流写出错误代码
              interpreterContext.out.write("ERROR: " + c.source() + "\n");
              code = InterpreterResult.Code.INCOMPLETE;
            } catch (IOException e1) {
              e1.printStackTrace();
            }
          }

        }
      }
      //remaining返回代码除去source的剩余部分,执行eval后剩余的部分,也就是还未被执行的Snippet
      input = c.remaining();
    }
    return new InterpreterResult(code);
  }

  public void cancel(InterpreterContext interpreterContext) {

  }

  public FormType getFormType() {
    return FormType.NATIVE;
  }//三种可选NATIVE,SIMPLE,NONE,具体差异并没有搞清楚,跟具体逻辑实现无关,普遍遇到过这里报错,但还没有搞清原因

  public int getProgress(InterpreterContext interpreterContext) {
    return 0;
  }
}

java9打包

在pom文件中引入插件,其实3.1版本即可

    <properties>
<maven-compiler-plugin.version>3.6.1maven-compiler-plugin.version>
    properties>
<build>
        <plugins>
            <plugin>
<groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>${maven-compiler-plugin.version}version>
             plugin>
        plugins>
    build>

github上一些其他复杂的手段,使用toolchains等,我并没有用上
https://muyinchen.github.io/2017/07/19/%E5%A6%82%E4%BD%95%E5%9C%A8Maven%E9%A1%B9%E7%9B%AE%E4%B8%AD%E8%AE%BE%E7%BD%AEJava%209/
https://github.com/cfdobber/maven-java9-jigsaw
https://cwiki.apache.org/confluence/display/MAVEN/Java+9+-+Jigsaw

你可能感兴趣的:(zeppelin)