You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by mo...@apache.org on 2017/01/30 20:42:46 UTC

[3/3] zeppelin git commit: [ZEPPELIN-2020] Invoke method from resource

[ZEPPELIN-2020] Invoke method from resource

### What is this PR for?
This PR provides a way to invoke method remotely against the resource in the resource pool.
This will be particularly useful when the object in the resource pool is non-serializable (like jdbc connection, spark dataframe).

This PR extends data access API of Resource from

```
class Resource {
  // get object
  public Object get();
}
```

to
```
class Resource {
  // get object
  public Object get()

  // call method of object and get return value
  public Object invokeMethod(String methodName, Class [] paramTypes, Object [] params);

  // call method of object and save return value into resourcepool
  public Resource invokeMethod(String methodName, Class [] paramTypes, Object [] params, String returnResourceName);
}
```

### What type of PR is it?
Feature

### Todos
* [x] - API for remote method invocation and get result
* [x] - API for remote method invocation and store result into resource pool
* [x] - Unittest

### What is the Jira issue?
https://issues.apache.org/jira/browse/ZEPPELIN-2020

### How should this be tested?
Unittest included

### Questions:
* Does the licenses files need update? no
* Is there breaking changes for older versions? no
* Does this needs documentation? no

Author: Lee moon soo <mo...@apache.org>

Closes #1951 from Leemoonsoo/invoke_method_from_resource and squashes the following commits:

42d7eae [Lee moon soo] Add comments
30e62f2 [Lee moon soo] make RemoteResource.invokeMethod returns Resource when return resource name is specified
74b502e [Lee moon soo] Support method invocation on the resource in local,remote resource pool


Project: http://git-wip-us.apache.org/repos/asf/zeppelin/repo
Commit: http://git-wip-us.apache.org/repos/asf/zeppelin/commit/42bcf420
Tree: http://git-wip-us.apache.org/repos/asf/zeppelin/tree/42bcf420
Diff: http://git-wip-us.apache.org/repos/asf/zeppelin/diff/42bcf420

Branch: refs/heads/master
Commit: 42bcf4206ab27952311dd246d9e3778aa8dc2836
Parents: 65e1d36
Author: Lee moon soo <mo...@apache.org>
Authored: Sun Jan 29 08:23:52 2017 +0900
Committer: Lee moon soo <mo...@apache.org>
Committed: Tue Jan 31 05:42:38 2017 +0900

----------------------------------------------------------------------
 .../InvokeResourceMethodEventMessage.java       |   94 +
 .../remote/RemoteInterpreterEventClient.java    |  143 +
 .../remote/RemoteInterpreterEventPoller.java    |   95 +-
 .../remote/RemoteInterpreterServer.java         |   68 +
 .../thrift/InterpreterCompletion.java           |    2 +-
 .../thrift/RemoteApplicationResult.java         |    2 +-
 .../thrift/RemoteInterpreterContext.java        |    2 +-
 .../thrift/RemoteInterpreterEvent.java          |    2 +-
 .../thrift/RemoteInterpreterEventType.java      |    5 +-
 .../thrift/RemoteInterpreterResult.java         |    2 +-
 .../thrift/RemoteInterpreterResultMessage.java  |    2 +-
 .../thrift/RemoteInterpreterService.java        | 2729 +++++++++++++++---
 .../ZeppelinServerResourceParagraphRunner.java  |    2 +-
 .../zeppelin/resource/LocalResourcePool.java    |    4 +-
 .../zeppelin/resource/RemoteResource.java       |   43 +-
 .../org/apache/zeppelin/resource/Resource.java  |   83 +-
 .../resource/ResourcePoolConnector.java         |   20 +
 .../main/thrift/RemoteInterpreterService.thrift |    7 +-
 .../mock/MockInterpreterResourcePool.java       |   12 +-
 .../resource/DistributedResourcePoolTest.java   |   51 +
 .../zeppelin/resource/ResourceSetTest.java      |    8 +-
 21 files changed, 3007 insertions(+), 369 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/InvokeResourceMethodEventMessage.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/InvokeResourceMethodEventMessage.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/InvokeResourceMethodEventMessage.java
new file mode 100644
index 0000000..a2b0690
--- /dev/null
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/InvokeResourceMethodEventMessage.java
@@ -0,0 +1,94 @@
+/*
+ * 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.interpreter.remote;
+
+import org.apache.zeppelin.resource.ResourceId;
+
+/**
+ * message payload to invoke method of resource in the resourcepool
+ */
+public class InvokeResourceMethodEventMessage {
+  public final ResourceId resourceId;
+  public final String methodName;
+  public final String[] paramClassnames;
+  public final Object[] params;
+  public final String returnResourceName;
+
+  public InvokeResourceMethodEventMessage(
+      ResourceId resourceId,
+      String methodName,
+      Class[] paramtypes,
+      Object[] params,
+      String returnResourceName
+  ) {
+    this.resourceId = resourceId;
+    this.methodName = methodName;
+    if (paramtypes != null) {
+      paramClassnames = new String[paramtypes.length];
+      for (int i = 0; i < paramClassnames.length; i++) {
+        paramClassnames[i] = paramtypes[i].getName();
+      }
+    } else {
+      paramClassnames = null;
+    }
+
+    this.params = params;
+    this.returnResourceName = returnResourceName;
+  }
+
+  public Class [] getParamTypes() throws ClassNotFoundException {
+    if (paramClassnames == null) {
+      return null;
+    }
+
+    Class [] types = new Class[paramClassnames.length];
+    for (int i = 0; i < paramClassnames.length; i++) {
+      types[i] = this.getClass().getClassLoader().loadClass(paramClassnames[i]);
+    }
+
+    return types;
+  }
+
+  public boolean shouldPutResultIntoResourcePool() {
+    return (returnResourceName != null);
+  }
+
+  @Override
+  public int hashCode() {
+    String hash = resourceId.hashCode() + methodName;
+    if (paramClassnames != null) {
+      for (String name : paramClassnames) {
+        hash += name;
+      }
+    }
+    if (returnResourceName != null) {
+      hash += returnResourceName;
+    }
+
+    return hash.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof InvokeResourceMethodEventMessage) {
+      InvokeResourceMethodEventMessage r = (InvokeResourceMethodEventMessage) o;
+      return r.hashCode() == hashCode();
+    } else {
+      return false;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
index 900d1ac..606d35f 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventClient.java
@@ -50,6 +50,7 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
   private final List<RemoteInterpreterEvent> eventQueue = new LinkedList<>();
   private final List<ResourceSet> getAllResourceResponse = new LinkedList<>();
   private final Map<ResourceId, Object> getResourceResponse = new HashMap<>();
+  private final Map<InvokeResourceMethodEventMessage, Object> getInvokeResponse = new HashMap<>();
   private final Gson gson = new Gson();
 
   /**
@@ -165,6 +166,112 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
   }
 
   /**
+   * Invoke method and save result in resourcePool as another resource
+   * @param resourceId
+   * @param methodName
+   * @param paramTypes
+   * @param params
+   * @return
+   */
+  @Override
+  public Object invokeMethod(
+      ResourceId resourceId,
+      String methodName,
+      Class[] paramTypes,
+      Object[] params) {
+    logger.debug("Request Invoke method {} of Resource {}", methodName, resourceId.getName());
+
+    InvokeResourceMethodEventMessage invokeMethod = new InvokeResourceMethodEventMessage(
+        resourceId,
+        methodName,
+        paramTypes,
+        params,
+        null);
+
+    synchronized (getInvokeResponse) {
+      // wait for previous response consumed
+      while (getInvokeResponse.containsKey(invokeMethod)) {
+        try {
+          getInvokeResponse.wait();
+        } catch (InterruptedException e) {
+          logger.warn(e.getMessage(), e);
+        }
+      }
+      // send request
+      Gson gson = new Gson();
+
+      sendEvent(new RemoteInterpreterEvent(
+          RemoteInterpreterEventType.RESOURCE_INVOKE_METHOD,
+          gson.toJson(invokeMethod)));
+      // wait for response
+      while (!getInvokeResponse.containsKey(invokeMethod)) {
+        try {
+          getInvokeResponse.wait();
+        } catch (InterruptedException e) {
+          logger.warn(e.getMessage(), e);
+        }
+      }
+      Object o = getInvokeResponse.remove(invokeMethod);
+      getInvokeResponse.notifyAll();
+      return o;
+    }
+  }
+
+  /**
+   * Invoke method and save result in resourcePool as another resource
+   * @param resourceId
+   * @param methodName
+   * @param paramTypes
+   * @param params
+   * @param returnResourceName
+   * @return
+   */
+  @Override
+  public Resource invokeMethod(
+      ResourceId resourceId,
+      String methodName,
+      Class[] paramTypes,
+      Object[] params,
+      String returnResourceName) {
+    logger.debug("Request Invoke method {} of Resource {}", methodName, resourceId.getName());
+
+    InvokeResourceMethodEventMessage invokeMethod = new InvokeResourceMethodEventMessage(
+        resourceId,
+        methodName,
+        paramTypes,
+        params,
+        returnResourceName);
+
+    synchronized (getInvokeResponse) {
+      // wait for previous response consumed
+      while (getInvokeResponse.containsKey(invokeMethod)) {
+        try {
+          getInvokeResponse.wait();
+        } catch (InterruptedException e) {
+          logger.warn(e.getMessage(), e);
+        }
+      }
+      // send request
+      Gson gson = new Gson();
+
+      sendEvent(new RemoteInterpreterEvent(
+          RemoteInterpreterEventType.RESOURCE_INVOKE_METHOD,
+          gson.toJson(invokeMethod)));
+      // wait for response
+      while (!getInvokeResponse.containsKey(invokeMethod)) {
+        try {
+          getInvokeResponse.wait();
+        } catch (InterruptedException e) {
+          logger.warn(e.getMessage(), e);
+        }
+      }
+      Resource o = (Resource) getInvokeResponse.remove(invokeMethod);
+      getInvokeResponse.notifyAll();
+      return o;
+    }
+  }
+
+  /**
    * Supposed to call from RemoteInterpreterEventPoller
    */
   public void putResponseGetAllResources(List<String> resources) {
@@ -211,6 +318,42 @@ public class RemoteInterpreterEventClient implements ResourcePoolConnector {
 
   /**
    * Supposed to call from RemoteInterpreterEventPoller
+   * @param invokeMessage json serialized InvokeMessage
+   * @param object java serialized of the object
+   */
+  public void putResponseInvokeMethod(
+      InvokeResourceMethodEventMessage invokeMessage, ByteBuffer object) {
+    Object o = null;
+    try {
+      o = Resource.deserializeObject(object);
+    } catch (IOException e) {
+      logger.error(e.getMessage(), e);
+    } catch (ClassNotFoundException e) {
+      logger.error(e.getMessage(), e);
+    }
+
+    synchronized (getInvokeResponse) {
+      getInvokeResponse.put(invokeMessage, o);
+      getInvokeResponse.notifyAll();
+    }
+  }
+
+  /**
+   * Supposed to call from RemoteInterpreterEventPoller
+   * @param invokeMessage invoke message
+   * @param resource remote resource
+   */
+  public void putResponseInvokeMethod(
+      InvokeResourceMethodEventMessage invokeMessage, Resource resource) {
+    synchronized (getInvokeResponse) {
+      getInvokeResponse.put(invokeMessage, resource);
+      getInvokeResponse.notifyAll();
+    }
+  }
+
+
+  /**
+   * Supposed to call from RemoteInterpreterEventPoller
    * @return next available event
    */
   public RemoteInterpreterEvent pollEvent() {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
index e2a8add..e794140 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterEventPoller.java
@@ -38,6 +38,8 @@ import org.apache.zeppelin.resource.ResourceSet;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.nio.ByteBuffer;
 import java.util.LinkedList;
 import java.util.List;
@@ -62,6 +64,8 @@ public class RemoteInterpreterEventPoller extends Thread {
   private RemoteInterpreterProcess interpreterProcess;
   private InterpreterGroup interpreterGroup;
 
+  Gson gson = new Gson();
+
   public RemoteInterpreterEventPoller(
       RemoteInterpreterProcessListener listener,
       ApplicationEventListener appListener) {
@@ -117,8 +121,6 @@ public class RemoteInterpreterEventPoller extends Thread {
         interpreterProcess.releaseClient(client, broken);
       }
 
-      Gson gson = new Gson();
-
       AngularObjectRegistry angularObjectRegistry = interpreterGroup.getAngularObjectRegistry();
 
       try {
@@ -160,6 +162,12 @@ public class RemoteInterpreterEventPoller extends Thread {
           logger.debug("RESOURCE_GET {} {}", resourceId.getResourcePoolId(), resourceId.getName());
           Object o = getResource(resourceId);
           sendResourceResponseGet(resourceId, o);
+        } else if (event.getType() == RemoteInterpreterEventType.RESOURCE_INVOKE_METHOD) {
+          String message = event.getData();
+          InvokeResourceMethodEventMessage invokeMethodMessage =
+              gson.fromJson(message, InvokeResourceMethodEventMessage.class);
+          Object ret = invokeResourceMethod(invokeMethodMessage);
+          sendInvokeMethodResult(invokeMethodMessage, ret);
         } else if (event.getType() == RemoteInterpreterEventType.OUTPUT_APPEND) {
           // on output append
           Map<String, String> outputAppend = gson.fromJson(
@@ -383,8 +391,6 @@ public class RemoteInterpreterEventPoller extends Thread {
     return resourceSet;
   }
 
-
-
   private void sendResourceResponseGet(ResourceId resourceId, Object o) {
     Client client = null;
     boolean broken = false;
@@ -444,6 +450,87 @@ public class RemoteInterpreterEventPoller extends Thread {
     return null;
   }
 
+  public void sendInvokeMethodResult(InvokeResourceMethodEventMessage message, Object o) {
+    Client client = null;
+    boolean broken = false;
+    try {
+      client = interpreterProcess.getClient();
+      Gson gson = new Gson();
+      String invokeMessage = gson.toJson(message);
+      ByteBuffer obj;
+      if (o == null) {
+        obj = ByteBuffer.allocate(0);
+      } else {
+        obj = Resource.serializeObject(o);
+      }
+      client.resourceResponseInvokeMethod(invokeMessage, obj);
+    } catch (Exception e) {
+      logger.error(e.getMessage(), e);
+      broken = true;
+    } finally {
+      if (client != null) {
+        interpreterProcess.releaseClient(client, broken);
+      }
+    }
+  }
+
+  private Object invokeResourceMethod(InvokeResourceMethodEventMessage message) {
+    ResourceId resourceId = message.resourceId;
+    InterpreterGroup intpGroup = InterpreterGroup.getByInterpreterGroupId(
+        resourceId.getResourcePoolId());
+    if (intpGroup == null) {
+      return null;
+    }
+
+    RemoteInterpreterProcess remoteInterpreterProcess = intpGroup.getRemoteInterpreterProcess();
+    if (remoteInterpreterProcess == null) {
+      ResourcePool localPool = intpGroup.getResourcePool();
+      if (localPool != null) {
+        Resource res = localPool.get(resourceId.getName());
+        if (res != null) {
+          try {
+            return res.invokeMethod(
+                message.methodName,
+                message.getParamTypes(),
+                message.params,
+                message.returnResourceName);
+          } catch (Exception e) {
+            logger.error(e.getMessage(), e);
+            return null;
+          }
+        } else {
+          // object is null. can't invoke any method
+          logger.error("Can't invoke method {} on null object", message.methodName);
+          return null;
+        }
+      } else {
+        logger.error("no resource pool");
+        return null;
+      }
+    } else if (interpreterProcess.isRunning()) {
+      Client client = null;
+      boolean broken = false;
+      try {
+        client = remoteInterpreterProcess.getClient();
+        ByteBuffer res = client.resourceInvokeMethod(
+            resourceId.getNoteId(),
+            resourceId.getParagraphId(),
+            resourceId.getName(),
+            gson.toJson(message));
+        Object o = Resource.deserializeObject(res);
+        return o;
+      } catch (Exception e) {
+        logger.error(e.getMessage(), e);
+        broken = true;
+      } finally {
+        if (client != null) {
+          intpGroup.getRemoteInterpreterProcess().releaseClient(client, broken);
+        }
+      }
+    }
+    return null;
+  }
+
   private void waitQuietly() {
     try {
       synchronized (this) {

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
index 879b4f5..8624b57 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/remote/RemoteInterpreterServer.java
@@ -20,6 +20,7 @@ package org.apache.zeppelin.interpreter.remote;
 import java.io.IOException;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.net.URL;
 import java.nio.ByteBuffer;
 import java.util.*;
@@ -956,6 +957,73 @@ public class RemoteInterpreterServer
   }
 
   @Override
+  public ByteBuffer resourceInvokeMethod(
+      String noteId, String paragraphId, String resourceName, String invokeMessage) {
+    InvokeResourceMethodEventMessage message =
+        gson.fromJson(invokeMessage, InvokeResourceMethodEventMessage.class);
+
+    Resource resource = resourcePool.get(noteId, paragraphId, resourceName, false);
+    if (resource == null || resource.get() == null) {
+      return ByteBuffer.allocate(0);
+    } else {
+      try {
+        Object o = resource.get();
+        Method method = o.getClass().getMethod(
+            message.methodName,
+            message.getParamTypes());
+        Object ret = method.invoke(o, message.params);
+        if (message.shouldPutResultIntoResourcePool()) {
+          // if return resource name is specified,
+          // then put result into resource pool
+          // and return empty byte buffer
+          resourcePool.put(
+              noteId,
+              paragraphId,
+              message.returnResourceName,
+              ret);
+          return ByteBuffer.allocate(0);
+        } else {
+          // if return resource name is not specified,
+          // then return serialized result
+          ByteBuffer serialized = Resource.serializeObject(ret);
+          if (serialized == null) {
+            return ByteBuffer.allocate(0);
+          } else {
+            return serialized;
+          }
+        }
+      } catch (Exception e) {
+        logger.error(e.getMessage(), e);
+        return ByteBuffer.allocate(0);
+      }
+    }
+  }
+
+  /**
+   * Get payload of resource from remote
+   * @param invokeResourceMethodEventMessage json serialized InvokeResourcemethodEventMessage
+   * @param object java serialized of the object
+   * @throws TException
+   */
+  @Override
+  public void resourceResponseInvokeMethod(
+      String invokeResourceMethodEventMessage, ByteBuffer object) throws TException {
+    InvokeResourceMethodEventMessage message =
+        gson.fromJson(invokeResourceMethodEventMessage, InvokeResourceMethodEventMessage.class);
+
+    if (message.shouldPutResultIntoResourcePool()) {
+      Resource resource = resourcePool.get(
+          message.resourceId.getNoteId(),
+          message.resourceId.getParagraphId(),
+          message.returnResourceName,
+          true);
+      eventClient.putResponseInvokeMethod(message, resource);
+    } else {
+      eventClient.putResponseInvokeMethod(message, object);
+    }
+  }
+
+  @Override
   public void angularRegistryPush(String registryAsString) throws TException {
     try {
       Map<String, Map<String, AngularObject>> deserializedRegistry = gson

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
index 2d1c165..8a1bc7d 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/InterpreterCompletion.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2017-1-25")
 public class InterpreterCompletion implements org.apache.thrift.TBase<InterpreterCompletion, InterpreterCompletion._Fields>, java.io.Serializable, Cloneable, Comparable<InterpreterCompletion> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("InterpreterCompletion");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
index 5c73049..ebb8579 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteApplicationResult.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2017-1-25")
 public class RemoteApplicationResult implements org.apache.thrift.TBase<RemoteApplicationResult, RemoteApplicationResult._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteApplicationResult> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteApplicationResult");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
index e721cb3..6a24e56 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterContext.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2017-1-25")
 public class RemoteInterpreterContext implements org.apache.thrift.TBase<RemoteInterpreterContext, RemoteInterpreterContext._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterContext> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterContext");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
index 5f4201a..39c4f81 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEvent.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2017-1-25")
 public class RemoteInterpreterEvent implements org.apache.thrift.TBase<RemoteInterpreterEvent, RemoteInterpreterEvent._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterEvent> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterEvent");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
index fc20cd9..7ca406c 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterEventType.java
@@ -42,7 +42,8 @@ public enum RemoteInterpreterEventType implements org.apache.thrift.TEnum {
   ANGULAR_REGISTRY_PUSH(11),
   APP_STATUS_UPDATE(12),
   META_INFOS(13),
-  REMOTE_ZEPPELIN_SERVER_RESOURCE(14);
+  REMOTE_ZEPPELIN_SERVER_RESOURCE(14),
+  RESOURCE_INVOKE_METHOD(15);
 
   private final int value;
 
@@ -91,6 +92,8 @@ public enum RemoteInterpreterEventType implements org.apache.thrift.TEnum {
         return META_INFOS;
       case 14:
         return REMOTE_ZEPPELIN_SERVER_RESOURCE;
+      case 15:
+        return RESOURCE_INVOKE_METHOD;
       default:
         return null;
     }

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
index ce15084..4929efa 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResult.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2017-1-25")
 public class RemoteInterpreterResult implements org.apache.thrift.TBase<RemoteInterpreterResult, RemoteInterpreterResult._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterResult> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResult");
 

http://git-wip-us.apache.org/repos/asf/zeppelin/blob/42bcf420/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java
----------------------------------------------------------------------
diff --git a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java
index c55c72e..eb1261e 100644
--- a/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java
+++ b/zeppelin-interpreter/src/main/java/org/apache/zeppelin/interpreter/thrift/RemoteInterpreterResultMessage.java
@@ -51,7 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked"})
-@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2016-11-29")
+@Generated(value = "Autogenerated by Thrift Compiler (0.9.2)", date = "2017-1-25")
 public class RemoteInterpreterResultMessage implements org.apache.thrift.TBase<RemoteInterpreterResultMessage, RemoteInterpreterResultMessage._Fields>, java.io.Serializable, Cloneable, Comparable<RemoteInterpreterResultMessage> {
   private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("RemoteInterpreterResultMessage");