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