001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2023–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.scopelet;
015
016import java.lang.invoke.VarHandle;
017
018import java.util.Objects;
019
020import java.util.function.Supplier;
021
022import org.microbean.bean.Destruction;
023
024import static java.lang.invoke.MethodHandles.lookup;
025
026/**
027 * An {@link AutoCloseable} pairing of a contextual instance that can be destroyed with a {@link Destructor} that can
028 * destroy it and a {@link Destruction} view of the {@link org.microbean.bean.Creation} that caused it to be created.
029 *
030 * @param <I> the contextual instance type
031 *
032 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
033 *
034 * @see Instance.Destructor
035 *
036 * @see Destruction
037 */
038public final class Instance<I> implements AutoCloseable, Supplier<I> {
039
040
041  /*
042   * Static fields.
043   */
044
045
046  private static final VarHandle CLOSED;
047
048  static {
049    try {
050      CLOSED = lookup().findVarHandle(Instance.class, "closed", boolean.class);
051    } catch (final NoSuchFieldException | IllegalAccessException reflectiveOperationException) {
052      throw (Error)new ExceptionInInitializerError(reflectiveOperationException.getMessage()).initCause(reflectiveOperationException);
053    }
054  }
055
056
057  /*
058   * Instance fields.
059   */
060
061
062  private final I object;
063
064  private final Destructor<I> destructor;
065
066  private final Destruction destruction;
067
068  private volatile boolean closed;
069
070
071  /*
072   * Constructors.
073   */
074
075
076  /**
077   * Creates a new {@link Instance}.
078   *
079   * @param contextualInstance a contextual instance that has just been created; may be {@code null}
080   *
081   * @param destructor a {@link Destructor} capable of (eventually) destroying the supplied {@code contextualInstance};
082   * may be {@code null}
083   *
084   * @param destruction a {@link Destruction}; may be {@code null}
085   */
086  public Instance(final I contextualInstance,
087                  final Destructor<I> destructor,
088                  final Destruction destruction) {
089    super();
090    this.destruction = destruction;
091    this.object = contextualInstance;
092    this.destructor = destructor == null ? Instance::sink : destructor;
093  }
094
095
096  /*
097   * Instance methods.
098   */
099
100
101  @Override // AutoCloseable
102  public final void close() {
103    if (CLOSED.compareAndSet(this, false, true)) { // volatile read/write
104      try (this.destruction) {
105        this.destructor.destroy(this.object, this.destruction);
106      }
107    }
108  }
109
110  /**
111   * Returns {@code true} if and only if this {@link Instance} has been {@linkplain #close() closed}.
112   *
113   * @return {@code true} if and only if this {@link Instance} has been {@linkplain #close() closed}
114   */
115  public final boolean closed() {
116    return this.closed; // volatile read
117  }
118
119  @Override // Object
120  public final boolean equals(final Object other) {
121    if (other == this) {
122      return true;
123    } else if (other != null && this.getClass() == other.getClass()) {
124      // We don't want "closedness" to factor in here because it isn't part of hashCode(). But we want to use the
125      // results of get(). Fortunately, that method is final. So we can just use direct field access.
126      return Objects.equals(this.object, ((Instance<?>)other).object);
127    } else {
128      return false;
129    }
130  }
131
132  /**
133   * Returns the contextual instance this {@link Instance} holds, which may be {@code null}.
134   *
135   * @return the contextual instance this {@link Instance} holds, which may be {@code null}
136   */
137  @Override // Supplier<I>
138  public final I get() {
139    if (this.closed()) { // volatile read, effectively
140      throw new IllegalStateException("closed");
141    }
142    return this.object;
143  }
144
145  @Override // Object
146  public final int hashCode() {
147    // We don't want "closedness" to factor in here because it isn't part of equals(). But we want to use the results of
148    // get(). Fortunately, that method is final. So we can just use direct field access.
149    return this.object.hashCode();
150  }
151
152  @Override // Object
153  public final String toString() {
154    return String.valueOf(this.object);
155  }
156
157
158  /*
159   * Static methods.
160   */
161
162
163  private static final void sink() {
164
165  }
166
167  private static final <A, B> void sink(final A a, final B b) {
168
169  }
170
171
172  /*
173   * Inner and nested classes.
174   */
175
176
177  /**
178   * An interface whose implementations can destroy contextual instances.
179   *
180   * <p>This is commonly implemented in terms of a method reference to the {@link
181   * org.microbean.bean.Factory#destroy(Object, Destruction)} method.</p>
182   *
183   * @param <I> the contextual instance type
184   *
185   * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
186   *
187   * @see Destruction
188   *
189   * @see org.microbean.bean.Factory#destroy(Object, Destruction)
190   */
191  @FunctionalInterface
192  public static interface Destructor<I> {
193
194    /**
195     * Destroys the supplied contextual instance.
196     *
197     * @param i the contextual instance to destroy; may be {@code null}
198     *
199     * @param creation the {@link Destruction} view of the {@link org.microbean.bean.Creation} implementation that
200     * caused the contextual instance to be created; may be {@code null}
201     */
202    public void destroy(final I i, final Destruction creation);
203
204  }
205
206}