001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.common.utils; 018 019import java.io.*; 020 021/** 022 * Various utility methods for creating and working with throwables. 023 */ 024public class ThrowableUtils { 025 026 /** 027 * Interface used with {@link Utils#safeSupplier(SupplierWithThrowable)}. 028 */ 029 @FunctionalInterface 030 public interface SupplierWithThrowable<T> { 031 032 /** 033 * Gets a result. 034 * 035 * @return a result 036 * @throws Throwable if supplier threw an exception. 037 */ 038 T get() throws Throwable; 039 } 040 041 /** 042 * Creates a new {@link RuntimeException}. 043 * 044 * @param cause The caused-by exception. 045 * @return A new {@link RuntimeException}, or the same exception if it's already of that type. 046 */ 047 public static RuntimeException asRuntimeException(Throwable cause) { 048 return cast(RuntimeException.class, cause); 049 } 050 051 /** 052 * Casts or wraps the specified throwable to the specified type. 053 * 054 * @param <T> The class to cast to. 055 * @param type The class to cast to. 056 * @param t The throwable to cast. 057 * @return Either the same exception if it's already the specified type, or a wrapped exception. 058 */ 059 public static <T> T cast(Class<T> type, Throwable t) { 060 try { 061 return type.isInstance(t) ? type.cast(t) : type.getConstructor(Throwable.class).newInstance(t); 062 } catch (Exception e) { 063 throw new IllegalArgumentException(e); 064 } 065 } 066 067 /** 068 * Same as {@link Throwable#getCause()} but searches the throwable chain for an exception of the specified type. 069 * 070 * @param c The throwable type to search for. 071 * @param <T> The throwable type to search for. 072 * @param t The throwable to search. 073 * @return The exception, or <jk>null</jk> if not found. 074 */ 075 public static <T extends Throwable> T getCause(Class<T> c, Throwable t) { 076 while (t != null) { 077 t = t.getCause(); 078 if (c.isInstance(t)) 079 return c.cast(t); 080 } 081 return null; 082 } 083 084 /** 085 * Convenience method for getting a stack trace as a string. 086 * 087 * @param t The throwable to get the stack trace from. 088 * @return The same content that would normally be rendered via <c>t.printStackTrace()</c> 089 */ 090 public static String getStackTrace(Throwable t) { 091 var sw = new StringWriter(); 092 try (var pw = new PrintWriter(sw)) { 093 t.printStackTrace(pw); 094 } 095 return sw.toString(); 096 } 097 098 /** 099 * Calculates a 16-bit hash for the specified throwable based on it's stack trace. 100 * 101 * @param t The throwable to calculate the stack trace on. 102 * @param stopClass Optional stop class on which to stop calculation of a stack trace beyond when found. 103 * @return A calculated hash. 104 */ 105 public static int hash(Throwable t, String stopClass) { 106 var i = 0; 107 while (t != null) { 108 for (var e : t.getStackTrace()) { 109 if (e.getClassName().equals(stopClass)) 110 break; 111 if (e.getClassName().indexOf('$') == -1) 112 i ^= e.hashCode(); 113 } 114 t = t.getCause(); 115 } 116 return i; 117 } 118}