hive-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From vgumas...@apache.org
Subject svn commit: r1674123 - in /hive/trunk: common/src/java/org/apache/hadoop/hive/conf/ itests/hive-minikdc/src/test/java/org/apache/hive/minikdc/ itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/ jdbc/src/java/org/apache/hive/jdbc/ servic...
Date Thu, 16 Apr 2015 18:09:54 GMT
Author: vgumashta
Date: Thu Apr 16 18:09:53 2015
New Revision: 1674123

URL: http://svn.apache.org/r1674123
Log:
HIVE-9710: HiveServer2 should support cookie based authentication, when using HTTP transport.
(Hari Sankar Sivarama Subramaniyan reviewed by Larry McCay, Vaibhav Gumashta)

Added:
    hive/trunk/itests/hive-minikdc/src/test/java/org/apache/hive/minikdc/TestJdbcWithMiniKdcCookie.java
    hive/trunk/service/src/java/org/apache/hive/service/CookieSigner.java
    hive/trunk/service/src/test/org/apache/hive/service/TestCookieSigner.java
    hive/trunk/service/src/test/org/apache/hive/service/cli/thrift/ThriftCliServiceTestWithCookie.java
Modified:
    hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
    hive/trunk/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIService.java
    hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java
    hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java
    hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java
    hive/trunk/jdbc/src/java/org/apache/hive/jdbc/Utils.java
    hive/trunk/service/src/java/org/apache/hive/service/auth/HttpAuthUtils.java
    hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java

Modified: hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
URL: http://svn.apache.org/viewvc/hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java (original)
+++ hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java Thu Apr 16 18:09:53
2015
@@ -1715,6 +1715,21 @@ public class HiveConf extends Configurat
         "Keepalive time for an idle http worker thread. When the number of workers exceeds
min workers, " +
         "excessive threads are killed after this time interval."),
 
+    // Cookie based authentication
+    HIVE_SERVER2_THRIFT_HTTP_COOKIE_AUTH_ENABLED("hive.server2.thrift.http.cookie.auth.enabled",
true,
+        "When true, HiveServer2 in HTTP transport mode, will use cookie based authentication
mechanism."),
+    HIVE_SERVER2_THRIFT_HTTP_COOKIE_MAX_AGE("hive.server2.thrift.http.cookie.max.age", "86400s",
+        new TimeValidator(TimeUnit.SECONDS),
+        "Maximum age in seconds for server side cookie used by HS2 in HTTP mode."),
+    HIVE_SERVER2_THRIFT_HTTP_COOKIE_DOMAIN("hive.server2.thrift.http.cookie.domain", null,
+        "Domain for the HS2 generated cookies"),
+    HIVE_SERVER2_THRIFT_HTTP_COOKIE_PATH("hive.server2.thrift.http.cookie.path", null,
+        "Path for the HS2 generated cookies"),
+    HIVE_SERVER2_THRIFT_HTTP_COOKIE_IS_SECURE("hive.server2.thrift.http.cookie.is.secure",
true,
+        "Secure attribute of the HS2 generated cookie."),
+    HIVE_SERVER2_THRIFT_HTTP_COOKIE_IS_HTTPONLY("hive.server2.thrift.http.cookie.is.httponly",
true,
+        "HttpOnly attribute of the HS2 generated cookie."),
+
     // binary transport settings
     HIVE_SERVER2_THRIFT_PORT("hive.server2.thrift.port", 10000,
         "Port number of HiveServer2 Thrift interface when hive.server2.transport.mode is
'binary'."),

Added: hive/trunk/itests/hive-minikdc/src/test/java/org/apache/hive/minikdc/TestJdbcWithMiniKdcCookie.java
URL: http://svn.apache.org/viewvc/hive/trunk/itests/hive-minikdc/src/test/java/org/apache/hive/minikdc/TestJdbcWithMiniKdcCookie.java?rev=1674123&view=auto
==============================================================================
--- hive/trunk/itests/hive-minikdc/src/test/java/org/apache/hive/minikdc/TestJdbcWithMiniKdcCookie.java
(added)
+++ hive/trunk/itests/hive-minikdc/src/test/java/org/apache/hive/minikdc/TestJdbcWithMiniKdcCookie.java
Thu Apr 16 18:09:53 2015
@@ -0,0 +1,102 @@
+package org.apache.hive.minikdc;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.Connection;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Properties;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
+import org.apache.hive.jdbc.HiveConnection;
+import org.apache.hive.jdbc.miniHS2.MiniHS2;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class TestJdbcWithMiniKdcCookie {
+  private static MiniHS2 miniHS2 = null;
+  private static MiniHiveKdc miniHiveKdc = null;
+  private Connection hs2Conn;
+  File dataFile;
+  protected static HiveConf hiveConf = new HiveConf();
+  private static String HIVE_NON_EXISTENT_USER = "hive_no_exist";
+
+  @BeforeClass
+  public static void beforeTest() throws Exception {
+    hiveConf.setVar(ConfVars.HIVE_SERVER2_TRANSPORT_MODE, MiniHS2.HS2_HTTP_MODE);
+    System.err.println("Testing using HS2 mode : "
+      + hiveConf.getVar(ConfVars.HIVE_SERVER2_TRANSPORT_MODE));
+    hiveConf.setBoolVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_AUTH_ENABLED,
+      true);
+    // set a small time unit as cookie max age so that the server sends a 401
+    hiveConf.setTimeVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_MAX_AGE,
+      1, TimeUnit.SECONDS);
+    hiveConf.setBoolVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_IS_SECURE, false);
+    hiveConf.setBoolVar(ConfVars.HIVE_SUPPORT_CONCURRENCY, false);
+    miniHiveKdc = MiniHiveKdc.getMiniHiveKdc(hiveConf);
+    miniHS2 = MiniHiveKdc.getMiniHS2WithKerb(miniHiveKdc, hiveConf);
+    miniHS2.start(new HashMap<String, String>());
+  }
+
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    if (hs2Conn != null) {
+      try {
+        hs2Conn.close();
+      } catch (Exception e) {
+        // Ignore shutdown errors since there are negative tests
+      }
+    }
+  }
+
+  @AfterClass
+  public static void afterTest() throws Exception {
+    miniHS2.stop();
+  }
+
+  @Test
+  public void testCookie() throws Exception {
+    String tableName = "test_cookie";
+    dataFile = new File(hiveConf.get("test.data.files"), "kv1.txt");
+    Connection hs2Conn = getConnection(MiniHiveKdc.HIVE_TEST_USER_1);
+
+    Statement stmt = hs2Conn.createStatement();
+
+    // create table
+    stmt.execute("create table " + tableName + "(key int, value string) ");
+    stmt.execute("load data local inpath '" + dataFile + "' into table " + tableName);
+
+    // run a query in a loop so that we hit a 401 occasionally
+    for (int i = 0; i < 10; i++) {
+      stmt.execute("select * from " + tableName );
+    }
+    stmt.execute("drop table " + tableName);
+    stmt.close();
+  }
+
+  @Test
+  public void testCookieNegative() throws Exception {
+    try {
+      // Trying to connect with a non-existent user should still fail with
+      // login failure.
+      getConnection(HIVE_NON_EXISTENT_USER);
+    } catch (IOException e) {
+      Assert.assertTrue(e.getMessage().contains("Login failure"));
+    }
+  }
+
+  private Connection getConnection(String userName) throws Exception {
+    miniHiveKdc.loginUser(userName);
+    return new HiveConnection(miniHS2.getJdbcURL(), new Properties());
+  }
+}

Modified: hive/trunk/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIService.java
URL: http://svn.apache.org/viewvc/hive/trunk/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIService.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIService.java
(original)
+++ hive/trunk/itests/hive-unit/src/test/java/org/apache/hive/service/cli/thrift/TestThriftHttpCLIService.java
Thu Apr 16 18:09:53 2015
@@ -160,7 +160,7 @@ public class TestThriftHttpCLIService ex
     String httpUrl = transportMode + "://" + host + ":" + port +
         "/" + thriftHttpPath + "/";
     httpClient.addRequestInterceptor(
-        new HttpBasicAuthInterceptor(USERNAME, PASSWORD, null, null));
+        new HttpBasicAuthInterceptor(USERNAME, PASSWORD, null, null, false));
     return new THttpClient(httpUrl, httpClient);
   }
 

Modified: hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java
URL: http://svn.apache.org/viewvc/hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java (original)
+++ hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HiveConnection.java Thu Apr 16 18:09:53
2015
@@ -291,7 +291,7 @@ public class HiveConnection implements j
        */
       requestInterceptor =
           new HttpKerberosRequestInterceptor(sessConfMap.get(JdbcConnectionParams.AUTH_PRINCIPAL),
-              host, getServerHttpUrl(useSsl), assumeSubject, cookieStore, cookieName);
+              host, getServerHttpUrl(useSsl), assumeSubject, cookieStore, cookieName, useSsl);
     }
     else {
       /**
@@ -299,7 +299,7 @@ public class HiveConnection implements j
        * In https mode, the entire information is encrypted
        */
       requestInterceptor = new HttpBasicAuthInterceptor(getUserName(), getPassword(),
-                                                        cookieStore, cookieName);
+                                                        cookieStore, cookieName, useSsl);
     }
     // Configure http client for cookie based authentication
     if (isCookieEnabled) {

Modified: hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java
URL: http://svn.apache.org/viewvc/hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java (original)
+++ hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpBasicAuthInterceptor.java Thu Apr 16
18:09:53 2015
@@ -42,9 +42,10 @@ public class HttpBasicAuthInterceptor im
   CookieStore cookieStore;
   boolean isCookieEnabled;
   String cookieName;
+  boolean isSSL;
 
   public HttpBasicAuthInterceptor(String username, String password, CookieStore cookieStore,
-                           String cn) {
+                           String cn, boolean isSSL) {
     if(username != null){
       credentials = new UsernamePasswordCredentials(username, password);
     }
@@ -52,6 +53,7 @@ public class HttpBasicAuthInterceptor im
     this.cookieStore = cookieStore;
     isCookieEnabled = (cookieStore != null);
     cookieName = cn;
+    this.isSSL = isSSL;
   }
 
   @Override
@@ -64,9 +66,10 @@ public class HttpBasicAuthInterceptor im
     // 1. Cookie Authentication is disabled OR
     // 2. The first time when the request is sent OR
     // 3. The server returns a 401, which sometimes means the cookie has expired
+    // 4. The cookie is secured where as the client connect does not use SSL
     if (!isCookieEnabled || ((httpContext.getAttribute(Utils.HIVE_SERVER2_RETRY_KEY) == null
&&
         (cookieStore == null || (cookieStore != null &&
-        Utils.needToSendCredentials(cookieStore, cookieName)))) ||
+        Utils.needToSendCredentials(cookieStore, cookieName, isSSL)))) ||
         (httpContext.getAttribute(Utils.HIVE_SERVER2_RETRY_KEY) != null &&
          httpContext.getAttribute(Utils.HIVE_SERVER2_RETRY_KEY).
          equals(Utils.HIVE_SERVER2_RETRY_TRUE)))) {

Modified: hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java
URL: http://svn.apache.org/viewvc/hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java (original)
+++ hive/trunk/jdbc/src/java/org/apache/hive/jdbc/HttpKerberosRequestInterceptor.java Thu
Apr 16 18:09:53 2015
@@ -44,18 +44,28 @@ public class HttpKerberosRequestIntercep
   boolean assumeSubject;
   CookieStore cookieStore;
   boolean isCookieEnabled;
+  // NB: The purpose of isSSL flag is as follows:
+  // This flag is useful when the HS2 server sends a secure cookie and
+  // the client is in a non-ssl mode. Here, the client replay of the cookie
+  // doesnt reach the server. If we don't send credentials in such a scenario,
+  // the server  would send a 401 error back to the client.
+  // Thus, we would need 2 cycles instead of 1 cycle to process an incoming request if
+  // isSSL is absent.
+  boolean isSSL;
   String cookieName;
 
   // A fair reentrant lock
   private static ReentrantLock kerberosLock = new ReentrantLock(true);
 
   public HttpKerberosRequestInterceptor(String principal, String host,
-      String serverHttpUrl, boolean assumeSubject, CookieStore cs, String cn) {
+      String serverHttpUrl, boolean assumeSubject, CookieStore cs, String cn,
+      boolean isSSL) {
     this.principal = principal;
     this.host = host;
     this.serverHttpUrl = serverHttpUrl;
     this.assumeSubject = assumeSubject;
     this.cookieStore = cs;
+    this.isSSL = isSSL;
     isCookieEnabled = (cs != null);
     cookieName = cn;
   }
@@ -79,10 +89,12 @@ public class HttpKerberosRequestIntercep
       // Generate the kerberos ticket under the following scenarios:
       // 1. Cookie Authentication is disabled OR
       // 2. The first time when the request is sent OR
-      // 3. The server returns a 401, which sometimes means the cookie has expired
-      if (!isCookieEnabled || ((httpContext.getAttribute(Utils.HIVE_SERVER2_RETRY_KEY) ==
null &&
+      // 3. The server returns a 401, which sometimes means the cookie has expired OR
+      // 4. The cookie is secured where as the client connect does not use SSL
+      if (!isCookieEnabled ||
+         ((httpContext.getAttribute(Utils.HIVE_SERVER2_RETRY_KEY) == null &&
           (cookieStore == null || (cookieStore != null &&
-          Utils.needToSendCredentials(cookieStore, cookieName)))) ||
+          Utils.needToSendCredentials(cookieStore, cookieName, isSSL)))) ||
           (httpContext.getAttribute(Utils.HIVE_SERVER2_RETRY_KEY) != null &&
           httpContext.getAttribute(Utils.HIVE_SERVER2_RETRY_KEY).
           equals(Utils.HIVE_SERVER2_RETRY_TRUE)))) {

Modified: hive/trunk/jdbc/src/java/org/apache/hive/jdbc/Utils.java
URL: http://svn.apache.org/viewvc/hive/trunk/jdbc/src/java/org/apache/hive/jdbc/Utils.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/jdbc/src/java/org/apache/hive/jdbc/Utils.java (original)
+++ hive/trunk/jdbc/src/java/org/apache/hive/jdbc/Utils.java Thu Apr 16 18:09:53 2015
@@ -579,10 +579,11 @@ public class Utils {
    * has a valid cookie and the client need not send Credentials for validation purpose.
    * @param cookieStore The cookie Store
    * @param cookieName Name of the cookie which needs to be validated
+   * @param isSSL Whether this is a http/https connection
    * @return true or false based on whether the client needs to send the credentials or
    * not to the server.
    */
-  static boolean needToSendCredentials(CookieStore cookieStore, String cookieName) {
+  static boolean needToSendCredentials(CookieStore cookieStore, String cookieName, boolean
isSSL) {
     if (cookieName == null || cookieStore == null) {
       return true;
     }
@@ -590,6 +591,12 @@ public class Utils {
     List<Cookie> cookies = cookieStore.getCookies();
 
     for (Cookie c : cookies) {
+      // If this is a secured cookie and the current connection is non-secured,
+      // then, skip this cookie. We need to skip this cookie because, the cookie
+      // replay will not be transmitted to the server.
+      if (c.isSecure() && !isSSL) {
+        continue;
+      }
       if (c.getName().equals(cookieName)) {
         return false;
       }

Added: hive/trunk/service/src/java/org/apache/hive/service/CookieSigner.java
URL: http://svn.apache.org/viewvc/hive/trunk/service/src/java/org/apache/hive/service/CookieSigner.java?rev=1674123&view=auto
==============================================================================
--- hive/trunk/service/src/java/org/apache/hive/service/CookieSigner.java (added)
+++ hive/trunk/service/src/java/org/apache/hive/service/CookieSigner.java Thu Apr 16 18:09:53
2015
@@ -0,0 +1,90 @@
+package org.apache.hive.service;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.Log;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * The cookie signer generates a signature based on SHA digest
+ * and appends it to the cookie value generated at the
+ * server side. It uses SHA digest algorithm to sign and verify signatures.
+ */
+public class CookieSigner {
+  private static final String SIGNATURE = "&s=";
+  private static final String SHA_STRING = "SHA";
+  private byte[] secretBytes;
+  private static final Log LOG = LogFactory.getLog(CookieSigner.class);
+
+  /**
+   * Constructor
+   * @param secret Secret Bytes
+   */
+  public CookieSigner(byte[] secret) {
+    if (secret == null) {
+      throw new IllegalArgumentException(" NULL Secret Bytes");
+    }
+    this.secretBytes = secret.clone();
+  }
+
+  /**
+   * Sign the cookie given the string token as input.
+   * @param str Input token
+   * @return Signed token that can be used to create a cookie
+   */
+  public String signCookie(String str) {
+    if (str == null || str.isEmpty()) {
+      throw new IllegalArgumentException("NULL or empty string to sign");
+    }
+    String signature = getSignature(str);
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Signature generated for " + str + " is " + signature);
+    }
+    return str + SIGNATURE + signature;
+  }
+
+  /**
+   * Verify a signed string and extracts the original string.
+   * @param signedStr The already signed string
+   * @return Raw Value of the string without the signature
+   */
+  public String verifyAndExtract(String signedStr) {
+    int index = signedStr.lastIndexOf(SIGNATURE);
+    if (index == -1) {
+      throw new IllegalArgumentException("Invalid input sign: " + signedStr);
+    }
+    String originalSignature = signedStr.substring(index + SIGNATURE.length());
+    String rawValue = signedStr.substring(0, index);
+    String currentSignature = getSignature(rawValue);
+
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Signature generated for " + rawValue + " inside verify is " + currentSignature);
+    }
+    if (!originalSignature.equals(currentSignature)) {
+      throw new IllegalArgumentException("Invalid sign, original = " + originalSignature
+
+        " current = " + currentSignature);
+    }
+    return rawValue;
+  }
+
+  /**
+   * Get the signature of the input string based on SHA digest algorithm.
+   * @param str Input token
+   * @return Signed String
+   */
+  private String getSignature(String str) {
+    try {
+      MessageDigest md = MessageDigest.getInstance(SHA_STRING);
+      md.update(str.getBytes());
+      md.update(secretBytes);
+      byte[] digest = md.digest();
+      return new Base64(0).encodeToString(digest);
+    } catch (NoSuchAlgorithmException ex) {
+      throw new RuntimeException("Invalid SHA digest String: " + SHA_STRING +
+        " " + ex.getMessage(), ex);
+    }
+  }
+}

Modified: hive/trunk/service/src/java/org/apache/hive/service/auth/HttpAuthUtils.java
URL: http://svn.apache.org/viewvc/hive/trunk/service/src/java/org/apache/hive/service/auth/HttpAuthUtils.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/service/src/java/org/apache/hive/service/auth/HttpAuthUtils.java (original)
+++ hive/trunk/service/src/java/org/apache/hive/service/auth/HttpAuthUtils.java Thu Apr 16
18:09:53 2015
@@ -21,10 +21,19 @@ package org.apache.hive.service.auth;
 import java.security.AccessControlContext;
 import java.security.AccessController;
 import java.security.PrivilegedExceptionAction;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+import java.util.StringTokenizer;
 
 import javax.security.auth.Subject;
 
 import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.hive.shims.ShimLoader;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.http.protocol.BasicHttpContext;
@@ -42,6 +51,13 @@ public final class HttpAuthUtils {
   public static final String AUTHORIZATION = "Authorization";
   public static final String BASIC = "Basic";
   public static final String NEGOTIATE = "Negotiate";
+  private static final Log LOG = LogFactory.getLog(HttpAuthUtils.class);
+  private static final String COOKIE_ATTR_SEPARATOR = "&";
+  private static final String COOKIE_CLIENT_USER_NAME = "cu";
+  private static final String COOKIE_CLIENT_RAND_NUMBER = "rn";
+  private static final String COOKIE_KEY_VALUE_SEPARATOR = "=";
+  private final static Set<String> COOKIE_ATTRIBUTES =
+    new HashSet<String>(Arrays.asList(COOKIE_CLIENT_USER_NAME, COOKIE_CLIENT_RAND_NUMBER));
 
   /**
    * @return Stringified Base64 encoded kerberosAuthHeader on success
@@ -68,6 +84,62 @@ public final class HttpAuthUtils {
     }
   }
 
+  /**
+   * Creates and returns a HS2 cookie token.
+   * @param clientUserName Client User name.
+   * @return An unsigned cookie token generated from input parameters.
+   * The final cookie generated is of the following format :
+   * cu=<username>&rn=<randomNumber>&s=<cookieSignature>
+   */
+  public static String createCookieToken(String clientUserName) {
+    StringBuffer sb = new StringBuffer();
+    sb.append(COOKIE_CLIENT_USER_NAME).append(COOKIE_KEY_VALUE_SEPARATOR).append(clientUserName).
+    append(COOKIE_ATTR_SEPARATOR);
+    sb.append(COOKIE_CLIENT_RAND_NUMBER).append(COOKIE_KEY_VALUE_SEPARATOR).
+    append((new Random(System.currentTimeMillis())).nextLong());
+    return sb.toString();
+  }
+
+  /**
+   * Parses a cookie token to retrieve client user name.
+   * @param tokenStr Token String.
+   * @return A valid user name if input is of valid format, else returns null.
+   */
+  public static String getUserNameFromCookieToken(String tokenStr) {
+    Map<String, String> map = splitCookieToken(tokenStr);
+
+    if (!map.keySet().equals(COOKIE_ATTRIBUTES)) {
+      LOG.error("Invalid token with missing attributes " + tokenStr);
+      return null;
+    }
+    return map.get(COOKIE_CLIENT_USER_NAME);
+  }
+
+  /**
+   * Splits the cookie token into attributes pairs.
+   * @param str input token.
+   * @return a map with the attribute pairs of the token if the input is valid.
+   * Else, returns null.
+   */
+  private static Map<String, String> splitCookieToken(String tokenStr) {
+    Map<String, String> map = new HashMap<String, String>();
+    StringTokenizer st = new StringTokenizer(tokenStr, COOKIE_ATTR_SEPARATOR);
+
+    while (st.hasMoreTokens()) {
+      String part = st.nextToken();
+      int separator = part.indexOf(COOKIE_KEY_VALUE_SEPARATOR);
+      if (separator == -1) {
+        LOG.error("Invalid token string " + tokenStr);
+        return null;
+      }
+      String key = part.substring(0, separator);
+      String value = part.substring(separator + 1);
+      map.put(key, value);
+    }
+    return map;
+  }
+
+
   private HttpAuthUtils() {
     throw new UnsupportedOperationException("Can't initialize class");
   }

Modified: hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
URL: http://svn.apache.org/viewvc/hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java?rev=1674123&r1=1674122&r2=1674123&view=diff
==============================================================================
--- hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
(original)
+++ hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
Thu Apr 16 18:09:53 2015
@@ -19,18 +19,26 @@
 package org.apache.hive.service.cli.thrift;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.security.PrivilegedExceptionAction;
+import java.util.Arrays;
 import java.util.Map;
+import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.NewCookie;
 
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.binary.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
 import org.apache.hadoop.hive.shims.HadoopShims.KerberosNameShim;
 import org.apache.hadoop.hive.shims.ShimLoader;
 import org.apache.hadoop.security.UserGroupInformation;
@@ -41,6 +49,7 @@ import org.apache.hive.service.auth.Http
 import org.apache.hive.service.auth.HttpAuthenticationException;
 import org.apache.hive.service.auth.PasswdAuthenticationProvider;
 import org.apache.hive.service.cli.session.SessionManager;
+import org.apache.hive.service.CookieSigner;
 import org.apache.thrift.TProcessor;
 import org.apache.thrift.protocol.TProtocolFactory;
 import org.apache.thrift.server.TServlet;
@@ -63,6 +72,18 @@ public class ThriftHttpServlet extends T
   private final String authType;
   private final UserGroupInformation serviceUGI;
   private final UserGroupInformation httpUGI;
+  private HiveConf hiveConf = new HiveConf();
+
+  // Class members for cookie based authentication.
+  private CookieSigner signer;
+  public static final String AUTH_COOKIE = "hive.server2.auth";
+  private static final Random RAN = new Random();
+  private boolean isCookieAuthEnabled;
+  private String cookieDomain;
+  private String cookiePath;
+  private int cookieMaxAge;
+  private boolean isCookieSecure;
+  private boolean isHttpOnlyCookie;
 
   public ThriftHttpServlet(TProcessor processor, TProtocolFactory protocolFactory,
       String authType, UserGroupInformation serviceUGI, UserGroupInformation httpUGI) {
@@ -70,34 +91,80 @@ public class ThriftHttpServlet extends T
     this.authType = authType;
     this.serviceUGI = serviceUGI;
     this.httpUGI = httpUGI;
+    this.isCookieAuthEnabled = hiveConf.getBoolVar(
+      ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_AUTH_ENABLED);
+    // Initialize the cookie based authentication related variables.
+    if (isCookieAuthEnabled) {
+      // Generate the signer with secret.
+      String secret = Long.toString(RAN.nextLong());
+      LOG.debug("Using the random number as the secret for cookie generation " + secret);
+      this.signer = new CookieSigner(secret.getBytes());
+      this.cookieMaxAge = (int) hiveConf.getTimeVar(
+        ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_MAX_AGE, TimeUnit.SECONDS);
+      this.cookieDomain = hiveConf.getVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_DOMAIN);
+      this.cookiePath = hiveConf.getVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_PATH);
+      this.isCookieSecure = hiveConf.getBoolVar(
+        ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_IS_SECURE);
+      this.isHttpOnlyCookie = hiveConf.getBoolVar(
+        ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_IS_HTTPONLY);
+    }
   }
 
   @Override
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
-    String clientUserName;
+    String clientUserName = null;
     String clientIpAddress;
+    boolean requireNewCookie = false;
+
     try {
-      // For a kerberos setup
-      if(isKerberosAuthMode(authType)) {
-        clientUserName = doKerberosAuth(request);
-        String doAsQueryParam = getDoAsQueryParam(request.getQueryString());
-        if (doAsQueryParam != null) {
-          SessionManager.setProxyUserName(doAsQueryParam);
+      // If the cookie based authentication is already enabled, parse the
+      // request and validate the request cookies.
+      if (isCookieAuthEnabled) {
+        clientUserName = validateCookie(request);
+        requireNewCookie = (clientUserName == null);
+        if (requireNewCookie) {
+          LOG.info("Could not validate cookie sent, will try to generate a new cookie");
         }
       }
-      else {
-        clientUserName = doPasswdAuth(request, authType);
+      // If the cookie based authentication is not enabled or the request does
+      // not have a valid cookie, use the kerberos or password based authentication
+      // depending on the server setup.
+      if (clientUserName == null) {
+        // For a kerberos setup
+        if (isKerberosAuthMode(authType)) {
+          clientUserName = doKerberosAuth(request);
+          String doAsQueryParam = getDoAsQueryParam(request.getQueryString());
+
+          if (doAsQueryParam != null) {
+            SessionManager.setProxyUserName(doAsQueryParam);
+          }
+        }
+        // For password based authentication
+        else {
+          clientUserName = doPasswdAuth(request, authType);
+        }
       }
       LOG.debug("Client username: " + clientUserName);
       // Set the thread local username to be used for doAs if true
       SessionManager.setUserName(clientUserName);
-
       clientIpAddress = request.getRemoteAddr();
       LOG.debug("Client IP Address: " + clientIpAddress);
       // Set the thread local ip address
       SessionManager.setIpAddress(clientIpAddress);
+      // Generate new cookie and add it to the response
+      if (requireNewCookie &&
+          !authType.equalsIgnoreCase(HiveAuthFactory.AuthTypes.NOSASL.toString())) {
+        String cookieToken = HttpAuthUtils.createCookieToken(clientUserName);
+        Cookie hs2Cookie = createCookie(signer.signCookie(cookieToken));
 
+        if (isHttpOnlyCookie) {
+          response.setHeader("SET-COOKIE", getHttpOnlyCookieHeader(hs2Cookie));
+        } else {
+          response.addCookie(hs2Cookie);
+        }
+        LOG.info("Cookie added for clientUserName " + clientUserName);
+      }
       super.doPost(request, response);
     }
     catch (HttpAuthenticationException e) {
@@ -118,6 +185,127 @@ public class ThriftHttpServlet extends T
   }
 
   /**
+   * Retrieves the client name from cookieString. If the cookie does not
+   * correspond to a valid client, the function returns null.
+   * @param cookies HTTP Request cookies.
+   * @return Client Username if cookieString has a HS2 Generated cookie that is currently
valid.
+   * Else, returns null.
+   */
+  private String getClientNameFromCookie(Cookie[] cookies) {
+    // Current Cookie Name, Current Cookie Value
+    String currName, currValue;
+
+    // Following is the main loop which iterates through all the cookies send by the client.
+    // The HS2 generated cookies are of the format hive.server2.auth=<value>
+    // A cookie which is identified as a hiveserver2 generated cookie is validated
+    // by calling signer.verifyAndExtract(). If the validation passes, send the
+    // username for which the cookie is validated to the caller. If no client side
+    // cookie passes the validation, return null to the caller.
+    for (Cookie currCookie : cookies) {
+      // Get the cookie name
+      currName = currCookie.getName();
+      if (!currName.equals(AUTH_COOKIE)) {
+        // Not a HS2 generated cookie, continue.
+        continue;
+      }
+      // If we reached here, we have match for HS2 generated cookie
+      currValue = currCookie.getValue();
+      // Validate the value.
+      currValue = signer.verifyAndExtract(currValue);
+      // Retrieve the user name, do the final validation step.
+      if (currValue != null) {
+        String userName = HttpAuthUtils.getUserNameFromCookieToken(currValue);
+
+        if (userName == null) {
+          LOG.warn("Invalid cookie token " + currValue);
+          continue;
+        }
+        //We have found a valid cookie in the client request.
+        if (LOG.isDebugEnabled()) {
+          LOG.debug("Validated the cookie for user " + userName);
+        }
+        return userName;
+	  }
+    }
+    // No valid HS2 generated cookies found, return null
+    return null;
+  }
+
+  /**
+   * Convert cookie array to human readable cookie string
+   * @param cookies Cookie Array
+   * @return String containing all the cookies separated by a newline character.
+   * Each cookie is of the format [key]=[value]
+   */
+  private String toCookieStr(Cookie[] cookies) {
+	String cookieStr = "";
+
+	for (Cookie c : cookies) {
+     cookieStr += c.getName() + "=" + c.getValue() + " ;\n";
+    }
+    return cookieStr;
+  }
+
+  /**
+   * Validate the request cookie. This function iterates over the request cookie headers
+   * and finds a cookie that represents a valid client/server session. If it finds one, it
+   * returns the client name associated with the session. Else, it returns null.
+   * @param request The HTTP Servlet Request send by the client
+   * @return Client Username if the request has valid HS2 cookie, else returns null
+   * @throws UnsupportedEncodingException
+   */
+  private String validateCookie(HttpServletRequest request) throws UnsupportedEncodingException
{
+    // Find all the valid cookies associated with the request.
+    Cookie[] cookies = request.getCookies();
+
+    if (cookies == null) {
+      if (LOG.isDebugEnabled()) {
+        LOG.debug("No valid cookies associated with the request " + request);
+      }
+      return null;
+    }
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Received cookies: " + toCookieStr(cookies));
+    }
+    return getClientNameFromCookie(cookies);
+  }
+
+  /**
+   * Generate a server side cookie given the cookie value as the input.
+   * @param str Input string token.
+   * @return The generated cookie.
+   * @throws UnsupportedEncodingException
+   */
+  private Cookie createCookie(String str) throws UnsupportedEncodingException {
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("Cookie name = " + AUTH_COOKIE + " value = " + str);
+    }
+    Cookie cookie = new Cookie(AUTH_COOKIE, str);
+
+    cookie.setMaxAge(cookieMaxAge);
+    if (cookieDomain != null) {
+      cookie.setDomain(cookieDomain);
+    }
+    if (cookiePath != null) {
+      cookie.setPath(cookiePath);
+    }
+    cookie.setSecure(isCookieSecure);
+    return cookie;
+  }
+
+  /**
+   * Generate httponly cookie from HS2 cookie
+   * @param cookie HS2 generated cookie
+   * @return The httponly cookie
+   */
+  private static String getHttpOnlyCookieHeader(Cookie cookie) {
+    NewCookie newCookie = new NewCookie(cookie.getName(), cookie.getValue(),
+      cookie.getPath(), cookie.getDomain(), cookie.getVersion(),
+      cookie.getComment(), cookie.getMaxAge(), cookie.getSecure());
+    return newCookie + "; HttpOnly";
+  }
+
+  /**
    * Do the LDAP/PAM authentication
    * @param request
    * @param authType

Added: hive/trunk/service/src/test/org/apache/hive/service/TestCookieSigner.java
URL: http://svn.apache.org/viewvc/hive/trunk/service/src/test/org/apache/hive/service/TestCookieSigner.java?rev=1674123&view=auto
==============================================================================
--- hive/trunk/service/src/test/org/apache/hive/service/TestCookieSigner.java (added)
+++ hive/trunk/service/src/test/org/apache/hive/service/TestCookieSigner.java Thu Apr 16 18:09:53
2015
@@ -0,0 +1,59 @@
+/**
+ * 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.hive.service;
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * CLIServiceTest.
+ *
+ */
+public class TestCookieSigner extends TestCase {
+
+  protected static CookieSigner cs;
+  private static final Random RAN = new Random();
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @Before
+  public void setUp() throws Exception {
+    cs = new CookieSigner(Long.toString(RAN.nextLong()).getBytes());
+  }
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void testVerifyAndExtract() throws Exception {
+    String originalStr = "cu=scott";
+    String signedStr = cs.signCookie(originalStr);
+    assert(cs.verifyAndExtract(signedStr).equals(originalStr));
+  }
+}

Added: hive/trunk/service/src/test/org/apache/hive/service/cli/thrift/ThriftCliServiceTestWithCookie.java
URL: http://svn.apache.org/viewvc/hive/trunk/service/src/test/org/apache/hive/service/cli/thrift/ThriftCliServiceTestWithCookie.java?rev=1674123&view=auto
==============================================================================
--- hive/trunk/service/src/test/org/apache/hive/service/cli/thrift/ThriftCliServiceTestWithCookie.java
(added)
+++ hive/trunk/service/src/test/org/apache/hive/service/cli/thrift/ThriftCliServiceTestWithCookie.java
Thu Apr 16 18:09:53 2015
@@ -0,0 +1,221 @@
+
+
+/**
+ * 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.hive.service.cli.thrift;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.hive.conf.HiveConf;
+import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
+import org.apache.hadoop.hive.metastore.MetaStoreUtils;
+import org.apache.hive.service.Service;
+import org.apache.hive.service.auth.HiveAuthFactory.AuthTypes;
+import org.apache.hive.service.cli.OperationHandle;
+import org.apache.hive.service.cli.OperationState;
+import org.apache.hive.service.cli.OperationStatus;
+import org.apache.hive.service.cli.SessionHandle;
+import org.apache.hive.service.server.HiveServer2;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * ThriftCLIServiceTestWithCookie.
+ */
+public class ThriftCliServiceTestWithCookie {
+
+  protected static int port;
+  protected static String host = "localhost";
+  protected static HiveServer2 hiveServer2;
+  protected static ThriftCLIServiceClient client;
+  protected static HiveConf hiveConf;
+  protected static String USERNAME = "anonymous";
+  protected static String PASSWORD = "anonymous";
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+    // Find a free port
+    port = MetaStoreUtils.findFreePort();
+    hiveServer2 = new HiveServer2();
+    hiveConf = new HiveConf();
+    hiveConf.setBoolVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_AUTH_ENABLED, true);
+    // Set the cookie max age to a very low value so that
+    // the server sends 401 very frequently
+    hiveConf.setTimeVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_MAX_AGE, 1, TimeUnit.SECONDS);
+    hiveConf.setBoolVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_COOKIE_IS_SECURE, false);
+    hiveConf.setVar(ConfVars.HIVE_SERVER2_TRANSPORT_MODE, "http");
+    hiveConf.setVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_PATH, "cliservice");
+
+    assertNotNull(port);
+    assertNotNull(hiveServer2);
+    assertNotNull(hiveConf);
+
+    hiveConf.setBoolVar(ConfVars.HIVE_SERVER2_ENABLE_DOAS, false);
+    hiveConf.setVar(ConfVars.HIVE_SERVER2_THRIFT_BIND_HOST, host);
+    hiveConf.setIntVar(ConfVars.HIVE_SERVER2_THRIFT_HTTP_PORT, port);
+    hiveConf.setVar(ConfVars.HIVE_SERVER2_AUTHENTICATION, AuthTypes.NOSASL.toString());
+
+    startHiveServer2WithConf(hiveConf);
+
+    client = getServiceClientInternal();
+  }
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+    stopHiveServer2();
+  }
+
+  protected static void startHiveServer2WithConf(HiveConf hiveConf) throws Exception {
+    hiveServer2.init(hiveConf);
+    // Start HiveServer2 with given config
+    // Fail if server doesn't start
+    try {
+      hiveServer2.start();
+    } catch (Throwable t) {
+      t.printStackTrace();
+      fail();
+    }
+    // Wait for startup to complete
+    Thread.sleep(2000);
+    System.out.println("HiveServer2 started on port " + port);
+  }
+
+  protected static void stopHiveServer2() throws Exception {
+    if (hiveServer2 != null) {
+      hiveServer2.stop();
+    }
+  }
+
+  protected static ThriftCLIServiceClient getServiceClientInternal() {
+    for (Service service : hiveServer2.getServices()) {
+      if (service instanceof ThriftBinaryCLIService) {
+        return new ThriftCLIServiceClient((ThriftBinaryCLIService) service);
+      }
+      if (service instanceof ThriftHttpCLIService) {
+        return new ThriftCLIServiceClient((ThriftHttpCLIService) service);
+      }
+    }
+    throw new IllegalStateException("HiveServer2 not running Thrift service");
+  }
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  /**
+   * @throws java.lang.Exception
+   */
+  @After
+  public void tearDown() throws Exception {
+
+  }
+
+  @Test
+  public void testOpenSession() throws Exception {
+    // Open a new client session
+    SessionHandle sessHandle = client.openSession(USERNAME,
+        PASSWORD, new HashMap<String, String>());
+    // Session handle should not be null
+    assertNotNull("Session handle should not be null", sessHandle);
+    // Close client session
+    client.closeSession(sessHandle);
+  }
+
+  @Test
+  public void testGetFunctions() throws Exception {
+    SessionHandle sessHandle = client.openSession(USERNAME,
+        PASSWORD, new HashMap<String, String>());
+    assertNotNull("Session handle should not be null", sessHandle);
+
+    String catalogName = null;
+    String schemaName = null;
+    String functionName = "*";
+
+    OperationHandle opHandle = client.getFunctions(sessHandle, catalogName,
+        schemaName, functionName);
+
+    assertNotNull("Operation handle should not be null", opHandle);
+
+    client.closeSession(sessHandle);
+  }
+
+  /**
+   * Test synchronous query execution
+   * @throws Exception
+   */
+  @Test
+  public void testExecuteStatement() throws Exception {
+    Map<String, String> opConf = new HashMap<String, String>();
+    // Open a new client session
+    SessionHandle sessHandle = client.openSession(USERNAME,
+        PASSWORD, opConf);
+    // Session handle should not be null
+    assertNotNull("Session handle should not be null", sessHandle);
+
+    // Change lock manager to embedded mode
+    String queryString = "SET hive.lock.manager=" +
+        "org.apache.hadoop.hive.ql.lockmgr.EmbeddedLockManager";
+    client.executeStatement(sessHandle, queryString, opConf);
+
+    // Drop the table if it exists
+    queryString = "DROP TABLE IF EXISTS TEST_EXEC_THRIFT";
+    client.executeStatement(sessHandle, queryString, opConf);
+
+    // Create a test table
+    queryString = "CREATE TABLE TEST_EXEC_THRIFT(ID STRING)";
+    client.executeStatement(sessHandle, queryString, opConf);
+
+    // Execute another query
+    queryString = "SELECT ID+1 FROM TEST_EXEC_THRIFT";
+    OperationHandle opHandle = client.executeStatement(sessHandle,
+        queryString, opConf);
+    assertNotNull(opHandle);
+
+    OperationStatus opStatus = client.getOperationStatus(opHandle);
+    assertNotNull(opStatus);
+
+    OperationState state = opStatus.getState();
+    // Expect query to be completed now
+    assertEquals("Query should be finished", OperationState.FINISHED, state);
+
+    // Cleanup
+    queryString = "DROP TABLE TEST_EXEC_THRIFT";
+    client.executeStatement(sessHandle, queryString, opConf);
+
+    client.closeSession(sessHandle);
+  }
+}
+



Mime
View raw message