diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteAdmin.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteAdmin.java index 74da9f3b45b..f40551ba70e 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteAdmin.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/rest/client/RemoteAdmin.java @@ -19,15 +19,24 @@ package org.apache.hadoop.hbase.rest.client; +import java.io.ByteArrayInputStream; import java.io.IOException; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.rest.Constants; +import org.apache.hadoop.hbase.rest.model.StorageClusterStatusModel; +import org.apache.hadoop.hbase.rest.model.StorageClusterVersionModel; +import org.apache.hadoop.hbase.rest.model.TableListModel; import org.apache.hadoop.hbase.rest.model.TableSchemaModel; +import org.apache.hadoop.hbase.rest.model.VersionModel; import org.apache.hadoop.hbase.util.Bytes; @InterfaceAudience.Public @@ -40,8 +49,14 @@ public class RemoteAdmin { final int maxRetries; final long sleepTime; + // This unmarshaller is necessary for getting the /version/cluster resource. + // This resource does not support protobufs. Therefore this is necessary to + // request/interpret it as XML. + private static volatile Unmarshaller versionClusterUnmarshaller; + /** * Constructor + * * @param client * @param conf */ @@ -49,6 +64,16 @@ public class RemoteAdmin { this(client, conf, null); } + static Unmarshaller getUnmarsheller() throws JAXBException { + + if (versionClusterUnmarshaller == null) { + + RemoteAdmin.versionClusterUnmarshaller = JAXBContext.newInstance( + StorageClusterVersionModel.class).createUnmarshaller(); + } + return RemoteAdmin.versionClusterUnmarshaller; + } + /** * Constructor * @param client @@ -72,24 +97,156 @@ public class RemoteAdmin { return isTableAvailable(Bytes.toBytes(tableName)); } + /** + * @return string representing the rest api's version + * @throws IOEXception + * if the endpoint does not exist, there is a timeout, or some other + * general failure mode + */ + public VersionModel getRestVersion() throws IOException { + + StringBuilder path = new StringBuilder(); + path.append('/'); + if (accessToken != null) { + path.append(accessToken); + path.append('/'); + } + + path.append("version/rest"); + + int code = 0; + for (int i = 0; i < maxRetries; i++) { + Response response = client.get(path.toString(), + Constants.MIMETYPE_PROTOBUF); + code = response.getCode(); + switch (code) { + case 200: + + VersionModel v = new VersionModel(); + return (VersionModel) v.getObjectFromMessage(response.getBody()); + case 404: + throw new IOException("REST version not found"); + case 509: + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + } + break; + default: + throw new IOException("get request to " + path.toString() + + " returned " + code); + } + } + throw new IOException("get request to " + path.toString() + " timed out"); + } + + /** + * @return string representing the cluster's version + * @throws IOEXception if the endpoint does not exist, there is a timeout, or some other general failure mode + */ + public StorageClusterStatusModel getClusterStatus() throws IOException { + + StringBuilder path = new StringBuilder(); + path.append('/'); + if (accessToken !=null) { + path.append(accessToken); + path.append('/'); + } + + path.append("status/cluster"); + + int code = 0; + for (int i = 0; i < maxRetries; i++) { + Response response = client.get(path.toString(), + Constants.MIMETYPE_PROTOBUF); + code = response.getCode(); + switch (code) { + case 200: + StorageClusterStatusModel s = new StorageClusterStatusModel(); + return (StorageClusterStatusModel) s.getObjectFromMessage(response + .getBody()); + case 404: + throw new IOException("Cluster version not found"); + case 509: + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + } + break; + default: + throw new IOException("get request to " + path + " returned " + code); + } + } + throw new IOException("get request to " + path + " timed out"); + } + + /** + * @return string representing the cluster's version + * @throws IOEXception + * if the endpoint does not exist, there is a timeout, or some other + * general failure mode + */ + public StorageClusterVersionModel getClusterVersion() throws IOException { + + StringBuilder path = new StringBuilder(); + path.append('/'); + if (accessToken != null) { + path.append(accessToken); + path.append('/'); + } + + path.append("version/cluster"); + + int code = 0; + for (int i = 0; i < maxRetries; i++) { + Response response = client.get(path.toString(), Constants.MIMETYPE_XML); + code = response.getCode(); + switch (code) { + case 200: + try { + + return (StorageClusterVersionModel) getUnmarsheller().unmarshal( + new ByteArrayInputStream(response.getBody())); + } catch (JAXBException jaxbe) { + + throw new IOException( + "Issue parsing StorageClusterVersionModel object in XML form: " + + jaxbe.getLocalizedMessage()); + } + case 404: + throw new IOException("Cluster version not found"); + case 509: + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + } + break; + default: + throw new IOException(path.toString() + " request returned " + code); + } + } + throw new IOException("get request to " + path.toString() + + " request timed out"); + } + /** * @param tableName name of table to check * @return true if all regions of the table are available * @throws IOException if a remote or network exception occurs */ public boolean isTableAvailable(byte[] tableName) throws IOException { - StringBuilder sb = new StringBuilder(); - sb.append('/'); + StringBuilder path = new StringBuilder(); + path.append('/'); if (accessToken != null) { - sb.append(accessToken); - sb.append('/'); + path.append(accessToken); + path.append('/'); } - sb.append(Bytes.toStringBinary(tableName)); - sb.append('/'); - sb.append("exists"); + path.append(Bytes.toStringBinary(tableName)); + path.append('/'); + path.append("exists"); int code = 0; for (int i = 0; i < maxRetries; i++) { - Response response = client.get(sb.toString()); + Response response = client.get(path.toString(), Constants.MIMETYPE_PROTOBUF); code = response.getCode(); switch (code) { case 200: @@ -102,10 +259,10 @@ public class RemoteAdmin { } catch (InterruptedException e) { } break; default: - throw new IOException("exists request returned " + code); + throw new IOException("get request to " + path.toString() + " returned " + code); } } - throw new IOException("exists request timed out"); + throw new IOException("get request to " + path.toString() + " timed out"); } /** @@ -116,18 +273,18 @@ public class RemoteAdmin { public void createTable(HTableDescriptor desc) throws IOException { TableSchemaModel model = new TableSchemaModel(desc); - StringBuilder sb = new StringBuilder(); - sb.append('/'); + StringBuilder path = new StringBuilder(); + path.append('/'); if (accessToken != null) { - sb.append(accessToken); - sb.append('/'); + path.append(accessToken); + path.append('/'); } - sb.append(Bytes.toStringBinary(desc.getName())); - sb.append('/'); - sb.append("schema"); + path.append(Bytes.toStringBinary(desc.getName())); + path.append('/'); + path.append("schema"); int code = 0; for (int i = 0; i < maxRetries; i++) { - Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF, + Response response = client.put(path.toString(), Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput()); code = response.getCode(); switch (code) { @@ -139,10 +296,10 @@ public class RemoteAdmin { } catch (InterruptedException e) { } break; default: - throw new IOException("create request returned " + code); + throw new IOException("create request to " + path.toString() + " returned " + code); } } - throw new IOException("create request timed out"); + throw new IOException("create request to " + path.toString() + " timed out"); } /** @@ -160,18 +317,18 @@ public class RemoteAdmin { * @throws IOException if a remote or network exception occurs */ public void deleteTable(final byte [] tableName) throws IOException { - StringBuilder sb = new StringBuilder(); - sb.append('/'); + StringBuilder path = new StringBuilder(); + path.append('/'); if (accessToken != null) { - sb.append(accessToken); - sb.append('/'); + path.append(accessToken); + path.append('/'); } - sb.append(Bytes.toStringBinary(tableName)); - sb.append('/'); - sb.append("schema"); + path.append(Bytes.toStringBinary(tableName)); + path.append('/'); + path.append("schema"); int code = 0; - for (int i = 0; i < maxRetries; i++) { - Response response = client.delete(sb.toString()); + for (int i = 0; i < maxRetries; i++) { + Response response = client.delete(path.toString()); code = response.getCode(); switch (code) { case 200: @@ -182,10 +339,52 @@ public class RemoteAdmin { } catch (InterruptedException e) { } break; default: - throw new IOException("delete request returned " + code); + throw new IOException("delete request to " + path.toString() + " returned " + code); } } - throw new IOException("delete request timed out"); + throw new IOException("delete request to " + path.toString() + " timed out"); } + /** + * @return string representing the cluster's version + * @throws IOEXception + * if the endpoint does not exist, there is a timeout, or some other + * general failure mode + */ + public TableListModel getTableList() throws IOException { + + StringBuilder path = new StringBuilder(); + path.append('/'); + if (accessToken != null) { + path.append(accessToken); + path.append('/'); + } + + int code = 0; + for (int i = 0; i < maxRetries; i++) { + // Response response = client.get(path.toString(), + // Constants.MIMETYPE_XML); + Response response = client.get(path.toString(), + Constants.MIMETYPE_PROTOBUF); + code = response.getCode(); + switch (code) { + case 200: + TableListModel t = new TableListModel(); + return (TableListModel) t.getObjectFromMessage(response.getBody()); + case 404: + throw new IOException("Table list not found"); + case 509: + try { + Thread.sleep(sleepTime); + } catch (InterruptedException e) { + } + break; + default: + throw new IOException("get request to " + path.toString() + + " request returned " + code); + } + } + throw new IOException("get request to " + path.toString() + + " request timed out"); + } } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteAdmin.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteAdmin.java index 7aba279e4a7..9e874580e93 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteAdmin.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/rest/client/TestRemoteAdmin.java @@ -19,13 +19,22 @@ package org.apache.hadoop.hbase.rest.client; -import org.apache.hadoop.hbase.*; -import org.apache.hadoop.hbase.client.HBaseAdmin; -import org.apache.hadoop.hbase.rest.HBaseRESTTestingUtility; -import org.apache.hadoop.hbase.rest.client.Client; -import org.apache.hadoop.hbase.util.Bytes; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; -import static org.junit.Assert.*; +import java.util.List; + +import org.apache.hadoop.hbase.ClusterStatus; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.MediumTests; +import org.apache.hadoop.hbase.rest.HBaseRESTTestingUtility; +import org.apache.hadoop.hbase.rest.model.StorageClusterStatusModel; +import org.apache.hadoop.hbase.rest.model.TableModel; +import org.apache.hadoop.hbase.rest.model.VersionModel; +import org.apache.hadoop.hbase.util.Bytes; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -33,13 +42,15 @@ import org.junit.experimental.categories.Category; @Category(MediumTests.class) public class TestRemoteAdmin { - private static final String TABLE_1 = "TestRemoteAdmin_Table_1"; - private static final byte[] COLUMN_1 = Bytes.toBytes("a"); - static final HTableDescriptor DESC_1 = new HTableDescriptor(TABLE_1); private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); - private static final HBaseRESTTestingUtility REST_TEST_UTIL = + private static final HBaseRESTTestingUtility REST_TEST_UTIL = new HBaseRESTTestingUtility(); + private static final String TABLE_1 = "TestRemoteAdmin_Table_1"; + private static final String TABLE_2 = TABLE_1 + System.currentTimeMillis(); + private static final byte[] COLUMN_1 = Bytes.toBytes("a"); + static final HTableDescriptor DESC_1 = new HTableDescriptor(TABLE_1); + static final HTableDescriptor DESC_2 = new HTableDescriptor(TABLE_2); private static RemoteAdmin remoteAdmin; @BeforeClass @@ -69,5 +80,63 @@ public class TestRemoteAdmin { assertFalse(remoteAdmin.isTableAvailable(TABLE_1)); } -} + @Test + public void testGetRestVersion() throws Exception { + VersionModel RETURNED_REST_VERSION = remoteAdmin.getRestVersion(); + System.out.print("Returned version is: " + RETURNED_REST_VERSION); + + // Assert that it contains info about rest version, OS, JVM + assertTrue("Returned REST version did not contain info about rest.", + RETURNED_REST_VERSION.toString().contains("rest")); + assertTrue("Returned REST version did not contain info about the JVM.", + RETURNED_REST_VERSION.toString().contains("JVM")); + assertTrue("Returned REST version did not contain info about OS.", + RETURNED_REST_VERSION.toString().contains("OS")); + } + + @Test + public void testClusterVersion() throws Exception { + // testing the /version/cluster endpoint + final String HBASE_VERSION = TEST_UTIL.getHBaseCluster().getClusterStatus() + .getHBaseVersion(); + assertEquals("Cluster status from REST API did not match. ", HBASE_VERSION, + remoteAdmin.getClusterVersion().getVersion()); + } + + @Test + public void testClusterStatus() throws Exception { + + ClusterStatus status = TEST_UTIL.getHBaseClusterInterface() + .getClusterStatus(); + StorageClusterStatusModel returnedStatus = remoteAdmin.getClusterStatus(); + assertEquals( + "Region count from cluster status and returned status did not match up. ", + status.getRegionsCount(), returnedStatus.getRegions()); + assertEquals( + "Dead server count from cluster status and returned status did not match up. ", + status.getDeadServers(), returnedStatus.getDeadNodes().size()); + assertEquals( + "Number of requests from cluster status and returned status did not match up. ", + status.getRequestsCount(), returnedStatus.getRequests()); + } + + @Test + public void testListTables() throws Exception { + + remoteAdmin.createTable(DESC_2); + List tableList = remoteAdmin.getTableList().getTables(); + System.out.println("List of tables is: "); + boolean found = false; + for (TableModel tm : tableList) { + + if (tm.getName().equals(TABLE_2)) { + found = true; + break; + } + } + assertTrue("Table " + TABLE_2 + " was not found by get request to '/'", + found); + } + +}