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; 017import java.util.List; 018import java.util.NoSuchElementException; 019import java.util.Objects; 020 021import javax.lang.model.type.TypeMirror; 022 023import org.microbean.bean.AmbiguousReductionException; 024import org.microbean.bean.AttributedType; 025import org.microbean.bean.AutoCloseableRegistry; 026import org.microbean.bean.Bean; 027import org.microbean.bean.BeanException; 028import org.microbean.bean.Creation; 029import org.microbean.bean.DefaultAutoCloseableRegistry; 030import org.microbean.bean.Destruction; 031import org.microbean.bean.Factory; 032import org.microbean.bean.Id; 033import org.microbean.bean.Reducible; 034import org.microbean.bean.References; 035import org.microbean.bean.ReferencesSelector; 036import org.microbean.bean.Selectable; 037import org.microbean.bean.UnsatisfiedReductionException; 038 039import org.microbean.construct.Domain; 040 041/** 042 * A central object representing a request for dependencies that is a {@link Creation} (and therefore also a {@link 043 * Destruction}) and a {@link References}. 044 * 045 * <p>Instances of this class are the heart and soul of a dependency injection and acquisition system.</p> 046 * 047 * @param <I> the contextual instance type (see for example {@link Creation}) 048 * 049 * @param <R> the contextual reference type (see {@link References}) 050 * 051 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 052 * 053 * @see Creation 054 * 055 * @see Destruction 056 * 057 * @see References 058 * 059 * @see Factory#create(Creation) 060 * 061 * @see Factory#destroy(Object, Destruction) 062 */ 063public final class Request<I, R> implements Creation<I>, Destruction, References<R> { 064 065 066 /* 067 * Instance fields. 068 */ 069 070 071 private final Selectable<? super AttributedType, Bean<?>> s; 072 073 private final Instances instances; 074 075 private final AutoCloseableRegistry acr; 076 077 private final ClientProxier cp; 078 079 private final Bean<I> b; 080 081 private final AttributedType rType; 082 083 084 /* 085 * Constructors. 086 */ 087 088 089 /** 090 * Creates a new {@link Request}. 091 * 092 * @param s a {@link Selectable} providing access to {@link Bean}s by {@link AttributedType}; must not be {@code 093 * null}; must be safe for concurrent use by multiple threads; often assembled out of methods present in the {@link 094 * org.microbean.bean.Selectables} and {@link org.microbean.bean.Beans} classes, among other such utility classes 095 * 096 * @param instances an {@link Instances} responsible for using a {@link Bean} to acquire an appropriate {@link 097 * java.util.function.Supplier} of contextual instances; must not be {@code null} 098 * 099 * @param acr an {@link AutoCloseableRegistry}; may be {@code null} in which case a default implementation will be 100 * used instead 101 * 102 * @param cp a {@link ClientProxier}; must not be {@code null} 103 * 104 * @exception NullPointerException if {@code s}, {@code instances}, or {@code cp} is {@code null} 105 */ 106 public Request(final Selectable<? super AttributedType, Bean<?>> s, 107 final Instances instances, 108 final AutoCloseableRegistry acr, 109 final ClientProxier cp) { 110 this(s, instances, acr, cp, null, null); 111 } 112 113 private Request(final Selectable<? super AttributedType, Bean<?>> s, 114 final Instances instances, 115 final AutoCloseableRegistry acr, // nullable 116 final ClientProxier cp, 117 final Bean<I> b, 118 final AttributedType rType) { // the type of the references returned; nullable 119 this.s = Objects.requireNonNull(s, "s"); 120 this.instances = Objects.requireNonNull(instances, "instances"); 121 this.cp = Objects.requireNonNull(cp, "cp"); 122 this.acr = acr == null ? new DefaultAutoCloseableRegistry() : acr; 123 this.b = b; 124 this.rType = rType; 125 } 126 127 128 /* 129 * Instance methods. 130 */ 131 132 133 @Override // Destruction 134 public final void close() { 135 try (this.instances; this.acr) {} 136 } 137 138 @Override // Creation<I> 139 public final Id id() { 140 return this.b == null ? null : this.b.id(); 141 } 142 143 @Override // References<R> (Iterable<R>) 144 public final Iterator<R> iterator() { 145 return new ReferencesIterator(); 146 } 147 148 // Called by ReferencesIterator below 149 private final R get(final Request<R, Void> r) { 150 final Bean<R> bean = r.b; 151 final Id id = bean.id(); 152 if (this.instances.proxiable(id)) { 153 final R ref = this.cp.clientProxy(id, instances.supplier(bean, r)); 154 // TODO: we know that ref can be destroyed because it's from a normal scope, i.e. it is not itself a contextual 155 // instance, so save it in a collection somewhere so it can be destroyed via the #destroy(R) method (see CDI's 156 // Instances#destroy(Object)) 157 return ref; 158 } 159 // TODO: ask instances if ref is destroyable and save it off 160 return instances.supplier(bean, r).get(); 161 } 162 163 @Override // ReferencesSelector 164 public final <R> References<R> references(final AttributedType t) { 165 return new Request<>(this.s, this.instances, this.acr, this.cp, this.b, t); 166 } 167 168 @Override // References<R> 169 public final int size() { 170 return this.s.select(this.rType).size(); 171 } 172 173 @Override // References<R> 174 public final boolean destroy(final R r) { 175 if (r != null) { // and is in dependent scope; we'll deal with that later 176 // TODO: remove it from a collection of dependent refs returned by get(Request) above 177 } 178 return false; 179 } 180 181 182 /* 183 * Inner and nested classes. 184 */ 185 186 187 private final class ReferencesIterator implements Iterator<R> { 188 189 private Iterator<Bean<?>> i; 190 191 private R ref; 192 193 private ReferencesIterator() { 194 super(); 195 } 196 197 @Override // Iterator<R> 198 public final boolean hasNext() { 199 if (rType == null) { 200 return false; 201 } 202 if (this.i == null) { 203 this.i = s.select(rType).iterator(); 204 } 205 return this.i.hasNext(); 206 } 207 208 @Override // Iterator<R> 209 public final R next() { 210 if (rType == null) { 211 throw new NoSuchElementException(); 212 } 213 if (this.i == null) { 214 this.i = s.select(rType).iterator(); 215 } 216 @SuppressWarnings("unchecked") 217 final R ref = get(new Request<>(s, instances, acr.newChild(), cp, (Bean<R>)this.i.next(), null)); 218 this.ref = ref; 219 return ref; 220 } 221 222 @Override // Iterator<R> 223 public final void remove() { 224 final R ref = this.ref; 225 this.ref = null; 226 Request.this.destroy(ref); 227 } 228 229 } 230 231}