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.ipc; 019 020import static org.hamcrest.CoreMatchers.instanceOf; 021import static org.hamcrest.MatcherAssert.assertThat; 022import static org.junit.Assert.assertTrue; 023 024import java.io.IOException; 025import java.lang.reflect.Constructor; 026import java.lang.reflect.InvocationTargetException; 027import java.util.ArrayList; 028import java.util.List; 029import java.util.concurrent.CompletableFuture; 030import java.util.concurrent.TimeoutException; 031import org.apache.commons.lang3.mutable.MutableInt; 032import org.apache.hadoop.hbase.HBaseClassTestRule; 033import org.apache.hadoop.hbase.client.RegionInfoBuilder; 034import org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil; 035import org.apache.hadoop.hbase.exceptions.TimeoutIOException; 036import org.apache.hadoop.hbase.net.Address; 037import org.apache.hadoop.hbase.testclassification.ClientTests; 038import org.apache.hadoop.hbase.testclassification.SmallTests; 039import org.apache.hadoop.hbase.util.FutureUtils; 040import org.junit.ClassRule; 041import org.junit.Test; 042import org.junit.experimental.categories.Category; 043 044import org.apache.hbase.thirdparty.io.netty.channel.DefaultEventLoop; 045import org.apache.hbase.thirdparty.io.netty.channel.EventLoop; 046 047@Category({ ClientTests.class, SmallTests.class }) 048public class TestIPCUtil { 049 050 @ClassRule 051 public static final HBaseClassTestRule CLASS_RULE = 052 HBaseClassTestRule.forClass(TestIPCUtil.class); 053 054 private static Throwable create(Class<? extends Throwable> clazz) throws InstantiationException, 055 IllegalAccessException, InvocationTargetException, NoSuchMethodException { 056 try { 057 Constructor<? extends Throwable> c = clazz.getDeclaredConstructor(); 058 c.setAccessible(true); 059 return c.newInstance(); 060 } catch (NoSuchMethodException e) { 061 // fall through 062 } 063 064 try { 065 Constructor<? extends Throwable> c = clazz.getDeclaredConstructor(String.class); 066 c.setAccessible(true); 067 return c.newInstance("error"); 068 } catch (NoSuchMethodException e) { 069 // fall through 070 } 071 072 try { 073 Constructor<? extends Throwable> c = clazz.getDeclaredConstructor(Throwable.class); 074 c.setAccessible(true); 075 return c.newInstance(new Exception("error")); 076 } catch (NoSuchMethodException e) { 077 // fall through 078 } 079 080 try { 081 Constructor<? extends Throwable> c = 082 clazz.getDeclaredConstructor(String.class, Throwable.class); 083 c.setAccessible(true); 084 return c.newInstance("error", new Exception("error")); 085 } catch (NoSuchMethodException e) { 086 // fall through 087 } 088 089 Constructor<? extends Throwable> c = 090 clazz.getDeclaredConstructor(Throwable.class, Throwable.class); 091 c.setAccessible(true); 092 return c.newInstance(new Exception("error"), "error"); 093 } 094 095 /** 096 * See HBASE-21862, it is important to keep original exception type for connection exceptions. 097 */ 098 @Test 099 public void testWrapConnectionException() throws Exception { 100 List<Throwable> exceptions = new ArrayList<>(); 101 for (Class<? extends Throwable> clazz : ClientExceptionsUtil.getConnectionExceptionTypes()) { 102 exceptions.add(create(clazz)); 103 } 104 Address addr = Address.fromParts("127.0.0.1", 12345); 105 for (Throwable exception : exceptions) { 106 if (exception instanceof TimeoutException) { 107 assertThat(IPCUtil.wrapException(addr, null, exception), 108 instanceOf(TimeoutIOException.class)); 109 } else { 110 IOException ioe = 111 IPCUtil.wrapException(addr, RegionInfoBuilder.FIRST_META_REGIONINFO, exception); 112 // Assert that the exception contains the Region name if supplied. HBASE-25735. 113 // Not all exceptions get the region stuffed into it. 114 if (ioe.getMessage() != null) { 115 assertTrue(ioe.getMessage() 116 .contains(RegionInfoBuilder.FIRST_META_REGIONINFO.getRegionNameAsString())); 117 } 118 assertThat(ioe, instanceOf(exception.getClass())); 119 } 120 } 121 } 122 123 @Test 124 public void testExecute() throws Exception { 125 EventLoop eventLoop = new DefaultEventLoop(); 126 MutableInt executed = new MutableInt(0); 127 MutableInt numStackTraceElements = new MutableInt(0); 128 CompletableFuture<Void> future = new CompletableFuture<>(); 129 try { 130 IPCUtil.execute(eventLoop, new Runnable() { 131 132 @Override 133 public void run() { 134 int numElements = new Exception().getStackTrace().length; 135 int depth = executed.getAndIncrement(); 136 if (depth <= IPCUtil.MAX_DEPTH) { 137 if (numElements <= numStackTraceElements.intValue()) { 138 future.completeExceptionally( 139 new AssertionError("should call run directly but stack trace decreased from " 140 + numStackTraceElements.intValue() + " to " + numElements)); 141 return; 142 } 143 numStackTraceElements.setValue(numElements); 144 IPCUtil.execute(eventLoop, this); 145 } else { 146 if (numElements >= numStackTraceElements.intValue()) { 147 future.completeExceptionally( 148 new AssertionError("should call eventLoop.execute to prevent stack overflow but" 149 + " stack trace increased from " + numStackTraceElements.intValue() + " to " 150 + numElements)); 151 } else { 152 future.complete(null); 153 } 154 } 155 } 156 }); 157 FutureUtils.get(future); 158 } finally { 159 eventLoop.shutdownGracefully().get(); 160 } 161 } 162}