mirror of
https://github.com/apache/zeppelin
synced 2026-05-24 09:38:26 +00:00
groovy interpreter
This commit is contained in:
parent
0cb0f36baf
commit
fa779ea5df
11 changed files with 836 additions and 0 deletions
95
groovy/README.md
Normal file
95
groovy/README.md
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
## Groovy Interpreter
|
||||
|
||||
|
||||
### Samples
|
||||
|
||||
```groovy
|
||||
%groovy
|
||||
//get a parameter defined as z.angularBind('ngSearchParam', value, 'paragraph_id')
|
||||
//g is a context object for groovy to avoid mix with z object
|
||||
def param = g.angular('ngSearchParam')
|
||||
//send request https://www.googleapis.com/customsearch/v1?q=ngSearchParam_value
|
||||
def r = HTTP.get(
|
||||
//assume you defined the groovy interpreter property
|
||||
// `search_baseurl`='https://www.googleapis.com/customsearch/v1'
|
||||
//in groovy object o.getProperty('A') == o.'A' == o.A == o['A']
|
||||
url : g.search_baseurl,
|
||||
query: [ q: param ],
|
||||
headers: [
|
||||
'Accept':'application/json',
|
||||
//'Authorization:' : g.getProperty('search_auth'),
|
||||
]
|
||||
)
|
||||
//check response code
|
||||
if( r.response.code==200 ) {
|
||||
g.html().with{
|
||||
//g.html() renders %angular to output and returns groovy.xml.MarkupBuilder
|
||||
h2("the response ${r.response.code}")
|
||||
span( r.response.body )
|
||||
h2("headers")
|
||||
pre( r.response.headers.join('\n') )
|
||||
}
|
||||
} else {
|
||||
//just to show that it's possible to use println with multiline groovy string to render output
|
||||
println("""%angular
|
||||
<script> alert ("code=${r.response.code} \n msg=${r.response.message}") </script>
|
||||
""")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
```groovy
|
||||
%groovy
|
||||
|
||||
//renders a table with headers a, b, c and two rows
|
||||
g.table(
|
||||
[
|
||||
['a','b','c'],
|
||||
['a1','b1','c1'],
|
||||
['a2','b2','c2'],
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
### the `g` object
|
||||
|
||||
* `g.angular(String name)`
|
||||
|
||||
Returns angular object by name. Look up notebook scope first and then global scope.
|
||||
|
||||
|
||||
* `g.angularBind(String name, Object value)`
|
||||
|
||||
Assign a new `value` into angular object `name`
|
||||
|
||||
|
||||
* `java.util.Properties g.getProperties()`
|
||||
|
||||
returns all properties defined for this interpreter
|
||||
|
||||
|
||||
* `String g.getProperty('PROPERTY_NAME')`
|
||||
```groovy
|
||||
g.PROPERTY_NAME
|
||||
g.'PROPERTY_NAME'
|
||||
g['PROPERTY_NAME']
|
||||
g.getProperties().getProperty('PROPERTY_NAME')
|
||||
```
|
||||
|
||||
All above the accessor to named property defined in groovy interpreter.
|
||||
In this case with name `PROPERTY_NAME`
|
||||
|
||||
|
||||
* `groovy.xml.MarkupBuilder g.html()`
|
||||
|
||||
Starts or continues rendering of `%angular` to output and returns [groovy.xml.MarkupBuilder](https://www.google.com/search?q=groovy.xml.MarkupBuilder)
|
||||
MarkupBuilder is usefull to generate html (xml)
|
||||
|
||||
* `void g.table(obj)`
|
||||
|
||||
starts or continues rendering table rows.
|
||||
|
||||
obj: List(rows) of List(columns) where first line is a header
|
||||
|
||||
|
||||
|
||||
4
groovy/README.txt
Normal file
4
groovy/README.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
groovy language interpreter
|
||||
|
||||
project.name : ${project.name}
|
||||
project.version : ${project.version}
|
||||
152
groovy/pom.xml
Normal file
152
groovy/pom.xml
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
~ (the "License"); you may not use this file except in compliance with
|
||||
~ the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<artifactId>zeppelin</artifactId>
|
||||
<groupId>org.apache.zeppelin</groupId>
|
||||
<!--version>0.7</version-->
|
||||
<version>0.8.0-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>org.apache.zeppelin</groupId>
|
||||
<artifactId>zeppelin-groovy</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>0.8.0-SNAPSHOT</version>
|
||||
<name>Zeppelin: Groovy interpreter</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>zeppelin-interpreter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.codehaus.groovy/groovy-all -->
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
<version>2.4.7</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<!--TODO: comment local `maven-checkstyle-plugin` and use zeppelin common check style-->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
<executions>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
<version>1.3.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>enforce</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>2.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/../../interpreter/groovy</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
<includeScope>runtime</includeScope>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-artifact</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.directory}/../../interpreter/groovy</outputDirectory>
|
||||
<overWriteReleases>false</overWriteReleases>
|
||||
<overWriteSnapshots>false</overWriteSnapshots>
|
||||
<overWriteIfNewer>true</overWriteIfNewer>
|
||||
<includeScope>runtime</includeScope>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>${project.artifactId}</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>${project.packaging}</type>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<configuration>
|
||||
<descriptor>src/assembly/dep.xml</descriptor>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>create-archive</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
66
groovy/src/assembly/dep.xml
Normal file
66
groovy/src/assembly/dep.xml
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!--
|
||||
~ Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
~ contributor license agreements. See the NOTICE file distributed with
|
||||
~ this work for additional information regarding copyright ownership.
|
||||
~ The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
~ (the "License"); you may not use this file except in compliance with
|
||||
~ the License. You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
|
||||
<id>bin</id>
|
||||
<baseDirectory>groovy</baseDirectory>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<filtered>true</filtered>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
<include>revision.txt</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/src/main/groovy/</directory>
|
||||
<outputDirectory>/classes/</outputDirectory>
|
||||
<includes>
|
||||
<include>*.groovy</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
|
||||
<!--fileSet>
|
||||
<directory>${project.build.directory}/site</directory>
|
||||
<outputDirectory>docs</outputDirectory>
|
||||
</fileSet-->
|
||||
</fileSets>
|
||||
<dependencySets>
|
||||
<dependencySet>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<unpack>false</unpack>
|
||||
<scope>runtime</scope>
|
||||
<!--excludes>
|
||||
<exclude>junit:junit</exclude>
|
||||
</excludes-->
|
||||
</dependencySet>
|
||||
</dependencySets>
|
||||
</assembly>
|
||||
1
groovy/src/assembly/readme.txt
Normal file
1
groovy/src/assembly/readme.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
to assemble groovy interpreter separately
|
||||
154
groovy/src/main/groovy/HTTP.groovy
Normal file
154
groovy/src/main/groovy/HTTP.groovy
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import groovy.json.JsonOutput
|
||||
|
||||
/**
|
||||
* simple http rest client for groovy
|
||||
* by dlukyanov@ukr.net
|
||||
*/
|
||||
@groovy.transform.CompileStatic
|
||||
public class HTTP{
|
||||
//default response handler
|
||||
public static Closure TEXT_RECEIVER = {InputStream instr,Map ctx->
|
||||
return instr.getText( (String)ctx.encoding );
|
||||
}
|
||||
|
||||
public static Closure JSON_RECEIVER = { InputStream instr, Map ctx->
|
||||
return new groovy.json.JsonSlurper().parse(instr,(String)ctx.encoding);
|
||||
}
|
||||
|
||||
public static Closure FILE_RECEIVER(File f){
|
||||
return { InputStream instr, Map ctx->
|
||||
f<<instr;
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
public static Map<String,Object> get(Map<String,Object> ctx)throws IOException{
|
||||
ctx.put('method','GET');
|
||||
return send(ctx);
|
||||
}
|
||||
|
||||
public static Map<String,Object> post(Map<String,Object> ctx)throws IOException{
|
||||
ctx.put('method','POST');
|
||||
return send(ctx);
|
||||
}
|
||||
|
||||
public static Map<String,Object> put(Map<String,Object> ctx)throws IOException{
|
||||
ctx.put('method','PUT');
|
||||
return send(ctx);
|
||||
}
|
||||
|
||||
public static Map<String,Object> delete(Map<String,Object> ctx)throws IOException{
|
||||
ctx.put('method','DELETE');
|
||||
return send(ctx);
|
||||
}
|
||||
|
||||
public static Map<String,Object> send(Map<String,Object> ctx)throws IOException{
|
||||
String url = ctx.url;
|
||||
Map<String,String> headers = (Map<String,String>)ctx.headers;
|
||||
String method = ctx.method;
|
||||
Object body = ctx.body;
|
||||
String encoding = ctx.encoding?:"UTF-8";
|
||||
Closure receiver = (Closure)ctx.receiver;
|
||||
Map<String,String> query = (Map<String,String>)ctx.query;
|
||||
|
||||
//copy context and set default values
|
||||
ctx = [:] + ctx;
|
||||
ctx.encoding = encoding;
|
||||
String contentType="";
|
||||
|
||||
if(query){
|
||||
url+="?"+query.collect{k,v-> k+"="+URLEncoder.encode(v,'UTF-8') }.join('&')
|
||||
}
|
||||
|
||||
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
|
||||
|
||||
connection.setDoOutput(true);
|
||||
connection.setRequestMethod(method);
|
||||
if ( headers!=null && !headers.isEmpty() ) {
|
||||
//add headers
|
||||
for (Map.Entry<String, String> entry : headers.entrySet()) {
|
||||
connection.addRequestProperty(entry.getKey(), entry.getValue());
|
||||
if("content-type".equals(entry.getKey().toLowerCase()))contentType=entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
if(body!=null){
|
||||
//write body
|
||||
OutputStream out = connection.getOutputStream();
|
||||
if( body instanceof Closure ){
|
||||
((Closure)body).call(out, ctx);
|
||||
}else if(body instanceof InputStream){
|
||||
out << (InputStream)body;
|
||||
}else if(body instanceof Map){
|
||||
if( contentType.matches("(?i)[^/]+/json") ){
|
||||
out.withWriter((String)ctx.encoding){
|
||||
it.append( JsonOutput.toJson((Map)body) );
|
||||
it.flush();
|
||||
}
|
||||
}else{
|
||||
throw new IOException("Map body type supported only for */json content-type");
|
||||
}
|
||||
}else if(body instanceof CharSequence){
|
||||
out.withWriter((String)ctx.encoding){
|
||||
it.append((CharSequence)body);
|
||||
it.flush();
|
||||
}
|
||||
}else{
|
||||
throw new IOException("Unsupported body type: "+body.getClass());
|
||||
}
|
||||
out.flush();
|
||||
out.close();
|
||||
out=null;
|
||||
}
|
||||
|
||||
Map response = [:];
|
||||
ctx.response = response;
|
||||
response.code = connection.getResponseCode();
|
||||
response.message = connection.getResponseMessage();
|
||||
response.headers = connection.getHeaderFields();
|
||||
|
||||
InputStream instr = null;
|
||||
|
||||
if( ((int)response.code)>=400 ){
|
||||
try{
|
||||
instr = connection.getErrorStream();
|
||||
}catch(Exception ei){}
|
||||
}else{
|
||||
try{
|
||||
instr = connection.getInputStream();
|
||||
}catch(java.io.IOException ei){
|
||||
throw new IOException("fail to open InputStream for http code "+response.code+":"+ei);
|
||||
}
|
||||
}
|
||||
|
||||
if(instr!=null) {
|
||||
instr = new BufferedInputStream(instr);
|
||||
if(receiver==null){
|
||||
if( response.headers['Content-Type']?.toString()?.indexOf('/json')>0 ){
|
||||
receiver=JSON_RECEIVER;
|
||||
} else receiver=TEXT_RECEIVER;
|
||||
}
|
||||
response.body = receiver(instr,ctx);
|
||||
instr.close();
|
||||
instr=null;
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
}
|
||||
2
groovy/src/main/groovy/readme.txt
Normal file
2
groovy/src/main/groovy/readme.txt
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
folder to place some groovy helpers to simplify custom code
|
||||
as an example there is an HTTP.goovy simple class to call http/rest services
|
||||
168
groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java
Normal file
168
groovy/src/main/java/org/apache/zeppelin/groovy/GObject.java
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.zeppelin.groovy;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import org.slf4j.Logger;
|
||||
import java.util.Properties;
|
||||
import java.util.Collection;
|
||||
|
||||
import groovy.xml.MarkupBuilder;
|
||||
import groovy.lang.Closure;
|
||||
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
|
||||
import org.apache.zeppelin.display.AngularObjectRegistry;
|
||||
import org.apache.zeppelin.display.AngularObject;
|
||||
|
||||
/**
|
||||
* Groovy interpreter for Zeppelin.
|
||||
*/
|
||||
public class GObject extends groovy.lang.GroovyObjectSupport {
|
||||
Logger log;
|
||||
StringWriter out;
|
||||
Properties props;
|
||||
InterpreterContext interpreterContext;
|
||||
|
||||
public GObject(Logger log, StringWriter out, Properties p, InterpreterContext ctx){
|
||||
this.log=log;
|
||||
this.out=out;
|
||||
this.interpreterContext=ctx;
|
||||
this.props=p;
|
||||
}
|
||||
|
||||
public Object getProperty(String key){
|
||||
if("log".equals(key))return log;
|
||||
return props.getProperty(key);
|
||||
}
|
||||
public void setProperty(String key, Object value){
|
||||
throw new RuntimeException("Set properties not supported: "+key+"="+value);
|
||||
}
|
||||
public Properties getProperties(){
|
||||
return props;
|
||||
}
|
||||
|
||||
private void startOutputType(String type){
|
||||
StringBuffer sb=out.getBuffer();
|
||||
if( sb.length()>0 ){
|
||||
if( sb.length()<type.length() || !type.equals(sb.substring(0,type.length())) ){
|
||||
log.error("try to start output `"+type+"` after non-"+type+" started");
|
||||
}
|
||||
}else{
|
||||
out.append(type);
|
||||
out.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* starts or continues rendering html/angular and returns MarkupBuilder to build html.
|
||||
* <pre> g.html().with{
|
||||
* h1("hello")
|
||||
* h2("world")
|
||||
* }</pre>
|
||||
*/
|
||||
public MarkupBuilder html(){
|
||||
startOutputType("%angular");
|
||||
return new MarkupBuilder(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* starts or continues rendering table rows
|
||||
* @param obj:
|
||||
* 1. List(rows) of List(columns) where first line is a header
|
||||
*/
|
||||
public void table(Object obj){
|
||||
if(obj==null)return;
|
||||
StringBuffer sb=out.getBuffer();
|
||||
startOutputType("%table");
|
||||
if(obj instanceof groovy.lang.Closure){
|
||||
//if closure run and get result collection
|
||||
obj = ((Closure)obj).call();
|
||||
}
|
||||
if(obj instanceof Collection){
|
||||
int count = 0;
|
||||
for(Object row : ((Collection)obj)){
|
||||
count++;
|
||||
boolean rowStarted = false;
|
||||
if(row instanceof Collection){
|
||||
for( Object field: ((Collection)row) ){
|
||||
if(rowStarted)sb.append('\t');
|
||||
sb.append(field);
|
||||
rowStarted=true;
|
||||
}
|
||||
}else{
|
||||
sb.append(row);
|
||||
}
|
||||
sb.append('\n');
|
||||
}
|
||||
}else{
|
||||
throw new RuntimeException("Not supported table value :"+obj.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private AngularObject getAngularObject(String name) {
|
||||
AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
|
||||
String noteId = interpreterContext.getNoteId();
|
||||
// try get local object
|
||||
AngularObject paragraphAo = registry.get(name, noteId, interpreterContext.getParagraphId());
|
||||
AngularObject noteAo = registry.get(name, noteId, null);
|
||||
|
||||
AngularObject ao = paragraphAo != null ? paragraphAo : noteAo;
|
||||
|
||||
if (ao == null) {
|
||||
// then global object
|
||||
ao = registry.get(name, null, null);
|
||||
}
|
||||
return ao;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get angular object. Look up notebook scope first and then global scope
|
||||
* @param name variable name
|
||||
* @return value
|
||||
*/
|
||||
public Object angular(String name) {
|
||||
AngularObject ao = getAngularObject(name);
|
||||
if (ao == null) {
|
||||
return null;
|
||||
} else {
|
||||
return ao.get();
|
||||
}
|
||||
}
|
||||
|
||||
public void angularBind(String name, Object o, String noteId) {
|
||||
AngularObjectRegistry registry = interpreterContext.getAngularObjectRegistry();
|
||||
|
||||
if (registry.get(name, noteId, null) == null) {
|
||||
registry.add(name, o, noteId, null);
|
||||
} else {
|
||||
registry.get(name, noteId, null).set(o);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create angular variable in notebook scope and bind with front end Angular display system.
|
||||
* If variable exists, it'll be overwritten.
|
||||
* @param name name of the variable
|
||||
* @param o value
|
||||
*/
|
||||
public void angularBind(String name, Object o) {
|
||||
angularBind(name, o, interpreterContext.getNoteId());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.zeppelin.groovy;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
/*
|
||||
import org.apache.commons.exec.CommandLine;
|
||||
import org.apache.commons.exec.DefaultExecutor;
|
||||
import org.apache.commons.exec.ExecuteException;
|
||||
import org.apache.commons.exec.ExecuteWatchdog;
|
||||
import org.apache.commons.exec.Executor;
|
||||
import org.apache.commons.exec.PumpStreamHandler;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
*/
|
||||
import org.apache.zeppelin.interpreter.Interpreter;
|
||||
import org.apache.zeppelin.interpreter.InterpreterContext;
|
||||
import org.apache.zeppelin.interpreter.InterpreterPropertyBuilder;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Code;
|
||||
import org.apache.zeppelin.interpreter.InterpreterResult.Type;
|
||||
import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
|
||||
import org.apache.zeppelin.scheduler.Job;
|
||||
import org.apache.zeppelin.scheduler.Scheduler;
|
||||
import org.apache.zeppelin.scheduler.SchedulerFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import groovy.lang.GroovyShell;
|
||||
import groovy.lang.Script;
|
||||
import org.codehaus.groovy.control.CompilerConfiguration;
|
||||
import org.codehaus.groovy.runtime.ResourceGroovyMethods;
|
||||
import org.codehaus.groovy.runtime.StackTraceUtils;
|
||||
|
||||
/**
|
||||
* Groovy interpreter for Zeppelin.
|
||||
*/
|
||||
public class GroovyInterpreter extends Interpreter {
|
||||
Logger log = LoggerFactory.getLogger(GroovyInterpreter.class);
|
||||
GroovyShell shell = null; //new GroovyShell();
|
||||
|
||||
public GroovyInterpreter(Properties property) {
|
||||
super(property);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open() {
|
||||
CompilerConfiguration conf = new CompilerConfiguration();
|
||||
conf.setDebug(true);
|
||||
shell = new GroovyShell(conf);
|
||||
String classes = getProperty("GROOVY_CLASSES");
|
||||
if(classes==null || classes.length()==0){
|
||||
try {
|
||||
File jar = new File(GroovyInterpreter.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
|
||||
classes = new File(jar.getParentFile(),"classes").toString();
|
||||
}catch(Exception e){}
|
||||
}
|
||||
log.info("groovy classes classpath: "+classes);
|
||||
if(classes!=null && classes.length()>0){
|
||||
shell.getClassLoader().addClasspath(classes);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
shell = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FormType getFormType() {
|
||||
return FormType.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProgress(InterpreterContext context) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Scheduler getScheduler() {
|
||||
return SchedulerFactory.singleton().createOrGetParallelScheduler(GroovyInterpreter.class.getName() + this.hashCode(), 10);
|
||||
}
|
||||
|
||||
private Job getRunningJob(String paragraphId) {
|
||||
Job foundJob = null;
|
||||
Collection<Job> jobsRunning = getScheduler().getJobsRunning();
|
||||
for (Job job : jobsRunning) {
|
||||
if (job.getId().equals(paragraphId)) {
|
||||
foundJob = job;
|
||||
}
|
||||
}
|
||||
return foundJob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InterpreterCompletion> completion(String buf, int cursor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String,Class<Script>> scriptCache = Collections.synchronizedMap( new WeakHashMap(1000) );
|
||||
Script getGroovyScript(String id, String scriptText) /*throws SQLException*/ {
|
||||
if(shell==null){
|
||||
throw new RuntimeException("Groovy Shell is not initialized: null");
|
||||
}
|
||||
try{
|
||||
Class<Script> clazz = scriptCache.get(scriptText);
|
||||
if(clazz==null){
|
||||
String scriptName=id+"_"+Long.toHexString(scriptText.hashCode())+".groovy";
|
||||
clazz = (Class<Script>) shell.parse(scriptText, scriptName).getClass();
|
||||
scriptCache.put(scriptText,clazz);
|
||||
}
|
||||
|
||||
Script script=(Script)clazz.newInstance();
|
||||
return script;
|
||||
}catch(Throwable t){
|
||||
throw new RuntimeException("Failed to parse groovy script: "+t,t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InterpreterResult interpret(String cmd, InterpreterContext contextInterpreter) {
|
||||
try {
|
||||
Script script = getGroovyScript(contextInterpreter.getParagraphId(), cmd);
|
||||
Job runningJob = getRunningJob(contextInterpreter.getParagraphId());
|
||||
runningJob.info().put("CURRENT_THREAD", Thread.currentThread()); //to be able to terminate thread
|
||||
Map bindings = script.getBinding().getVariables();
|
||||
bindings.clear();
|
||||
StringWriter out = new StringWriter( (int) (cmd.length()*1.75) );
|
||||
|
||||
bindings.put("g", new GObject(log, out, this.getProperty(), contextInterpreter) );
|
||||
bindings.put("out", new PrintWriter(out, true));
|
||||
script.run();
|
||||
bindings.clear();
|
||||
InterpreterResult result = new InterpreterResult(Code.SUCCESS, out.toString());
|
||||
log.info("RESULT: "+result);
|
||||
return result;
|
||||
}catch(Throwable t){
|
||||
t = StackTraceUtils.deepSanitize(t);
|
||||
String msg = t.toString()+"\n at "+t.getStackTrace()[0];
|
||||
log.error("Failed to run script: "+t+"\n" + cmd+"\n", t);
|
||||
return new InterpreterResult(Code.ERROR, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void cancel(InterpreterContext context) {
|
||||
Job runningJob = getRunningJob(context.getParagraphId());
|
||||
if (runningJob != null) {
|
||||
Map<String, Object> info = runningJob.info();
|
||||
Object object = info.get("CURRENT_THREAD");
|
||||
if (object instanceof Thread) {
|
||||
try {
|
||||
Thread t = (Thread) object;
|
||||
t.dumpStack();
|
||||
t.stop();
|
||||
}catch(Throwable t){
|
||||
log.error("Failed to cancel script: "+t, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
9
groovy/src/main/resources/interpreter-setting.json
Normal file
9
groovy/src/main/resources/interpreter-setting.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{
|
||||
"group": "groovy",
|
||||
"name": "groovy",
|
||||
"className": "org.apache.zeppelin.groovy.GroovyInterpreter",
|
||||
"properties": {
|
||||
}
|
||||
}
|
||||
]
|
||||
1
pom.xml
1
pom.xml
|
|
@ -56,6 +56,7 @@
|
|||
<module>zeppelin-zengine</module>
|
||||
<module>zeppelin-display</module>
|
||||
<module>spark-dependencies</module>
|
||||
<module>groovy</module>
|
||||
<module>spark</module>
|
||||
<module>markdown</module>
|
||||
<module>angular</module>
|
||||
|
|
|
|||
Loading…
Reference in a new issue