1   /**
2    * Copyright The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.client;
22  
23  import java.io.IOException;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.conf.Configuration;
28  import org.apache.hadoop.hbase.HBaseTestingUtility;
29  import org.apache.hadoop.hbase.MediumTests;
30  import org.apache.hadoop.hbase.MultithreadedTestUtil;
31  import org.apache.hadoop.hbase.MultithreadedTestUtil.RepeatingTestThread;
32  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
33  import org.apache.hadoop.hbase.client.HConnectionManager.HConnectionImplementation;
34  import org.apache.hadoop.hbase.util.Bytes;
35  import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
36  import org.apache.zookeeper.KeeperException;
37  import org.junit.AfterClass;
38  import org.junit.BeforeClass;
39  import org.junit.Test;
40  import org.junit.experimental.categories.Category;
41  
42  /**
43   * Various tests of using HConnection
44   */
45  @Category(MediumTests.class)
46  public class TestHConnection {
47    final Log LOG = LogFactory.getLog(getClass());
48    private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
49    private final long MILLIS_TO_WAIT_FOR_RACE = 1000;
50  
51    /**
52     * @throws java.lang.Exception
53     */
54    @BeforeClass
55    public static void setUpBeforeClass() throws Exception {
56      TEST_UTIL.startMiniCluster();
57    }
58  
59    /**
60     * @throws java.lang.Exception
61     */
62    @AfterClass
63    public static void tearDownAfterClass() throws Exception {
64      TEST_UTIL.shutdownMiniCluster();
65    }
66  
67    /**
68     * Thread that periodically aborts the connection
69     */
70    class AbortThread extends RepeatingTestThread {
71      final HConnection connection;
72  
73      public AbortThread(MultithreadedTestUtil.TestContext ctx, HConnection connection) {
74        super(ctx);
75        this.connection = connection;
76      }
77  
78      @Override
79      public void doAnAction() throws IOException {
80        connection.abort("test session expired", new KeeperException.SessionExpiredException());
81      }
82    };
83  
84    class HConnectionRaceTester extends HConnectionImplementation {
85      public HConnectionRaceTester(Configuration configuration, boolean managed) throws ZooKeeperConnectionException {
86        super(configuration, managed);
87      }
88  
89      /**
90       * Sleep after calling getZookeeperWatcher to attempt to trigger a race condition.
91       * If the HConnection retrieves the ZooKeeperWatcher but does not cache the value,
92       * by the time the new watcher is used, it could be null if the connection was aborted.
93       * This sleep will increase the time between when the watcher is retrieved and when it is used.
94       */
95      public ZooKeeperWatcher getZooKeeperWatcher() throws ZooKeeperConnectionException {
96        ZooKeeperWatcher zkw = super.getZooKeeperWatcher();
97        try {
98          Thread.sleep(10);
99        } catch (InterruptedException ie) {
100         // Ignore
101       }
102       return zkw;
103     }
104   }
105 
106   /**
107    * Test that a connection that is aborted while calling isTableDisabled doesn't NPE
108    */
109   @Test
110   public void testTableDisabledRace() throws Exception {
111     final HConnection connection = new HConnectionRaceTester(TEST_UTIL.getConfiguration(), true);
112     MultithreadedTestUtil.TestContext ctx =
113       new MultithreadedTestUtil.TestContext(TEST_UTIL.getConfiguration());
114     RepeatingTestThread disabledChecker = new RepeatingTestThread(ctx) {
115       @Override
116       public void doAnAction() throws IOException {
117         try {
118           connection.isTableDisabled(Bytes.toBytes("tableToCheck"));
119         } catch (IOException ioe) {
120           // Ignore.  ZK can legitimately fail, only care if we get a NullPointerException
121         }
122       }
123     };
124     AbortThread abortThread = new AbortThread(ctx, connection);
125 
126     ctx.addThread(disabledChecker);
127     ctx.addThread(abortThread);
128     ctx.startThreads();
129     ctx.waitFor(MILLIS_TO_WAIT_FOR_RACE);
130     ctx.stop();
131   }
132 
133   /**
134    * Test that a connection that is aborted while calling getCurrentNrNRS doesn't NPE
135    */
136   @Test
137   public void testGetCurrentNrHRSRace() throws Exception {
138     final HConnection connection = new HConnectionRaceTester(TEST_UTIL.getConfiguration(), true);
139     MultithreadedTestUtil.TestContext ctx =
140       new MultithreadedTestUtil.TestContext(TEST_UTIL.getConfiguration());
141     RepeatingTestThread getCurrentNrHRSCaller = new RepeatingTestThread(ctx) {
142       @Override
143       public void doAnAction() throws IOException {
144         try {
145           connection.getCurrentNrHRS();
146         } catch (IOException ioe) {
147           // Ignore.  ZK can legitimately fail, only care if we get a NullPointerException
148         }
149       }
150     };
151     AbortThread abortThread = new AbortThread(ctx, connection);
152 
153     ctx.addThread(getCurrentNrHRSCaller);
154     ctx.addThread(abortThread);
155     ctx.startThreads();
156     ctx.waitFor(MILLIS_TO_WAIT_FOR_RACE);
157     ctx.stop();
158   }
159 
160   @org.junit.Rule
161   public org.apache.hadoop.hbase.ResourceCheckerJUnitRule cu =
162     new org.apache.hadoop.hbase.ResourceCheckerJUnitRule();
163 }