001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–2025 microBean™. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 012 * specific language governing permissions and limitations under the License. 013 */ 014package org.microbean.interceptor; 015 016import java.lang.System.Logger; 017 018import java.lang.annotation.Annotation; 019 020import java.lang.invoke.MethodHandle; 021import java.lang.invoke.MethodHandles.Lookup; 022import java.lang.invoke.MethodType; 023import java.lang.invoke.VarHandle; 024 025import java.lang.reflect.Constructor; 026import java.lang.reflect.Method; 027 028import java.util.Arrays; 029import java.util.Collection; 030import java.util.Iterator; 031import java.util.List; 032import java.util.Map; 033import java.util.Objects; 034import java.util.Optional; 035import java.util.Set; 036 037import java.util.concurrent.ConcurrentHashMap; 038import java.util.concurrent.ConcurrentMap; 039 040import java.util.function.BiFunction; 041import java.util.function.Consumer; 042import java.util.function.Supplier; 043 044import jakarta.interceptor.InvocationContext; 045 046import static java.lang.System.getLogger; 047import static java.lang.System.Logger.Level.DEBUG; 048 049import static java.lang.invoke.MethodHandles.lookup; 050import static java.lang.invoke.MethodHandles.privateLookupIn; 051 052/** 053 * A utility class that makes {@link InterceptionFunction}s and {@link Runnable}s that intercept lifecycle events, 054 * constructions, and invocations of methods in accordance with the Jakarta Interceptors specification. 055 * 056 * @author <a href="https://about.me/lairdnelson/" target="_top">Laird Nelson</a> 057 */ 058public final class Interceptions { 059 060 061 /* 062 * Static fields. 063 */ 064 065 066 private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; 067 068 private static final Property<Object[]> EMPTY_OBJECT_ARRAY_PROPERTY = new Property<>(Interceptions::emptyObjectArray, false); 069 070 private static final Logger LOGGER = getLogger(Interceptions.class.getName()); 071 072 073 /* 074 * Instance fields. 075 */ 076 077 078 private final ConcurrentMap<String, Object> data; 079 080 private final Supplier<? extends Constructor<?>> constructorBootstrap; 081 082 private final Supplier<? extends Method> methodBootstrap; 083 084 private final Supplier<?> timerBootstrap; 085 086 private final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap; 087 088 089 /* 090 * Constructors. 091 */ 092 093 094 private Interceptions(final Supplier<? extends Constructor<?>> constructorBootstrap, 095 final Supplier<? extends Method> methodBootstrap, 096 final Supplier<?> timerBootstrap, 097 final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap) { 098 super(); 099 this.data = new ConcurrentHashMap<>(); 100 this.constructorBootstrap = constructorBootstrap == null ? Interceptions::returnNull : constructorBootstrap; 101 this.methodBootstrap = methodBootstrap == null ? Interceptions::returnNull : methodBootstrap; 102 this.timerBootstrap = timerBootstrap == null ? Interceptions::returnNull : timerBootstrap; 103 this.interceptorBindingsBootstrap = interceptorBindingsBootstrap == null ? Set::of : interceptorBindingsBootstrap; 104 } 105 106 107 /* 108 * Static methods. 109 */ 110 111 112 /** 113 * Returns a {@link Runnable} whose {@link Runnable#run() run()} method will invoke all supplied {@link 114 * InterceptorMethod}s in encounter order. 115 * 116 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} in which case the returned 117 * {@link Runnable}'s {@link Runnable#run() run()} method will do nothing 118 * 119 * @param targetBootstrap a {@link Supplier} that will be called for the initial value to be returned by the first 120 * invocation of {@link InvocationContext#getTarget()}; may be {@code null} in which case the value too will be {@code 121 * null} 122 * 123 * @return a {@link Runnable}; never {@code null} 124 */ 125 // Methodless lifecycle event interception. For example, post-construct interceptions by external interceptors only. 126 public static final Runnable ofLifecycleEvent(final Collection<? extends InterceptorMethod> interceptorMethods, 127 final Supplier<?> targetBootstrap) { 128 return ofLifecycleEvent(interceptorMethods, targetBootstrap, Set::of); 129 } 130 131 /** 132 * Returns a {@link Runnable} whose {@link Runnable#run() run()} method will invoke all supplied {@link 133 * InterceptorMethod}s in encounter order. 134 * 135 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} in which case the returned 136 * {@link Runnable}'s {@link Runnable#run() run()} method will do nothing 137 * 138 * @param targetBootstrap a {@link Supplier} that will be called for the initial value to be returned by the first 139 * invocation of {@link InvocationContext#getTarget()}; may be {@code null} in which case the value too will be {@code 140 * null} 141 * 142 * @param interceptorBindingsBootstrap a {@link Supplier} of a {@link Set} of {@link Annotation}s that will be called 143 * for the value to be returned by the first invocation of {@link InvocationContext#getInterceptorBindings()}; may be 144 * {@code null} in which case the value will be an {@linkplain Set#of() empty, immutable <code>Set</code>} 145 * 146 * @return a {@link Runnable}; never {@code null} 147 */ 148 // Methodless lifecycle event interception. For example, post-construct interceptions by external interceptors only. 149 public static final Runnable ofLifecycleEvent(final Collection<? extends InterceptorMethod> interceptorMethods, 150 final Supplier<?> targetBootstrap, 151 final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap) { 152 return () -> { 153 ff(interceptorMethods, 154 null, 155 targetBootstrap, 156 null, // argumentsValidator 157 false, // setTarget 158 null, // cb 159 null, // mb // TODO: may have to be the last im in the chain if it's defined on the target class (!); weird requirement 160 null, // tb 161 interceptorBindingsBootstrap) 162 .apply(EMPTY_OBJECT_ARRAY); 163 }; 164 } 165 166 /** 167 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 168 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 169 * Constructor}'s {@link Constructor#newInstance(Object...) newInstance(Object...)} method. 170 * 171 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 172 * 173 * @param lookup a {@link Lookup}; must not be {@code null} 174 * 175 * @param constructor the {@link Constructor} to invoke; may be {@code null} (rather uselessly) 176 * 177 * @return an {@link InterceptionFunction}; never {@code null} 178 * 179 * @exception NullPointerException if {@code lookup} is {@code null} 180 * 181 * @exception IllegalAccessException if {@linkplain Lookup#unreflectConstructor(Constructor) unreflecting} fails 182 */ 183 // Around-construct 184 public static final InterceptionFunction ofConstruction(final Collection<? extends InterceptorMethod> interceptorMethods, 185 final Lookup lookup, 186 final Constructor<?> constructor) 187 throws IllegalAccessException { 188 return ofConstruction(interceptorMethods, lookup, constructor, Set::of); 189 } 190 191 /** 192 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 193 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 194 * Constructor}'s {@link Constructor#newInstance(Object...) newInstance(Object...)} method. 195 * 196 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 197 * 198 * @param lookup a {@link Lookup}; must not be {@code null} 199 * 200 * @param constructor the {@link Constructor} to invoke; may be {@code null} (rather uselessly) 201 * 202 * @param interceptorBindingsBootstrap a {@link Supplier} of a {@link Set} of {@link Annotation}s that will be called 203 * for the value to be returned by the first invocation of {@link InvocationContext#getInterceptorBindings()}; may be 204 * {@code null} in which case the value will be an {@linkplain Set#of() empty, immutable <code>Set</code>} 205 * 206 * @return an {@link InterceptionFunction}; never {@code null} 207 * 208 * @exception NullPointerException if {@code lookup} is {@code null} 209 * 210 * @exception IllegalAccessException if {@linkplain Lookup#unreflectConstructor(Constructor) unreflecting} fails 211 */ 212 // Around-construct 213 public static final InterceptionFunction ofConstruction(final Collection<? extends InterceptorMethod> interceptorMethods, 214 final Lookup lookup, 215 final Constructor<?> constructor, 216 final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap) 217 throws IllegalAccessException { 218 return 219 ff(interceptorMethods, 220 terminalBiFunctionOf(lookup, constructor), 221 null, // targetBootstrap 222 a -> validate(constructor.getParameterTypes(), a), 223 true, // setTarget 224 () -> constructor, 225 null, // mb 226 null, // tb 227 interceptorBindingsBootstrap); 228 } 229 230 /** 231 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 232 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 233 * BiFunction}'s {@link BiFunction#apply(Object, Object) apply(Object, Object[])} method with {@code null} (the return 234 * value of {@link InvocationContext#getTarget()}, which will always be {@code null} in this scenario) and the return 235 * value of an invocation of {@link InvocationContext#getParameters()}. 236 * 237 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 238 * 239 * @param terminalBiFunction a {@link BiFunction} serving as a notional constructor that takes {@code null}, always, 240 * as its first argument, and the return value of an invocation of {@link InvocationContext#getParameters()} as its 241 * second argument, and returns a new instance; may be {@code null} (rather uselessly) 242 * 243 * @return an {@link InterceptionFunction}; never {@code null} 244 */ 245 // Around-construct 246 public static final InterceptionFunction ofConstruction(final Collection<? extends InterceptorMethod> interceptorMethods, 247 final BiFunction<? super Object, ? super Object[], ?> terminalBiFunction) { 248 return ofConstruction(interceptorMethods, terminalBiFunction, Set::of); 249 } 250 251 /** 252 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 253 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 254 * BiFunction}'s {@link BiFunction#apply(Object, Object) apply(Object Object[])} method with {@code null} (the return 255 * value of {@link InvocationContext#getTarget()}, which will always be {@code null} in this scenario) and the return 256 * value of an invocation of {@link InvocationContext#getParameters()}. 257 * 258 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 259 * 260 * @param terminalBiFunction a {@link BiFunction} serving as a notional constructor that takes {@code null}, always, 261 * as its first argument, and the return value of an invocation of {@link InvocationContext#getParameters()} as its 262 * second argument, and returns a new instance; may be {@code null} (rather uselessly) 263 * 264 * @param interceptorBindingsBootstrap a {@link Supplier} of a {@link Set} of {@link Annotation}s that will be called 265 * for the value to be returned by the first invocation of {@link InvocationContext#getInterceptorBindings()}; may be 266 * {@code null} in which case the value will be an {@linkplain Set#of() empty, immutable <code>Set</code>} 267 * 268 * @return an {@link InterceptionFunction}; never {@code null} 269 */ 270 // Around-construct 271 public static final InterceptionFunction ofConstruction(final Collection<? extends InterceptorMethod> interceptorMethods, 272 final BiFunction<? super Object, ? super Object[], ?> terminalBiFunction, 273 final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap) { 274 return 275 ff(interceptorMethods, 276 terminalBiFunction, 277 null, // targetBootstrap 278 null, // argumentsValidator 279 true, // setTarget 280 null, // cb 281 null, // mb 282 null, // tb 283 interceptorBindingsBootstrap); 284 } 285 286 /** 287 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 288 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 289 * Method}'s {@link Method#invoke(Object, Object...) invoke(Object, Object...)} method with the return value of {@link 290 * InvocationContext#getTarget()}, and with the return value of {@link InvocationContext#getParameters()}. 291 * 292 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 293 * 294 * @param lookup a {@link Lookup}; must not be {@code null} 295 * 296 * @param method a {@link Method} encapsulating the invocation to be intercepted whose {@link Method#invoke(Object, 297 * Object...) invoke(Object, Object...)} method takes the return value of {@link InvocationContext#getTarget()} as 298 * its first argument, and the return value of {@link InvocationContext#getParameters()} spread out appropriately as 299 * its trailing arguments; may be {@code null} (rather uselessly) 300 * 301 * @param targetBootstrap a {@link Supplier} that will be called for the initial value to be returned by the first 302 * invocation of {@link InvocationContext#getTarget()}; may be {@code null} in which case the value too will be {@code 303 * null} 304 * 305 * @return an {@link InterceptionFunction}; never {@code null} 306 * 307 * @exception NullPointerException if {@code lookup} is {@code null} 308 * 309 * @exception IllegalAccessException if {@linkplain Lookup#unreflect(Method) unreflecting} fails 310 */ 311 // Around-invoke or similar. 312 public static final InterceptionFunction ofInvocation(final Collection<? extends InterceptorMethod> interceptorMethods, 313 final Lookup lookup, 314 final Method method, // not nullable 315 final Supplier<?> targetBootstrap) 316 throws IllegalAccessException { 317 return ofInvocation(interceptorMethods, lookup, method, targetBootstrap, Set::of); 318 } 319 320 /** 321 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 322 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 323 * Method}'s {@link Method#invoke(Object, Object...) invoke(Object, Object...)} method with the return value of {@link 324 * InvocationContext#getTarget()}, and with the return value of {@link InvocationContext#getParameters()}. 325 * 326 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 327 * 328 * @param lookup a {@link Lookup}; must not be {@code null} 329 * 330 * @param method a {@link Method} encapsulating the invocation to be intercepted whose {@link Method#invoke(Object, 331 * Object...) invoke(Object, Object...)} method takes the return value of {@link InvocationContext#getTarget()} as 332 * its first argument, and the return value of {@link InvocationContext#getParameters()} spread out appropriately as 333 * its trailing arguments; may be {@code null} (rather uselessly) 334 * 335 * @param targetBootstrap a {@link Supplier} that will be called for the initial value to be returned by the first 336 * invocation of {@link InvocationContext#getTarget()}; may be {@code null} in which case the value too will be {@code 337 * null} 338 * 339 * @param interceptorBindingsBootstrap a {@link Supplier} of a {@link Set} of {@link Annotation}s that will be called 340 * for the value to be returned by the first invocation of {@link InvocationContext#getInterceptorBindings()}; may be 341 * {@code null} in which case the value will be an {@linkplain Set#of() empty, immutable <code>Set</code>} 342 * 343 * @return an {@link InterceptionFunction}; never {@code null} 344 * 345 * @exception NullPointerException if {@code lookup} is {@code null} 346 * 347 * @exception IllegalAccessException if {@linkplain Lookup#unreflect(Method) unreflecting} fails 348 */ 349 // Around-invoke or similar. 350 public static final InterceptionFunction ofInvocation(final Collection<? extends InterceptorMethod> interceptorMethods, 351 final Lookup lookup, 352 final Method method, // not nullable 353 final Supplier<?> targetBootstrap, 354 final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap) 355 throws IllegalAccessException { 356 return 357 ff(interceptorMethods, 358 method == null ? null : terminalBiFunctionOf(lookup, method), 359 targetBootstrap, 360 method == null ? null : a -> validate(method.getParameterTypes(), a), 361 false, // setTarget 362 null, // cb, 363 method == null ? null : () -> method, 364 null, // tb 365 interceptorBindingsBootstrap); 366 } 367 368 /** 369 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 370 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 371 * BiFunction}'s {@link BiFunction#apply(Object, Object) apply(Object Object[])} method with the return value of 372 * {@link InvocationContext#getTarget()}, and with the return value of {@link InvocationContext#getParameters()}. 373 * 374 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 375 * 376 * @param terminalBiFunction a {@link BiFunction} encapsulating the invocation to be intercepted that takes the return 377 * value of {@link InvocationContext#getTarget()} as its first argument, and the return value of {@link 378 * InvocationContext#getParameters()} as its second argument; may be {@code null} (rather uselessly) 379 * 380 * @param targetBootstrap a {@link Supplier} that will be called for the initial value to be returned by the first 381 * invocation of {@link InvocationContext#getTarget()}; may be {@code null} in which case the value too will be {@code 382 * null} 383 * 384 * @return an {@link InterceptionFunction}; never {@code null} 385 */ 386 // Around-invoke or similar. 387 public static final InterceptionFunction ofInvocation(final Collection<? extends InterceptorMethod> interceptorMethods, 388 final BiFunction<? super Object, ? super Object[], ?> terminalBiFunction, 389 final Supplier<?> targetBootstrap) { 390 return ofInvocation(interceptorMethods, terminalBiFunction, targetBootstrap, Set::of); 391 } 392 393 /** 394 * Returns an {@link InterceptionFunction} whose {@link InterceptionFunction#apply(Object...) apply(Object...)} method 395 * will invoke all supplied {@link InterceptorMethod}s in encounter order before invoking the supplied {@link 396 * BiFunction}'s {@link BiFunction#apply(Object, Object) apply(Object Object[])} method with the return value of 397 * {@link InvocationContext#getTarget()}, and with the return value of {@link InvocationContext#getParameters()}. 398 * 399 * @param interceptorMethods the {@link InterceptorMethod}s to invoke; may be {@code null} (rather uselessly) 400 * 401 * @param terminalBiFunction a {@link BiFunction} encapsulating the invocation to be intercepted that takes the return 402 * value of {@link InvocationContext#getTarget()} as its first argument, and the return value of {@link 403 * InvocationContext#getParameters()} as its second argument; may be {@code null} (rather uselessly) 404 * 405 * @param targetBootstrap a {@link Supplier} that will be called for the initial value to be returned by the first 406 * invocation of {@link InvocationContext#getTarget()}; may be {@code null} in which case the value too will be {@code 407 * null} 408 * 409 * @param interceptorBindingsBootstrap a {@link Supplier} of a {@link Set} of {@link Annotation}s that will be called 410 * for the value to be returned by the first invocation of {@link InvocationContext#getInterceptorBindings()}; may be 411 * {@code null} in which case the value will be an {@linkplain Set#of() empty, immutable <code>Set</code>} 412 * 413 * @return an {@link InterceptionFunction}; never {@code null} 414 */ 415 // Around-invoke or similar. 416 public static final InterceptionFunction ofInvocation(final Collection<? extends InterceptorMethod> interceptorMethods, 417 final BiFunction<? super Object, ? super Object[], ?> terminalBiFunction, 418 final Supplier<?> targetBootstrap, 419 final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap) { 420 return 421 ff(interceptorMethods, 422 terminalBiFunction, 423 targetBootstrap, 424 null, // argumentsValidator 425 false, // setTarget 426 null, // cb, 427 null, // mb, 428 null, // tb, 429 interceptorBindingsBootstrap); 430 } 431 432 // ff for function factory :-) 433 private static InterceptionFunction ff(final Collection<? extends InterceptorMethod> interceptorMethods, 434 final BiFunction<? super Object, ? super Object[], ?> tbf, 435 final Supplier<?> targetBootstrap, 436 final Consumer<? super Object[]> argumentsValidator, 437 final boolean setTarget, 438 final Supplier<? extends Constructor<?>> cb, 439 final Supplier<? extends Method> mb, 440 final Supplier<?> tb, 441 final Supplier<? extends Set<Annotation>> interceptorBindingsBootstrap) { 442 final Interceptions i; 443 final List<? extends InterceptorMethod> ims; 444 if (tbf == null) { 445 if (interceptorMethods == null || interceptorMethods.isEmpty()) { 446 return Interceptions::returnNull; 447 } 448 ims = List.copyOf(interceptorMethods); 449 // We can get away with hoisting this Property out of the function scope because we know it will never change. 450 final Property<Object> target = new Property<Object>(targetBootstrap, false); 451 i = new Interceptions(cb, mb, tb, interceptorBindingsBootstrap); 452 return a -> 453 i.new State(target) 454 .new Context(ims.iterator()) 455 .proceed(); 456 } else if (interceptorMethods == null || interceptorMethods.isEmpty()) { 457 final Property<Object> target = new Property<Object>(targetBootstrap, false); 458 return 459 argumentsValidator == null ? 460 Interceptions::returnNull : 461 a -> { 462 argumentsValidator.accept(a); 463 return tbf.apply(target.get(), a); 464 }; 465 } else { 466 ims = List.copyOf(interceptorMethods); 467 i = new Interceptions(cb, mb, tb, interceptorBindingsBootstrap); 468 if (setTarget) { 469 return a -> { 470 final State s = i.new State(new Property<>(targetBootstrap, true), 471 new Property<>(a == null ? Interceptions::emptyObjectArray : () -> a, 472 argumentsValidator, 473 true)); 474 final State.Context c = s 475 .new Context(ims.iterator(), 476 (t, a2) -> { 477 s.target(tbf.apply(t, a2)); 478 return s.target(); 479 }); 480 final Object v = c.proceed(); 481 final Object t = c.getTarget(); 482 if (v != t && LOGGER.isLoggable(DEBUG)) { 483 LOGGER.log(DEBUG, "around-construct proceed() return value: " + v + "; returning getTarget() return value: " + t); 484 } 485 return t; 486 }; 487 } 488 // We can get away with hoisting this Property out of the function scope because we know it will never change. 489 final Property<Object> target = new Property<Object>(targetBootstrap, false); 490 return a -> { 491 return 492 i.new State(target, new Property<Object[]>(a == null ? Interceptions::emptyObjectArray : () -> a, 493 argumentsValidator, 494 true)) 495 .new Context(ims.iterator(), tbf) 496 .proceed(); 497 }; 498 } 499 } 500 501 /** 502 * Creates and returns a {@link BiFunction} encapsulating the supplied {@link Constructor}. 503 * 504 * @param lookup a {@link Lookup}; must not be {@code null} 505 * 506 * @param c a {@link Constructor}; must not be {@code null} 507 * 508 * @return a {@link BiFunction} encapsulating the supplied {@link Constructor}; never {@code null} 509 * 510 * @exception NullPointerException if any argumebnt is {@code null} 511 * 512 * @exception IllegalAccessException if {@linkplain Lookup#unreflectConstructor(Constructor) unreflecting} fails 513 */ 514 public static final BiFunction<Object, Object[], Object> terminalBiFunctionOf(final Lookup lookup, final Constructor<?> c) 515 throws IllegalAccessException { 516 return terminalBiFunctionOf(privateLookupIn(c.getDeclaringClass(), lookup).unreflectConstructor(c)); 517 } 518 519 /** 520 * Creates and returns a {@link BiFunction} encapsulating the supplied {@link Method}. 521 * 522 * @param lookup a {@link Lookup}; must not be {@code null} 523 * 524 * @param m a {@link Method}; must not be {@code null} 525 * 526 * @return a {@link BiFunction} encapsulating the supplied {@link Method}; never {@code null} 527 * 528 * @exception NullPointerException if {@code m} is {@code null} 529 * 530 * @exception IllegalAccessException if {@linkplain Lookup#unreflect(Method) unreflecting} fails 531 */ 532 public static final BiFunction<Object, Object[], Object> terminalBiFunctionOf(final Lookup lookup, final Method m) 533 throws IllegalAccessException { 534 return terminalBiFunctionOf(privateLookupIn(m.getDeclaringClass(), lookup).unreflect(m)); 535 } 536 537 /** 538 * Creates and returns a {@link BiFunction} encapsulating the supplied {@link MethodHandle}. 539 * 540 * @param mh a {@link MethodHandle}; must not be {@code null} 541 * 542 * @return a {@link BiFunction} encapsulating the supplied {@link MethodHandle}; never {@code null} 543 * 544 * @exception NullPointerException if {@code mh} is {@code null} 545 */ 546 public static final BiFunction<Object, Object[], Object> terminalBiFunctionOf(MethodHandle mh) { 547 mh = mh.asType(mh.type().changeReturnType(Object.class)); 548 final MethodType mt = mh.type(); 549 final int pc = mt.parameterCount(); 550 final MethodHandle terminalBiFunction; 551 if (pc == 0) { 552 terminalBiFunction = mh; 553 return (t, a) -> { 554 try { 555 return terminalBiFunction.invokeExact(); 556 } catch (final RuntimeException | Error e) { 557 throw e; 558 } catch (final InterruptedException e) { 559 Thread.currentThread().interrupt(); 560 throw new IllegalStateException(e.getMessage(), e); 561 } catch (final Throwable e) { 562 throw new IllegalStateException(e.getMessage(), e); 563 } 564 }; 565 } else if (pc == 1) { 566 if (mt.parameterType(0) == Object[].class) { 567 terminalBiFunction = mh; // no need to spread 568 return (t, a) -> { 569 try { 570 return terminalBiFunction.invokeExact(a); 571 } catch (final RuntimeException | Error e) { 572 throw e; 573 } catch (final InterruptedException e) { 574 Thread.currentThread().interrupt(); 575 throw new IllegalStateException(e.getMessage(), e); 576 } catch (final Throwable e) { 577 throw new IllegalStateException(e.getMessage(), e); 578 } 579 }; 580 } 581 terminalBiFunction = mh.asType(mt.changeParameterType(0, Object.class)); 582 return (t, a) -> { 583 try { 584 return terminalBiFunction.invokeExact(t); 585 } catch (final RuntimeException | Error e) { 586 throw e; 587 } catch (final InterruptedException e) { 588 Thread.currentThread().interrupt(); 589 throw new IllegalStateException(e.getMessage(), e); 590 } catch (final Throwable e) { 591 throw new IllegalStateException(e.getMessage(), e); 592 } 593 }; 594 } 595 terminalBiFunction = mh.asType((mt.changeParameterType(0, Object.class))).asSpreader(Object[].class, pc - 1); 596 return (t, a) -> { 597 try { 598 return terminalBiFunction.invokeExact(t, a); 599 } catch (final RuntimeException | Error e) { 600 throw e; 601 } catch (final InterruptedException e) { 602 Thread.currentThread().interrupt(); 603 throw new IllegalStateException(e.getMessage(), e); 604 } catch (final Throwable e) { 605 throw new IllegalStateException(e.getMessage(), e); 606 } 607 }; 608 } 609 610 /** 611 * A convenience method that ensures that every element of the supplied {@code arguments} array can be assigned to a 612 * reference bearing the corresponding {@link Class} drawn from the supplied {@code parameterTypes} array. 613 * 614 * <p>Boxing, unboxing and widening conversions are taken into consideration.</p> 615 * 616 * <p>This method implements the logic implied, but nowhere actually specified, by the contract of {@link 617 * InvocationContext#setParameters(Object[])}.</p> 618 * 619 * @param parameterTypes an array of {@link Class} instances; may be {@code null}; must not contain {@code null} 620 * elements or {@code void.class}; must have a length equal to that of the supplied {@code arguments} array 621 * 622 * @param arguments an array of {@link Object}s; may be {@code null}; must have a length equal to that of the supplied 623 * {@code parameterTypes} array 624 * 625 * @exception IllegalArgumentException if validation fails, i.e. if the length of {@code parameterTypes} is not equal 626 * to the length of {@code arguments}, or if an element of {@code parameterTypes} is {@code null} or {@code 627 * void.class}, or if not every element of the supplied {@code arguments} array can be assigned to a reference bearing 628 * the corresponding {@link Class} drawn from the supplied {@code parameterTypes} array 629 */ 630 public static final void validate(final Class<?>[] parameterTypes, final Object[] arguments) { 631 final int parameterTypesLength = parameterTypes == null ? 0 : parameterTypes.length; 632 final int argumentsLength = arguments == null ? 0 : arguments.length; 633 if (argumentsLength != parameterTypesLength) { 634 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 635 "; arguments: " + Arrays.toString(arguments)); 636 } 637 for (int i = 0; i < argumentsLength; i++) { 638 final Class<?> parameterType = parameterTypes[i]; 639 if (parameterType == null || parameterType == void.class) { 640 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 641 "; arguments: " + Arrays.toString(arguments) + 642 "; parameter type: " + parameterType); 643 } 644 final Object argument = arguments[i]; 645 if (argument == null) { 646 if (parameterType.isPrimitive() || parameterType != Void.class) { 647 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 648 "; arguments: " + Arrays.toString(arguments) + 649 "; parameter type: " + parameterType.getName() + 650 "; argument: null"); 651 } 652 } else { 653 final Class<?> argumentType = argument.getClass(); 654 if (parameterType != argumentType) { 655 if (parameterType == boolean.class) { 656 if (argumentType != Boolean.class) { 657 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 658 "; arguments: " + Arrays.toString(arguments) + 659 "; parameter type: boolean" + 660 "; argument type: " + argumentType.getName()); 661 } 662 } else if (parameterType == Boolean.class) { 663 if (argumentType != boolean.class) { 664 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 665 "; arguments: " + Arrays.toString(arguments) + 666 "; parameter type: java.lang.Boolean" + 667 "; argument type: " + argumentType.getName()); 668 } 669 } else if (parameterType == byte.class) { 670 if (argumentType != Byte.class) { 671 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 672 "; arguments: " + Arrays.toString(arguments) + 673 "; parameter type: byte" + 674 "; argument type: " + argumentType.getName()); 675 } 676 } else if (parameterType == Byte.class) { 677 if (argumentType != byte.class) { 678 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 679 "; arguments: " + Arrays.toString(arguments) + 680 "; parameter type: java.lang.Byte" + 681 "; argument type: " + argumentType.getName()); 682 } 683 } else if (parameterType == char.class) { 684 if (argumentType != Character.class) { 685 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 686 "; arguments: " + Arrays.toString(arguments) + 687 "; parameter type: char" + 688 "; argument type: " + argumentType.getName()); 689 } 690 } else if (parameterType == Character.class) { 691 if (argumentType != char.class) { 692 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 693 "; arguments: " + Arrays.toString(arguments) + 694 "; parameter type: java.lang.Character" + 695 "; argument type: " + argumentType.getName()); 696 } 697 } else if (parameterType == double.class) { 698 if (argumentType != byte.class && 699 argumentType != char.class && 700 argumentType != Double.class && 701 argumentType != float.class && 702 argumentType != int.class && 703 argumentType != long.class && 704 argumentType != short.class) { 705 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 706 "; arguments: " + Arrays.toString(arguments) + 707 "; parameter type: double" + 708 "; argument type: " + argumentType.getName()); 709 } 710 } else if (parameterType == Double.class) { 711 if (argumentType != double.class) { 712 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 713 "; arguments: " + Arrays.toString(arguments) + 714 "; parameter type: java.lang.Double" + 715 "; argument type: " + argumentType.getName()); 716 } 717 } else if (parameterType == float.class) { 718 if (argumentType != byte.class && 719 argumentType != char.class && 720 argumentType != Float.class && 721 argumentType != int.class && 722 argumentType != long.class && 723 argumentType != short.class) { 724 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 725 "; arguments: " + Arrays.toString(arguments) + 726 "; parameter type: float" + 727 "; argument type: " + argumentType.getName()); 728 } 729 } else if (parameterType == Float.class) { 730 if (argumentType != float.class) { 731 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 732 "; arguments: " + Arrays.toString(arguments) + 733 "; parameter type: java.lang.Float" + 734 "; argument type: " + argumentType.getName()); 735 } 736 } else if (parameterType == int.class) { 737 if (argumentType != byte.class && 738 argumentType != char.class && 739 argumentType != Integer.class && 740 argumentType != short.class) { 741 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 742 "; arguments: " + Arrays.toString(arguments) + 743 "; parameter type: int; argument type: " + argumentType.getName()); 744 } 745 } else if (parameterType == Integer.class) { 746 if (argumentType != int.class) { 747 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 748 "; arguments: " + Arrays.toString(arguments) + 749 "; parameter type: java.lang.Integer" + 750 "; argument type: " + argumentType.getName()); 751 } 752 } else if (parameterType == long.class) { 753 if (argumentType != byte.class && 754 argumentType != char.class && 755 argumentType != int.class && 756 argumentType != Long.class && 757 argumentType != short.class) { 758 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 759 "; arguments: " + Arrays.toString(arguments) + 760 "; parameter type: long" + 761 "; argument type: " + argumentType.getName()); 762 } 763 } else if (parameterType == Long.class) { 764 if (argumentType != long.class) { 765 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 766 "; arguments: " + Arrays.toString(arguments) + 767 "; parameter type: java.lang.Long" + 768 "; argument type: " + argumentType.getName()); 769 } 770 } else if (parameterType == short.class) { 771 if (argumentType != byte.class && 772 argumentType != Short.class) { 773 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 774 "; arguments: " + Arrays.toString(arguments) + 775 "; parameter type: byte" + 776 "; argument type: " + argumentType.getName()); 777 } 778 } else if (parameterType == Short.class) { 779 if (argumentType != short.class) { 780 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 781 "; arguments: " + Arrays.toString(arguments) + 782 "; parameter type: java.lang.Short" + 783 "; argument type: " + argumentType.getName()); 784 } 785 } else if (parameterType == Void.class || !parameterType.isAssignableFrom(argumentType)) { 786 throw new IllegalArgumentException("parameter types: " + Arrays.toString(parameterTypes) + 787 "; arguments: " + Arrays.toString(arguments) + 788 "; parameter type: " + parameterType.getName() + 789 "; argument type: " + argumentType.getName()); 790 } 791 } 792 } 793 } 794 } 795 796 private static final Object[] emptyObjectArray() { 797 return EMPTY_OBJECT_ARRAY; 798 } 799 800 private static final <T> T returnNull() { 801 return null; 802 } 803 804 private static final <T, U> T returnNull(final U ignored) { 805 return null; 806 } 807 808 private static final <T> void throwIllegalStateException(final T ignored) { 809 throw new IllegalStateException(); 810 811 } 812 813 814 /* 815 * Inner and nested classes. 816 */ 817 818 819 private final class State { 820 821 822 /* 823 * Instance fields. 824 */ 825 826 827 private final Property<Object> target; 828 829 private final Property<Object[]> arguments; 830 831 832 /* 833 * Constructors. 834 */ 835 836 837 private State(final Property<Object> target) { 838 this(target, null); 839 } 840 841 private State(final Property<Object> target, 842 final Property<Object[]> arguments) { 843 super(); 844 this.target = Objects.requireNonNull(target, "target"); 845 this.arguments = arguments == null ? EMPTY_OBJECT_ARRAY_PROPERTY : arguments; 846 } 847 848 849 /* 850 * Instance methods. 851 */ 852 853 854 private final Object target() { 855 return this.target.get(); 856 } 857 858 private final void target(final Object target) { 859 this.target.accept(target); 860 } 861 862 private final Object[] arguments() { 863 final Object[] a = this.arguments.get(); 864 return a == null ? EMPTY_OBJECT_ARRAY : a.clone(); 865 } 866 867 private final void arguments(final Object[] a) { 868 this.arguments.accept(a == null ? EMPTY_OBJECT_ARRAY : a.clone()); 869 } 870 871 872 /* 873 * Inner and nested classes. 874 */ 875 876 877 private final class Context implements InvocationContext { 878 879 880 /* 881 * Instance fields. 882 */ 883 884 885 private final Supplier<?> proceeder; 886 887 888 /* 889 * Constructors. 890 */ 891 892 893 private Context(final Iterator<? extends InterceptorMethod> iterator) { 894 this(iterator, (Supplier<?>)Interceptions::returnNull); 895 } 896 897 private Context(final Iterator<? extends InterceptorMethod> iterator, 898 final BiFunction<? super Object, ? super Object[], ?> tbf) { 899 this(iterator, () -> tbf.apply(target(), arguments())); 900 } 901 902 private Context(final Iterator<? extends InterceptorMethod> iterator, 903 final Supplier<?> f) { 904 super(); 905 if (iterator == null || !iterator.hasNext()) { 906 this.proceeder = f == null ? Interceptions::returnNull : f; 907 } else { 908 final InterceptorMethod im = Objects.requireNonNull(iterator.next()); 909 final Context c = new Context(iterator, f); 910 this.proceeder = () -> { 911 try { 912 return im.intercept(c); 913 } catch (final RuntimeException e) { 914 throw e; 915 } catch (final InterruptedException e) { 916 Thread.currentThread().interrupt(); 917 throw new InterceptorException(e.getMessage(), e); 918 } catch (final Exception e) { 919 throw new InterceptorException(e.getMessage(), e); 920 } 921 }; 922 } 923 } 924 925 926 /* 927 * Instance methods. 928 */ 929 930 931 @Override // InvocationContext 932 public final Constructor<?> getConstructor() { 933 return constructorBootstrap.get(); 934 } 935 936 @Override // InvocationContext 937 public final Map<String, Object> getContextData() { 938 return data; 939 } 940 941 @Override // InvocationContext 942 public final Set<Annotation> getInterceptorBindings() { 943 return interceptorBindingsBootstrap.get(); 944 } 945 946 @Override // InvocationContext 947 public final Method getMethod() { 948 return methodBootstrap.get(); 949 } 950 951 @Override // InvocationContext 952 public final Object[] getParameters() { 953 return arguments(); 954 } 955 956 @Override // InvocationContext 957 public final Object getTarget() { 958 return target(); 959 } 960 961 @Override // InvocationContext 962 public final Object getTimer() { 963 return timerBootstrap.get(); 964 } 965 966 @Override // InvocationContext 967 public final Object proceed() { 968 return this.proceeder.get(); 969 } 970 971 @Override // InvocationContext 972 public final void setParameters(final Object[] arguments) { 973 arguments(arguments); 974 } 975 976 } 977 978 } 979 980 private static final class Property<T> implements Consumer<T>, Supplier<T> { 981 982 983 /* 984 * Static fields. 985 */ 986 987 988 private static final VarHandle VALUE; 989 990 static { 991 try { 992 VALUE = lookup().findVarHandle(Property.class, "value", Optional.class); 993 } catch (final IllegalAccessException | NoSuchFieldException e) { 994 throw (ExceptionInInitializerError)new ExceptionInInitializerError(e.getMessage()).initCause(e); 995 } 996 } 997 998 999 /* 1000 * Instance fields. 1001 */ 1002 1003 1004 // set only through/using VALUE 1005 private volatile Optional<T> value; 1006 1007 private final Supplier<T> reader; 1008 1009 private final Consumer<? super T> writer; 1010 1011 1012 /* 1013 * Constructors. 1014 */ 1015 1016 1017 private Property(final Supplier<? extends T> bootstrap, final boolean mutable) { 1018 this(bootstrap, null, mutable); 1019 } 1020 1021 private Property(final Supplier<? extends T> bootstrap, final Consumer<? super T> validator, final boolean mutable) { 1022 super(); 1023 if (bootstrap == null) { 1024 if (mutable) { 1025 VALUE.set(this, Optional.empty()); // no need for volatile semantics here 1026 this.reader = () -> ((Optional<T>)VALUE.getVolatile(this)).orElse(null); 1027 if (validator == null) { 1028 this.writer = v -> { 1029 VALUE.setVolatile(this, Optional.ofNullable(v)); 1030 }; 1031 } else { 1032 validator.accept(null); // make sure the Optional.empty() assignment above was OK 1033 this.writer = v -> { 1034 validator.accept(v); 1035 VALUE.setVolatile(this, Optional.ofNullable(v)); 1036 }; 1037 } 1038 } else { 1039 this.reader = Interceptions::returnNull; 1040 this.writer = Interceptions::throwIllegalStateException; 1041 } 1042 } else if (mutable) { 1043 if (validator == null) { 1044 this.reader = () -> { 1045 Optional<T> o = (Optional<T>)VALUE.getVolatile(this); 1046 if (o == null) { 1047 o = Optional.ofNullable(bootstrap.get()); 1048 if (!VALUE.compareAndSet(this, null, o)) { 1049 o = (Optional<T>)VALUE.getVolatile(this); 1050 } 1051 } 1052 return o.orElse(null); 1053 }; 1054 this.writer = v -> { 1055 VALUE.setVolatile(this, Optional.ofNullable(v)); 1056 }; 1057 } else { 1058 this.reader = () -> { 1059 Optional<T> o = (Optional<T>)VALUE.getVolatile(this); 1060 if (o == null) { 1061 final T v = bootstrap.get(); 1062 validator.accept(v); 1063 o = Optional.ofNullable(v); 1064 if (!VALUE.compareAndSet(this, null, o)) { 1065 o = (Optional<T>)VALUE.getVolatile(this); 1066 } 1067 } 1068 return o.orElse(null); 1069 }; 1070 this.writer = v -> { 1071 validator.accept(v); 1072 VALUE.setVolatile(this, Optional.ofNullable(v)); 1073 }; 1074 } 1075 } else { // !mutable 1076 this.writer = Interceptions::throwIllegalStateException; 1077 if (validator == null) { 1078 this.reader = () -> { 1079 Optional<T> o = (Optional<T>)VALUE.getVolatile(this); 1080 if (o == null) { 1081 o = Optional.ofNullable(bootstrap.get()); 1082 if (!VALUE.compareAndSet(this, null, o)) { 1083 o = (Optional<T>)VALUE.getVolatile(this); 1084 } 1085 } 1086 return o.orElse(null); 1087 }; 1088 } else { 1089 this.reader = () -> { 1090 Optional<T> o = (Optional<T>)VALUE.getVolatile(this); 1091 if (o == null) { 1092 final T v = bootstrap.get(); 1093 validator.accept(v); 1094 o = Optional.ofNullable(v); 1095 if (!VALUE.compareAndSet(this, null, o)) { 1096 o = (Optional<T>)VALUE.getVolatile(this); 1097 } 1098 } 1099 return o.orElse(null); 1100 }; 1101 } 1102 } 1103 } 1104 1105 1106 /* 1107 * Instance methods. 1108 */ 1109 1110 1111 @Override // Supplier<T> 1112 public final T get() { 1113 return this.reader.get(); 1114 } 1115 1116 @Override // Consumer<T> 1117 public final void accept(final T value) { 1118 this.writer.accept(value); 1119 } 1120 1121 @Override 1122 public final String toString() { 1123 return String.valueOf(this.get()); 1124 } 1125 1126 } 1127 1128}