001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2023–2024 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.Request;
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, an {@link AutoCloseable} that can release its dependent objects when needed, and a {@link Request} that
029 * caused it to be created.
030 *
031 * @param <I> the type of the instance
032 *
033 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
034 *
035 * @see Instance.Destructor
036 *
037 * @see Request
038 */
039public final class Instance<I> implements AutoCloseable, Supplier<I> {
040
041  private static final VarHandle CLOSED;
042
043  static {
044    try {
045      CLOSED = lookup().findVarHandle(Instance.class, "closed", boolean.class);
046    } catch (final NoSuchFieldException | IllegalAccessException reflectiveOperationException) {
047      throw (Error)new ExceptionInInitializerError(reflectiveOperationException.getMessage()).initCause(reflectiveOperationException);
048    }
049  }
050
051  private final I object;
052
053  private final Destructor<I> destructor;
054
055  private final AutoCloseable releaser;
056
057  private final Request<I> creationRequest;
058
059  private volatile boolean closed;
060
061  /**
062   * Creates a new {@link Instance}.
063   *
064   * @param contextualInstance a contextual instance that has just been created; may be {@code null}
065   *
066   * @param destructor a {@link Destructor} capable of (eventually) destroying the supplied {@code contextualInstance};
067   * may be {@code null}
068   *
069   * @param creationRequest a {@link Request} that is the reason for this creation; may be {@code null}
070   */
071  public Instance(final I contextualInstance,
072                  final Destructor<I> destructor,
073                  final Request<I> creationRequest) {
074    this(contextualInstance, destructor, creationRequest instanceof AutoCloseable ac ? ac : null, creationRequest);
075  }
076
077  private Instance(final I object,
078                   final Destructor<I> destructor,
079                   final AutoCloseable releaser, // often the same object as creationRequest
080                   final Request<I> creationRequest) { // often the same object as releaser
081    super();
082    // All of these things are nullable on purpose.
083    this.object = object;
084    this.releaser = releaser == null ? Instance::sink : releaser;
085    this.destructor = destructor == null ? Instance::sink : destructor;
086    this.creationRequest = creationRequest;
087  }
088
089  /**
090   * Returns the contextual instance this {@link Instance} holds, which may be {@code null}.
091   *
092   * @return the contextual instance this {@link Instance} holds, which may be {@code null}
093   */
094  @Override // Supplier<I>
095  public final I get() {
096    if (this.closed()) { // volatile read, effectively
097      throw new IllegalStateException("closed");
098    }
099    return this.object;
100  }
101
102  @Override // AutoCloseable
103  public final void close() {
104    if (CLOSED.compareAndSet(this, false, true)) { // volatile read/write
105      RuntimeException t = null;
106      try {
107        this.destructor.destroy(this.object, this.creationRequest);
108      } catch (final RuntimeException e) {
109        t = e;
110      } finally {
111        try {
112          this.releaser.close();
113        } catch (final RuntimeException e) {
114          if (t == null) {
115            throw e;
116          }
117          t.addSuppressed(e);
118        } catch (final InterruptedException e) {
119          Thread.currentThread().interrupt();
120          if (t == null) {
121            throw new ScopeletException(e.getMessage(), e);
122          }
123          t.addSuppressed(e);
124        } catch (final Exception e) {
125          if (t == null) {
126            throw new ScopeletException(e.getMessage(), e);
127          }
128          t.addSuppressed(e);
129        }
130      }
131      if (t != null) {
132        throw t;
133      }
134    }
135  }
136
137  /**
138   * Returns {@code true} if and only if this {@link Instance} has been {@linkplain #close() closed}.
139   *
140   * @return {@code true} if and only if this {@link Instance} has been {@linkplain #close() closed}
141   */
142  public final boolean closed() {
143    return this.closed; // volatile read
144  }
145
146  @Override
147  public final int hashCode() {
148    // We don't want "closedness" to factor in here because it isn't part of equals(). But we want to use the results of
149    // get(). Fortunately, that method is final. So we can just use direct field access.
150    return this.object.hashCode();
151  }
152
153  @Override
154  public final boolean equals(final Object other) {
155    if (other == this) {
156      return true;
157    } else if (other != null && this.getClass() == other.getClass()) {
158      // We don't want "closedness" to factor in here because it isn't part of hashCode(). But we want to use the
159      // results of get(). Fortunately, that method is final. So we can just use direct field access.
160      return Objects.equals(this.object, ((Instance<?>)other).object);
161    } else {
162      return false;
163    }
164  }
165
166  @Override
167  public final String toString() {
168    return String.valueOf(this.object);
169  }
170
171  private static final void sink() {
172
173  }
174
175  private static final <A, B> void sink(final A a, final B b) {
176
177  }
178
179  /**
180   * An interface whose implementations can destroy contextual instances.
181   *
182   * @param <I> the type borne by instances
183   *
184   * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
185   */
186  @FunctionalInterface
187  public static interface Destructor<I> {
188
189    /**
190     * Destroys the supplied contextual instance.
191     *
192     * @param i the contextual instance to destroy; may be {@code null}
193     *
194     * @param creationRequest the {@link Request} that caused the contextual instance to be created; may be {@code null}
195     */
196    public void destroy(final I i, final Request<I> creationRequest);
197
198  }
199
200}