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