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 static org.apache.hadoop.hbase.TableName.META_TABLE_NAME; 021import static org.junit.jupiter.api.Assertions.assertInstanceOf; 022import static org.junit.jupiter.api.Assertions.assertNull; 023import static org.junit.jupiter.api.Assertions.assertThrows; 024import static org.mockito.ArgumentMatchers.any; 025import static org.mockito.Mockito.doAnswer; 026import static org.mockito.Mockito.mock; 027import static org.mockito.Mockito.mockStatic; 028import static org.mockito.Mockito.when; 029 030import java.util.List; 031import java.util.concurrent.CompletableFuture; 032import java.util.concurrent.ExecutionException; 033import java.util.concurrent.TimeUnit; 034import java.util.concurrent.atomic.AtomicReference; 035import org.apache.hadoop.hbase.ClientMetaTableAccessor; 036import org.apache.hadoop.hbase.HRegionLocation; 037import org.apache.hadoop.hbase.TableName; 038import org.apache.hadoop.hbase.TableNotFoundException; 039import org.apache.hadoop.hbase.testclassification.ClientTests; 040import org.apache.hadoop.hbase.testclassification.MediumTests; 041import org.apache.hadoop.hbase.util.FutureUtils; 042import org.junit.jupiter.api.AfterEach; 043import org.junit.jupiter.api.BeforeEach; 044import org.junit.jupiter.api.Tag; 045import org.junit.jupiter.api.Test; 046import org.mockito.MockedStatic; 047 048import org.apache.hbase.thirdparty.io.netty.util.HashedWheelTimer; 049 050@Tag(MediumTests.TAG) 051@Tag(ClientTests.TAG) 052public class TestCompactFromClient { 053 054 private org.apache.logging.log4j.core.Appender mockAppender; 055 056 @BeforeEach 057 public void setUp() { 058 mockAppender = mock(org.apache.logging.log4j.core.Appender.class); 059 when(mockAppender.getName()).thenReturn("mockAppender"); 060 when(mockAppender.isStarted()).thenReturn(true); 061 } 062 063 @AfterEach 064 public void tearDown() { 065 ((org.apache.logging.log4j.core.Logger) org.apache.logging.log4j.LogManager 066 .getLogger(FutureUtils.class)).removeAppender(mockAppender); 067 } 068 069 @Test 070 public void testCompactTableWithNullLocations() throws Exception { 071 AtomicReference<String> msg = new AtomicReference<>(); 072 AtomicReference<Throwable> throwable = new AtomicReference<>(); 073 074 ((org.apache.logging.log4j.core.Logger) org.apache.logging.log4j.LogManager 075 .getLogger(FutureUtils.class)).addAppender(mockAppender); 076 doAnswer(invocation -> { 077 org.apache.logging.log4j.core.LogEvent logEvent = 078 invocation.getArgument(0, org.apache.logging.log4j.core.LogEvent.class); 079 org.apache.logging.log4j.Level originalLevel = logEvent.getLevel(); 080 if (originalLevel.equals(org.apache.logging.log4j.Level.ERROR)) { 081 msg.set(logEvent.getMessage().getFormattedMessage()); 082 throwable.set(logEvent.getThrown()); 083 } 084 return null; 085 }).when(mockAppender).append(any(org.apache.logging.log4j.core.LogEvent.class)); 086 087 TableName tableName = TableName.valueOf("testCompactNullLocations"); 088 CompletableFuture<List<HRegionLocation>> nullLocationsFuture = 089 CompletableFuture.completedFuture(null); 090 091 try ( 092 MockedStatic<ClientMetaTableAccessor> mockedMeta = mockStatic(ClientMetaTableAccessor.class); 093 AsyncConnectionImpl connection = mock(AsyncConnectionImpl.class)) { 094 mockedMeta.when(() -> ClientMetaTableAccessor.getTableHRegionLocations(any(AsyncTable.class), 095 any(TableName.class))).thenReturn(nullLocationsFuture); 096 AsyncTable<AdvancedScanResultConsumer> metaTable = mock(AsyncTable.class); 097 when(connection.getTable(META_TABLE_NAME)).thenReturn(metaTable); 098 099 HashedWheelTimer hashedWheelTimer = mock(HashedWheelTimer.class); 100 AsyncAdminBuilderBase asyncAdminBuilderBase = mock(AsyncAdminBuilderBase.class); 101 RawAsyncHBaseAdmin admin = 102 new RawAsyncHBaseAdmin(connection, hashedWheelTimer, asyncAdminBuilderBase); 103 104 CompletableFuture<Void> future = admin.compact(tableName, CompactType.NORMAL); 105 // future.get() throws ExecutionException, and the cause is TableNotFoundException 106 ExecutionException ex = 107 assertThrows(ExecutionException.class, () -> future.get(1, TimeUnit.SECONDS)); 108 assertInstanceOf(TableNotFoundException.class, ex.getCause(), 109 "Expected TableNotFoundException as the cause of ExecutionException"); 110 111 assertNull(msg.get(), "No error message should be logged"); 112 assertNull(throwable.get(), "No Exception should be logged"); 113 } 114 } 115}