001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.rest;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021import static org.junit.jupiter.api.Assertions.assertTrue;
022
023import java.io.ByteArrayInputStream;
024import java.io.IOException;
025import java.net.InetSocketAddress;
026import java.util.ArrayList;
027import java.util.Iterator;
028import java.util.List;
029import javax.xml.bind.JAXBContext;
030import javax.xml.bind.JAXBException;
031import org.apache.hadoop.hbase.CellUtil;
032import org.apache.hadoop.hbase.HBaseTestingUtil;
033import org.apache.hadoop.hbase.HRegionLocation;
034import org.apache.hadoop.hbase.ServerName;
035import org.apache.hadoop.hbase.TableName;
036import org.apache.hadoop.hbase.client.Connection;
037import org.apache.hadoop.hbase.client.Durability;
038import org.apache.hadoop.hbase.client.Put;
039import org.apache.hadoop.hbase.client.RegionInfo;
040import org.apache.hadoop.hbase.client.RegionLocator;
041import org.apache.hadoop.hbase.client.Table;
042import org.apache.hadoop.hbase.rest.client.Client;
043import org.apache.hadoop.hbase.rest.client.Cluster;
044import org.apache.hadoop.hbase.rest.client.Response;
045import org.apache.hadoop.hbase.rest.model.TableInfoModel;
046import org.apache.hadoop.hbase.rest.model.TableListModel;
047import org.apache.hadoop.hbase.rest.model.TableModel;
048import org.apache.hadoop.hbase.rest.model.TableRegionModel;
049import org.apache.hadoop.hbase.testclassification.MediumTests;
050import org.apache.hadoop.hbase.testclassification.RestTests;
051import org.apache.hadoop.hbase.util.Bytes;
052import org.junit.jupiter.api.AfterAll;
053import org.junit.jupiter.api.BeforeAll;
054import org.junit.jupiter.api.Tag;
055import org.junit.jupiter.api.Test;
056import org.slf4j.Logger;
057import org.slf4j.LoggerFactory;
058
059@Tag(RestTests.TAG)
060@Tag(MediumTests.TAG)
061public class TestTableResource {
062
063  private static final Logger LOG = LoggerFactory.getLogger(TestTableResource.class);
064
065  private static final TableName TABLE = TableName.valueOf("TestTableResource");
066  private static final String COLUMN_FAMILY = "test";
067  private static final String COLUMN = COLUMN_FAMILY + ":qualifier";
068  private static final int NUM_REGIONS = 4;
069  private static List<HRegionLocation> regionMap;
070
071  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
072  private static final HBaseRESTTestingUtility REST_TEST_UTIL = new HBaseRESTTestingUtility();
073  private static Client client;
074  private static JAXBContext context;
075
076  @BeforeAll
077  public static void setUpBeforeClass() throws Exception {
078    TEST_UTIL.startMiniCluster(3);
079    REST_TEST_UTIL.startServletContainer(TEST_UTIL.getConfiguration());
080    client = new Client(new Cluster().add("localhost", REST_TEST_UTIL.getServletPort()));
081    context = JAXBContext.newInstance(TableModel.class, TableInfoModel.class, TableListModel.class,
082      TableRegionModel.class);
083    TEST_UTIL.createMultiRegionTable(TABLE, Bytes.toBytes(COLUMN_FAMILY), NUM_REGIONS);
084    byte[] k = new byte[3];
085    byte[][] famAndQf = CellUtil.parseColumn(Bytes.toBytes(COLUMN));
086    List<Put> puts = new ArrayList<>();
087    for (byte b1 = 'a'; b1 < 'z'; b1++) {
088      for (byte b2 = 'a'; b2 < 'z'; b2++) {
089        for (byte b3 = 'a'; b3 < 'z'; b3++) {
090          k[0] = b1;
091          k[1] = b2;
092          k[2] = b3;
093          Put put = new Put(k);
094          put.setDurability(Durability.SKIP_WAL);
095          put.addColumn(famAndQf[0], famAndQf[1], k);
096          puts.add(put);
097        }
098      }
099    }
100
101    Connection connection = TEST_UTIL.getConnection();
102
103    Table table = connection.getTable(TABLE);
104    table.put(puts);
105    table.close();
106
107    RegionLocator regionLocator = connection.getRegionLocator(TABLE);
108    List<HRegionLocation> m = regionLocator.getAllRegionLocations();
109
110    // should have four regions now
111    assertEquals(NUM_REGIONS, m.size());
112    regionMap = m;
113    LOG.error("regions: {}", regionMap);
114    regionLocator.close();
115  }
116
117  @AfterAll
118  public static void tearDownAfterClass() throws Exception {
119    REST_TEST_UTIL.shutdownServletContainer();
120    TEST_UTIL.shutdownMiniCluster();
121  }
122
123  private static void checkTableList(TableListModel model) {
124    boolean found = false;
125    Iterator<TableModel> tables = model.getTables().iterator();
126    assertTrue(tables.hasNext());
127    while (tables.hasNext()) {
128      TableModel table = tables.next();
129      if (table.getName().equals(TABLE.getNameAsString())) {
130        found = true;
131        break;
132      }
133    }
134    assertTrue(found);
135  }
136
137  void checkTableInfo(TableInfoModel model) {
138    assertEquals(model.getName(), TABLE.getNameAsString());
139    Iterator<TableRegionModel> regions = model.getRegions().iterator();
140    assertTrue(regions.hasNext());
141    while (regions.hasNext()) {
142      TableRegionModel region = regions.next();
143      boolean found = false;
144      LOG.debug("looking for region {}", region.getName());
145      for (HRegionLocation e : regionMap) {
146        RegionInfo hri = e.getRegion();
147        // getRegionNameAsString uses Bytes.toStringBinary which escapes some non-printable
148        // characters
149        String hriRegionName = Bytes.toString(hri.getRegionName());
150        String regionName = region.getName();
151        LOG.debug("comparing to region {}", hriRegionName);
152        if (hriRegionName.equals(regionName)) {
153          found = true;
154          byte[] startKey = hri.getStartKey();
155          byte[] endKey = hri.getEndKey();
156          ServerName serverName = e.getServerName();
157          InetSocketAddress sa =
158            new InetSocketAddress(serverName.getHostname(), serverName.getPort());
159          String location = sa.getHostName() + ":" + Integer.valueOf(sa.getPort());
160          assertEquals(hri.getRegionId(), region.getId());
161          assertTrue(Bytes.equals(startKey, region.getStartKey()));
162          assertTrue(Bytes.equals(endKey, region.getEndKey()));
163          assertEquals(location, region.getLocation());
164          break;
165        }
166      }
167      assertTrue(found, "Couldn't find region " + region.getName());
168    }
169  }
170
171  @Test
172  public void testTableListText() throws IOException {
173    Response response = client.get("/", Constants.MIMETYPE_TEXT);
174    assertEquals(200, response.getCode());
175    assertEquals(Constants.MIMETYPE_TEXT, response.getHeader("content-type"));
176  }
177
178  @Test
179  public void testTableListXML() throws IOException, JAXBException {
180    Response response = client.get("/", Constants.MIMETYPE_XML);
181    assertEquals(200, response.getCode());
182    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
183    TableListModel model = (TableListModel) context.createUnmarshaller()
184      .unmarshal(new ByteArrayInputStream(response.getBody()));
185    checkTableList(model);
186  }
187
188  @Test
189  public void testTableListJSON() throws IOException {
190    Response response = client.get("/", Constants.MIMETYPE_JSON);
191    assertEquals(200, response.getCode());
192    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
193  }
194
195  @Test
196  public void testTableListPB() throws IOException, JAXBException {
197    Response response = client.get("/", Constants.MIMETYPE_PROTOBUF);
198    assertEquals(200, response.getCode());
199    assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
200    TableListModel model = new TableListModel();
201    model.getObjectFromMessage(response.getBody());
202    checkTableList(model);
203    response = client.get("/", Constants.MIMETYPE_PROTOBUF_IETF);
204    assertEquals(200, response.getCode());
205    assertEquals(Constants.MIMETYPE_PROTOBUF_IETF, response.getHeader("content-type"));
206    model = new TableListModel();
207    model.getObjectFromMessage(response.getBody());
208    checkTableList(model);
209  }
210
211  @Test
212  public void testTableInfoText() throws IOException {
213    Response response = client.get("/" + TABLE + "/regions", Constants.MIMETYPE_TEXT);
214    assertEquals(200, response.getCode());
215    assertEquals(Constants.MIMETYPE_TEXT, response.getHeader("content-type"));
216  }
217
218  @Test
219  public void testTableInfoXML() throws IOException, JAXBException {
220    Response response = client.get("/" + TABLE + "/regions", Constants.MIMETYPE_XML);
221    assertEquals(200, response.getCode());
222    assertEquals(Constants.MIMETYPE_XML, response.getHeader("content-type"));
223    TableInfoModel model = (TableInfoModel) context.createUnmarshaller()
224      .unmarshal(new ByteArrayInputStream(response.getBody()));
225    checkTableInfo(model);
226  }
227
228  @Test
229  public void testTableInfoJSON() throws IOException {
230    Response response = client.get("/" + TABLE + "/regions", Constants.MIMETYPE_JSON);
231    assertEquals(200, response.getCode());
232    assertEquals(Constants.MIMETYPE_JSON, response.getHeader("content-type"));
233  }
234
235  @Test
236  public void testTableInfoPB() throws IOException, JAXBException {
237    Response response = client.get("/" + TABLE + "/regions", Constants.MIMETYPE_PROTOBUF);
238    assertEquals(200, response.getCode());
239    assertEquals(Constants.MIMETYPE_PROTOBUF, response.getHeader("content-type"));
240    TableInfoModel model = new TableInfoModel();
241    model.getObjectFromMessage(response.getBody());
242    checkTableInfo(model);
243    response = client.get("/" + TABLE + "/regions", Constants.MIMETYPE_PROTOBUF_IETF);
244    assertEquals(200, response.getCode());
245    assertEquals(Constants.MIMETYPE_PROTOBUF_IETF, response.getHeader("content-type"));
246    model = new TableInfoModel();
247    model.getObjectFromMessage(response.getBody());
248    checkTableInfo(model);
249  }
250
251  @Test
252  public void testTableNotFound() throws IOException {
253    String notExistTable = "notexist";
254    Response response1 = client.get("/" + notExistTable + "/schema", Constants.MIMETYPE_JSON);
255    assertEquals(404, response1.getCode());
256    Response response2 = client.get("/" + notExistTable + "/regions", Constants.MIMETYPE_XML);
257    assertEquals(404, response2.getCode());
258  }
259}