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.util; 019 020import java.io.IOException; 021import java.lang.reflect.Constructor; 022import java.lang.reflect.InvocationTargetException; 023import java.util.ArrayList; 024import java.util.List; 025 026import org.apache.yetus.audience.InterfaceAudience; 027import org.apache.hadoop.hbase.shaded.protobuf.generated.ErrorHandlingProtos.ForeignExceptionMessage; 028import org.apache.hadoop.hbase.shaded.protobuf.generated.ErrorHandlingProtos.GenericExceptionMessage; 029import org.apache.hadoop.hbase.shaded.protobuf.generated.ErrorHandlingProtos.StackTraceElementMessage; 030 031/** 032 * Helper to convert Exceptions and StackTraces from/to protobuf. 033 * (see ErrorHandling.proto for the internal of the proto messages) 034 */ 035@InterfaceAudience.Private 036public final class ForeignExceptionUtil { 037 private ForeignExceptionUtil() { } 038 039 public static Exception toException(final ForeignExceptionMessage eem) { 040 Exception re; 041 try { 042 re = createException(Exception.class, eem); 043 } catch (Throwable e) { 044 re = new Exception(eem.getGenericException().getMessage()); 045 } 046 return setExceptionDetails(re, eem); 047 } 048 049 public static IOException toIOException(final ForeignExceptionMessage eem) { 050 IOException re; 051 try { 052 re = createException(IOException.class, eem); 053 } catch (Throwable e) { 054 re = new IOException(eem.getGenericException().getMessage()); 055 } 056 return setExceptionDetails(re, eem); 057 } 058 059 private static <T extends Exception> T createException(final Class<T> clazz, 060 final ForeignExceptionMessage eem) throws ClassNotFoundException, NoSuchMethodException, 061 InstantiationException, IllegalAccessException, InvocationTargetException { 062 final GenericExceptionMessage gem = eem.getGenericException(); 063 final Class<?> realClass = Class.forName(gem.getClassName()); 064 final Class<? extends T> cls = realClass.asSubclass(clazz); 065 final Constructor<? extends T> cn = cls.getConstructor(String.class); 066 cn.setAccessible(true); 067 return cn.newInstance(gem.getMessage()); 068 } 069 070 private static <T extends Exception> T setExceptionDetails(final T exception, 071 final ForeignExceptionMessage eem) { 072 final GenericExceptionMessage gem = eem.getGenericException(); 073 final StackTraceElement[] trace = toStackTrace(gem.getTraceList()); 074 exception.setStackTrace(trace); 075 return exception; 076 } 077 078 public static ForeignExceptionMessage toProtoForeignException(final Throwable t) { 079 return toProtoForeignException(null, t); 080 } 081 082 public static ForeignExceptionMessage toProtoForeignException(String source, Throwable t) { 083 GenericExceptionMessage.Builder gemBuilder = GenericExceptionMessage.newBuilder(); 084 gemBuilder.setClassName(t.getClass().getName()); 085 if (t.getMessage() != null) { 086 gemBuilder.setMessage(t.getMessage()); 087 } 088 // set the stack trace, if there is one 089 List<StackTraceElementMessage> stack = toProtoStackTraceElement(t.getStackTrace()); 090 if (stack != null) { 091 gemBuilder.addAllTrace(stack); 092 } 093 GenericExceptionMessage payload = gemBuilder.build(); 094 ForeignExceptionMessage.Builder exception = ForeignExceptionMessage.newBuilder(); 095 exception.setGenericException(payload); 096 if (source != null) { 097 exception.setSource(source); 098 } 099 100 return exception.build(); 101 } 102 103 /** 104 * Convert a stack trace to list of {@link StackTraceElement}. 105 * @param trace the stack trace to convert to protobuf message 106 * @return <tt>null</tt> if the passed stack is <tt>null</tt>. 107 */ 108 public static List<StackTraceElementMessage> toProtoStackTraceElement(StackTraceElement[] trace) { 109 // if there is no stack trace, ignore it and just return the message 110 if (trace == null) { 111 return null; 112 } 113 114 // build the stack trace for the message 115 List<StackTraceElementMessage> pbTrace = new ArrayList<>(trace.length); 116 for (StackTraceElement elem : trace) { 117 StackTraceElementMessage.Builder stackBuilder = StackTraceElementMessage.newBuilder(); 118 stackBuilder.setDeclaringClass(elem.getClassName()); 119 if (elem.getFileName() != null) { 120 stackBuilder.setFileName(elem.getFileName()); 121 } 122 stackBuilder.setLineNumber(elem.getLineNumber()); 123 stackBuilder.setMethodName(elem.getMethodName()); 124 pbTrace.add(stackBuilder.build()); 125 } 126 return pbTrace; 127 } 128 129 /** 130 * Unwind a serialized array of {@link StackTraceElementMessage}s to a 131 * {@link StackTraceElement}s. 132 * @param traceList list that was serialized 133 * @return the deserialized list or <tt>null</tt> if it couldn't be unwound (e.g. wasn't set on 134 * the sender). 135 */ 136 public static StackTraceElement[] toStackTrace(List<StackTraceElementMessage> traceList) { 137 if (traceList == null || traceList.isEmpty()) { 138 return new StackTraceElement[0]; // empty array 139 } 140 StackTraceElement[] trace = new StackTraceElement[traceList.size()]; 141 for (int i = 0; i < traceList.size(); i++) { 142 StackTraceElementMessage elem = traceList.get(i); 143 trace[i] = new StackTraceElement( 144 elem.getDeclaringClass(), elem.getMethodName(), 145 elem.hasFileName() ? elem.getFileName() : null, 146 elem.getLineNumber()); 147 } 148 return trace; 149 } 150}