HBASE-7621 REST client (RemoteHTable) doesn't support binary row keys
Signed-off-by: Andrew Purtell <apurtell@apache.org>
This commit is contained in:
parent
34b668b0a9
commit
7145e46b7a
|
@ -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,19 +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).getQualifierArray(),
|
||||
((KeyValue) o).getQualifierOffset(), ((KeyValue) o).getQualifierLength()));
|
||||
sb.append(toURLEncodedBytes(CellUtil.cloneQualifier((KeyValue)o)));
|
||||
} else {
|
||||
throw new RuntimeException("object type not handled");
|
||||
}
|
||||
|
@ -151,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();
|
||||
|
@ -163,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);
|
||||
|
@ -211,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));
|
||||
|
@ -220,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));
|
||||
|
@ -230,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;
|
||||
|
@ -260,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++) {
|
||||
|
@ -402,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());
|
||||
|
@ -459,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,
|
||||
|
@ -530,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++) {
|
||||
|
@ -684,9 +677,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++) {
|
||||
|
@ -728,9 +721,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++) {
|
||||
|
@ -890,4 +883,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,16 +59,35 @@ import org.junit.experimental.categories.Category;
|
|||
|
||||
@Category({RestTests.class, 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
|
||||
|
|
Loading…
Reference in New Issue