001package io.avaje.inject.aop; 002 003import java.lang.reflect.Method; 004import java.util.Arrays; 005 006/** 007 * Method invocation using in {@link MethodInterceptor#invoke(Invocation)} for Aspects. 008 * <p> 009 * Represents a method invocation that can be intercepted with additional before and after 010 * invocation logic. 011 */ 012public interface Invocation { 013 014 /** 015 * Invoke the underlying method returning the result. 016 * 017 * @return The result of the method call. This will return null for void methods. 018 * @throws Throwable Exception thrown by underlying method 019 */ 020 Object invoke() throws Throwable; 021 022 /** 023 * Invoke the underlying method returning the result. Checked exceptions will be caught and 024 * rethrown as {@code InvocationException}s. 025 * 026 * @return The result of the method call. This will return null for void methods. 027 */ 028 default Object invokeUnchecked() { 029 try { 030 return invoke(); 031 } catch (final RuntimeException e) { 032 throw e; 033 } catch (final Throwable e) { 034 throw new InvocationException(e); 035 } 036 } 037 038 /** 039 * Set the result that will be returned to the caller. 040 * <p> 041 * This will replace a prior result set by calling {@code #invoke} or can be used 042 * to provide a result allowing to skip calling {@code #invoke} altogether. 043 * 044 * @param result The result that will be returned to the caller. 045 */ 046 void result(Object result); 047 048 /** 049 * Return the arguments used for this invocation. 050 */ 051 Object[] arguments(); 052 053 /** 054 * Return the arguments additionally appending the throwable. 055 */ 056 Object[] arguments(Throwable e); 057 058 /** 059 * Return the method being called for this invocation. 060 */ 061 Method method(); 062 063 /** 064 * Return the 'this' instance of the invocation. 065 * <p> 066 * This is typically used when invoking fallback/recovery methods. 067 */ 068 Object instance(); 069 070 /** 071 * Invocation base type for both callable and runnable methods. 072 * 073 * @param <T> The result type 074 */ 075 abstract class Base<T> implements Invocation { 076 077 protected Method method; 078 protected Object[] args; 079 protected Object instance; 080 protected T result; 081 082 /** 083 * Set the instance, method and arguments for the invocation. 084 */ 085 public Base<T> with(Object instance, Method method, Object... args) { 086 this.instance = instance; 087 this.method = method; 088 this.args = args; 089 return this; 090 } 091 092 @SuppressWarnings("unchecked") 093 @Override 094 public void result(Object result) { 095 this.result = (T) result; 096 } 097 098 /** 099 * Return the final invocation result. 100 */ 101 public T finalResult() { 102 return result; 103 } 104 105 @Override 106 public Object[] arguments() { 107 return args; 108 } 109 110 @Override 111 public Object[] arguments(Throwable e) { 112 if (args == null || args.length == 0) { 113 return new Object[]{e}; 114 } else { 115 Object[] newArgs = Arrays.copyOf(args, args.length + 1); 116 newArgs[args.length] = e; 117 return newArgs; 118 } 119 } 120 121 @Override 122 public Method method() { 123 return method; 124 } 125 126 @Override 127 public Object instance() { 128 return instance; 129 } 130 131 /** 132 * Wrap this invocation using a methodInterceptor returning the wrapped call. 133 * <p> 134 * This invocation is effectively nested inside the returned invocation. 135 * 136 * @param methodInterceptor The method interceptor to use to wrap this call with 137 * @return The wrapped call 138 */ 139 public abstract Base<T> wrap(MethodInterceptor methodInterceptor); 140 } 141 142 /** 143 * Runnable based Invocation. 144 */ 145 final class Run extends Base<Void> { 146 147 private final CheckedRunnable delegate; 148 149 /** 150 * Create with a given closure to run. 151 */ 152 public Run(CheckedRunnable delegate) { 153 this.delegate = delegate; 154 } 155 156 @Override 157 public Object invoke() throws Throwable { 158 delegate.invoke(); 159 return null; 160 } 161 162 @Override 163 public Base<Void> wrap(MethodInterceptor methodInterceptor) { 164 return new Invocation.Run(() -> methodInterceptor.invoke(this)) 165 .with(instance, method, args); 166 } 167 168 } 169 170 /** 171 * Callable based Invocation with checked exceptions. 172 */ 173 final class Call<T> extends Base<T> { 174 175 private final CheckedSupplier<T> delegate; 176 177 /** 178 * Create with a given supplier. 179 */ 180 public Call(CheckedSupplier<T> delegate) { 181 this.delegate = delegate; 182 } 183 184 @Override 185 public Object invoke() throws Throwable { 186 result = delegate.invoke(); 187 return result; 188 } 189 190 @Override 191 public T finalResult() { 192 return result; 193 } 194 195 @Override 196 public Base<T> wrap(MethodInterceptor methodInterceptor) { 197 return new Invocation.Call<T>(() -> { 198 final Call<T> delegate = this; 199 methodInterceptor.invoke(delegate); 200 return delegate.finalResult(); 201 }).with(instance, method, args); 202 } 203 } 204 205 /** 206 * Runnable with checked exceptions. 207 */ 208 @FunctionalInterface 209 interface CheckedRunnable { 210 211 /** 212 * Invoke the method. 213 */ 214 void invoke() throws Throwable; 215 } 216 217 /** 218 * Callable with checked exceptions. 219 * 220 * @param <T> The result type 221 */ 222 @FunctionalInterface 223 interface CheckedSupplier<T> { 224 225 /** 226 * Invoke the method returning the result. 227 */ 228 T invoke() throws Throwable; 229 } 230}