1 /* 2 * Copyright (c) 2012-2024, jcabi.com 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 1) Redistributions of source code must retain the above 8 * copyright notice, this list of conditions and the following 9 * disclaimer. 2) Redistributions in binary form must reproduce the above 10 * copyright notice, this list of conditions and the following 11 * disclaimer in the documentation and/or other materials provided 12 * with the distribution. 3) Neither the name of the jcabi.com nor 13 * the names of its contributors may be used to endorse or promote 14 * products derived from this software without specific prior written 15 * permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 19 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 22 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 28 * OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 package com.jcabi.aspects.aj; 31 32 import com.jcabi.aspects.Immutable; 33 import com.jcabi.aspects.UnitedThrow; 34 import java.lang.reflect.Constructor; 35 import java.lang.reflect.Method; 36 import java.util.Objects; 37 import org.aspectj.lang.ProceedingJoinPoint; 38 import org.aspectj.lang.annotation.Around; 39 import org.aspectj.lang.annotation.Aspect; 40 import org.aspectj.lang.reflect.MethodSignature; 41 42 /** 43 * Throw single exception out of method. 44 * 45 * @since 0.13 46 * @checkstyle NonStaticMethodCheck (100 lines) 47 */ 48 @Aspect 49 @Immutable 50 public final class SingleException { 51 52 /** 53 * Catch all exceptions and throw a single selected exception. 54 * 55 * <p>Try NOT to change the signature of this method, in order to keep 56 * it backward compatible. 57 * 58 * @param point Joint point 59 * @return The result of call 60 * @throws Throwable If something goes wrong inside 61 */ 62 @Around 63 ( 64 // @checkstyle StringLiteralsConcatenation (2 lines) 65 "execution(* * (..))" 66 + " && @annotation(com.jcabi.aspects.UnitedThrow)" 67 ) 68 @SuppressWarnings({"PMD.AvoidCatchingThrowable", "PMD.PreserveStackTrace"}) 69 // @checkstyle IllegalThrowsCheck (1 line) 70 public Object wrap(final ProceedingJoinPoint point) throws Throwable { 71 final Method method = 72 ((MethodSignature) point.getSignature()).getMethod(); 73 final UnitedThrow annot = method.getAnnotation(UnitedThrow.class); 74 final Class<? extends Throwable> clz = SingleException.clazz( 75 method, 76 annot 77 ); 78 try { 79 return point.proceed(); 80 // @checkstyle IllegalCatch (1 line) 81 } catch (final Throwable ex) { 82 Throwable throwable = ex; 83 if (!clz.isAssignableFrom(ex.getClass())) { 84 if (SingleException.exists(clz)) { 85 throwable = clz.getConstructor(Throwable.class) 86 .newInstance(ex); 87 } else { 88 throwable = clz.getConstructor().newInstance(); 89 } 90 } 91 throw throwable; 92 } 93 } 94 95 /** 96 * Check if there is a constructor with single Throwable argument. 97 * @param clz Class to check. 98 * @return Whether constructor exists. 99 */ 100 private static boolean exists(final Class<? extends Throwable> clz) { 101 boolean found = false; 102 for (final Constructor<?> ctr : clz.getConstructors()) { 103 if (ctr.getParameterTypes().length == 1 104 && Objects.equals(ctr.getParameterTypes()[0], Throwable.class)) { 105 found = true; 106 break; 107 } 108 } 109 return found; 110 } 111 112 /** 113 * Get required exception class. 114 * @param method Method declaring exception. 115 * @param annot UnitedThrow annotation. 116 * @return Class of exception. 117 */ 118 @SuppressWarnings("unchecked") 119 private static Class<? extends Throwable> clazz(final Method method, 120 final UnitedThrow annot) { 121 Class<? extends Throwable> clz = annot.value(); 122 if (Objects.equals(clz, UnitedThrow.None.class)) { 123 if (method.getExceptionTypes().length == 0) { 124 clz = IllegalStateException.class; 125 } else { 126 clz = (Class<? extends Throwable>) method 127 .getExceptionTypes()[0]; 128 } 129 } 130 return clz; 131 } 132 }