001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2025–2026 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.reference; 015 016import java.util.Iterator; 017 018import java.util.function.Supplier; 019 020import javax.lang.model.AnnotatedConstruct; 021 022import javax.lang.model.type.TypeMirror; 023 024import org.microbean.assign.Annotated; 025import org.microbean.assign.Selectable; 026 027import org.microbean.bean.Bean; 028import org.microbean.bean.BeanException; 029import org.microbean.bean.Creation; 030import org.microbean.bean.Destruction; 031import org.microbean.bean.Id; 032import org.microbean.bean.References; 033import org.microbean.bean.ReferencesSelector; 034 035import org.microbean.construct.Domain; 036 037import org.microbean.proxy.Proxy; 038 039import static java.util.Collections.emptyIterator; 040 041import static java.util.Objects.requireNonNull; 042 043/** 044 * A central object representing a request for dependencies that is a {@link Creation} (and therefore also, per 045 * contract, a {@link Destruction}), a {@link DestructorRegistry}, and a {@link References}. 046 * 047 * <p>Instances of this class are the heart and soul of a dependency injection and acquisition system.</p> 048 * 049 * @param <I> the contextual instance type being instantiated (see for example {@link Creation}) 050 * 051 * @param <R> the contextual reference type being sought (see {@link References}) 052 * 053 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 054 * 055 * @see Creation 056 * 057 * @see Destruction 058 * 059 * @see DestructorRegistry 060 * 061 * @see References 062 * 063 * @see org.microbean.bean.Factory#create(Creation) 064 * 065 * @see org.microbean.bean.Factory#destroy(Object, Destruction) 066 */ 067public final class Request<I, R> implements Creation<I>, Destruction, DestructorRegistry, References<R> { 068 069 070 /* 071 * Instance fields. 072 */ 073 074 075 private final Domain domain; 076 077 private final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> beans; 078 079 private final Instances instances; 080 081 private final DestructorTree destructorTree; 082 083 private final ClientProxier cp; 084 085 private final Bean<I> b; // nullable; B and R must then be (effectively) Void 086 087 private final Annotated<? extends AnnotatedConstruct> rConstruct; // nullable; R must then be Void 088 089 090 /* 091 * Constructors. 092 */ 093 094 095 /** 096 * Creates a new {@link Request}. 097 * 098 * @param d a {@link Domain}; must not be {@code null} 099 * 100 * @param s a {@link Selectable} providing access to {@link Bean}s by {@link AnnotatedConstruct}; must not be {@code 101 * null}; must be safe for concurrent use by multiple threads; often assembled out of methods present in the {@link 102 * org.microbean.bean.Selectables} and {@link org.microbean.bean.Beans} classes, among other such utility classes 103 * 104 * @param instances an {@link Instances} responsible for using a {@link Bean} to acquire an appropriate {@link 105 * Supplier} of contextual instances; must not be {@code null} 106 * 107 * @param cp a {@link ClientProxier}; must not be {@code null} 108 * 109 * @exception NullPointerException if any argument is {@code null} 110 * 111 * @see #Request(Domain, Selectable, Instances, DestructorTree, ClientProxier) 112 */ 113 public Request(final Domain d, 114 final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> s, 115 final Instances instances, 116 final ClientProxier cp) { 117 this(d, s, instances, null, cp, null, null); 118 } 119 120 /** 121 * Creates a new {@link Request}. 122 * 123 * @param d a {@link Domain}; must not be {@code null} 124 * 125 * @param s a {@link Selectable} providing access to {@link Bean}s by {@link AnnotatedConstruct}; must not be {@code 126 * null}; must be safe for concurrent use by multiple threads; often assembled out of methods present in the {@link 127 * org.microbean.bean.Selectables} and {@link org.microbean.bean.Beans} classes, among other such utility classes 128 * 129 * @param instances an {@link Instances} responsible for using a {@link Bean} to acquire an appropriate {@link 130 * Supplier} of contextual instances; must not be {@code null} 131 * 132 * @param destructorTree a {@link DestructorTree}; may be {@code null} in which case a default implementation will be used 133 * instead 134 * 135 * @param cp a {@link ClientProxier}; must not be {@code null} 136 * 137 * @exception NullPointerException if {@code s}, {@code instances}, or {@code cp} is {@code null} 138 */ 139 public Request(final Domain d, 140 final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> s, 141 final Instances instances, 142 final DestructorTree destructorTree, // nullable 143 final ClientProxier cp) { 144 this(d, s, instances, destructorTree, cp, null, null); 145 } 146 147 private Request(final Domain d, 148 final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> s, 149 final Instances instances, 150 final DestructorTree destructorTree, // nullable 151 final ClientProxier cp, 152 final Bean<I> b, // nullable 153 final Annotated<? extends AnnotatedConstruct> rConstruct) { // the type of the references returned (<R>); nullable 154 this.domain = requireNonNull(d, "d"); 155 this.beans = requireNonNull(s, "s"); 156 this.instances = requireNonNull(instances, "instances"); 157 this.cp = requireNonNull(cp, "cp"); 158 this.destructorTree = destructorTree == null ? new DefaultDestructorTree() : destructorTree; 159 this.b = b; 160 this.rConstruct = rConstruct; 161 } 162 163 164 /* 165 * Instance methods. 166 */ 167 168 169 @Override // Destruction 170 public final void close() { 171 this.destructorTree.close(); 172 } 173 174 @Override // ReferencesSelector 175 public final boolean destroy(final Object r) { 176 final Destructor destructor = this.destructorTree.remove(r instanceof Proxy<?> p ? p.$proxied() : r); 177 if (destructor != null) { 178 destructor.destroy(); // I keep going back and forth on whether this should be under some kind of lock, or whether the Destructor contract covers it 179 return true; 180 } 181 return false; 182 } 183 184 @Override // ReferencesSelector 185 public final Domain domain() { 186 return this.domain; 187 } 188 189 @Override // Creation<I> 190 public final Id id() { 191 return this.b == null ? null : this.b.id(); 192 } 193 194 @Override // References<R> (Iterable<R>) 195 public final Iterator<R> iterator() { 196 return new ReferencesIterator(); // inner class; see below 197 } 198 199 @Override // ReferencesSelector 200 public final <R> R reference(final Bean<R> bean) { 201 final Supplier<? extends R> supplier = this.instances.supplier(bean, this.newChild(bean)); // newChild is critical 202 final Id id = bean.id(); 203 return this.instances.proxiable(id) ? this.cp.clientProxy(id, supplier) : supplier.get(); 204 } 205 206 @Override // ReferencesSelector 207 @SuppressWarnings("unchecked") 208 public final <X> References<X> references(final Annotated<? extends AnnotatedConstruct> rConstruct) { 209 return this.rConstruct == rConstruct ? (References<X>)this : 210 // This basically returns "this" but with a new rConstruct. But Request is immutable so we make a copy. 211 new Request<>(this.domain, 212 this.beans, 213 this.instances, 214 this.destructorTree, // deliberately NO this.destructorTree.newChild() call 215 this.cp, 216 this.b, // nullable; <I> will then be (effectively) Void 217 rConstruct); // nullable; <X> will then be Void 218 } 219 220 @Override // DestructorTree (DestructorRegistry) 221 public final boolean register(final Object reference, final Destructor destructor) { 222 return this.destructorTree.register(reference, destructor); 223 } 224 225 @Override // References<R> 226 public final int size() { 227 return this.rConstruct == null ? 0 : this.beans.select(this.rConstruct).size(); 228 } 229 230 /* 231 * Private instance methods. 232 */ 233 234 private final Iterator<Bean<?>> beanIterator() { 235 return this.rConstruct == null ? emptyIterator() : this.beans.select(this.rConstruct).iterator(); 236 } 237 238 @SuppressWarnings("unchecked") 239 private final <X> Request<X, ?> newChild(final Bean<X> b) { 240 if (b == null) { 241 if (this.b == null) { 242 return (Request<X, R>)this; // both <X> and <R> are effectively Void 243 } 244 } else if (b.equals(this.b)) { 245 return (Request<X, R>)this; 246 } 247 return 248 new Request<X, Void>(this.domain, 249 this.beans, 250 this.instances, 251 this.destructorTree.newChild(), // critical; !b.equals(this.b) 252 this.cp, 253 b, // nullable; if so, <X> better resolve to Void 254 null); // rConstruct; <R> resolves to Void 255 } 256 257 258 /* 259 * Inner and nested classes. 260 */ 261 262 263 // NOT thread-safe. 264 private final class ReferencesIterator implements Iterator<R> { 265 266 private Iterator<Bean<?>> i; 267 268 private R ref; 269 270 private ReferencesIterator() { 271 super(); 272 if (Request.this.rConstruct == null) { 273 this.i = emptyIterator(); 274 } 275 } 276 277 @Override // Iterator<R> 278 public final boolean hasNext() { 279 if (this.i == null) { 280 this.i = beanIterator(); 281 } 282 return this.i.hasNext(); 283 } 284 285 @Override // Iterator<R> 286 @SuppressWarnings("unchecked") 287 public final R next() { 288 if (this.i == null) { 289 this.i = beanIterator(); 290 } 291 return this.ref = reference((Bean<R>)this.i.next()); 292 } 293 294 @Override // Iterator<R> 295 public final void remove() { 296 final R ref = this.ref; 297 if (ref == null) { 298 throw new IllegalStateException(); // per Iterator#remove() contract 299 } 300 this.ref = null; 301 destroy(ref); 302 } 303 304 } 305 306}