001// Copyright 2008-2013, 2023 The Apache Software Foundation 002// 003// Licensed under the Apache License, Version 2.0 (the "License"); 004// you may not use this file except in compliance with the License. 005// You may obtain a copy of the License at 006// 007// http://www.apache.org/licenses/LICENSE-2.0 008// 009// Unless required by applicable law or agreed to in writing, software 010// distributed under the License is distributed on an "AS IS" BASIS, 011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012// See the License for the specific language governing permissions and 013// limitations under the License. 014 015package org.apache.tapestry5.commons.util; 016 017import java.lang.annotation.Annotation; 018 019import org.apache.tapestry5.commons.services.ClassPropertyAdapter; 020import org.apache.tapestry5.commons.services.PropertyAccess; 021 022/** 023 * Contains static methods useful for manipulating exceptions. 024 */ 025public class ExceptionUtils 026{ 027 /** 028 * Locates a particular type of exception, working its way via the cause property of each exception in the exception 029 * stack. 030 * 031 * @param t the outermost exception 032 * @param type the type of exception to search for 033 * @return the first exception of the given type, if found, or null 034 */ 035 public static <T extends Throwable> T findCause(Throwable t, Class<T> type) 036 { 037 Throwable current = t; 038 039 while (current != null) 040 { 041 if (type.isInstance(current)) 042 { 043 return type.cast(current); 044 } 045 046 // Not a match, work down. 047 048 current = current.getCause(); 049 } 050 051 return null; 052 } 053 054 /** 055 * Locates a particular type of exception, working its way down via any property that returns some type of Exception. 056 * This is more expensive, but more accurate, than {@link #findCause(Throwable, Class)} as it works with older exceptions 057 * that do not properly implement the (relatively new) {@linkplain Throwable#getCause() cause property}. 058 * 059 * @param t the outermost exception 060 * @param type the type of exception to search for 061 * @param access used to access properties 062 * @return the first exception of the given type, if found, or null 063 */ 064 public static <T extends Throwable> T findCause(Throwable t, Class<T> type, PropertyAccess access) 065 { 066 Throwable current = t; 067 068 while (current != null) 069 { 070 if (type.isInstance(current)) 071 { 072 return type.cast(current); 073 } 074 075 Throwable next = null; 076 077 ClassPropertyAdapter adapter = access.getAdapter(current); 078 079 for (String name : adapter.getPropertyNames()) 080 { 081 082 Object value = adapter.getPropertyAdapter(name).get(current); 083 084 if (value != null && value != current && value instanceof Throwable) 085 { 086 next = (Throwable) value; 087 break; 088 } 089 } 090 091 current = next; 092 } 093 094 095 return null; 096 } 097 098 /** 099 * Extracts the message from an exception. If the exception's message is null, returns the exceptions class name. 100 * 101 * @param exception 102 * to extract message from 103 * @return message or class name 104 * @since 5.4 105 */ 106 public static String toMessage(Throwable exception) 107 { 108 assert exception != null; 109 110 String message = exception.getMessage(); 111 112 if (message != null) 113 return message; 114 115 return exception.getClass().getName(); 116 } 117 118 /** 119 * Tells whether an exception annotated with a given annotation is found in the stack 120 * trace. 121 * @return <code>true</code> or <code>false</code> 122 * @since 5.8.3 123 */ 124 public static boolean isAnnotationInStackTrace(Throwable t, Class<? extends Annotation> annotationClass) 125 { 126 boolean answer = false; 127 Throwable current = t; 128 129 while (current != null) 130 { 131 if (current.getClass().isAnnotationPresent(annotationClass)) 132 { 133 answer = true; 134 break; 135 } 136 137 // Not a match, work down. 138 139 current = current.getCause(); 140 } 141 142 return answer; 143 } 144}