001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 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.producer; 015 016import static java.lang.invoke.MethodHandles.Lookup; 017 018import java.lang.reflect.InvocationHandler; 019import java.lang.reflect.Method; 020 021import java.util.List; 022import java.util.Map; 023import java.util.Objects; 024 025import java.util.concurrent.ConcurrentHashMap; 026 027import java.util.function.Supplier; 028 029import javax.lang.model.element.ExecutableElement; 030import javax.lang.model.element.TypeElement; 031 032import javax.lang.model.type.TypeMirror; 033 034import org.microbean.construct.Domain; 035 036import org.microbean.interceptor.InterceptionFunction; 037import org.microbean.interceptor.InterceptorMethod; 038 039import org.microbean.producer.InterceptionProxier.Specification; 040 041import org.microbean.proxy.AbstractReflectiveProxier; 042import org.microbean.proxy.Proxy; 043 044import static java.lang.System.identityHashCode; 045 046import static java.lang.invoke.MethodHandles.lookup; 047 048import static java.lang.reflect.InvocationHandler.invokeDefault; 049 050import static java.lang.reflect.Proxy.newProxyInstance; 051 052import static org.microbean.interceptor.Interceptions.ofInvocation; 053 054/** 055 * An {@link AbstractReflectiveProxier} implementation that uses the proxy features of the Java Development Kit. 056 * 057 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 058 */ 059public final class ReflectiveInterceptionProxier extends AbstractReflectiveProxier<Specification> { 060 061 private static final Lookup lookup = lookup(); 062 063 /** 064 * Creates a new {@link ReflectiveInterceptionProxier}. 065 * 066 * @param domain a {@link Domain}; must not be {@code null} 067 */ 068 public ReflectiveInterceptionProxier(final Domain domain) { 069 super(domain); 070 } 071 072 @Override // AbstractReflectiveProxier<Specification> 073 @SuppressWarnings("unchecked") 074 protected final <R> Proxy<R> proxy(final Specification ps, 075 final Class<?>[] interfaces, 076 final Supplier<? extends R> instanceSupplier) { 077 final Map<Method, ExecutableElement> ees = new ConcurrentHashMap<>(); 078 final Map<Method, InterceptionFunction> fs = new ConcurrentHashMap<>(); 079 final R instance = Objects.requireNonNull(instanceSupplier.get(), "instanceSupplier.get()"); 080 return 081 (Proxy<R>)newProxyInstance(this.classLoader(), interfaces, new InvocationHandler() { 082 @Override // InvocationHandler 083 public final Object invoke(final Object p, final Method m, final Object[] a) throws Throwable { 084 return switch (m) { 085 case null -> throw new NullPointerException("m"); 086 case Method x when equalsMethod(m) -> p == a[0]; 087 case Method x when hashCodeMethod(m) -> identityHashCode(p); 088 default -> { 089 final List<InterceptorMethod> interceptorMethods = 090 ps.interceptorMethods(ees.computeIfAbsent(m, ReflectiveInterceptionProxier.this::executableElement)); 091 if (interceptorMethods.isEmpty()) { 092 yield m.isDefault() ? invokeDefault(instance, m, a) : m.invoke(instance, a); 093 } 094 yield fs.computeIfAbsent(m, m0 -> { 095 try { 096 return ofInvocation(interceptorMethods, lookup, m0, () -> instance, null); 097 } catch (final IllegalAccessException e) { 098 throw new IllegalArgumentException("m0: " + m0, e); 099 } 100 }) 101 .apply(a); 102 } 103 }; 104 } 105 }); 106 } 107 108}