You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ap...@apache.org on 2016/08/19 19:31:10 UTC
[2/2] hbase git commit: HBASE-7621 REST client (RemoteHTable) doesn't
support binary row keys
HBASE-7621 REST client (RemoteHTable) doesn't support binary row keys
Signed-off-by: Andrew Purtell <ap...@apache.org>
Project: http://git-wip-us.apache.org/repos/asf/hbase/repo
Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/b3ca9a9b
Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/b3ca9a9b
Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/b3ca9a9b
Branch: refs/heads/branch-1
Commit: b3ca9a9b2f91ce5d43c30113cbfbbbaf8cade9c0
Parents: a857a60
Author: Keith David Winkler <kd...@monsanto.com>
Authored: Tue Aug 16 21:23:41 2016 -0500
Committer: Andrew Purtell <ap...@apache.org>
Committed: Fri Aug 19 12:24:14 2016 -0700
----------------------------------------------------------------------
.../hadoop/hbase/rest/client/RemoteHTable.java | 59 +++++++++++---------
.../hbase/rest/client/TestRemoteTable.java | 41 ++++++++++----
2 files changed, 64 insertions(+), 36 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/hbase/blob/b3ca9a9b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
index 63b18b1..146cc72 100644
--- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteHTable.java
@@ -21,6 +21,8 @@ package org.apache.hadoop.hbase.rest.client;
import java.io.IOException;
import java.io.InterruptedIOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
@@ -92,9 +94,9 @@ public class RemoteHTable implements Table {
final long startTime, final long endTime, final int maxVersions) {
StringBuffer sb = new StringBuffer();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append('/');
- sb.append(Bytes.toStringBinary(row));
+ sb.append(toURLEncodedBytes(row));
Set families = familyMap.entrySet();
if (families != null) {
Iterator i = familyMap.entrySet().iterator();
@@ -104,18 +106,18 @@ public class RemoteHTable implements Table {
Collection quals = (Collection)e.getValue();
if (quals == null || quals.isEmpty()) {
// this is an unqualified family. append the family name and NO ':'
- sb.append(Bytes.toStringBinary((byte[])e.getKey()));
+ sb.append(toURLEncodedBytes((byte[])e.getKey()));
} else {
Iterator ii = quals.iterator();
while (ii.hasNext()) {
- sb.append(Bytes.toStringBinary((byte[])e.getKey()));
+ sb.append(toURLEncodedBytes((byte[])e.getKey()));
sb.append(':');
Object o = ii.next();
// Puts use byte[] but Deletes use KeyValue
if (o instanceof byte[]) {
- sb.append(Bytes.toStringBinary((byte[])o));
+ sb.append(toURLEncodedBytes((byte[])o));
} else if (o instanceof KeyValue) {
- sb.append(Bytes.toStringBinary(((KeyValue)o).getQualifier()));
+ sb.append(toURLEncodedBytes(((KeyValue)o).getQualifier()));
} else {
throw new RuntimeException("object type not handled");
}
@@ -150,7 +152,7 @@ public class RemoteHTable implements Table {
protected String buildMultiRowSpec(final byte[][] rows, int maxVersions) {
StringBuilder sb = new StringBuilder();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append("/multiget/");
if (rows == null || rows.length == 0) {
return sb.toString();
@@ -162,7 +164,7 @@ public class RemoteHTable implements Table {
sb.append('&');
}
sb.append("row=");
- sb.append(Bytes.toStringBinary(rk));
+ sb.append(toURLEncodedBytes(rk));
}
sb.append("&v=");
sb.append(maxVersions);
@@ -210,8 +212,6 @@ public class RemoteHTable implements Table {
/**
* Constructor
- * @param client
- * @param name
*/
public RemoteHTable(Client client, String name) {
this(client, HBaseConfiguration.create(), Bytes.toBytes(name));
@@ -219,9 +219,6 @@ public class RemoteHTable implements Table {
/**
* Constructor
- * @param client
- * @param conf
- * @param name
*/
public RemoteHTable(Client client, Configuration conf, String name) {
this(client, conf, Bytes.toBytes(name));
@@ -229,9 +226,6 @@ public class RemoteHTable implements Table {
/**
* Constructor
- * @param client
- * @param conf
- * @param name
*/
public RemoteHTable(Client client, Configuration conf, byte[] name) {
this.client = client;
@@ -259,7 +253,7 @@ public class RemoteHTable implements Table {
public HTableDescriptor getTableDescriptor() throws IOException {
StringBuilder sb = new StringBuilder();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append('/');
sb.append("schema");
for (int i = 0; i < maxRetries; i++) {
@@ -401,9 +395,9 @@ public class RemoteHTable implements Table {
CellSetModel model = buildModelFromPut(put);
StringBuilder sb = new StringBuilder();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append('/');
- sb.append(Bytes.toStringBinary(put.getRow()));
+ sb.append(toURLEncodedBytes(put.getRow()));
for (int i = 0; i < maxRetries; i++) {
Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
model.createProtobufOutput());
@@ -458,7 +452,7 @@ public class RemoteHTable implements Table {
// build path for multiput
StringBuilder sb = new StringBuilder();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append("/$multiput"); // can be any nonexistent row
for (int i = 0; i < maxRetries; i++) {
Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
@@ -529,7 +523,7 @@ public class RemoteHTable implements Table {
}
StringBuffer sb = new StringBuffer();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append('/');
sb.append("scanner");
for (int i = 0; i < maxRetries; i++) {
@@ -682,9 +676,9 @@ public class RemoteHTable implements Table {
CellSetModel model = buildModelFromPut(put);
StringBuilder sb = new StringBuilder();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append('/');
- sb.append(Bytes.toStringBinary(put.getRow()));
+ sb.append(toURLEncodedBytes(put.getRow()));
sb.append("?check=put");
for (int i = 0; i < maxRetries; i++) {
@@ -726,9 +720,9 @@ public class RemoteHTable implements Table {
CellSetModel model = buildModelFromPut(put);
StringBuilder sb = new StringBuilder();
sb.append('/');
- sb.append(Bytes.toStringBinary(name));
+ sb.append(Bytes.toString(name));
sb.append('/');
- sb.append(Bytes.toStringBinary(row));
+ sb.append(toURLEncodedBytes(row));
sb.append("?check=delete");
for (int i = 0; i < maxRetries; i++) {
@@ -897,4 +891,19 @@ public class RemoteHTable implements Table {
public void setWriteRpcTimeout(int writeRpcTimeout) {
throw new UnsupportedOperationException();
}
+
+ /*
+ * Only a small subset of characters are valid in URLs.
+ *
+ * Row keys, column families, and qualifiers cannot be appended to URLs without first URL
+ * escaping. Table names are ok because they can only contain alphanumeric, ".","_", and "-"
+ * which are valid characters in URLs.
+ */
+ private static String toURLEncodedBytes(byte[] row) {
+ try {
+ return URLEncoder.encode(new String(row, "UTF-8"), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new IllegalStateException("URLEncoder doesn't support UTF-8", e);
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/hbase/blob/b3ca9a9b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
----------------------------------------------------------------------
diff --git a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
index 121ff65..342fc4e 100644
--- a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
+++ b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteTable.java
@@ -59,16 +59,35 @@ import org.junit.experimental.categories.Category;
@Category(MediumTests.class)
public class TestRemoteTable {
- private static final TableName TABLE = TableName.valueOf("TestRemoteTable");
- private static final byte[] ROW_1 = Bytes.toBytes("testrow1");
- private static final byte[] ROW_2 = Bytes.toBytes("testrow2");
- private static final byte[] ROW_3 = Bytes.toBytes("testrow3");
- private static final byte[] ROW_4 = Bytes.toBytes("testrow4");
- private static final byte[] COLUMN_1 = Bytes.toBytes("a");
- private static final byte[] COLUMN_2 = Bytes.toBytes("b");
- private static final byte[] COLUMN_3 = Bytes.toBytes("c");
- private static final byte[] QUALIFIER_1 = Bytes.toBytes("1");
- private static final byte[] QUALIFIER_2 = Bytes.toBytes("2");
+
+ // Verify that invalid URL characters and arbitrary bytes are escaped when
+ // constructing REST URLs per HBASE-7621. RemoteHTable should support row keys
+ // and qualifiers containing any byte for all table operations.
+ private static final String INVALID_URL_CHARS_1 =
+ "|\"\\^{}\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000B\u000C";
+
+ // HColumnDescriptor prevents certain characters in column names. The following
+ // are examples of characters are allowed in column names but are not valid in
+ // URLs.
+ private static final String INVALID_URL_CHARS_2 = "|^{}\u0242";
+
+ // Besides alphanumeric these characters can also be present in table names.
+ private static final String VALID_TABLE_NAME_CHARS = "_-.";
+
+ private static final TableName TABLE =
+ TableName.valueOf("TestRemoteTable" + VALID_TABLE_NAME_CHARS);
+
+ private static final byte[] ROW_1 = Bytes.toBytes("testrow1" + INVALID_URL_CHARS_1);
+ private static final byte[] ROW_2 = Bytes.toBytes("testrow2" + INVALID_URL_CHARS_1);
+ private static final byte[] ROW_3 = Bytes.toBytes("testrow3" + INVALID_URL_CHARS_1);
+ private static final byte[] ROW_4 = Bytes.toBytes("testrow4"+ INVALID_URL_CHARS_1);
+
+ private static final byte[] COLUMN_1 = Bytes.toBytes("a" + INVALID_URL_CHARS_2);
+ private static final byte[] COLUMN_2 = Bytes.toBytes("b" + INVALID_URL_CHARS_2);
+ private static final byte[] COLUMN_3 = Bytes.toBytes("c" + INVALID_URL_CHARS_2);
+
+ private static final byte[] QUALIFIER_1 = Bytes.toBytes("1" + INVALID_URL_CHARS_1);
+ private static final byte[] QUALIFIER_2 = Bytes.toBytes("2" + INVALID_URL_CHARS_1);
private static final byte[] VALUE_1 = Bytes.toBytes("testvalue1");
private static final byte[] VALUE_2 = Bytes.toBytes("testvalue2");
@@ -322,7 +341,7 @@ public class TestRemoteTable {
assertNotNull(value);
assertTrue(Bytes.equals(VALUE_2, value));
- assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable"), remoteTable.getTableName()));
+ assertTrue(Bytes.equals(Bytes.toBytes("TestRemoteTable" + VALID_TABLE_NAME_CHARS), remoteTable.getTableName()));
}
@Test