This is an automated email from the ASF dual-hosted git repository.

jongyoul pushed a commit to branch branch-0.12
in repository https://gitbox.apache.org/repos/asf/zeppelin.git


The following commit(s) were added to refs/heads/branch-0.12 by this push:
     new b5c54d684c [ZEPPELIN-6163] HBase interpreter supports hbase-2.x
b5c54d684c is described below

commit b5c54d684cb8b0ff30c90aa81b5cc8006d27527e
Author: Paul Zhang <xzhang...@126.com>
AuthorDate: Tue Apr 22 14:42:13 2025 +0800

    [ZEPPELIN-6163] HBase interpreter supports hbase-2.x
    
    ### What is this PR for?
    
    Currently in Zeppelin HBase interpreter does not support HBase 2.x ruby 
syntax.
    
    This patch added hbase-2.x support.
    
    This closes ZEPPELIN-6163
    
    ### What type of PR is it?
    
    Improvement
    
    ### Todos
    
    ### What is the Jira issue?
    * Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/
    * Put link here, and add [ZEPPELIN-*Jira number*] in PR title, eg. 
[ZEPPELIN-533]
    
    [ZEPPELIN-6163](https://issues.apache.org/jira/browse/ZEPPELIN-6163)
    
    ### How should this be tested?
    * Strongly recommended: add automated unit tests for any new or changed 
behavior
    * Outline any manual steps to test the PR here.
    
    Compiled and tested manually.
    
    ### Screenshots (if appropriate)
    
    ### Questions:
    * Does the license files need to update? No
    * Is there breaking changes for older versions? No
    * Does this needs documentation? No
    
    Closes #4908 from paul8263/hbase-2.x.
    
    Signed-off-by: Jongyoul Lee <jongy...@gmail.com>
    (cherry picked from commit 52ea80f312b7a19dcce865a6ca8249a2f5fb49cd)
    Signed-off-by: Jongyoul Lee <jongy...@gmail.com>
---
 docs/interpreter/hbase.md                          |  24 +--
 hbase/pom.xml                                      |  10 +-
 .../apache/zeppelin/hbase/HbaseInterpreter.java    | 206 +++++++++++----------
 hbase/src/main/resources/interpreter-setting.json  |  12 --
 .../zeppelin/hbase/HbaseInterpreterTest.java       |  36 +---
 .../zeppelin/integration/HBaseIntegrationTest.java |  91 +++++++++
 .../integration/HBaseIntegrationTest172.java       |  36 ++++
 .../integration/HBaseIntegrationTest2418.java      |  36 ++++
 .../org/apache/zeppelin/test/DownloadUtils.java    |  47 ++++-
 .../apache/zeppelin/test/DownloadUtilsTest.java    |  10 +-
 10 files changed, 339 insertions(+), 169 deletions(-)

diff --git a/docs/interpreter/hbase.md b/docs/interpreter/hbase.md
index 50228407c9..0afbc726fa 100644
--- a/docs/interpreter/hbase.md
+++ b/docs/interpreter/hbase.md
@@ -28,19 +28,7 @@ limitations under the License.
 To get start with HBase, please see [HBase 
Quickstart](https://hbase.apache.org/book.html#quickstart).
 
 ## HBase release supported
-By default, Zeppelin is built against HBase 1.0.x releases. To work with HBase 
1.1.x releases, use the following build command:
-
-```bash
-# HBase 1.1.4
-./mvnw clean package -DskipTests -Phadoop-2.6 -Dhadoop.version=2.6.0 -P 
build-distr -Dhbase.hbase.version=1.1.4 -Dhbase.hadoop.version=2.6.0
-```
-
-To work with HBase 1.2.0+, use the following build command:
-
-```bash
-# HBase 1.2.0
-./mvnw clean package -DskipTests -Phadoop-2.6 -Dhadoop.version=2.6.0 -P 
build-distr -Dhbase.hbase.version=1.2.0 -Dhbase.hadoop.version=2.6.0
-```
+Zeppelin is built against HBase 1.x and 2.x releases. 
 
 ## Configuration
 
@@ -55,16 +43,6 @@ To work with HBase 1.2.0+, use the following build command:
     <td>/usr/lib/hbase</td>
     <td>Installation directory of HBase, defaults to HBASE_HOME in 
environment</td>
   </tr>
-  <tr>
-    <td>hbase.ruby.sources</td>
-    <td>lib/ruby</td>
-    <td>Path to Ruby scripts relative to 'hbase.home'</td>
-  </tr>
-  <tr>
-    <td>zeppelin.hbase.test.mode</td>
-    <td>false</td>
-    <td>Disable checks for unit and manual tests</td>
-  </tr>
 </table>
 
 If you want to connect to HBase running on a cluster, you'll need to follow 
the next step.
diff --git a/hbase/pom.xml b/hbase/pom.xml
index 4c33d68c1e..b468216e6f 100644
--- a/hbase/pom.xml
+++ b/hbase/pom.xml
@@ -33,14 +33,16 @@
   <properties>
     <!--library versions-->
     <interpreter.name>hbase</interpreter.name>
-    <jruby.version>1.6.8</jruby.version>
   </properties>
 
   <dependencies>
     <dependency>
-      <groupId>org.jruby</groupId>
-      <artifactId>jruby-complete</artifactId>
-      <version>${jruby.version}</version>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-lang3</artifactId>
     </dependency>
   </dependencies>
 
diff --git 
a/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java 
b/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java
index 6400fae479..fd8952ba65 100644
--- a/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java
+++ b/hbase/src/main/java/org/apache/zeppelin/hbase/HbaseInterpreter.java
@@ -14,118 +14,135 @@
 
 package org.apache.zeppelin.hbase;
 
-import org.jruby.embed.LocalContextScope;
-import org.jruby.embed.ScriptingContainer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
+import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.StringWriter;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Properties;
 
+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.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.zeppelin.interpreter.Interpreter;
 import org.apache.zeppelin.interpreter.InterpreterContext;
 import org.apache.zeppelin.interpreter.InterpreterException;
 import org.apache.zeppelin.interpreter.InterpreterResult;
-import org.apache.zeppelin.interpreter.thrift.InterpreterCompletion;
 import org.apache.zeppelin.scheduler.Scheduler;
 import org.apache.zeppelin.scheduler.SchedulerFactory;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
- * Support for HBase Shell. All the commands documented here
- * http://hbase.apache.org/book.html#shell is supported.
- *
- * Requirements:
- * HBase Shell should be installed on the same machine. To be more specific, 
the following dir.
- * should be available: 
https://github.com/apache/hbase/tree/master/hbase-shell/src/main/ruby
- * HBase Shell should be able to connect to the HBase cluster from terminal. 
This makes sure
- * that the client is configured properly.
- *
- * The interpreter takes 3 config parameters:
- * hbase.home: Root directory where HBase is installed. Default is 
/usr/lib/hbase/
- * hbase.ruby.sources: Dir where shell ruby code is installed.
- *                          Path is relative to hbase.home. Default: lib/ruby
- * zeppelin.hbase.test.mode: (Testing only) Disable checks for unit and manual 
tests. Default: false
+ * HBase interpreter. It uses the hbase shell to interpret the commands.
  */
 public class HbaseInterpreter extends Interpreter {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(HbaseInterpreter.class);
+
   public static final String HBASE_HOME = "hbase.home";
-  public static final String HBASE_RUBY_SRC = "hbase.ruby.sources";
-  public static final String HBASE_TEST_MODE = "zeppelin.hbase.test.mode";
 
-  private static final Logger LOGGER = 
LoggerFactory.getLogger(HbaseInterpreter.class);
-  private ScriptingContainer scriptingContainer;
+  private static final Path TEMP_FOLDER = 
Paths.get(System.getProperty("java.io.tmpdir"),
+          "zeppelin-hbase-scripts");
 
-  private StringWriter writer;
+  private Map<String, Executor> runningProcesses = new HashMap<>();
 
-  public HbaseInterpreter(Properties property) {
-    super(property);
+  private Map<String, File> tempFiles = new HashMap<>();
+
+  private static final int SIGTERM_CODE = 143;
+
+  private long commandTimeout = 60000;
+
+  public HbaseInterpreter(Properties properties) {
+    super(properties);
   }
 
   @Override
   public void open() throws InterpreterException {
-    this.scriptingContainer  = new 
ScriptingContainer(LocalContextScope.SINGLETON);
-    this.writer = new StringWriter();
-    scriptingContainer.setOutput(this.writer);
-
-    if (!Boolean.parseBoolean(getProperty(HBASE_TEST_MODE))) {
-      String hbaseHome = getProperty(HBASE_HOME);
-      String rubySrc = getProperty(HBASE_RUBY_SRC);
-      Path absRubySrc = Paths.get(hbaseHome, rubySrc).toAbsolutePath();
-
-      LOGGER.info("Home:" + hbaseHome);
-      LOGGER.info("Ruby Src:" + rubySrc);
-
-      File f = absRubySrc.toFile();
-      if (!f.exists() || !f.isDirectory()) {
-        throw new InterpreterException("HBase ruby sources is not available at 
'" + absRubySrc
-            + "'");
-      }
-
-      LOGGER.info("Absolute Ruby Source:" + absRubySrc.toString());
-      // hirb.rb:41 requires the following system properties to be set.
-      Properties sysProps = System.getProperties();
-      sysProps.setProperty(HBASE_RUBY_SRC, absRubySrc.toString());
-
-      Path absHirbPath = Paths.get(hbaseHome, "bin/hirb.rb");
-      try {
-        FileInputStream fis = new FileInputStream(absHirbPath.toFile());
-        this.scriptingContainer.runScriptlet(fis, "hirb.rb");
-        fis.close();
-      } catch (IOException e) {
-        throw new InterpreterException(e.getCause());
-      }
-    }
+    // Do nothing
   }
 
   @Override
   public void close() {
-    if (this.scriptingContainer != null) {
-      this.scriptingContainer.terminate();
-    }
+    runningProcesses.clear();
+    runningProcesses = null;
+    tempFiles.clear();
+    tempFiles = null;
   }
 
   @Override
-  public InterpreterResult interpret(String cmd, InterpreterContext 
interpreterContext) {
+  public InterpreterResult interpret(String st, InterpreterContext context) {
+    LOGGER.debug("Run HBase shell script: {}", st);
+
+    if (StringUtils.isEmpty(st)) {
+      return new InterpreterResult(InterpreterResult.Code.SUCCESS);
+    }
+
+    String paragraphId = context.getParagraphId();
+    final File scriptFile;
+    try {
+      // Write script in a temporary file
+      // The script is enriched with extensions
+      scriptFile = createTempFile(paragraphId);
+      FileUtils.write(scriptFile, st + "\nexit");
+    } catch (IOException e) {
+      LOGGER.error("Can not write script in temp file", e);
+      return new InterpreterResult(InterpreterResult.Code.ERROR, 
e.getMessage());
+    }
+
+    InterpreterResult result = new 
InterpreterResult(InterpreterResult.Code.SUCCESS);
+
+    final DefaultExecutor executor = new DefaultExecutor();
+    final ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
+
+    executor.setStreamHandler(new PumpStreamHandler(context.out, errorStream));
+    executor.setWatchdog(new ExecuteWatchdog(commandTimeout));
+
+    String hbaseCmdPath = Paths.get(getProperty(HBASE_HOME), "bin", 
"hbase").toString();
+    final CommandLine cmdLine = CommandLine.parse(hbaseCmdPath);
+    cmdLine.addArgument("shell", false);
+    cmdLine.addArgument(scriptFile.getAbsolutePath(), false);
+
     try {
-      LOGGER.info(cmd);
-      this.writer.getBuffer().setLength(0);
-      this.scriptingContainer.runScriptlet(cmd);
-      this.writer.flush();
-      LOGGER.debug(writer.toString());
-      return new InterpreterResult(InterpreterResult.Code.SUCCESS, 
writer.getBuffer().toString());
-    } catch (Throwable t) {
-      LOGGER.error("Can not run '" + cmd + "'", t);
-      return new InterpreterResult(InterpreterResult.Code.ERROR, 
t.getMessage());
+      executor.execute(cmdLine);
+      runningProcesses.put(paragraphId, executor);
+    } catch (ExecuteException e) {
+      LOGGER.error("Can not run script in paragraph {}", paragraphId, e);
+
+      final int exitValue = e.getExitValue();
+      InterpreterResult.Code code = InterpreterResult.Code.ERROR;
+      String msg = errorStream.toString();
+
+      if (exitValue == SIGTERM_CODE) {
+        code = InterpreterResult.Code.INCOMPLETE;
+        msg = msg + "Paragraph received a SIGTERM.\n";
+        LOGGER.info("The paragraph {} stopped executing: {}", paragraphId, 
msg);
+      }
+
+      msg += "ExitValue: " + exitValue;
+      result = new InterpreterResult(code, msg);
+    } catch (IOException e) {
+      LOGGER.error("Can not run script in paragraph {}", paragraphId, e);
+      result = new InterpreterResult(InterpreterResult.Code.ERROR, 
e.getMessage());
+    } finally {
+      deleteTempFile(paragraphId);
+      stopProcess(paragraphId);
     }
+    return result;
   }
 
   @Override
-  public void cancel(InterpreterContext context) {}
+  public void cancel(InterpreterContext context) {
+    stopProcess(context.getParagraphId());
+    deleteTempFile(context.getParagraphId());
+  }
 
   @Override
   public FormType getFormType() {
@@ -143,30 +160,27 @@ public class HbaseInterpreter extends Interpreter {
         HbaseInterpreter.class.getName() + this.hashCode());
   }
 
-  @Override
-  public List<InterpreterCompletion> completion(String buf, int cursor,
-      InterpreterContext interpreterContext) {
-    return null;
+  private void stopProcess(String paragraphId) {
+    Executor executor = runningProcesses.remove(paragraphId);
+    if (null != executor) {
+      final ExecuteWatchdog watchdog = executor.getWatchdog();
+      watchdog.destroyProcess();
+    }
   }
 
-  private static String getSystemDefault(
-      String envName,
-      String propertyName,
-      String defaultValue) {
-
-    if (envName != null && !envName.isEmpty()) {
-      String envValue = System.getenv().get(envName);
-      if (envValue != null) {
-        return envValue;
-      }
+  private File createTempFile(String paragraphId) throws IOException {
+    if (!Files.exists(TEMP_FOLDER)) {
+      Files.createDirectory(TEMP_FOLDER);
     }
+    File temp = Files.createTempFile(TEMP_FOLDER, paragraphId, 
".txt").toFile();
+    tempFiles.put(paragraphId, temp);
+    return temp;
+  }
 
-    if (propertyName != null && !propertyName.isEmpty()) {
-      String propValue = System.getProperty(propertyName);
-      if (propValue != null) {
-        return propValue;
-      }
+  private void deleteTempFile(String paragraphId) {
+    File tmpFile = tempFiles.remove(paragraphId);
+    if (null != tmpFile) {
+      FileUtils.deleteQuietly(tmpFile);
     }
-    return defaultValue;
   }
 }
diff --git a/hbase/src/main/resources/interpreter-setting.json 
b/hbase/src/main/resources/interpreter-setting.json
index c5d89f083d..6d7a79c185 100644
--- a/hbase/src/main/resources/interpreter-setting.json
+++ b/hbase/src/main/resources/interpreter-setting.json
@@ -10,18 +10,6 @@
         "defaultValue": "/usr/lib/hbase/",
         "description": "Installation directory of HBase",
         "type": "string"
-      },
-      "hbase.ruby.sources": {
-        "propertyName": "hbase.ruby.sources",
-        "defaultValue": "lib/ruby",
-        "description": "Path to Ruby scripts relative to 'hbase.home'",
-        "type": "string"
-      },
-      "zeppelin.hbase.test.mode": {
-        "propertyName": "zeppelin.hbase.test.mode",
-        "defaultValue": false,
-        "description": "Disable checks for unit and manual tests",
-        "type": "checkbox"
       }
     },
     "editor": {
diff --git 
a/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java 
b/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java
index 5af152243b..460fc1f061 100644
--- a/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java
+++ b/hbase/src/test/java/org/apache/zeppelin/hbase/HbaseInterpreterTest.java
@@ -14,16 +14,14 @@
 
 package org.apache.zeppelin.hbase;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-import java.util.Properties;
-
 import org.apache.zeppelin.interpreter.InterpreterException;
-import org.apache.zeppelin.interpreter.InterpreterResult;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 
+import java.util.Properties;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
 /**
  * Tests for HBase Interpreter.
  */
@@ -34,8 +32,6 @@ public class HbaseInterpreterTest {
   public static void setUp() throws NullPointerException, InterpreterException 
{
     Properties properties = new Properties();
     properties.put("hbase.home", "");
-    properties.put("hbase.ruby.sources", "");
-    properties.put("zeppelin.hbase.test.mode", "true");
 
     hbaseInterpreter = new HbaseInterpreter(properties);
     hbaseInterpreter.open();
@@ -45,28 +41,4 @@ public class HbaseInterpreterTest {
   void newObject() {
     assertNotNull(hbaseInterpreter);
   }
-
-  @Test
-  void putsTest() {
-    InterpreterResult result = hbaseInterpreter.interpret("puts \"Hello 
World\"", null);
-    assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, 
result.message().get(0).getType());
-    assertEquals("Hello World\n", result.message().get(0).getData());
-  }
-
-  public void putsLoadPath() {
-    InterpreterResult result = hbaseInterpreter.interpret(
-            "require 'two_power'; puts twoToThePowerOf(4)", null);
-    assertEquals(InterpreterResult.Code.SUCCESS, result.code());
-    assertEquals(InterpreterResult.Type.TEXT, 
result.message().get(0).getType());
-    assertEquals("16\n", result.message().get(0).getData());
-  }
-
-  @Test
-  void testException() {
-    InterpreterResult result = hbaseInterpreter.interpret("plot practical 
joke", null);
-    assertEquals(InterpreterResult.Code.ERROR, result.code());
-    assertEquals("(NameError) undefined local variable or method `joke' for 
main:Object",
-            result.message().get(0).getData());
-  }
 }
diff --git 
a/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest.java
 
b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest.java
new file mode 100644
index 0000000000..b8e7e18693
--- /dev/null
+++ 
b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.integration;
+
+
+import org.apache.zeppelin.test.DownloadUtils;
+import org.apache.zeppelin.MiniZeppelinServer;
+import org.apache.zeppelin.interpreter.ExecutionContext;
+import org.apache.zeppelin.interpreter.Interpreter;
+import org.apache.zeppelin.interpreter.InterpreterContext;
+import org.apache.zeppelin.interpreter.InterpreterFactory;
+import org.apache.zeppelin.interpreter.InterpreterResult;
+import org.apache.zeppelin.interpreter.InterpreterSetting;
+import org.apache.zeppelin.interpreter.InterpreterSettingManager;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public abstract class HBaseIntegrationTest {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(HBaseIntegrationTest.class);
+
+  private static InterpreterFactory interpreterFactory;
+  protected static InterpreterSettingManager interpreterSettingManager;
+
+  private String hbaseHome;
+
+  private static MiniZeppelinServer zepServer;
+
+  public void prepareHBase(String hbaseVersion) {
+    LOGGER.info("Testing HBase Version: " + hbaseVersion);
+    this.hbaseHome = DownloadUtils.downloadHBase(hbaseVersion);
+  }
+
+  @BeforeAll
+  static void init() throws Exception {
+    zepServer = new 
MiniZeppelinServer(HBaseIntegrationTest.class.getSimpleName());
+    zepServer.addInterpreter("hbase");
+    zepServer.copyBinDir();
+    zepServer.copyLogProperties();
+    zepServer.start();
+  }
+
+  @AfterAll
+  public static void tearDown() throws Exception {
+    zepServer.destroy();
+  }
+
+  @BeforeEach
+  void setup() {
+    interpreterSettingManager = 
zepServer.getService(InterpreterSettingManager.class);
+    interpreterFactory = new InterpreterFactory(interpreterSettingManager);
+  }
+
+  @Test
+  public void testHBaseInterpreter() throws Exception {
+    InterpreterSetting hbaseInterpreterSetting = 
interpreterSettingManager.getInterpreterSettingByName("hbase");
+    hbaseInterpreterSetting.setProperty("hbase.home", hbaseHome);
+
+    Interpreter hbaseInterpreter = interpreterFactory.getInterpreter("hbase", 
new ExecutionContext("user1", "note1", "hbase"));
+
+    InterpreterContext context = new 
InterpreterContext.Builder().setNoteId("note1").setParagraphId("paragraph_1").build();
+    InterpreterResult interpreterResult = hbaseInterpreter.interpret("puts 
'hello'", context);
+
+    assertEquals(InterpreterResult.Code.SUCCESS, interpreterResult.code(), 
interpreterResult.toString());
+    assertTrue(interpreterResult.message().get(0).getData().contains("hello"));
+
+    interpreterSettingManager.close();
+  }
+
+}
diff --git 
a/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest172.java
 
b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest172.java
new file mode 100644
index 0000000000..2979959869
--- /dev/null
+++ 
b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest172.java
@@ -0,0 +1,36 @@
+/*
+ * 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.integration;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+
+public class HBaseIntegrationTest172 {
+
+  @Nested
+  @DisplayName("HBase 1.x")
+  public class HBase172 extends HBaseIntegrationTest {
+
+    @BeforeEach
+    public void downloadHBase() {
+      prepareHBase("1.7.2");
+    }
+  }
+
+}
diff --git 
a/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest2418.java
 
b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest2418.java
new file mode 100644
index 0000000000..d495d73a6c
--- /dev/null
+++ 
b/zeppelin-interpreter-integration/src/test/java/org/apache/zeppelin/integration/HBaseIntegrationTest2418.java
@@ -0,0 +1,36 @@
+/*
+ * 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.integration;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+
+public class HBaseIntegrationTest2418 {
+
+  @Nested
+  @DisplayName("HBase 2.x")
+  public class HBase2418 extends HBaseIntegrationTest {
+
+    @BeforeEach
+    public void downloadHBase() {
+      prepareHBase("2.4.18");
+    }
+  }
+
+}
diff --git 
a/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java 
b/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java
index 37332b6c22..684b740de9 100644
--- a/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java
+++ b/zeppelin-test/src/main/java/org/apache/zeppelin/test/DownloadUtils.java
@@ -198,7 +198,6 @@ public class DownloadUtils {
     return targetSparkHomeFolder.getAbsolutePath();
   }
 
-
   public static void download(String url, int retries, File dst) throws 
IOException {
     download(new URL(url), retries, dst);
   }
@@ -626,4 +625,50 @@ public class DownloadUtils {
     }
     return "incubator/livy/" + livyVersion + "/apache-livy-" + livyVersion + 
"-bin.zip";
   }
+
+  /**
+   * Download of a HBase distribution
+   *
+   * @param version HBase version
+   * @return home of HBase installation
+   */
+  public static String downloadHBase(String version) {
+    File hbaseDownloadFolder = new File(downloadFolder, "hbase");
+    hbaseDownloadFolder.mkdir();
+    File targetHBaseHomeFolder = new File(hbaseDownloadFolder, "hbase-" + 
version);
+    if (targetHBaseHomeFolder.exists()) {
+      LOGGER.info("Skip to download HBase {} as it is already downloaded.", 
version);
+      return targetHBaseHomeFolder.getAbsolutePath();
+    }
+    File hbaseTGZ = new File(hbaseDownloadFolder, "hbase-" + version + 
".tar.gz");
+    try {
+      URL mirrorURL = new URL(MIRROR_URL + generateHBaseDownloadUrl(version));
+      URL archiveURL = new URL(ARCHIVE_URL + 
generateHBaseDownloadUrl(version));
+      LOGGER.info("Download HBase {}", version);
+      download(new DownloadRequest(mirrorURL, archiveURL), hbaseTGZ);
+      ProgressBarBuilder pbb = new ProgressBarBuilder()
+          .setTaskName("Unarchive")
+          .setUnit("MiB", 1048576) // setting the progress bar to use MiB as 
the unit
+          .setStyle(ProgressBarStyle.ASCII)
+          .setUpdateIntervalMillis(1000)
+          .setConsumer(new DelegatingProgressBarConsumer(LOGGER::info));
+      try (
+          InputStream fis = Files.newInputStream(hbaseTGZ.toPath());
+          InputStream pbis = ProgressBar.wrap(fis, pbb);
+          InputStream bis = new BufferedInputStream(pbis);
+          InputStream gzis = new GzipCompressorInputStream(bis);
+          ArchiveInputStream<TarArchiveEntry> o = new 
TarArchiveInputStream(gzis)) {
+        LOGGER.info("Unarchive HBase {} to {}", version, 
targetHBaseHomeFolder);
+        unarchive(o, targetHBaseHomeFolder, 1);
+        LOGGER.info("Unarchive HBase {} done", version);
+      }
+    } catch (IOException e) {
+      throw new RuntimeException("Unable to download HBase");
+    }
+    return targetHBaseHomeFolder.getAbsolutePath();
+  }
+
+  private static String generateHBaseDownloadUrl(String hbaseVersion) {
+    return "hbase/" + hbaseVersion + "/hbase-" + hbaseVersion + "-bin.tar.gz";
+  }
 }
diff --git 
a/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java 
b/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java
index 6bed71683c..ffe357729f 100644
--- 
a/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java
+++ 
b/zeppelin-test/src/test/java/org/apache/zeppelin/test/DownloadUtilsTest.java
@@ -76,5 +76,13 @@ class DownloadUtilsTest {
     assertTrue(sparkHomePath.toFile().exists());
     assertTrue(sparkHomePath.toFile().isDirectory());
   }
-
+  
+  @Test
+  void downloadHBase() {
+    String hbaseHome = DownloadUtils.downloadHBase("2.14.8");
+    Path hbaseHomePath = Paths.get(hbaseHome);
+    assertTrue(hbaseHomePath.toFile().exists());
+    assertTrue(hbaseHomePath.toFile().isDirectory());
+  }
+  
 }

Reply via email to