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.reference; 015 016import 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.Function; 028import java.util.function.Supplier; 029 030import javax.lang.model.type.DeclaredType; 031import javax.lang.model.type.TypeMirror; 032 033import javax.lang.model.element.TypeElement; 034 035import org.microbean.bean.Id; 036 037import org.microbean.construct.Domain; 038 039import org.microbean.proxy.AbstractReflectiveProxier; 040import org.microbean.proxy.Proxy; 041import org.microbean.proxy.ProxySpecification; 042 043import static java.lang.invoke.MethodHandles.publicLookup; 044 045import static java.lang.reflect.Proxy.newProxyInstance; 046 047/** 048 * An {@link AbstractReflectiveProxier} implementation that uses {@link java.lang.reflect.Proxy java.lang.reflect.Proxy} 049 * machinery. 050 * 051 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 052 */ 053public class ReflectiveClientProxier extends AbstractReflectiveProxier<ProxySpecification> implements ClientProxier { 054 055 private static final Lookup lookup = publicLookup(); 056 057 private static final Map<ProxySpecification, Object> proxyInstances = new ConcurrentHashMap<>(); 058 059 /** 060 * Creates a new {@link ReflectiveClientProxier}. 061 * 062 * @param domain a {@link Domain}; must not be {@code null} 063 * 064 * @exception NullPointerException if {@code domain} is {@code null} 065 */ 066 public ReflectiveClientProxier(final Domain domain) { 067 super(domain); 068 } 069 070 @Override // ClientProxier 071 public <R> R clientProxy(final Id id, final Supplier<? extends R> instanceSupplier) { 072 return this.proxy(new ProxySpecification(this.domain(), id), instanceSupplier).$cast(); 073 } 074 075 /** 076 * Returns a {@link Proxy} appropriate for the supplied {@linkplain ProxySpecification specification} and {@link 077 * Supplier} of contextual instances. 078 * 079 * @param <R> the contextual instance type 080 * 081 * @param ps an appropriate {@linkplain ProxySpecification proxy specification}; must not be {@code null} 082 * 083 * @param interfaces the interfaces to implement; every element is guaranteed to {@linkplain Class#isInterface() be an 084 * interface} 085 * 086 * @param instanceSupplier a {@link Supplier} of contextual instances; must not be {@code null}; may or may not create 087 * a new contextual instance each time it is invoked; may or may not be invoked multiple times depending on the 088 * subclass implementation 089 * 090 * @return a non-{@code null} {@link Proxy} 091 * 092 * @exception NullPointerException if any argument is {@code null} 093 */ 094 @Override // AbstractReflectiveProxier 095 @SuppressWarnings("unchecked") 096 protected final <R> Proxy<R> proxy(final ProxySpecification ps, 097 final Class<?>[] interfaces, 098 final Supplier<? extends R> instanceSupplier) { 099 return 100 (Proxy<R>)proxyInstances 101 .computeIfAbsent(ps, 102 ps0 -> newProxyInstance(this.classLoader(), 103 interfaces, 104 new InvocationHandler() { 105 @Override // InvocationHandler 106 public final Object invoke(final Object p, 107 final Method m, 108 final Object[] a) 109 throws Throwable { 110 return switch (m) { 111 case Method x when equalsMethod(x) -> p == a[0]; 112 case Method x when hashCodeMethod(x) -> System.identityHashCode(p); 113 default -> m.invoke(instanceSupplier.get(), a); 114 }; 115 } 116 })); 117 } 118 119}