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}