001package io.avaje.inject.aop; 002 003import java.lang.reflect.Method; 004import java.util.function.Consumer; 005import java.util.function.Function; 006 007/** 008 * Method invocation using in {@link MethodInterceptor#invoke(Invocation)} for Aspects. 009 * <p> 010 * Represents a method invocation that can be intercepted with additional before and after 011 * invocation logic. 012 */ 013public interface Invocation { 014 015 /** 016 * Invoke the underlying method returning the result. 017 * 018 * @return The result of the method call. This will return null for void methods. 019 * @throws Throwable Exception thrown by underlying method 020 */ 021 Object invoke() throws Throwable; 022 023 /** 024 * Invoke the underlying method returning the result. Checked exceptions will be caught and 025 * rethrown as {@code InvocationException}s. 026 * 027 * @return The result of the method call. This will return null for void methods. 028 */ 029 default Object invokeUnchecked() { 030 try { 031 return invoke(); 032 } catch (final RuntimeException e) { 033 throw e; 034 } catch (final Throwable e) { 035 throw new InvocationException(e); 036 } 037 } 038 039 /** 040 * Set the result that will be returned to the caller. 041 * <p> 042 * This will replace a prior result set by calling {@code #invoke} or can be used 043 * to provide a result allowing to skip calling {@code #invoke} altogether. 044 * 045 * @param result The result that will be returned to the caller. 046 */ 047 void result(Object result); 048 049 /** 050 * Return the arguments used for this invocation. 051 */ 052 Object[] arguments(); 053 054 /** 055 * Return the method being called for this invocation. 056 */ 057 Method method(); 058 059 /** 060 * Return the 'this' instance of the invocation. 061 * <p> 062 * This is typically used when invoking fallback/recovery methods. 063 */ 064 Object instance(); 065 066 /** 067 * Return whether this invocation has a registered recovery method 068 */ 069 boolean hasRecoveryMethod(); 070 071 /** 072 * Invoke the recovery method associated for this invocation and return the result. 073 * 074 * @return The result of the method call. This will return null for void methods. 075 * @throws IllegalStateException if no fallback method is configured with this invocation 076 */ 077 Object invokeRecoveryMethod(Throwable t); 078 079 /** 080 * Invocation base type for both callable and runnable methods. 081 * 082 * @param <T> The result type 083 */ 084 abstract class Base<T> implements Invocation { 085 086 protected Method method; 087 protected Object[] args; 088 protected Object instance; 089 protected T result; 090 091 /** 092 * Set the instance, method and arguments for the invocation. 093 */ 094 public Base<T> with(Object instance, Method method, Object... args) { 095 this.instance = instance; 096 this.method = method; 097 this.args = args; 098 return this; 099 } 100 101 @SuppressWarnings("unchecked") 102 @Override 103 public void result(Object result) { 104 this.result = (T) result; 105 } 106 107 /** 108 * Return the final invocation result. 109 */ 110 public T finalResult() { 111 return result; 112 } 113 114 @Override 115 public Object[] arguments() { 116 return args; 117 } 118 119 @Override 120 public Method method() { 121 return method; 122 } 123 124 @Override 125 public Object instance() { 126 return instance; 127 } 128 129 /** 130 * Wrap this invocation using a methodInterceptor returning the wrapped call. 131 * <p> 132 * This invocation is effectively nested inside the returned invocation. 133 * 134 * @param methodInterceptor The method interceptor to use to wrap this call with 135 * @return The wrapped call 136 */ 137 public abstract Base<T> wrap(MethodInterceptor methodInterceptor); 138 139 protected void noRecovery(Object recover) { 140 if (recover == null) { 141 throw new IllegalStateException("No recovery method available for this invocation"); 142 } 143 } 144 } 145 146 /** 147 * Runnable based Invocation. 148 */ 149 final class Run extends Base<Void> { 150 151 private final CheckedRunnable delegate; 152 private Consumer<Throwable> fallback; 153 154 /** 155 * Create with a given closure to run. 156 */ 157 public Run(CheckedRunnable delegate) { 158 this.delegate = delegate; 159 } 160 161 @Override 162 public Object invoke() throws Throwable { 163 delegate.invoke(); 164 return null; 165 } 166 167 /** 168 * Register a fallback method which can be used to recover from an exception 169 * while intercepting an invocation 170 */ 171 public Run fallback(Consumer<Throwable> fallback) { 172 this.fallback = fallback; 173 return this; 174 } 175 176 @Override 177 public Base<Void> wrap(MethodInterceptor methodInterceptor) { 178 return new Invocation.Run(() -> methodInterceptor.invoke(this)).with(instance, method, args); 179 } 180 181 @Override 182 public boolean hasRecoveryMethod() { 183 return fallback != null; 184 } 185 186 @Override 187 public Object invokeRecoveryMethod(Throwable t) { 188 noRecovery(fallback); 189 fallback.accept(t); 190 return null; 191 } 192 193 @Override 194 public Run with(Object instance, Method method, Object... args) { 195 super.with(instance, method, args); 196 return this; 197 } 198 } 199 200 /** 201 * Callable based Invocation with checked exceptions. 202 */ 203 final class Call<T> extends Base<T> { 204 205 private final CheckedSupplier<T> delegate; 206 private Function<Throwable, T> fallback; 207 208 /** 209 * Create with a given supplier. 210 */ 211 public Call(CheckedSupplier<T> delegate) { 212 this.delegate = delegate; 213 } 214 215 @Override 216 public Object invoke() throws Throwable { 217 result = delegate.invoke(); 218 return result; 219 } 220 221 @Override 222 public T finalResult() { 223 return result; 224 } 225 226 @Override 227 public Call<T> with(Object instance, Method method, Object... args) { 228 super.with(instance, method, args); 229 return this; 230 } 231 232 /** 233 * register a fallback method which can be used to recover from an exception while intercepting 234 * an invocation 235 */ 236 public Call<T> fallback(Function<Throwable, T> fallback) { 237 this.fallback = fallback; 238 return this; 239 } 240 241 @Override 242 public Base<T> wrap(MethodInterceptor methodInterceptor) { 243 return new Invocation.Call<>(() -> { 244 final Call<T> delegate = this; 245 methodInterceptor.invoke(delegate); 246 return delegate.finalResult(); 247 }).with(instance, method, args); 248 } 249 250 @Override 251 public boolean hasRecoveryMethod() { 252 return fallback != null; 253 } 254 255 @Override 256 public Object invokeRecoveryMethod(Throwable t) { 257 noRecovery(fallback); 258 var result = fallback.apply(t); 259 super.result(result); 260 return result; 261 } 262 } 263 264 /** 265 * Runnable with checked exceptions. 266 */ 267 @FunctionalInterface 268 interface CheckedRunnable { 269 270 /** 271 * Invoke the method. 272 */ 273 void invoke() throws Throwable; 274 } 275 276 /** 277 * Callable with checked exceptions. 278 * 279 * @param <T> The result type 280 */ 281 @FunctionalInterface 282 interface CheckedSupplier<T> { 283 284 /** 285 * Invoke the method returning the result. 286 */ 287 T invoke() throws Throwable; 288 } 289}