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;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertNotNull;
022import static org.junit.Assert.assertNull;
023import static org.junit.Assert.assertTrue;
024
025import java.io.IOException;
026import java.util.ArrayList;
027import java.util.List;
028import java.util.NavigableMap;
029import org.apache.hadoop.hbase.client.ClusterConnection;
030import org.apache.hadoop.hbase.client.HConnectionTestingUtility;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.client.RegionInfoBuilder;
033import org.apache.hadoop.hbase.client.Result;
034import org.apache.hadoop.hbase.ipc.HBaseRpcController;
035import org.apache.hadoop.hbase.testclassification.MediumTests;
036import org.apache.hadoop.hbase.testclassification.MiscTests;
037import org.apache.hadoop.hbase.util.Bytes;
038import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
039import org.junit.After;
040import org.junit.Before;
041import org.junit.ClassRule;
042import org.junit.Test;
043import org.junit.experimental.categories.Category;
044import org.mockito.Mockito;
045import org.mockito.invocation.InvocationOnMock;
046import org.mockito.stubbing.Answer;
047import org.slf4j.Logger;
048import org.slf4j.LoggerFactory;
049
050import org.apache.hbase.thirdparty.com.google.protobuf.RpcController;
051import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException;
052
053import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos;
054import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanRequest;
055import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ScanResponse;
056
057/**
058 * Test MetaTableAccessor but without spinning up a cluster.
059 * We mock regionserver back and forth (we do spin up a zk cluster).
060 */
061@Category({MiscTests.class, MediumTests.class})
062public class TestMetaTableAccessorNoCluster {
063
064  @ClassRule
065  public static final HBaseClassTestRule CLASS_RULE =
066      HBaseClassTestRule.forClass(TestMetaTableAccessorNoCluster.class);
067
068  private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableAccessorNoCluster.class);
069  private static final  HBaseTestingUtility UTIL = new HBaseTestingUtility();
070  private static final Abortable ABORTABLE = new Abortable() {
071    boolean aborted = false;
072    @Override
073    public void abort(String why, Throwable e) {
074      LOG.info(why, e);
075      this.aborted = true;
076      throw new RuntimeException(e);
077    }
078    @Override
079    public boolean isAborted()  {
080      return this.aborted;
081    }
082  };
083
084  @Before
085  public void before() throws Exception {
086    UTIL.startMiniZKCluster();
087  }
088
089  @After
090  public void after() throws IOException {
091    UTIL.shutdownMiniZKCluster();
092  }
093
094  @Test
095  public void testGetHRegionInfo() throws IOException {
096    assertNull(MetaTableAccessor.getRegionInfo(new Result()));
097
098    List<Cell> kvs = new ArrayList<>();
099    Result r = Result.create(kvs);
100    assertNull(MetaTableAccessor.getRegionInfo(r));
101
102    byte [] f = HConstants.CATALOG_FAMILY;
103    // Make a key value that doesn't have the expected qualifier.
104    kvs.add(new KeyValue(HConstants.EMPTY_BYTE_ARRAY, f,
105      HConstants.SERVER_QUALIFIER, f));
106    r = Result.create(kvs);
107    assertNull(MetaTableAccessor.getRegionInfo(r));
108    // Make a key that does not have a regioninfo value.
109    kvs.add(new KeyValue(HConstants.EMPTY_BYTE_ARRAY, f,
110      HConstants.REGIONINFO_QUALIFIER, f));
111    RegionInfo hri = MetaTableAccessor.getRegionInfo(Result.create(kvs));
112    assertTrue(hri == null);
113    // OK, give it what it expects
114    kvs.clear();
115    kvs.add(new KeyValue(HConstants.EMPTY_BYTE_ARRAY, f,
116      HConstants.REGIONINFO_QUALIFIER, RegionInfo.toByteArray(RegionInfoBuilder.FIRST_META_REGIONINFO)));
117    hri = MetaTableAccessor.getRegionInfo(Result.create(kvs));
118    assertNotNull(hri);
119    assertTrue(RegionInfo.COMPARATOR.compare(hri, RegionInfoBuilder.FIRST_META_REGIONINFO) == 0);
120  }
121
122  /**
123   * Test that MetaTableAccessor will ride over server throwing
124   * "Server not running" IOEs.
125   * @see <a href="https://issues.apache.org/jira/browse/HBASE-3446">HBASE-3446</a>
126   * @throws IOException
127   * @throws InterruptedException
128   */
129  @Test
130  public void testRideOverServerNotRunning()
131      throws IOException, InterruptedException, ServiceException {
132    // Need a zk watcher.
133    ZKWatcher zkw = new ZKWatcher(UTIL.getConfiguration(),
134      this.getClass().getSimpleName(), ABORTABLE, true);
135    // This is a servername we use in a few places below.
136    ServerName sn = ServerName.valueOf("example.com", 1234, System.currentTimeMillis());
137
138    ClusterConnection connection = null;
139    try {
140      // Mock an ClientProtocol. Our mock implementation will fail a few
141      // times when we go to open a scanner.
142      final ClientProtos.ClientService.BlockingInterface implementation =
143        Mockito.mock(ClientProtos.ClientService.BlockingInterface.class);
144      // When scan called throw IOE 'Server not running' a few times
145      // before we return a scanner id.  Whats WEIRD is that these
146      // exceptions do not show in the log because they are caught and only
147      // printed if we FAIL.  We eventually succeed after retry so these don't
148      // show.  We will know if they happened or not because we will ask
149      // mockito at the end of this test to verify that scan was indeed
150      // called the wanted number of times.
151      List<Cell> kvs = new ArrayList<>();
152      final byte [] rowToVerify = Bytes.toBytes("rowToVerify");
153      kvs.add(new KeyValue(rowToVerify,
154        HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
155          RegionInfo.toByteArray(RegionInfoBuilder.FIRST_META_REGIONINFO)));
156      kvs.add(new KeyValue(rowToVerify,
157        HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
158        Bytes.toBytes(sn.getHostAndPort())));
159      kvs.add(new KeyValue(rowToVerify,
160        HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
161        Bytes.toBytes(sn.getStartcode())));
162      final List<CellScannable> cellScannables = new ArrayList<>(1);
163      cellScannables.add(Result.create(kvs));
164      final ScanResponse.Builder builder = ScanResponse.newBuilder();
165      for (CellScannable result : cellScannables) {
166        builder.addCellsPerResult(((Result)result).size());
167      }
168      Mockito.when(implementation.scan((RpcController) Mockito.any(), (ScanRequest) Mockito.any()))
169          .thenThrow(new ServiceException("Server not running (1 of 3)"))
170          .thenThrow(new ServiceException("Server not running (2 of 3)"))
171          .thenThrow(new ServiceException("Server not running (3 of 3)"))
172          .thenAnswer(new Answer<ScanResponse>() {
173            @Override
174            public ScanResponse answer(InvocationOnMock invocation) throws Throwable {
175              ((HBaseRpcController) invocation.getArgument(0)).setCellScanner(CellUtil
176                  .createCellScanner(cellScannables));
177              return builder.setScannerId(1234567890L).setMoreResults(false).build();
178            }
179          });
180      // Associate a spied-upon Connection with UTIL.getConfiguration.  Need
181      // to shove this in here first so it gets picked up all over; e.g. by
182      // HTable.
183      connection = HConnectionTestingUtility.getSpiedConnection(UTIL.getConfiguration());
184
185      // Fix the location lookup so it 'works' though no network.  First
186      // make an 'any location' object.
187      final HRegionLocation anyLocation =
188        new HRegionLocation(RegionInfoBuilder.FIRST_META_REGIONINFO, sn);
189      final RegionLocations rl = new RegionLocations(anyLocation);
190      // Return the RegionLocations object when locateRegion
191      // The ugly format below comes of 'Important gotcha on spying real objects!' from
192      // http://mockito.googlecode.com/svn/branches/1.6/javadoc/org/mockito/Mockito.html
193      Mockito.doReturn(rl).when
194      (connection).locateRegion((TableName)Mockito.any(), (byte[])Mockito.any(),
195              Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyInt());
196
197      // Now shove our HRI implementation into the spied-upon connection.
198      Mockito.doReturn(implementation).
199        when(connection).getClient(Mockito.any());
200
201      // Scan meta for user tables and verify we got back expected answer.
202      NavigableMap<RegionInfo, Result> hris =
203        MetaTableAccessor.getServerUserRegions(connection, sn);
204      assertEquals(1, hris.size());
205      assertTrue(RegionInfo.COMPARATOR.compare(hris.firstEntry().getKey(), RegionInfoBuilder.FIRST_META_REGIONINFO) == 0);
206      assertTrue(Bytes.equals(rowToVerify, hris.firstEntry().getValue().getRow()));
207      // Finally verify that scan was called four times -- three times
208      // with exception and then on 4th attempt we succeed
209      Mockito.verify(implementation, Mockito.times(4)).
210        scan((RpcController)Mockito.any(), (ScanRequest)Mockito.any());
211    } finally {
212      if (connection != null && !connection.isClosed()) connection.close();
213      zkw.close();
214    }
215  }
216}