diff --git a/.github/workflows/mvn-release-prepare-perform.yaml b/.github/workflows/mvn-release-prepare-perform.yaml index c858555..968b79f 100644 --- a/.github/workflows/mvn-release-prepare-perform.yaml +++ b/.github/workflows/mvn-release-prepare-perform.yaml @@ -24,20 +24,20 @@ jobs: steps: - id: 'checkout' name: 'Step: Check Out Project' - uses: 'actions/checkout@v4' + uses: 'actions/checkout@v6' with: fetch-depth: 1 persist-credentials: false - id: 'setup-java' name: 'Step: Set Up Java and Maven' - uses: 'actions/setup-java@v4' + uses: 'actions/setup-java@v5' with: cache: 'maven' distribution: 'temurin' gpg-passphrase: 'GPG_PASSPHRASE' gpg-private-key: '${{ secrets.GPG_PRIVATE_KEY }}' - java-version: '23' - mvn-toolchain-id: 'Temurin 23' + java-version: '25' + mvn-toolchain-id: 'Temurin 25' mvn-toolchain-vendor: 'openjdk' # see ../../pom.xml server-id: 'sonatype-oss-repository-hosting' # see https://github.com/microbean/microbean-parent/blob/master/pom.xml#L38 server-password: 'SONATYPE_OSSRH_PASSWORD' diff --git a/.github/workflows/mvn-verify.yaml b/.github/workflows/mvn-verify.yaml index a414901..faefc80 100644 --- a/.github/workflows/mvn-verify.yaml +++ b/.github/workflows/mvn-verify.yaml @@ -12,18 +12,18 @@ jobs: steps: - id: 'checkout' name: 'Step: Checkout' - uses: 'actions/checkout@v4' + uses: 'actions/checkout@v6' with: fetch-depth: 1 persist-credentials: false - id: 'setup-java' name: 'Step: Set Up Java and Maven' - uses: 'actions/setup-java@v4' + uses: 'actions/setup-java@v5' with: cache: 'maven' distribution: 'temurin' - java-version: '23' - mvn-toolchain-id: 'Temurin 23' + java-version: '25' + mvn-toolchain-id: 'Temurin 25' mvn-toolchain-vendor: 'openjdk' # see ../../pom.xml - id: 'mvn-verify' name: 'Step: Maven Verify' diff --git a/README.md b/README.md index f23c5dd..0c4ac42 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ dependency: org.microbean microbean-proxy - 0.0.2 + 0.0.3 ``` diff --git a/pom.xml b/pom.xml index 8c3fdd3..fecd324 100644 --- a/pom.xml +++ b/pom.xml @@ -52,20 +52,11 @@ - - sonatype-oss-repository-hosting - - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - Github Pages microBean™ Proxy Site https://microbean.github.io/microbean-proxy/ - - sonatype-oss-repository-hosting - https://oss.sonatype.org/content/repositories/snapshots - @@ -89,7 +80,6 @@ deployment [maven-release-plugin] [skip ci] v@{project.version} - false @@ -101,12 +91,8 @@ true false - - - true - - https://oss.sonatype.org/ - 10 + + ${project.name} v${project.version} UTF8 @@ -125,7 +111,7 @@ org.junit junit-bom - 5.12.2 + 6.0.1 pom import @@ -135,19 +121,19 @@ org.microbean microbean-attributes - 0.0.2 + 0.0.5 org.microbean microbean-bean - 0.0.18 + 0.0.20 org.microbean microbean-construct - 0.0.10 + 0.0.18 @@ -194,11 +180,11 @@ maven-antrun-plugin - 3.1.0 + 3.2.0 maven-assembly-plugin - 3.7.1 + 3.8.0 maven-checkstyle-plugin @@ -308,13 +294,13 @@ com.puppycrawl.tools checkstyle - 10.23.1 + 12.1.2 maven-clean-plugin - 3.4.1 + 3.5.0 @@ -329,7 +315,7 @@ maven-compiler-plugin - 3.14.0 + 3.14.1 -Xlint:all @@ -339,7 +325,7 @@ maven-dependency-plugin - 3.8.1 + 3.9.0 maven-deploy-plugin @@ -347,12 +333,11 @@ maven-enforcer-plugin - 3.5.0 + 3.6.2 maven-gpg-plugin - - 3.2.7 + 3.2.8 maven-install-plugin @@ -360,11 +345,11 @@ maven-jar-plugin - 3.4.2 + 3.5.0 maven-javadoc-plugin - 3.11.2 + 3.12.0 true @@ -396,16 +381,15 @@ maven-release-plugin - - 3.1.1 + 3.3.1 maven-resources-plugin - 3.3.1 + 3.4.0 maven-scm-plugin - 2.1.0 + 2.2.1 maven-scm-publish-plugin @@ -417,7 +401,7 @@ maven-source-plugin - 3.3.1 + 3.4.0 attach-sources @@ -429,7 +413,7 @@ maven-surefire-plugin - 3.5.3 + 3.5.4 maven-toolchains-plugin @@ -438,35 +422,25 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.3.0 + 4.9.8.2 org.codehaus.mojo versions-maven-plugin - 2.18.0 + 2.20.1 io.smallrye jandex-maven-plugin - 3.3.0 + 3.5.3 - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 + org.sonatype.central + central-publishing-maven-plugin + 0.9.0 true - - - - com.thoughtworks.xstream - xstream - 1.4.20 - - - sonatype-oss-repository-hosting - ${nexusUrl} - ${autoReleaseAfterClose} + central.sonatype.com @@ -512,8 +486,8 @@ - org.sonatype.plugins - nexus-staging-maven-plugin + org.sonatype.central + central-publishing-maven-plugin diff --git a/src/main/java/org/microbean/proxy/AbstractProxier.java b/src/main/java/org/microbean/proxy/AbstractProxier.java index 3fc1b09..a0c8bd5 100644 --- a/src/main/java/org/microbean/proxy/AbstractProxier.java +++ b/src/main/java/org/microbean/proxy/AbstractProxier.java @@ -13,12 +13,12 @@ */ package org.microbean.proxy; -import java.util.Objects; - import java.util.function.Supplier; import org.microbean.construct.Domain; +import static java.util.Objects.requireNonNull; + /** * An abstract base class for subclassses that create {@linkplain Proxy proxies}. * @@ -29,8 +29,20 @@ public abstract sealed class AbstractProxier permits AbstractReflectiveProxier, AbstractToolkitProxier { + + /* + * Instance fields. + */ + + private final Domain domain; + + /* + * Constructors. + */ + + /** * Creates a new {@link AbstractProxier} implementation. * @@ -39,9 +51,15 @@ public abstract sealed class AbstractProxier * @exception NullPointerException if {@code domain} is {@code null} */ protected AbstractProxier(final Domain domain) { - this.domain = Objects.requireNonNull(domain, "domain"); + this.domain = requireNonNull(domain, "domain"); } + + /* + * Instance methods. + */ + + /** * Returns the {@link ClassLoader} for loading classes. * @@ -55,7 +73,7 @@ protected AbstractProxier(final Domain domain) { protected ClassLoader classLoader() { return Thread.currentThread().getContextClassLoader(); } - + /** * Returns the {@link Domain} supplied at construction time. * diff --git a/src/main/java/org/microbean/proxy/AbstractReflectiveProxier.java b/src/main/java/org/microbean/proxy/AbstractReflectiveProxier.java index 93dbf67..63c67fe 100644 --- a/src/main/java/org/microbean/proxy/AbstractReflectiveProxier.java +++ b/src/main/java/org/microbean/proxy/AbstractReflectiveProxier.java @@ -13,53 +13,45 @@ */ package org.microbean.proxy; -import java.lang.reflect.Constructor; -import java.lang.reflect.Executable; -import java.lang.reflect.GenericArrayType; -import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; -import java.lang.reflect.ParameterizedType; -import java.lang.reflect.Type; -import java.lang.reflect.TypeVariable; -import java.lang.reflect.WildcardType; import java.util.List; import java.util.function.Supplier; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.Parameterizable; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import java.util.function.Supplier; - import org.microbean.construct.Domain; /** * An {@link AbstractProxier} that helps subclasses create {@link Proxy proxies} using the {@link * java.lang.reflect.Proxy java.lang.reflect.Proxy} machinery present in the Java Development Kit. * - *

This class also contains various {@code protected} utility methods that help with converting reflective {@link - * Type}s and {@link Executable}s to their {@link TypeMirror} and {@link ExecutableElement} counterparts.

- * * @param the {@link ProxySpecification} type * * @author Laird Nelson * * @see #proxy(ProxySpecification, Supplier) - * - * @see #type(Type) - * - * @see #executableElement(Executable) */ public abstract non-sealed class AbstractReflectiveProxier extends AbstractProxier { + + /* + * Static fields. + */ + + private static final TypeMirror[] EMPTY_TYPE_MIRROR_ARRAY = new TypeMirror[0]; + + /* + * Constructors. + */ + + /** * Creates a new {@link AbstractReflectiveProxier} implementation. * @@ -71,6 +63,12 @@ protected AbstractReflectiveProxier(final Domain domain) { super(domain); } + + /* + * Instance methods. + */ + + /** * Returns {@code true} if the supplied {@link Method} is the {@link Object#equals(Object) * java.lang.Object#equals(java.lang.Object)} method. @@ -82,6 +80,7 @@ protected AbstractReflectiveProxier(final Domain domain) { * * @exception NullPointerException if {@code m} is {@code null} */ + // (Convenience.) protected final boolean equalsMethod(final Method m) { return m.getDeclaringClass() == Object.class && @@ -91,35 +90,6 @@ protected final boolean equalsMethod(final Method m) { m.getName().equals("equals"); } - /** - * Returns an {@link ExecutableElement} corresponding to the supplied {@link Executable}. - * - * @param e an {@link Executable}; must not be {@code null} - * - * @return an {@link ExecutableElement} corresponding to the supplied {@link Executable}; never {@code null} - * - * @exception NullPointerException if {@code e} is {@code null} - * - * @exception IllegalArgumentException if somehow {@code e} is neither a {@link Constructor} nor a {@link Method} - */ - protected final ExecutableElement executableElement(final Executable e) { - final Domain domain = domain(); - return switch (e) { - case null -> throw new NullPointerException("e"); - case Constructor c -> - domain.executableElement(domain.typeElement(c.getDeclaringClass().getCanonicalName()), - domain.noType(TypeKind.VOID), - "", - this.types(c.getParameterTypes())); - case Method m -> - domain.executableElement(domain.typeElement(m.getDeclaringClass().getCanonicalName()), - this.type(m.getReturnType()), - m.getName(), - this.types(m.getParameterTypes())); - default -> throw new IllegalArgumentException("e: " + e); - }; - } - /** * Returns {@code true} if the supplied {@link Method} is the {@link Object#hashCode() java.lang.Object#hashCode()} * method. @@ -131,6 +101,7 @@ protected final ExecutableElement executableElement(final Executable e) { * * @exception NullPointerException if {@code m} is {@code null} */ + // (Convenience.) protected final boolean hashCodeMethod(final Method m) { return m.getDeclaringClass() == Object.class && @@ -139,27 +110,6 @@ protected final boolean hashCodeMethod(final Method m) { m.getName().equals("hashCode"); } - /** - * Returns a {@link Parameterizable} corresponding to the supplied {@link GenericDeclaration}. - * - * @param gd a {@link GenericDeclaration}; must not be {@code null} - * - * @return a {@link Parameterizable} corresponding to the supplied {@link GenericDeclaration}; never {@code null} - * - * @exception NullPointerException if {@code gd} is {@code null} - * - * @exception IllegalArgumentException if {@code gd} is neither a {@link Class} nor an {@link Executable} - */ - protected final Parameterizable parameterizable(final GenericDeclaration gd) { - final Domain domain = this.domain(); - return switch (gd) { - case null -> throw new NullPointerException("gd"); - case Class c -> domain.typeElement(c.getCanonicalName()); - case Executable e -> this.executableElement(e); - default -> throw new IllegalArgumentException("gd: " + gd); - }; - } - /** * Returns a {@link Proxy} appropriate for the supplied specification and {@link Supplier} of instances. * @@ -223,79 +173,4 @@ protected abstract Proxy proxy(final PS ps, final Class[] interfaces, final Supplier instanceSupplier); - /** - * Returns the {@link TypeMirror} corresponding to the supplied {@link Type}. - * - * @param t a {@link Type}; must not be {@code null} - * - * @return the {@link TypeMirror} corresponding to the supplied {@link Type}; never {@code null} - * - * @exception NullPointerException if {@code t} is {@code null} - * - * @exception IllegalArgumentException if {@code t} is not a {@link Class}, {@link GenericArrayType}, {@link - * ParameterizedType}, {@link TypeVariable} or {@link WildcardType} - */ - protected final TypeMirror type(final Type t) { - // TODO: anywhere there is domain.declaredType(), consider passing - // domain.moduleElement(this.getClass().getModule().getName()) as the first argument. Not sure how this works - // exactly but I think it might be necessary. - final Domain domain = this.domain(); - return switch (t) { - case null -> throw new NullPointerException("t"); - case Class c when t == boolean.class -> domain.primitiveType(TypeKind.BOOLEAN); - case Class c when t == byte.class -> domain.primitiveType(TypeKind.BYTE); - case Class c when t == char.class -> domain.primitiveType(TypeKind.CHAR); - case Class c when t == double.class -> domain.primitiveType(TypeKind.DOUBLE); - case Class c when t == float.class -> domain.primitiveType(TypeKind.FLOAT); - case Class c when t == int.class -> domain.primitiveType(TypeKind.INT); - case Class c when t == long.class -> domain.primitiveType(TypeKind.LONG); - case Class c when t == short.class -> domain.primitiveType(TypeKind.SHORT); - case Class c when t == void.class -> domain.noType(TypeKind.VOID); - case Class c when t == Object.class -> domain.javaLangObject().asType(); // cheap and easy optimization - case Class c when c.isArray() -> domain.arrayTypeOf(this.type(c.getComponentType())); - case Class c -> domain.declaredType(c.getCanonicalName()); - case GenericArrayType g -> domain.arrayTypeOf(this.type(g.getGenericComponentType())); - case ParameterizedType pt when pt.getOwnerType() == null -> - domain.declaredType(domain.typeElement(((Class)pt.getRawType()).getCanonicalName()), - this.types(pt.getActualTypeArguments())); - case ParameterizedType pt -> - domain.declaredType((DeclaredType)this.type(pt.getOwnerType()), - domain.typeElement(((Class)pt.getRawType()).getCanonicalName()), - this.types(pt.getActualTypeArguments())); - case TypeVariable tv -> domain.typeVariable(this.parameterizable(tv.getGenericDeclaration()), tv.getName()); - case WildcardType w when w.getLowerBounds().length <= 0 -> domain.wildcardType(this.type(w.getUpperBounds()[0]), null); - case WildcardType w -> domain.wildcardType(null, this.type(w.getLowerBounds()[0])); - default -> throw new IllegalArgumentException("t: " + t); - }; - } - - /** - * Returns an array of {@link TypeMirror}s whose elements correspond to the elements in the supplied {@link Type} array. - * - * @param ts an array of {@link Type}s; must not be {@code null} - * - * @return an array of {@link TypeMirror}s whose elements correspond to the elements in the supplied {@link Type} - * array; never {@code null} - * - * @exception NullPointerException if {@code ts} is {@code null} or contains {@code null} elements - * - * @exception IllegalArgumentException if any element of {@code ts} is deemed illegal by the {@link #type(Type)} - * method - * - * @see #type(Type) - */ - protected final TypeMirror[] types(final Type[] ts) { - if (ts.length <= 0) { - return EMPTY_TYPE_MIRROR_ARRAY; - } else if (ts.length == 1) { - // cheap and easy optimization - return new TypeMirror[] { type(ts[0]) }; - } - final TypeMirror[] rv = new TypeMirror[ts.length]; - for (int i = 0; i < ts.length; i++) { - rv[i] = type(ts[i]); - } - return rv; - } - } diff --git a/src/main/java/org/microbean/proxy/AbstractToolkitProxier.java b/src/main/java/org/microbean/proxy/AbstractToolkitProxier.java index 8f04887..849d946 100644 --- a/src/main/java/org/microbean/proxy/AbstractToolkitProxier.java +++ b/src/main/java/org/microbean/proxy/AbstractToolkitProxier.java @@ -13,21 +13,18 @@ */ package org.microbean.proxy; -import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.System.Logger; -import java.util.Objects; - -import java.util.function.Supplier; - import org.microbean.construct.Domain; import static java.lang.System.getLogger; import static java.lang.System.Logger.Level.DEBUG; +import static java.util.Objects.requireNonNull; + /** * An {@link AbstractProxier} built using some kind of toolkit, such as Byte Buddy and the like. @@ -40,10 +37,28 @@ */ public abstract non-sealed class AbstractToolkitProxier extends AbstractProxier { + + /* + * Static fields. + */ + + private static final Logger LOGGER = getLogger(AbstractToolkitProxier.class.getName()); + + /* + * Instance fields. + */ + + private final Lookup lookup; + + /* + * Constructors. + */ + + /** * Creates a new {@link AbstractToolkitProxier}. * @@ -58,9 +73,15 @@ public abstract non-sealed class AbstractToolkitProxier interfaces; private final List attributes; - + private final String name; @@ -81,17 +82,16 @@ public ProxySpecification(final Domain domain, final Id id) { this.attributes = id.attributes(); final BeanTypeList types = id.types(); final TypeMirror t = types.get(0); // putative superclass - if (t.getKind() != TypeKind.DECLARED || domain.javaLangObject(t) && types.size() == 1) { + if (t.getKind() != DECLARED || domain.javaLangObject(t) && types.size() == 1) { throw new IllegalArgumentException("id: " + id); - } else if (((DeclaredType)t).asElement().getKind() == ElementKind.INTERFACE) { + } else if (((DeclaredType)t).asElement().getKind() == INTERFACE) { this.sc = (DeclaredType)domain.javaLangObject().asType(); this.interfaces = types; } else if (!proxiableBeanType(t)) { throw new IllegalArgumentException("id: " + id); } else { this.sc = (DeclaredType)t; - final int interfaceIndex = types.interfaceIndex(); - this.interfaces = interfaceIndex < 0 ? List.of() : types.subList(interfaceIndex, types.size()); + this.interfaces = types.interfaces(); } this.name = computeName(domain, this.sc, this.interfaces); } @@ -187,7 +187,7 @@ static final String computeName(final Domain domain, final DeclaredType supercla // TODO: there will absolutely be edge cases here and we know this is not complete. - if (superclass.getKind() != TypeKind.DECLARED) { + if (superclass.getKind() != DECLARED) { throw new IllegalArgumentException("superclass: " + superclass); } final DeclaredType proxyClassSibling; @@ -197,7 +197,7 @@ static final String computeName(final Domain domain, final DeclaredType supercla } // Interface-only. There will be at least one and it will be the most specialized. proxyClassSibling = (DeclaredType)interfaces.get(0); - if (proxyClassSibling.getKind() != TypeKind.DECLARED) { + if (proxyClassSibling.getKind() != DECLARED) { throw new IllegalArgumentException("interfaces: " + interfaces); } } else { diff --git a/src/site/site.xml b/src/site/site.xml index fb69fe9..b8967ea 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -13,7 +13,7 @@ org.apache.maven.skins maven-fluido-skin - 2.0.0 + 2.1.0