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}