Repository: phoenix
Updated Branches:
  refs/heads/master 437bf6881 -> dddcffad2


PHOENIX-19 Enhance JDBC connection of Phoenix to support connecting to a Secure 
HBase cluster (Anil Gupta)


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

Branch: refs/heads/master
Commit: dddcffad28c1a338e63dfa476619fec3f47db489
Parents: 437bf68
Author: James Taylor <jtay...@salesforce.com>
Authored: Sun Jun 8 10:45:38 2014 -0700
Committer: James Taylor <jtay...@salesforce.com>
Committed: Sun Jun 8 10:52:34 2014 -0700

----------------------------------------------------------------------
 .../phoenix/jdbc/PhoenixEmbeddedDriver.java     | 100 ++++++++++++++++---
 .../query/ConnectionQueryServicesImpl.java      |  19 +++-
 .../org/apache/phoenix/query/QueryServices.java |   3 +
 .../phoenix/jdbc/PhoenixEmbeddedDriverTest.java |   9 ++
 4 files changed, 117 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
index 8cfe3c2..10c24b8 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriver.java
@@ -169,7 +169,7 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
             StringTokenizer tokenizer = new StringTokenizer(url == null ? "" : 
url.substring(PhoenixRuntime.JDBC_PROTOCOL.length()),DELIMITERS, true);
             int i = 0;
             boolean isMalformedUrl = false;
-            String[] tokens = new String[3];
+            String[] tokens = new String[5];
             String token = null;
             while (tokenizer.hasMoreTokens() && 
!(token=tokenizer.nextToken()).equals(TERMINATOR) && tokenizer.hasMoreTokens() 
&& i < tokens.length) {
                 token = tokenizer.nextToken();
@@ -188,14 +188,41 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
                     try {
                         port = Integer.parseInt(tokens[1]);
                         isMalformedUrl = port < 0;
+                        if(i == 4){
+                               if(!tokens[2].endsWith(".keytab")){
+                                       isMalformedUrl = true;
+                               }
+                               tokens[4] = tokens[3];
+                               tokens[3] = tokens[2];
+                               tokens[2] = null;
+                        }
                     } catch (NumberFormatException e) {
                         // If we have 3 tokens, then the second one must be a 
port.
                         // If we only have 2 tokens, the second one might be 
the root node:
                         // Assume that is the case if we get a 
NumberFormatException
-                        if (! (isMalformedUrl = i == 3) ) {
+                        if (!tokens[1].startsWith("/")) {
+                            isMalformedUrl = true;
+                        }
+                        if (i == 2) {
+                            tokens[4] = null;
+                            tokens[3] = null;
+                            tokens[2] = tokens[1];
+                            tokens[1] = null;
+                        } else if (i == 3) {
+                            tokens[4] = tokens[2];
+                            tokens[3] = tokens[1];
+                            tokens[2] = null;
+                            tokens[1] = null;
+                        } else if (i == 4) {
+                            tokens[4] = tokens[3];
+                            tokens[3] = tokens[2];
+                            tokens[2] = tokens[1];
+                            tokens[1] = null;
+                        } else if (i == 5) {
+                            tokens[4] = tokens[3];
+                            tokens[3] = tokens[2];
                             tokens[2] = tokens[1];
                         }
-                        
                     }
                 }
             }
@@ -203,13 +230,15 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
                 throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.MALFORMED_CONNECTION_URL)
                 .setMessage(url).build().buildException();
             }
-            return new ConnectionInfo(tokens[0],port,tokens[2]);
+            return new ConnectionInfo(tokens[0],port,tokens[2], tokens[3], 
tokens[4]);
         }
         
         public ConnectionInfo normalize(ReadOnlyProps props) throws 
SQLException {
             String zookeeperQuorum = this.getZookeeperQuorum();
             Integer port = this.getPort();
             String rootNode = this.getRootNode();
+            String keytab = this.getKeytab();
+            String principal = this.getPrincipal();
             // Normalize connInfo so that a url explicitly specifying versus 
implicitly inheriting
             // the default values will both share the same 
ConnectionQueryServices.
             if (zookeeperQuorum == null) {
@@ -244,24 +273,45 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
                 throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.MALFORMED_CONNECTION_URL)
                 .setMessage("Root node may not be specified when using the 
connectionless url \"" + this.toString() + "\"").build().buildException();
             }
-            return new ConnectionInfo(zookeeperQuorum, port, rootNode);
+            if(keytab == null){
+                if (!isConnectionless) {
+                        keytab = props.get(QueryServices.HBASE_CLIENT_KEYTAB);
+                }
+            }
+            if(principal == null){
+                if (!isConnectionless) {
+                       principal = 
props.get(QueryServices.HBASE_CLIENT_PRINCIPAL);
+                }
+              }
+            if (keytab == null || keytab.equals("")) return new 
ConnectionInfo(zookeeperQuorum,
+                    port, rootNode);
+            else return new ConnectionInfo(zookeeperQuorum, port, rootNode, 
keytab, principal);
         }
         
         private final Integer port;
         private final String rootNode;
         private final String zookeeperQuorum;
         private final boolean isConnectionless;
+        private final String principal;
+        private final String keytab;
         
         // used for testing
-        ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode) {
+        ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode, 
String keytab, String principal) {
             this.zookeeperQuorum = zookeeperQuorum;
             this.port = port;
             this.rootNode = rootNode;
             this.isConnectionless = 
PhoenixRuntime.CONNECTIONLESS.equals(zookeeperQuorum);
+            this.principal = principal;
+            this.keytab = keytab;
+        }
+        
+        // used for testing
+        ConnectionInfo(String zookeeperQuorum, Integer port, String rootNode) {
+               this(zookeeperQuorum, port, rootNode, null, null);
         }
 
         public ReadOnlyProps asProps() {
-            Map<String,String> connectionProps = 
Maps.newHashMapWithExpectedSize(3);
+            Map<String, String> connectionProps = 
Maps.newHashMapWithExpectedSize(3);
             if (getZookeeperQuorum() != null) {
                 connectionProps.put(QueryServices.ZOOKEEPER_QUARUM_ATTRIB, 
getZookeeperQuorum());
             }
@@ -271,7 +321,14 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
             if (getRootNode() != null) {
                 connectionProps.put(QueryServices.ZOOKEEPER_ROOT_NODE_ATTRIB, 
getRootNode());
             }
-            return connectionProps.isEmpty() ? ReadOnlyProps.EMPTY_PROPS : new 
ReadOnlyProps(connectionProps.entrySet().iterator());
+            if (getKeytab() != null) {
+                connectionProps.put(QueryServices.HBASE_CLIENT_KEYTAB, 
getKeytab());
+            }
+            if (getPrincipal() != null) {
+                connectionProps.put(QueryServices.HBASE_CLIENT_PRINCIPAL, 
getPrincipal());
+            }
+            return connectionProps.isEmpty() ? ReadOnlyProps.EMPTY_PROPS : new 
ReadOnlyProps(
+                    connectionProps.entrySet().iterator());
         }
         
         public boolean isConnectionless() {
@@ -289,6 +346,14 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
         public String getRootNode() {
             return rootNode;
         }
+        
+        public String getKeytab() {
+            return keytab;
+        }
+
+        public String getPrincipal() {
+            return principal;
+        }
 
         @Override
         public int hashCode() {
@@ -297,6 +362,8 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
             result = prime * result + ((zookeeperQuorum == null) ? 0 : 
zookeeperQuorum.hashCode());
             result = prime * result + ((port == null) ? 0 : port.hashCode());
             result = prime * result + ((rootNode == null) ? 0 : 
rootNode.hashCode());
+            result = prime * result + ((keytab == null) ? 0 : 
keytab.hashCode());
+            result = prime * result + ((principal == null) ? 0 : 
keytab.hashCode());
             return result;
         }
 
@@ -305,7 +372,7 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
             if (this == obj) return true;
             if (obj == null) return false;
             if (getClass() != obj.getClass()) return false;
-            ConnectionInfo other = (ConnectionInfo)obj;
+            ConnectionInfo other = (ConnectionInfo) obj;
             if (zookeeperQuorum == null) {
                 if (other.zookeeperQuorum != null) return false;
             } else if (!zookeeperQuorum.equals(other.zookeeperQuorum)) return 
false;
@@ -315,13 +382,22 @@ public abstract class PhoenixEmbeddedDriver implements 
Driver, org.apache.phoeni
             if (rootNode == null) {
                 if (other.rootNode != null) return false;
             } else if (!rootNode.equals(other.rootNode)) return false;
+            if (keytab == null) {
+                if (other.keytab != null) return false;
+            } else if (!keytab.equals(other.keytab)) return false;
+            if (principal == null) {
+                if (other.principal != null) return false;
+            } else if (!principal.equals(other.principal)) return false;
             return true;
         }
         
         @Override
-        public String toString() {
-            return zookeeperQuorum + (port == null ? "" : ":" + port) + 
(rootNode == null ? "" : ":" + rootNode);
-        }
+               public String toString() {
+                       return zookeeperQuorum + (port == null ? "" : ":" + 
port)
+                                       + (rootNode == null ? "" : ":" + 
rootNode)
+                                       + (keytab == null ? "" : ":" + keytab)
+                                       + (principal == null ? "" : ":" + 
principal);
+               }
     }
 
     public static boolean isTestUrl(String url) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
index cce5f0a..acb2239 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/query/ConnectionQueryServicesImpl.java
@@ -64,7 +64,6 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
-import com.google.protobuf.HBaseZeroCopyByteString;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.Cell;
 import org.apache.hadoop.hbase.HColumnDescriptor;
@@ -89,10 +88,12 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.ipc.BlockingRpcCallback;
 import org.apache.hadoop.hbase.ipc.ServerRpcController;
 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto;
+import org.apache.hadoop.hbase.security.User;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.hbase.util.Pair;
 import org.apache.hadoop.hbase.util.VersionInfo;
 import org.apache.hadoop.hbase.zookeeper.ZKConfig;
+import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.phoenix.compile.MutationPlan;
 import org.apache.phoenix.coprocessor.GroupedAggregateRegionObserver;
 import org.apache.phoenix.coprocessor.MetaDataEndpointImpl;
@@ -171,6 +172,7 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 import com.google.common.io.Closeables;
+import com.google.protobuf.HBaseZeroCopyByteString;
 
 
 public class ConnectionQueryServicesImpl extends DelegateQueryServices 
implements ConnectionQueryServices {
@@ -252,6 +254,15 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
     
     private void openConnection() throws SQLException {
         try {
+            // check if we need to authenticate with kerberos
+            String clientKeytab = config.get(HBASE_CLIENT_KEYTAB);
+            String clientPrincipal = config.get(HBASE_CLIENT_PRINCIPAL);
+            if (clientKeytab != null && clientPrincipal != null) {
+                logger.info("Trying to connect to a secure cluster with 
keytab:" + clientKeytab);
+                UserGroupInformation.setConfiguration(config);
+                User.login(config, HBASE_CLIENT_KEYTAB, 
HBASE_CLIENT_PRINCIPAL, null);
+                logger.info("Successfull login to secure cluster!!");
+            }
             this.connection = 
HBaseFactoryProvider.getHConnectionFactory().createConnection(this.config);
         } catch (IOException e) {
             throw new 
SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_ESTABLISH_CONNECTION)
@@ -410,7 +421,7 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
                 byte[] currentKey = HConstants.EMPTY_START_ROW;
                 do {
                   HRegionLocation regionLocation = 
connection.getRegionLocation(
-                    TableName.valueOf(tableName), currentKey, reload);
+                      TableName.valueOf(tableName), currentKey, reload);
                   locations.add(regionLocation);
                   currentKey = regionLocation.getRegionInfo().getEndKey();
                 } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW));
@@ -1711,6 +1722,7 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
         incrementSequenceValues(sequenceKeys, timestamp, values, exceptions, 
1, Sequence.Action.RESERVE);
     }
 
+    @SuppressWarnings("deprecation")
     private void incrementSequenceValues(List<SequenceKey> keys, long 
timestamp, long[] values, SQLException[] exceptions, int factor, 
Sequence.Action action) throws SQLException {
         List<Sequence> sequences = 
Lists.newArrayListWithExpectedSize(keys.size());
         for (SequenceKey key : keys) {
@@ -1783,6 +1795,7 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
         }
     }
 
+    @SuppressWarnings("deprecation")
     @Override
     public void returnSequences(List<SequenceKey> keys, long timestamp, 
SQLException[] exceptions) throws SQLException {
         List<Sequence> sequences = 
Lists.newArrayListWithExpectedSize(keys.size());
@@ -1856,6 +1869,7 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
 
     // Take no locks, as this only gets run when there are no open connections
     // so there's no danger of contention.
+    @SuppressWarnings("deprecation")
     private void returnAllSequences(ConcurrentMap<SequenceKey,Sequence> 
sequenceMap) throws SQLException {
         List<Append> mutations = 
Lists.newArrayListWithExpectedSize(sequenceMap.size());
         for (Sequence sequence : sequenceMap.values()) {
@@ -2271,6 +2285,7 @@ public class ConnectionQueryServicesImpl extends 
DelegateQueryServices implement
      * @throws IOException
      * @throws SQLException
      */
+    @SuppressWarnings("deprecation")
     private WhiteList upgradeCoprocessorsTo3_0(HBaseAdmin admin, boolean 
forceUpgrade) throws IOException, SQLException {
         String files = config.get(QueryServices.AUTO_UPGRADE_WHITELIST_ATTRIB);
         WhiteList coprocUpgradeWhiteList = new WhiteList(files);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
index 44ec932..9c751ba 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -44,6 +44,9 @@ public interface QueryServices extends SQLCloseable {
     public static final String QUEUE_SIZE_ATTRIB = "phoenix.query.queueSize";
     public static final String THREAD_TIMEOUT_MS_ATTRIB = 
"phoenix.query.timeoutMs";
     public static final String SPOOL_THRESHOLD_BYTES_ATTRIB = 
"phoenix.query.spoolThresholdBytes";
+    public static final String HBASE_CLIENT_KEYTAB = "hbase.myclient.keytab";
+    public static final String HBASE_CLIENT_PRINCIPAL = 
"hbase.myclient.principal";
+
     
     /**
         * max size to spool the the result into

http://git-wip-us.apache.org/repos/asf/phoenix/blob/dddcffad/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
 
b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
index 23c7951..a9b09e5 100644
--- 
a/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/jdbc/PhoenixEmbeddedDriverTest.java
@@ -47,6 +47,10 @@ public class PhoenixEmbeddedDriverTest {
             "jdbc:phoenix:v1,v2,v3:/hbase;test=true",
             "jdbc:phoenix:v1,v2,v3:123:/hbase",
             "jdbc:phoenix:v1,v2,v3:123:/hbase;test=false",
+            
"jdbc:phoenix:v1,v2,v3:123:/hbase:/user.keytab:user/principal;test=false",
+            "jdbc:phoenix:v1,v2,v3:123:/user.keytab:user/principal;test=false",
+            "jdbc:phoenix:v1,v2,v3:/user.keytab:user/principal;test=false",
+            
"jdbc:phoenix:v1,v2,v3:/hbase:/user.keytab:user/principal;test=false"
         };
         ConnectionInfo[] infos = new ConnectionInfo[] {
             new ConnectionInfo(null,null,null),
@@ -65,6 +69,10 @@ public class PhoenixEmbeddedDriverTest {
             new ConnectionInfo("v1,v2,v3",null,"/hbase"),
             new ConnectionInfo("v1,v2,v3",123,"/hbase"),
             new ConnectionInfo("v1,v2,v3",123,"/hbase"),
+            new ConnectionInfo("v1,v2,v3",123,"/hbase", 
"/user.keytab","user/principal" ),
+            new ConnectionInfo("v1,v2,v3",123, null, 
"/user.keytab","user/principal" ),
+            new ConnectionInfo("v1,v2,v3", null, null, 
"/user.keytab","user/principal" ),
+            new ConnectionInfo("v1,v2,v3",null,"/hbase", 
"/user.keytab","user/principal" )
         };
         assertEquals(urls.length,infos.length);
         for (int i = 0; i < urls.length; i++) {
@@ -92,6 +100,7 @@ public class PhoenixEmbeddedDriverTest {
             "jdbc:phoenix:v1,v2,v3:123a:/hbase;test=true",
             "jdbc:phoenix:v1,v2,v3:123::/hbase",
             "jdbc:phoenix:v1,v2,v3:123::/hbase;test=false",
+            "jdbc:phoenix:v1,v2,v3:123:/hbase:user;test=false"
         };
         for (String url : urls) {
             try {

Reply via email to