From c1bf74b745e59c46553981000ac4f321394f7ff9 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Sun, 20 Apr 2025 12:18:28 -0700 Subject: [PATCH] Uptakes microbean-bean 0.0.17 and microbean-reference 0.0.3 Signed-off-by: Laird Nelson --- README.md | 2 +- pom.xml | 23 +-- .../java/org/microbean/scopelet/Instance.java | 152 +++++++++--------- .../microbean/scopelet/MapBackedScopelet.java | 134 ++++++++------- .../org/microbean/scopelet/NoneScopelet.java | 40 ++--- .../microbean/scopelet/ScopedInstances.java | 65 ++++---- .../java/org/microbean/scopelet/Scopelet.java | 126 +++------------ .../microbean/scopelet/SingletonScopelet.java | 20 +-- 8 files changed, 237 insertions(+), 325 deletions(-) diff --git a/README.md b/README.md index 98d78e9..8ccd399 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ dependency: org.microbean microbean-scopelet - 0.0.7 + 0.0.8 ``` diff --git a/pom.xml b/pom.xml index fa56a16..99e2f8a 100644 --- a/pom.xml +++ b/pom.xml @@ -125,7 +125,7 @@ org.junit junit-bom - 5.11.4 + 5.12.2 pom import @@ -147,7 +147,7 @@ org.microbean microbean-bean - 0.0.16 + 0.0.17 @@ -159,7 +159,7 @@ org.microbean microbean-reference - 0.0.2 + 0.0.3 @@ -196,7 +196,7 @@ microbean-reference compile - + @@ -210,7 +210,7 @@ junit-jupiter-engine test - + @@ -453,14 +453,7 @@ maven-surefire-plugin - 3.5.2 - - - org.apache.maven.surefire - surefire-junit-platform - 3.5.2 - - + 3.5.3 maven-toolchains-plugin @@ -469,7 +462,7 @@ com.github.spotbugs spotbugs-maven-plugin - 4.9.2.0 + 4.9.3.0 org.codehaus.mojo @@ -479,7 +472,7 @@ io.smallrye jandex-maven-plugin - 3.2.7 + 3.3.0 org.sonatype.plugins diff --git a/src/main/java/org/microbean/scopelet/Instance.java b/src/main/java/org/microbean/scopelet/Instance.java index cc38e0b..b2b158b 100644 --- a/src/main/java/org/microbean/scopelet/Instance.java +++ b/src/main/java/org/microbean/scopelet/Instance.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–2025 microBean™. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -19,25 +19,30 @@ import java.util.function.Supplier; -import org.microbean.bean.Request; +import org.microbean.bean.Destruction; import static java.lang.invoke.MethodHandles.lookup; /** * An {@link AutoCloseable} pairing of a contextual instance that can be destroyed with a {@link Destructor} that can - * destroy it, an {@link AutoCloseable} that can release its dependent objects when needed, and a {@link Request} that - * caused it to be created. + * destroy it and a {@link Destruction} view of the {@link org.microbean.bean.Creation} that caused it to be created. * - * @param the type of the instance + * @param the contextual instance type * * @author Laird Nelson * * @see Instance.Destructor * - * @see Request + * @see Destruction */ public final class Instance implements AutoCloseable, Supplier { + + /* + * Static fields. + */ + + private static final VarHandle CLOSED; static { @@ -48,16 +53,26 @@ public final class Instance implements AutoCloseable, Supplier { } } + + /* + * Instance fields. + */ + + private final I object; private final Destructor destructor; - private final AutoCloseable releaser; - - private final Request creationRequest; + private final Destruction destruction; private volatile boolean closed; + + /* + * Constructors. + */ + + /** * Creates a new {@link Instance}. * @@ -66,70 +81,28 @@ public final class Instance implements AutoCloseable, Supplier { * @param destructor a {@link Destructor} capable of (eventually) destroying the supplied {@code contextualInstance}; * may be {@code null} * - * @param creationRequest a {@link Request} that is the reason for this creation; may be {@code null} + * @param destruction a {@link Destruction}; may be {@code null} */ public Instance(final I contextualInstance, final Destructor destructor, - final Request creationRequest) { - this(contextualInstance, destructor, creationRequest instanceof AutoCloseable ac ? ac : null, creationRequest); - } - - private Instance(final I object, - final Destructor destructor, - final AutoCloseable releaser, // often the same object as creationRequest - final Request creationRequest) { // often the same object as releaser + final Destruction destruction) { super(); - // All of these things are nullable on purpose. - this.object = object; - this.releaser = releaser == null ? Instance::sink : releaser; + this.destruction = destruction; + this.object = contextualInstance; this.destructor = destructor == null ? Instance::sink : destructor; - this.creationRequest = creationRequest; } - /** - * Returns the contextual instance this {@link Instance} holds, which may be {@code null}. - * - * @return the contextual instance this {@link Instance} holds, which may be {@code null} + + /* + * Instance methods. */ - @Override // Supplier - public final I get() { - if (this.closed()) { // volatile read, effectively - throw new IllegalStateException("closed"); - } - return this.object; - } + @Override // AutoCloseable public final void close() { if (CLOSED.compareAndSet(this, false, true)) { // volatile read/write - RuntimeException t = null; - try { - this.destructor.destroy(this.object, this.creationRequest); - } catch (final RuntimeException e) { - t = e; - } finally { - try { - this.releaser.close(); - } catch (final RuntimeException e) { - if (t == null) { - throw e; - } - t.addSuppressed(e); - } catch (final InterruptedException e) { - Thread.currentThread().interrupt(); - if (t == null) { - throw new ScopeletException(e.getMessage(), e); - } - t.addSuppressed(e); - } catch (final Exception e) { - if (t == null) { - throw new ScopeletException(e.getMessage(), e); - } - t.addSuppressed(e); - } - } - if (t != null) { - throw t; + try (this.destruction) { + this.destructor.destroy(this.object, this.destruction); } } } @@ -143,14 +116,7 @@ public final boolean closed() { return this.closed; // volatile read } - @Override - public final int hashCode() { - // We don't want "closedness" to factor in here because it isn't part of equals(). But we want to use the results of - // get(). Fortunately, that method is final. So we can just use direct field access. - return this.object.hashCode(); - } - - @Override + @Override // Object public final boolean equals(final Object other) { if (other == this) { return true; @@ -163,11 +129,37 @@ public final boolean equals(final Object other) { } } - @Override + /** + * Returns the contextual instance this {@link Instance} holds, which may be {@code null}. + * + * @return the contextual instance this {@link Instance} holds, which may be {@code null} + */ + @Override // Supplier + public final I get() { + if (this.closed()) { // volatile read, effectively + throw new IllegalStateException("closed"); + } + return this.object; + } + + @Override // Object + public final int hashCode() { + // We don't want "closedness" to factor in here because it isn't part of equals(). But we want to use the results of + // get(). Fortunately, that method is final. So we can just use direct field access. + return this.object.hashCode(); + } + + @Override // Object public final String toString() { return String.valueOf(this.object); } + + /* + * Static methods. + */ + + private static final void sink() { } @@ -176,12 +168,25 @@ private static final void sink(final A a, final B b) { } + + /* + * Inner and nested classes. + */ + + /** * An interface whose implementations can destroy contextual instances. * - * @param the type borne by instances + *

This is commonly implemented in terms of a method reference to the {@link + * org.microbean.bean.Factory#destroy(Object, Destruction)} method.

+ * + * @param the contextual instance type * * @author Laird Nelson + * + * @see Destruction + * + * @see org.microbean.bean.Factory#destroy(Object, Destruction) */ @FunctionalInterface public static interface Destructor { @@ -191,9 +196,10 @@ public static interface Destructor { * * @param i the contextual instance to destroy; may be {@code null} * - * @param creationRequest the {@link Request} that caused the contextual instance to be created; may be {@code null} + * @param creation the {@link Destruction} view of the {@link org.microbean.bean.Creation} implementation that + * caused the contextual instance to be created; may be {@code null} */ - public void destroy(final I i, final Request creationRequest); + public void destroy(final I i, final Destruction creation); } diff --git a/src/main/java/org/microbean/scopelet/MapBackedScopelet.java b/src/main/java/org/microbean/scopelet/MapBackedScopelet.java index be7adf8..67b405b 100644 --- a/src/main/java/org/microbean/scopelet/MapBackedScopelet.java +++ b/src/main/java/org/microbean/scopelet/MapBackedScopelet.java @@ -24,10 +24,9 @@ import java.util.function.Supplier; +import org.microbean.bean.Creation; +import org.microbean.bean.Destruction; import org.microbean.bean.Factory; -import org.microbean.bean.Request; - -import org.microbean.attributes.Attributes; /** * A thread-safe, partial {@link Scopelet} implementation backed by {@link ConcurrentMap} machinery. @@ -35,13 +34,27 @@ * @param the {@link MapBackedScopelet} subclass extending this class * * @author Laird Nelson + * + * @see #instance(Object, Factory, Creation) */ public abstract class MapBackedScopelet> extends Scopelet { + + /* + * Instance fields. + */ + + private final ConcurrentMap> instances; private final ConcurrentMap creationLocks; + + /* + * Constructors. + */ + + /** * Creates a new {@link MapBackedScopelet}. * @@ -53,23 +66,72 @@ protected MapBackedScopelet() { this.instances = new ConcurrentHashMap<>(); } + + /* + * Instance methods. + */ + + + @Override // Scopelet + public void close() { + if (this.closed()) { + return; + } + super.close(); // critical + final Iterator> i = this.creationLocks.entrySet().iterator(); + while (i.hasNext()) { + final Entry e = i.next(); + try { + e.getValue().unlock(); + } finally { + i.remove(); + } + } + final Iterator>> i2 = this.instances.entrySet().iterator(); + while (i2.hasNext()) { + final Entry> e = i2.next(); + try { + e.getValue().close(); + } finally { + i2.remove(); + } + } + } + // All parameters are nullable. @Override // Scopelet - public I instance(final Object beanId, - final Factory factory, - final Request request) { + public I instance(final Object beanId, final Factory factory, final Creation creation) { if (!this.active()) { throw new InactiveScopeletException(); } else if (beanId == null) { return null; } - final Supplier s = this.supplier(beanId, factory, request); + final Supplier s = this.supplier(beanId, factory, creation); return s == null ? null : s.get(); } + // If candidate is not present in this.creationLocks, puts it in in a locked state atomically and returns + // it. Otherwise returns the pre-existing creation lock, which, by definition, will already be locked. + private final ReentrantLock lockedCreationLock(final Object id, final ReentrantLock candidate) { + if (candidate.isLocked()) { + throw new IllegalArgumentException("candidate.isLocked(): " + candidate); + } + try { + return this.creationLocks.computeIfAbsent(id, x -> lock(candidate)); + } catch (final RuntimeException | Error justBeingCareful) { + // ReentrantLock#lock() is not documented to throw anything, but if it does, make sure we unlock it. + try { + candidate.unlock(); + } catch (final RuntimeException | Error suppressMe) { + justBeingCareful.addSuppressed(suppressMe); + } + throw justBeingCareful; + } + } + private final Supplier supplier(final Object id, final Factory factory, - final Request request) { + final Creation creation) { // (Don't use computeIfAbsent().) @SuppressWarnings("unchecked") final Supplier supplier = (Supplier)this.instances.get(id); @@ -103,9 +165,9 @@ private final Supplier supplier(final Object id, // Perform creation. @SuppressWarnings("unchecked") final Instance newInstance = - new Instance(factory == this ? (I)this : factory.create(request), + new Instance(factory == this ? (I)this : factory.create(creation), factory::destroy, // Destructor - request); + (Destruction)creation); // Put the created instance into our instance map. There will not be a pre-existing instance. final Object previous = this.instances.put(id, newInstance); @@ -130,7 +192,8 @@ private final Supplier supplier(final Object id, // inserted into the map. It will therefore be unlocked (it was never locked in the first place). Discard it in // preparation for switching locks to creationLock instead. assert !newLock.isLocked() : "newLock was locked: " + newLock; - assert !this.creationLocks.containsValue(newLock) : "Creation lock contained " + newLock + "; creationLock: " + creationLock; + assert !this.creationLocks.containsValue(newLock) : + "Creation locks contained " + newLock + "; creationLock: " + creationLock; // Lock and unlock in rapid succession. Why? lock() will block if another thread is currently creating, and will // return immediately if it is not. This is kind of a cheap way of doing Object.wait(). try { @@ -152,7 +215,7 @@ private final Supplier supplier(final Object id, throw new CreationCycleDetectedException(); } // The other thread finished creating; let's try again to pick up its results. - return this.supplier(id, factory, request); // RECURSIVE + return this.supplier(id, factory, creation); // RECURSIVE } @Override // Scopelet @@ -170,50 +233,11 @@ public boolean remove(final Object id) { return false; } - @Override // Scopelet - public void close() { - if (this.closed()) { - return; - } - super.close(); // critical - final Iterator> i = this.creationLocks.entrySet().iterator(); - while (i.hasNext()) { - final Entry e = i.next(); - try { - e.getValue().unlock(); - } finally { - i.remove(); - } - } - final Iterator>> i2 = this.instances.entrySet().iterator(); - while (i2.hasNext()) { - final Entry> e = i2.next(); - try { - e.getValue().close(); - } finally { - i2.remove(); - } - } - } - // If candidate is not present in this.creationLocks, puts it in in a locked state atomically and returns - // it. Otherwise returns the pre-existing creation lock, which, by definition, will already be locked. - private final ReentrantLock lockedCreationLock(final Object id, final ReentrantLock candidate) { - if (candidate.isLocked()) { - throw new IllegalArgumentException("candidate.isLocked(): " + candidate); - } - try { - return this.creationLocks.computeIfAbsent(id, x -> lock(candidate)); - } catch (final RuntimeException | Error justBeingCareful) { - // ReentrantLock#lock() is not documented to throw anything, but if it does, make sure we unlock it. - try { - candidate.unlock(); - } catch (final RuntimeException | Error suppressMe) { - justBeingCareful.addSuppressed(suppressMe); - } - throw justBeingCareful; - } - } + /* + * Static methods. + */ + private static final T lock(final T candidate) { candidate.lock(); diff --git a/src/main/java/org/microbean/scopelet/NoneScopelet.java b/src/main/java/org/microbean/scopelet/NoneScopelet.java index 842b274..b81e2d1 100644 --- a/src/main/java/org/microbean/scopelet/NoneScopelet.java +++ b/src/main/java/org/microbean/scopelet/NoneScopelet.java @@ -19,20 +19,16 @@ import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; -import java.util.Objects; import java.util.Optional; import org.microbean.bean.AutoCloseableRegistry; +import org.microbean.bean.Creation; +import org.microbean.bean.Destruction; import org.microbean.bean.DisposableReference; import org.microbean.bean.Factory; -import org.microbean.bean.Request; - -import org.microbean.construct.Domain; import static java.lang.constant.ConstantDescs.BSM_INVOKE; -import static org.microbean.assign.Qualifiers.anyQualifier; - /** * A {@link Scopelet} implementation that does not cache objects at all. * @@ -43,38 +39,30 @@ public class NoneScopelet extends Scopelet implements Constable { private static final boolean useDisposableReferences = Boolean.parseBoolean(System.getProperty("useDisposableReferences", "false")); - private final Domain domain; - /** * Creates a new {@link NoneScopelet}. - * - * @param domain a {@link Domain}; must not be {@code null} - * - * @exception NullPointerException if {@code domain} is {@code null} */ - public NoneScopelet(final Domain domain) { + public NoneScopelet() { super(); - this.domain = Objects.requireNonNull(domain, "domain"); } // All parameters are nullable. // Non-final to permit subclasses to, e.g., add logging. @Override // Scopelet - public I instance(final Object ignoredBeanId, - final Factory factory, - final Request request) { + public I instance(final Object ignoredBeanId, final Factory factory, final Creation creation) { if (!this.active()) { throw new InactiveScopeletException(); } else if (factory == null) { return null; } - final I returnValue = factory.create(request); + final I returnValue = factory.create(creation); if (factory.destroys()) { if (useDisposableReferences) { - // Merely creating a DisposableReference will cause it to get disposed IF garbage collection runs (which is not guaranteed). - new DisposableReference<>(returnValue, referent -> factory.destroy(referent, request)); - } else if (request instanceof AutoCloseableRegistry acr) { - acr.register(new Instance<>(returnValue, factory::destroy, request)); + // Merely creating a DisposableReference will cause it to get disposed *IF* garbage collection runs (which is not + // guaranteed). + new DisposableReference<>(returnValue, referent -> factory.destroy(referent, (Destruction)creation)); + } else if (creation instanceof AutoCloseableRegistry acr) { + acr.register(new Instance(returnValue, factory::destroy, (Destruction)creation)); } else { // TODO: warn or otherwise point out that dependencies will not be destroyed } @@ -84,11 +72,9 @@ public I instance(final Object ignoredBeanId, @Override // Constable public Optional describeConstable() { - return (this.domain instanceof Constable c ? c.describeConstable() : Optional.empty()) - .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE, - MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), - ClassDesc.of(Domain.class.getName())), - domainDesc)); + return + Optional.of(DynamicConstantDesc.of(BSM_INVOKE, + MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName())))); } } diff --git a/src/main/java/org/microbean/scopelet/ScopedInstances.java b/src/main/java/org/microbean/scopelet/ScopedInstances.java index f280303..0074ad2 100644 --- a/src/main/java/org/microbean/scopelet/ScopedInstances.java +++ b/src/main/java/org/microbean/scopelet/ScopedInstances.java @@ -31,14 +31,13 @@ import org.microbean.bean.AmbiguousReductionException; import org.microbean.bean.AttributedType; import org.microbean.bean.Bean; +import org.microbean.bean.Creation; import org.microbean.bean.Factory; import org.microbean.bean.Id; import org.microbean.bean.RankedReducer; import org.microbean.bean.Reducer; import org.microbean.bean.Reducible; -import org.microbean.bean.Request; import org.microbean.bean.Selectable; -import org.microbean.bean.Reducer; import org.microbean.construct.Domain; @@ -52,13 +51,35 @@ * An {@link Instances} implementation that is based on scopes. * * @author Laird Nelson + * + * @see #supplier(Bean, Creation) + * + * @see Instances */ public class ScopedInstances implements Instances { + + /* + * Static fields. + */ + + private static final Attributes FOR_INSTANTIATION = Attributes.of("ForInstantiation"); + + /* + * Instance fields. + */ + + private final TypeMirror scopeletType; + + /* + * Constructors. + */ + + /** * Creates a new {@link ScopedInstances}. * @@ -111,7 +132,7 @@ private final Attributes findScopeId(final Id id) { } return scopeId; } - + /** * Finds and returns the nearest scope identifier in the forest represented by the supplied {@link * Attributes}. @@ -179,7 +200,7 @@ private final boolean normal(final Attributes a) { private final boolean primordial(final Attributed a) { return this.primordial(a.attributes()); } - + /** * Returns {@code true} if and only if the supplied {@link Collection} of {@link Attributes} is deemed to designate * something as primordial. @@ -199,20 +220,6 @@ protected boolean primordial(final Collection c) { return c.contains(primordialQualifier()); } - /** - * Returns {@code true} if and only if the supplied {@link Request} is deemed to be primordial. - * - *

A {@link Request} is normally primordial if it is {@code null} or if an invocation of its {@link - * Request#primordial() primordial()} method returns {@code true}.

- * - * @param r a {@link Request}; may be {@code null} in which case {@code true} will be returned - * - * @return {@code true} if and only if the supplied {@link Request} is deemed to be primordial - */ - protected boolean primordial(final Request r) { - return r == null || r.primordial(); - } - /** * Returns {@code true} if and only if the supplied {@link Id} is proxiable. * @@ -222,7 +229,8 @@ protected boolean primordial(final Request r) { * * @exception NullPointerException if {@code id} is {@code null} */ - protected boolean proxiable(final Id id) { + @Override // Instances + public boolean proxiable(final Id id) { if (!id.types().proxiable()) { return false; } @@ -231,27 +239,14 @@ protected boolean proxiable(final Id id) { } @Override // Instances - public final boolean proxiable(final Request r) { - return !this.primordial(r) && this.proxiable(r.beanReduction().bean().id()); - } - - @Override // Instances - public final Supplier supplier(final Request request) { - if (this.primordial(request)) { - // The supplied Request is a request for a Request, i.e. it's primordial, so return a Supplier that simply returns - // the request. - @SuppressWarnings("unchecked") - final I instance = (I)request; - return () -> instance; - } - final Bean bean = request.beanReduction().bean(); - final Factory factory = bean.factory(); + public final Supplier supplier(final Bean bean, final Creation request) { final Id id = bean.id(); final Attributes scopeId = this.findScopeId(id); // In this implementation, all Ids must have scopes. if (scopeId == null) { throw new IllegalStateException(); } + final Factory factory = bean.factory(); if (factory instanceof Scopelet && this.primordial(scopeId)) { // This is a request for, e.g., the Singleton Scopelet, which backs the primordial (notional) singleton scope. // Scopelets are always their own factories. The Scopelet implementing the primordial scope (normally Singleton) @@ -264,7 +259,7 @@ public final Supplier supplier(final Request request) { return factory::singleton; } final AttributedType t = AttributedType.of(this.scopeletType, findScopeId(scopeId), FOR_INSTANTIATION); - return () -> request.>reference(t).instance(id, factory, request); // assumes a specific kind of reduction; see #reducible + return () -> request.>references(t).get().instance(id, factory, request); // assumes a specific kind of reduction; see #reducible } diff --git a/src/main/java/org/microbean/scopelet/Scopelet.java b/src/main/java/org/microbean/scopelet/Scopelet.java index 8a8015a..40154d9 100644 --- a/src/main/java/org/microbean/scopelet/Scopelet.java +++ b/src/main/java/org/microbean/scopelet/Scopelet.java @@ -13,7 +13,6 @@ */ package org.microbean.scopelet; -import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles.Lookup; import java.lang.invoke.VarHandle; @@ -25,8 +24,10 @@ import org.microbean.attributes.Value; import org.microbean.bean.Bean; +import org.microbean.bean.Creation; import org.microbean.bean.Factory; -import org.microbean.bean.Request; + +import static java.lang.invoke.MethodHandles.lookup; import static org.microbean.assign.Qualifiers.primordialQualifier; import static org.microbean.assign.Qualifiers.qualifier; @@ -37,6 +38,8 @@ * @param the {@link Scopelet} subtype extending this class * * @author Laird Nelson + * + * @see #instance(Object, Factory, Creation) */ public abstract class Scopelet> implements AutoCloseable, Factory { @@ -51,7 +54,7 @@ public abstract class Scopelet> implements AutoCloseable, private static final VarHandle ME; static { - final Lookup lookup = MethodHandles.lookup(); + final Lookup lookup = lookup(); try { CLOSED = lookup.findVarHandle(Scopelet.class, "closed", boolean.class); ME = lookup.findVarHandle(Scopelet.class, "me", Scopelet.class); @@ -128,7 +131,7 @@ protected Scopelet() { */ @Override // Factory @SuppressWarnings("unchecked") - public final S create(final Request r) { + public final S create(final Creation r) { if (ME.compareAndSet(this, null, this)) { // volatile write if (r != null) { // TODO: emit initialized event @@ -138,12 +141,12 @@ public final S create(final Request r) { } /** - * Returns this {@link Scopelet} if it has been created via the {@link #create(Request)} method, or {@code null} if + * Returns this {@link Scopelet} if it has been created via the {@link #create(Creation)} method, or {@code null} if * that method has not yet been invoked. * - * @return this {@link Scopelet} if it has been "{@linkplain #create(Request) created}"; {@code null} otherwise + * @return this {@link Scopelet} if it has been "{@linkplain #create(Creation) created}"; {@code null} otherwise * - * @see #create(Request) + * @see #create(Creation) */ @Override // Factory public final S singleton() { @@ -152,13 +155,13 @@ public final S singleton() { /** * Returns {@code true} when invoked to indicate that {@link Scopelet} implementations {@linkplain - * Factory#destroy(Object, Request) destroy} what they {@linkplain #create(Request) create}. + * Factory#destroy(Object, org.microbean.bean.Destruction) destroy} what they {@linkplain #create(Creation) create}. * * @return {@code true} when invoked * - * @see Factory#destroy(Object, Request) + * @see Factory#destroy(Object, org.microbean.bean.Destruction) * - * @see #create(Request) + * @see #create(Creation) */ @Override // Factory public final boolean destroys() { @@ -185,94 +188,10 @@ public boolean active() { return !this.closed(); // volatile read } - /** - * Checks to see if this {@link Scopelet} {@linkplain #active() is active} and then returns {@code true} if and only - * if, at the moment of an invocation, this {@link Scopelet} {@linkplain #active() is active} and already contains an - * object identified by the supplied {@link Object}. - * - *

The default implementation of this method checks to see if this {@link Scopelet} {@linkplain #active() is - * active}, and then {@code true} if and only if the result of invoking the {@link #instance(Object, Factory, - * Request)} method with the supplied {@code id}, {@code null}, and {@code null} is not {@code null}.

- * - *

Subclasses are encouraged to override this method to be more efficient or to use a different algorithm.

- * - * @param id the {@link Object} serving as an identifier; may be {@code null} in certain pathological cases - * - * @return {@code true} if and only if, at the moment of an invocation, this {@link Scopelet} {@linkplain #active() is - * active} and contains a preexisting object identified by the supplied {@link Object} - * - * @exception InactiveScopeletException if this {@link Scopelet} {@linkplain #active() is not active} - * - * @see #active() - * - * @see #instance(Object, Factory, Request) - */ - // @Deprecated // This method is not actually used but would need to exist for design flaws in CDI - public boolean containsId(final Object id) { - return (id instanceof Request r ? this.instance(r) : this.instance(id, null, null)) != null; - } - - /** - * Checks to see if this {@link Scopelet} {@linkplain #active() is active}, and then returns the preexisting - * contextual instance identified by the supplied {@link Object}, or {@code null} if no such instance exists. - * - *

This convenience method checks to see if this {@link Scopelet} {@linkplain #active() is active}, and then, if - * the supplied {@link Object} is not a {@link Request}, calls the {@link #instance(Object, Factory, Request)} method - * with the supplied {@code id}, {@code null}, and {@code null}, and returns its result.

- * - *

If the supplied {@link Object} is a {@link Request}, this method calls the {@link #instance(Request)} - * method with the supplied (cast) {@code id} and returns its result.

- * - * @param the type of contextual instance - * - * @param id an {@link Object} serving as an identifier; may be {@code null} in certain pathological cases - * - * @return the contextual instance identified by the supplied {@link Object}, or {@code null} if no such instance - * exists - * - * @exception InactiveScopeletException if this {@link Scopelet} {@linkplain #active() is not active} - * - * @see #instance(Object, Factory, Request) - * - * @see #instance(Request) - */ - // id is nullable. - @SuppressWarnings("unchecked") - public final I get(final Object id) { - return id instanceof Request r ? this.instance((Request)r) : this.instance(id, null, null); - } - - /** - * Checks to see if this {@link Scopelet} {@linkplain #active() is active} and then eturns a contextual instance - * identified by the {@linkplain Request#beanReduction() identifying information} present within the supplied {@link - * Request}, creating the instance and associating it with the {@linkplain Request#beanReduction() identifying - * information} present within the supplied {@link Request} if necessary. - * - * @param the type of contextual instance - * - * @param request a {@link Request}; may be {@code null} in which case the return value of an invocation of {@link - * #instance(Object, Factory, Request)} with {@code null} supplied for all three arguments will be returned instead - * - * @return an appropriate contextual instance, or {@code null} - * - * @exception InactiveScopeletException if this {@link Scopelet} {@linkplain #active() is not active} - * - * @see Request#beanReduction() - * - * @see #instance(Object, Factory, Request) - */ - public final I instance(final Request request) { - if (request == null || request.primordial()) { - return this.instance(null, null, request); - } - final Bean bean = request.beanReduction().bean(); - return this.instance(bean.id(), bean.factory(), request); - } - /** * Checks to see if this {@link Scopelet} {@linkplain #active() is active} and then returns a pre-existing or * created-on-demand contextual instance suitable for the combination of identifier, {@link Factory} and {@link - * Request}. + * Creation}, or {@code null} * * @param the type of contextual instance * @@ -280,15 +199,19 @@ public final I instance(final Request request) { * * @param factory a {@link Factory}; may be {@code null} * - * @param request a {@link Request}, typically the one in effect that is causing this method to be invoked in the + * @param creation a {@link Creation}, typically the one in effect that is causing this method to be invoked in the * first place; may be {@code null} * * @return a contextual instance, possibly pre-existing, or possibly created just in time, or {@code null} * * @exception InactiveScopeletException if this {@link Scopelet} {@linkplain #active() is not active} + * + * @exception ClassCastException if {@code creation} is non-{@code null} and does not implement {@link + * org.microbean.bean.Destruction}, a requirement of its contract + * + * @see Creation */ - // All parameters are nullable, perhaps pathologically. This helps permit super early bootstrapping. - public abstract I instance(final Object id, final Factory factory, final Request request); + public abstract I instance(final Object id, final Factory factory, final Creation creation); /** * Checks to see if this {@link Scopelet} {@linkplain #active() is active} and then removes any contextual instance @@ -303,7 +226,6 @@ public final I instance(final Request request) { * * @exception InactiveScopeletException if this {@link Scopelet} {@linkplain #active() is not active} */ - // id is nullable. public boolean remove(final Object id) { if (!this.active()) { throw new InactiveScopeletException(); @@ -315,15 +237,13 @@ public boolean remove(final Object id) { * Irrevocably closes this {@link Scopelet}, and, by doing so, notionally makes it irrevocably {@linkplain #closed() * closed} and {@linkplain #active() inactive}. * - *

Overrides of this method must call {@link Scopelet#close() super.close()} as part of their implementation or - * undefined behavior may result.

+ *

Overrides of this method must call {@link Scopelet#close() super.close()} as part of their + * implementation or undefined behavior may result.

* * @see #closed() * * @see #active() */ - // Most scopelets will want to override this to do additional work. They must call super.close() to ensure #closed() - // returns an appropriate value. @Override // AutoCloseable public void close() { CLOSED.compareAndSet(this, false, true); // volatile write diff --git a/src/main/java/org/microbean/scopelet/SingletonScopelet.java b/src/main/java/org/microbean/scopelet/SingletonScopelet.java index 9831622..8a0933b 100644 --- a/src/main/java/org/microbean/scopelet/SingletonScopelet.java +++ b/src/main/java/org/microbean/scopelet/SingletonScopelet.java @@ -19,11 +19,8 @@ import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; -import java.util.Objects; import java.util.Optional; -import org.microbean.construct.Domain; - import static java.lang.constant.ConstantDescs.BSM_INVOKE; /** @@ -33,27 +30,18 @@ */ public class SingletonScopelet extends MapBackedScopelet implements Constable { - private final Domain domain; - /** * Creates a new {@link SingletonScopelet}. - * - * @param domain a {@link Domain}; must not be {@code null} - * - * @exception NullPointerException if {@code domain} is {@code null} */ - public SingletonScopelet(final Domain domain) { + public SingletonScopelet() { super(); - this.domain = Objects.requireNonNull(domain, "domain"); } @Override // Constable public Optional describeConstable() { - return (this.domain instanceof Constable c ? c.describeConstable() : Optional.empty()) - .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE, - MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), - ClassDesc.of(Domain.class.getName())), - domainDesc)); + return + Optional.of(DynamicConstantDesc.of(BSM_INVOKE, + MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName())))); } }