Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ dependency:
<groupId>org.microbean</groupId>
<artifactId>microbean-scopelet</artifactId>
<!-- Always check https://search.maven.org/artifact/org.microbean/microbean-scopelet for up-to-date available versions. -->
<version>0.0.7</version>
<version>0.0.8</version>
</dependency>
```

Expand Down
23 changes: 8 additions & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>5.11.4</version>
<version>5.12.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand All @@ -147,7 +147,7 @@
<dependency>
<groupId>org.microbean</groupId>
<artifactId>microbean-bean</artifactId>
<version>0.0.16</version>
<version>0.0.17</version>
</dependency>

<dependency>
Expand All @@ -159,7 +159,7 @@
<dependency>
<groupId>org.microbean</groupId>
<artifactId>microbean-reference</artifactId>
<version>0.0.2</version>
<version>0.0.3</version>
</dependency>

</dependencies>
Expand Down Expand Up @@ -196,7 +196,7 @@
<artifactId>microbean-reference</artifactId>
<scope>compile</scope>
</dependency>

<!-- Test-scoped dependencies. -->

<dependency>
Expand All @@ -210,7 +210,7 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand Down Expand Up @@ -453,14 +453,7 @@
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.2</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit-platform</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
<version>3.5.3</version>
</plugin>
<plugin>
<artifactId>maven-toolchains-plugin</artifactId>
Expand All @@ -469,7 +462,7 @@
<plugin>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>4.9.2.0</version>
<version>4.9.3.0</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
Expand All @@ -479,7 +472,7 @@
<plugin>
<groupId>io.smallrye</groupId>
<artifactId>jandex-maven-plugin</artifactId>
<version>3.2.7</version>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.sonatype.plugins</groupId>
Expand Down
152 changes: 79 additions & 73 deletions src/main/java/org/microbean/scopelet/Instance.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 <I> the type of the instance
* @param <I> the contextual instance type
*
* @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
*
* @see Instance.Destructor
*
* @see Request
* @see Destruction
*/
public final class Instance<I> implements AutoCloseable, Supplier<I> {


/*
* Static fields.
*/


private static final VarHandle CLOSED;

static {
Expand All @@ -48,16 +53,26 @@ public final class Instance<I> implements AutoCloseable, Supplier<I> {
}
}


/*
* Instance fields.
*/


private final I object;

private final Destructor<I> destructor;

private final AutoCloseable releaser;

private final Request<I> creationRequest;
private final Destruction destruction;

private volatile boolean closed;


/*
* Constructors.
*/


/**
* Creates a new {@link Instance}.
*
Expand All @@ -66,70 +81,28 @@ public final class Instance<I> implements AutoCloseable, Supplier<I> {
* @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<I> destructor,
final Request<I> creationRequest) {
this(contextualInstance, destructor, creationRequest instanceof AutoCloseable ac ? ac : null, creationRequest);
}

private Instance(final I object,
final Destructor<I> destructor,
final AutoCloseable releaser, // often the same object as creationRequest
final Request<I> 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<I>
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);
}
}
}
Expand All @@ -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;
Expand All @@ -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<I>
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() {

}
Expand All @@ -176,12 +168,25 @@ private static final <A, B> void sink(final A a, final B b) {

}


/*
* Inner and nested classes.
*/


/**
* An interface whose implementations can destroy contextual instances.
*
* @param <I> the type borne by instances
* <p>This is commonly implemented in terms of a method reference to the {@link
* org.microbean.bean.Factory#destroy(Object, Destruction)} method.</p>
*
* @param <I> the contextual instance type
*
* @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
*
* @see Destruction
*
* @see org.microbean.bean.Factory#destroy(Object, Destruction)
*/
@FunctionalInterface
public static interface Destructor<I> {
Expand All @@ -191,9 +196,10 @@ public static interface Destructor<I> {
*
* @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<I> creationRequest);
public void destroy(final I i, final Destruction creation);

}

Expand Down
Loading