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.client;
019
020import java.io.IOException;
021import java.lang.reflect.InvocationTargetException;
022import java.lang.reflect.Method;
023import java.lang.reflect.UndeclaredThrowableException;
024import org.apache.commons.lang3.reflect.FieldUtils;
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.hbase.HBaseClassTestRule;
027import org.apache.hadoop.hbase.IntegrationTestingUtility;
028import org.apache.hadoop.hbase.security.UserProvider;
029import org.apache.hadoop.hbase.testclassification.ClientTests;
030import org.apache.hadoop.hbase.testclassification.MediumTests;
031import org.junit.AfterClass;
032import org.junit.Assert;
033import org.junit.BeforeClass;
034import org.junit.ClassRule;
035import org.junit.Test;
036import org.junit.experimental.categories.Category;
037import org.junit.runner.RunWith;
038import org.mockito.Mockito;
039import org.mockito.junit.MockitoJUnitRunner;
040
041@Category({ ClientTests.class, MediumTests.class })
042@RunWith(MockitoJUnitRunner.class)
043public class TestConnectionImplementationCacheMasterState {
044  @ClassRule
045  public static final HBaseClassTestRule CLASS_RULE =
046    HBaseClassTestRule.forClass(TestConnectionImplementationCacheMasterState.class);
047  private static final IntegrationTestingUtility TEST_UTIL = new IntegrationTestingUtility();
048
049  @BeforeClass
050  public static void beforeClass() throws Exception {
051    TEST_UTIL.startMiniCluster(1);
052  }
053
054  @AfterClass
055  public static void afterClass() throws Exception {
056    TEST_UTIL.shutdownMiniCluster();
057  }
058
059  @Test
060  public void testGetMaster_noCachedMasterState() throws IOException, IllegalAccessException {
061    Configuration conf = TEST_UTIL.getConfiguration();
062    conf.setLong(ConnectionImplementation.MASTER_STATE_CACHE_TIMEOUT_SEC, 0L);
063    ConnectionImplementation conn =
064      new ConnectionImplementation(conf, null, UserProvider.instantiate(conf).getCurrent());
065    ConnectionImplementation.MasterServiceState masterServiceState = spyMasterServiceState(conn);
066    conn.getMaster(); // This initializes the stubs but don't call isMasterRunning
067    conn.getMaster(); // Calls isMasterRunning since stubs are initialized. Invocation 1
068    conn.getMaster(); // Calls isMasterRunning since stubs are initialized. Invocation 2
069    Mockito.verify(masterServiceState, Mockito.times(2)).isMasterRunning();
070    conn.close();
071  }
072
073  @Test
074  public void testGetMaster_masterStateCacheHit() throws IOException, IllegalAccessException {
075    Configuration conf = TEST_UTIL.getConfiguration();
076    conf.setLong(ConnectionImplementation.MASTER_STATE_CACHE_TIMEOUT_SEC, 15L);
077    ConnectionImplementation conn =
078      new ConnectionImplementation(conf, null, UserProvider.instantiate(conf).getCurrent());
079    ConnectionImplementation.MasterServiceState masterServiceState = spyMasterServiceState(conn);
080    conn.getMaster(); // This initializes the stubs but don't call isMasterRunning
081    conn.getMaster(); // Uses cached value, don't call isMasterRunning
082    conn.getMaster(); // Uses cached value, don't call isMasterRunning
083    Mockito.verify(masterServiceState, Mockito.times(0)).isMasterRunning();
084    conn.close();
085  }
086
087  @Test
088  public void testGetMaster_masterStateCacheMiss()
089    throws IOException, InterruptedException, IllegalAccessException {
090    Configuration conf = TEST_UTIL.getConfiguration();
091    conf.setLong(ConnectionImplementation.MASTER_STATE_CACHE_TIMEOUT_SEC, 5L);
092    ConnectionImplementation conn =
093      new ConnectionImplementation(conf, null, UserProvider.instantiate(conf).getCurrent());
094    ConnectionImplementation.MasterServiceState masterServiceState = spyMasterServiceState(conn);
095    conn.getMaster(); // This initializes the stubs but don't call isMasterRunning
096    conn.getMaster(); // Uses cached value, don't call isMasterRunning
097    conn.getMaster(); // Uses cached value, don't call isMasterRunning
098    Thread.sleep(10000);
099    conn.getMaster(); // Calls isMasterRunning after cache expiry. Invocation 1
100    Mockito.verify(masterServiceState, Mockito.times(1)).isMasterRunning();
101    conn.close();
102  }
103
104  @Test
105  public void testIsKeepAliveMasterConnectedAndRunning_UndeclaredThrowableException()
106    throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
107    Configuration conf = TEST_UTIL.getConfiguration();
108    conf.setLong(ConnectionImplementation.MASTER_STATE_CACHE_TIMEOUT_SEC, 0);
109    ConnectionImplementation conn =
110      new ConnectionImplementation(conf, null, UserProvider.instantiate(conf).getCurrent());
111    conn.getMaster(); // Initializes stubs
112
113    ConnectionImplementation.MasterServiceState masterServiceState = spyMasterServiceState(conn);
114    Mockito.doThrow(new UndeclaredThrowableException(new Exception("DUMMY EXCEPTION")))
115      .when(masterServiceState).isMasterRunning();
116
117    // Verify that masterState is "false" because of to injected exception
118    boolean isKeepAliveMasterRunning =
119      (boolean) getIsKeepAliveMasterConnectedAndRunningMethod().invoke(conn);
120    Assert.assertFalse(isKeepAliveMasterRunning);
121    conn.close();
122  }
123
124  @Test
125  public void testIsKeepAliveMasterConnectedAndRunning_IOException()
126    throws IOException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
127    Configuration conf = TEST_UTIL.getConfiguration();
128    conf.setLong(ConnectionImplementation.MASTER_STATE_CACHE_TIMEOUT_SEC, 0);
129    ConnectionImplementation conn =
130      new ConnectionImplementation(conf, null, UserProvider.instantiate(conf).getCurrent());
131    conn.getMaster();
132
133    ConnectionImplementation.MasterServiceState masterServiceState = spyMasterServiceState(conn);
134    Mockito.doThrow(new IOException("DUMMY EXCEPTION")).when(masterServiceState).isMasterRunning();
135
136    boolean isKeepAliveMasterRunning =
137      (boolean) getIsKeepAliveMasterConnectedAndRunningMethod().invoke(conn);
138
139    // Verify that masterState is "false" because of to injected exception
140    Assert.assertFalse(isKeepAliveMasterRunning);
141    conn.close();
142  }
143
144  // Spy the masterServiceState object using reflection
145  private ConnectionImplementation.MasterServiceState
146    spyMasterServiceState(ConnectionImplementation conn) throws IllegalAccessException {
147    ConnectionImplementation.MasterServiceState spiedMasterServiceState =
148      Mockito.spy(conn.getMasterServiceState());
149    FieldUtils.writeDeclaredField(conn, "masterServiceState", spiedMasterServiceState, true);
150    return spiedMasterServiceState;
151  }
152
153  // Get isKeepAliveMasterConnectedAndRunning using reflection
154  private Method getIsKeepAliveMasterConnectedAndRunningMethod() throws NoSuchMethodException {
155    Method method =
156      ConnectionImplementation.class.getDeclaredMethod("isKeepAliveMasterConnectedAndRunning");
157    method.setAccessible(true);
158    return method;
159  }
160}