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.errorhandling; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertNotNull; 022import static org.junit.jupiter.api.Assertions.assertTrue; 023 024import java.io.IOException; 025import java.util.Objects; 026import org.apache.hadoop.hbase.testclassification.MasterTests; 027import org.apache.hadoop.hbase.testclassification.SmallTests; 028import org.junit.jupiter.api.Tag; 029import org.junit.jupiter.api.Test; 030 031/** 032 * Test that we correctly serialize exceptions from a remote source 033 */ 034@Tag(MasterTests.TAG) 035@Tag(SmallTests.TAG) 036public class TestForeignExceptionSerialization { 037 038 private static final String srcName = "someNode"; 039 040 /** 041 * Verify that we get back similar stack trace information before an after serialization. 042 */ 043 @Test 044 public void testSimpleException() throws IOException { 045 String data = "some bytes"; 046 ForeignException in = new ForeignException("SRC", new IllegalArgumentException(data)); 047 // check that we get the data back out 048 ForeignException e = ForeignException.deserialize(ForeignException.serialize(srcName, in)); 049 assertNotNull(e); 050 051 // now check that we get the right stack trace 052 StackTraceElement elem = new StackTraceElement(this.getClass().toString(), "method", "file", 1); 053 in.setStackTrace(new StackTraceElement[] { elem }); 054 e = ForeignException.deserialize(ForeignException.serialize(srcName, in)); 055 056 assertNotNull(e); 057 assertEquals(elem, e.getCause().getStackTrace()[0], "Stack trace got corrupted"); 058 assertEquals(1, e.getCause().getStackTrace().length, "Got an unexpectedly long stack trace"); 059 } 060 061 /** 062 * Compare that a generic exception's stack trace has the same stack trace elements after 063 * serialization and deserialization 064 */ 065 @Test 066 public void testRemoteFromLocal() throws IOException { 067 String errorMsg = "some message"; 068 Exception generic = new Exception(errorMsg); 069 generic.printStackTrace(); 070 assertTrue(generic.getMessage().contains(errorMsg)); 071 072 ForeignException e = ForeignException.deserialize(ForeignException.serialize(srcName, generic)); 073 074 // Workaround for java 11 - replaced assertArrayEquals with individual elements comparison 075 // using custom comparison helper method 076 assertEquals(generic.getStackTrace().length, e.getCause().getStackTrace().length, 077 "Stacktrace lengths don't match"); 078 for (int i = 0; i < generic.getStackTrace().length; i++) { 079 assertTrue( 080 compareStackTraceElement(generic.getStackTrace()[i], e.getCause().getStackTrace()[i]), 081 "Local stack trace got corrupted at " + i + "th index"); 082 } 083 084 e.printStackTrace(); // should have ForeignException and source node in it. 085 assertTrue(e.getCause().getCause() == null); 086 087 // verify that original error message is present in Foreign exception message 088 assertTrue(e.getCause().getMessage().contains(errorMsg)); 089 } 090 091 // Helper method to compare two stackTraceElements 092 private boolean compareStackTraceElement(StackTraceElement obj1, StackTraceElement obj2) { 093 return obj1.getClassName().equals(obj2.getClassName()) 094 && obj1.getLineNumber() == obj2.getLineNumber() 095 && Objects.equals(obj1.getMethodName(), obj2.getMethodName()) 096 && Objects.equals(obj1.getFileName(), obj2.getFileName()); 097 } 098}