View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.client;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertNotNull;
22  
23  import java.io.IOException;
24  import java.util.concurrent.atomic.AtomicLong;
25  
26  import org.apache.hadoop.conf.Configuration;
27  import org.apache.hadoop.hbase.RegionLocations;
28  import org.apache.hadoop.hbase.TableName;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.HRegionInfo;
31  import org.apache.hadoop.hbase.HRegionLocation;
32  import org.apache.hadoop.hbase.ServerName;
33  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
34  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos;
35  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
36  import org.apache.hadoop.hbase.util.Threads;
37  import org.apache.hadoop.hbase.client.ConnectionManager.HConnectionImplementation;
38  import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
39  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
40  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
41  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
42  import org.apache.hadoop.hbase.zookeeper.RecoverableZooKeeper;
43  import org.mockito.Mockito;
44  import org.mockito.invocation.InvocationOnMock;
45  import org.mockito.stubbing.Answer;
46  
47  /**
48   * {@link ClusterConnection} testing utility.
49   */
50  public class HConnectionTestingUtility {
51    /*
52     * Not part of {@link HBaseTestingUtility} because this class is not
53     * in same package as {@link HConnection}.  Would have to reveal ugly
54     * {@link HConnectionManager} innards to HBaseTestingUtility to give it access.
55     */
56    /**
57     * Get a Mocked {@link HConnection} that goes with the passed <code>conf</code>
58     * configuration instance.  Minimally the mock will return
59     * <code>conf</conf> when {@link ClusterConnection#getConfiguration()} is invoked.
60     * Be sure to shutdown the connection when done by calling
61     * {@link HConnectionManager#deleteConnection(Configuration)} else it
62     * will stick around; this is probably not what you want.
63     * @param conf configuration
64     * @return HConnection object for <code>conf</code>
65     * @throws ZooKeeperConnectionException
66     */
67    public static ClusterConnection getMockedConnection(final Configuration conf)
68    throws ZooKeeperConnectionException {
69      HConnectionKey connectionKey = new HConnectionKey(conf);
70      synchronized (ConnectionManager.CONNECTION_INSTANCES) {
71        HConnectionImplementation connection =
72            ConnectionManager.CONNECTION_INSTANCES.get(connectionKey);
73        if (connection == null) {
74          connection = Mockito.mock(HConnectionImplementation.class);
75          Mockito.when(connection.getConfiguration()).thenReturn(conf);
76          Mockito.when(connection.getRpcControllerFactory()).thenReturn(
77          Mockito.mock(RpcControllerFactory.class));
78          // we need a real retrying caller
79          RpcRetryingCallerFactory callerFactory = new RpcRetryingCallerFactory(conf);
80          Mockito.when(connection.getRpcRetryingCallerFactory()).thenReturn(callerFactory);
81          ConnectionManager.CONNECTION_INSTANCES.put(connectionKey, connection);
82        }
83        return connection;
84      }
85    }
86  
87    /**
88     * @param connection
89     */
90    private static void mockRegionLocator(final HConnectionImplementation connection) {
91      try {
92        Mockito.when(connection.getRegionLocator(Mockito.any(TableName.class))).thenAnswer(
93            new Answer<RegionLocator>() {
94              @Override
95              public RegionLocator answer(InvocationOnMock invocation) throws Throwable {
96                TableName tableName = (TableName) invocation.getArguments()[0];
97                return new HRegionLocator(tableName, connection);
98              }
99            });
100     } catch (IOException e) {
101     }
102   }
103 
104   /**
105    * Calls {@link #getMockedConnection(Configuration)} and then mocks a few
106    * more of the popular {@link ClusterConnection} methods so they do 'normal'
107    * operation (see return doc below for list). Be sure to shutdown the
108    * connection when done by calling
109    * {@link HConnectionManager#deleteConnection(Configuration)} else it
110    * will stick around; this is probably not what you want.
111    *
112    * @param conf Configuration to use
113    * @param admin An AdminProtocol; can be null but is usually
114    * itself a mock.
115    * @param client A ClientProtocol; can be null but is usually
116    * itself a mock.
117    * @param sn ServerName to include in the region location returned by this
118    * <code>connection</code>
119    * @param hri HRegionInfo to include in the location returned when
120    * getRegionLocator is called on the mocked connection
121    * @return Mock up a connection that returns a {@link Configuration} when
122    * {@link ClusterConnection#getConfiguration()} is called, a 'location' when
123    * {@link ClusterConnection#getRegionLocation(org.apache.hadoop.hbase.TableName, byte[], boolean)}
124    * is called,
125    * and that returns the passed {@link AdminProtos.AdminService.BlockingInterface} instance when
126    * {@link ClusterConnection#getAdmin(ServerName)} is called, returns the passed
127    * {@link ClientProtos.ClientService.BlockingInterface} instance when
128    * {@link ClusterConnection#getClient(ServerName)} is called (Be sure to call
129    * {@link HConnectionManager#deleteConnection(Configuration)}
130    * when done with this mocked Connection.
131    * @throws IOException
132    */
133   public static ClusterConnection getMockedConnectionAndDecorate(final Configuration conf,
134       final AdminProtos.AdminService.BlockingInterface admin,
135       final ClientProtos.ClientService.BlockingInterface client,
136       final ServerName sn, final HRegionInfo hri)
137   throws IOException {
138     HConnectionImplementation c = Mockito.mock(HConnectionImplementation.class);
139     Mockito.when(c.getConfiguration()).thenReturn(conf);
140     ConnectionManager.CONNECTION_INSTANCES.put(new HConnectionKey(conf), c);
141     Mockito.doNothing().when(c).close();
142     // Make it so we return a particular location when asked.
143     final HRegionLocation loc = new HRegionLocation(hri, sn);
144     mockRegionLocator(c);
145     Mockito.when(c.getRegionLocation((TableName) Mockito.any(),
146         (byte[]) Mockito.any(), Mockito.anyBoolean())).
147       thenReturn(loc);
148     Mockito.when(c.locateRegion((TableName) Mockito.any(), (byte[]) Mockito.any())).
149       thenReturn(loc);
150     Mockito.when(c.locateRegion((TableName) Mockito.any(), (byte[]) Mockito.any(),
151         Mockito.anyBoolean(), Mockito.anyBoolean(),  Mockito.anyInt()))
152         .thenReturn(new RegionLocations(loc));
153     if (admin != null) {
154       // If a call to getAdmin, return this implementation.
155       Mockito.when(c.getAdmin(Mockito.any(ServerName.class))).
156         thenReturn(admin);
157     }
158     if (client != null) {
159       // If a call to getClient, return this client.
160       Mockito.when(c.getClient(Mockito.any(ServerName.class))).
161         thenReturn(client);
162     }
163     NonceGenerator ng = Mockito.mock(NonceGenerator.class);
164     Mockito.when(c.getNonceGenerator()).thenReturn(ng);
165     AsyncProcess asyncProcess =
166         new AsyncProcess(c, conf, null, RpcRetryingCallerFactory.instantiate(conf), false,
167           RpcControllerFactory.instantiate(conf), conf.getInt(HConstants.HBASE_RPC_TIMEOUT_KEY,
168             HConstants.DEFAULT_HBASE_RPC_TIMEOUT));
169     Mockito.when(c.getAsyncProcess()).thenReturn(asyncProcess);
170     Mockito.doNothing().when(c).incCount();
171     Mockito.doNothing().when(c).decCount();
172     Mockito.when(c.getNewRpcRetryingCallerFactory(conf)).thenReturn(
173         RpcRetryingCallerFactory.instantiate(conf,
174             RetryingCallerInterceptorFactory.NO_OP_INTERCEPTOR, null));
175     Mockito.when(c.getRpcControllerFactory()).thenReturn(Mockito.mock(RpcControllerFactory.class));
176     HTableInterface t = Mockito.mock(HTableInterface.class);
177     Mockito.when(c.getTable((TableName)Mockito.any())).thenReturn(t);
178     ResultScanner rs = Mockito.mock(ResultScanner.class);
179     Mockito.when(t.getScanner((Scan)Mockito.any())).thenReturn(rs);
180     return c;
181   }
182 
183   /**
184    * Get a Mockito spied-upon {@link ClusterConnection} that goes with the passed
185    * <code>conf</code> configuration instance.
186    * Be sure to shutdown the connection when done by calling
187    * {@link HConnectionManager#deleteConnection(Configuration)} else it
188    * will stick around; this is probably not what you want.
189    * @param conf configuration
190    * @return HConnection object for <code>conf</code>
191    * @throws ZooKeeperConnectionException
192    * @see @link
193    * {http://mockito.googlecode.com/svn/branches/1.6/javadoc/org/mockito/Mockito.html#spy(T)}
194    */
195   public static ClusterConnection getSpiedConnection(final Configuration conf)
196   throws IOException {
197     HConnectionKey connectionKey = new HConnectionKey(conf);
198     synchronized (ConnectionManager.CONNECTION_INSTANCES) {
199       HConnectionImplementation connection =
200           ConnectionManager.CONNECTION_INSTANCES.get(connectionKey);
201       if (connection == null) {
202         connection = Mockito.spy(new HConnectionImplementation(conf, true));
203         ConnectionManager.CONNECTION_INSTANCES.put(connectionKey, connection);
204       }
205       return connection;
206     }
207   }
208 
209   public static ClusterConnection getSpiedClusterConnection(final Configuration conf)
210   throws IOException {
211     HConnectionKey connectionKey = new HConnectionKey(conf);
212     synchronized (ConnectionManager.CONNECTION_INSTANCES) {
213       HConnectionImplementation connection =
214           ConnectionManager.CONNECTION_INSTANCES.get(connectionKey);
215       if (connection == null) {
216         connection = Mockito.spy(new HConnectionImplementation(conf, true));
217         ConnectionManager.CONNECTION_INSTANCES.put(connectionKey, connection);
218       }
219       return connection;
220     }
221   }
222 
223   /**
224    * @return Count of extant connection instances
225    */
226   public static int getConnectionCount() {
227     synchronized (ConnectionManager.CONNECTION_INSTANCES) {
228       return ConnectionManager.CONNECTION_INSTANCES.size();
229     }
230   }
231 
232   public static HConnectionImplementation requireHConnImpl(Connection conn) {
233     assertNotNull("Cannot operate on a null Connection", conn);
234     assertEquals("This method requires an HConnectionImplementation",
235         HConnectionImplementation.class, conn.getClass());
236     return (HConnectionImplementation) conn;
237   }
238 
239   public static RecoverableZooKeeper unwrapZK(Connection conn) throws IOException {
240     return requireHConnImpl(conn).getKeepAliveZooKeeperWatcher().getRecoverableZooKeeper();
241   }
242 
243   public static void clearRegionCache(Connection conn) throws IOException {
244     requireHConnImpl(conn).clearRegionCache();
245   }
246 
247   /**
248    * This coproceesor sleep 2s at first increment/append rpc call.
249    */
250   public static class SleepAtFirstRpcCall extends BaseRegionObserver {
251     static final AtomicLong ct = new AtomicLong(0);
252     static final String SLEEP_TIME_CONF_KEY =
253         "hbase.coprocessor.SleepAtFirstRpcCall.sleepTime";
254     static final long DEFAULT_SLEEP_TIME = 2000;
255     static final AtomicLong sleepTime = new AtomicLong(DEFAULT_SLEEP_TIME);
256 
257     public SleepAtFirstRpcCall() {
258     }
259 
260     @Override
261     public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
262       RegionCoprocessorEnvironment env = e.getEnvironment();
263       Configuration conf = env.getConfiguration();
264       sleepTime.set(conf.getLong(SLEEP_TIME_CONF_KEY, DEFAULT_SLEEP_TIME));
265     }
266 
267     @Override
268     public Result postIncrement(final ObserverContext<RegionCoprocessorEnvironment> e,
269         final Increment increment, final Result result) throws IOException {
270       if (ct.incrementAndGet() == 1) {
271         Threads.sleep(sleepTime.get());
272       }
273       return result;
274     }
275 
276     @Override
277     public Result postAppend(final ObserverContext<RegionCoprocessorEnvironment> e,
278         final Append append, final Result result) throws IOException {
279       if (ct.incrementAndGet() == 1) {
280         Threads.sleep(sleepTime.get());
281       }
282       return result;
283     }
284   }
285 
286 }