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.clientproxy.bytebuddy; 015 016import java.lang.invoke.MethodHandles; 017import java.lang.invoke.MethodHandles.Lookup; 018 019import java.util.Map; 020import java.util.Objects; 021 022import java.util.concurrent.ConcurrentHashMap; 023 024import java.util.function.Supplier; 025 026import net.bytebuddy.dynamic.DynamicType; 027 028import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; 029 030import net.bytebuddy.pool.TypePool; 031 032import org.microbean.bean.Id; 033 034import org.microbean.construct.Domain; 035 036import org.microbean.proxy.AbstractToolkitProxier; 037import org.microbean.proxy.Proxy; 038import org.microbean.proxy.ProxySpecification; 039 040import org.microbean.reference.ClientProxier; 041import org.microbean.reference.ReferenceException; 042 043import static java.lang.invoke.MethodType.methodType; 044 045/** 046 * An {@link AbstractToolkitProxier} and {@link ClientProxier} that uses <a href="https://bytebuddy.net/#/">Byte 047 * Buddy</a> to {@linkplain #generate(ProxySpecification) generate} {@linkplain org.microbean.proxy.Proxy client 048 * proxies}. 049 * 050 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 051 * 052 * @see BBClientProxyClassGenerator 053 */ 054public final class BBClientProxier extends AbstractToolkitProxier<ProxySpecification, DynamicType.Unloaded<?>> implements ClientProxier { 055 056 private static final Map<ProxySpecification, Object> clientProxyInstances = new ConcurrentHashMap<>(); 057 058 private final TypeDefinitions tds; 059 060 private final BBClientProxyClassGenerator g; 061 062 /** 063 * Creates a new {@link BBClientProxier}. 064 * 065 * @param domain a {@link Domain}; must not be {@code null} 066 * 067 * @exception NullPointerException if any argument is {@code null} 068 * 069 * @see TypeElementTypePool#TypeElementTypePool(Domain) 070 * 071 * @see #BBClientProxier(Domain, TypePool) 072 */ 073 public BBClientProxier(final Domain domain) { 074 this(domain, new TypeElementTypePool(domain)); 075 } 076 077 /** 078 * Creates a new {@link BBClientProxier}. 079 * 080 * @param domain a {@link Domain}; must not be {@code null} 081 * 082 * @param typePool a {@link TypePool}; must not be {@code null} 083 * 084 * @exception NullPointerException if any argument is {@code null} 085 * 086 * @see TypeDefinitions#TypeDefinitions(TypePool) 087 * 088 * @see BBClientProxyClassGenerator#BBClientProxyClassGenerator(TypePool) 089 * 090 * @see #BBClientProxier(Domain, TypeDefinitions, BBClientProxyClassGenerator) 091 */ 092 public BBClientProxier(final Domain domain, 093 final TypePool typePool) { 094 this(domain, new TypeDefinitions(typePool), new BBClientProxyClassGenerator(typePool)); 095 } 096 097 /** 098 * Creates a new {@link BBClientProxier}. 099 * 100 * @param domain a {@link Domain}; must not be {@code null} 101 * 102 * @param tds a {@link TypeDefinitions}; must not be {@code null} 103 * 104 * @param g a {@link BBClientProxyClassGenerator}; must not be {@code null} 105 * 106 * @exception NullPointerException if any argument is {@code null} 107 */ 108 public BBClientProxier(final Domain domain, 109 final TypeDefinitions tds, 110 final BBClientProxyClassGenerator g) { 111 super(domain, MethodHandles.lookup()); 112 this.tds = Objects.requireNonNull(tds, "tds"); 113 this.g = Objects.requireNonNull(g, "g"); 114 } 115 116 @Override // ClientProxier 117 public <R> R clientProxy(final Id id, final Supplier<? extends R> instanceSupplier) { 118 return this.proxy(new ProxySpecification(this.domain(), id), instanceSupplier).$cast(); 119 } 120 121 @Override // AbstractClientProxier<DynamicType.Unloaded<?>> 122 protected final DynamicType.Unloaded<?> generate(final ProxySpecification ps) { 123 return 124 this.g.generate(ps.name(), 125 this.tds.typeDescription(ps.superclass()), 126 ps.interfaces().stream().map(this.tds::typeDescriptionGeneric).toList()); 127 } 128 129 @Override // AbstractToolkitProxier<ProxySpecification, DynamicType.Unloaded<?>> 130 @SuppressWarnings("unchecked") 131 public final <R> Proxy<R> proxy(final ProxySpecification ps, final Supplier<? extends R> instanceSupplier) { 132 return (Proxy<R>)clientProxyInstances.computeIfAbsent(ps, ps0 -> { 133 try { 134 final Class<?> proxyClass = this.proxyClass(ps); 135 this.getClass().getModule().addReads(proxyClass.getModule()); 136 return 137 this.lookup(proxyClass).findConstructor(proxyClass, methodType(void.class, Supplier.class)) 138 .asType(methodType(Object.class, Supplier.class)) 139 .invokeExact(instanceSupplier); 140 } catch (final RuntimeException | Error e) { 141 throw e; 142 } catch (final Throwable e) { 143 throw new ReferenceException(e.getMessage(), e); 144 } 145 }); 146 } 147 148 149 @Override // AbstractClientProxier<DynamicType.Unloaded<?>> 150 protected final Class<?> proxyClass(final DynamicType.Unloaded<?> dtu, final ClassLoader cl) 151 throws ClassNotFoundException { 152 // getTypeName() invoked on a TypeDescription will be its binary name (required by Class#forName(String)): 153 // https://javadoc.io/static/net.bytebuddy/byte-buddy/1.17.3/net/bytebuddy/description/type/TypeDefinition.html#getTypeName-- 154 final String binaryName = dtu.getTypeDescription().getSuperClass().asErasure().getTypeName(); 155 final Class<?> superclass = Class.forName(binaryName, false, cl); 156 return dtu.load(superclass.getClassLoader(), ClassLoadingStrategy.UsingLookup.of(lookup(superclass))).getLoaded(); 157 } 158 159}