001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2024–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.ArrayDeque; 017import java.util.Collection; 018import java.util.List; 019import java.util.Map; 020import java.util.Objects; 021import java.util.Queue; 022 023import org.microbean.bean.AttributedType; 024import org.microbean.bean.AutoCloseableRegistry; 025import org.microbean.bean.Bean; 026import org.microbean.bean.BeanQualifiersMatcher; 027import org.microbean.bean.BeanReduction; 028import org.microbean.bean.BeanTypeMatcher; 029import org.microbean.bean.Creation; 030import org.microbean.bean.DefaultAutoCloseableRegistry; 031import org.microbean.bean.IdMatcher; 032import org.microbean.bean.InterceptorBindingsMatcher; 033import org.microbean.bean.RankedReducer; 034import org.microbean.bean.Reducer; 035import org.microbean.bean.Reducible; 036import org.microbean.bean.Request; 037import org.microbean.bean.Selectable; 038 039import org.microbean.construct.Domain; 040 041import static org.microbean.bean.Beans.cachingSelectableOf; 042 043/** 044 * A basic implementation of the {@link Request} interface. 045 * 046 * @param <I> the type of contextual instance initiating the request 047 * 048 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 049 */ 050public class DefaultRequest<I> 051 implements Request<I>, 052 AutoCloseableRegistry, 053 Reducible<AttributedType, Bean<?>>, 054 Selectable<AttributedType, Bean<?>> { 055 056 057 /* 058 * Static fields. 059 */ 060 061 062 private static final ThreadLocal<Queue<Request<?>>> REQUESTS = ThreadLocal.withInitial(ArrayDeque::new); 063 064 065 /* 066 * Instance fields. 067 */ 068 069 070 private final AutoCloseableRegistry acr; 071 072 private final BeanReduction<I> beanReduction; 073 074 private final Creation<I> c; 075 076 private final ClientProxier cp; 077 078 private final Instances instances; 079 080 private final Reducible<AttributedType, Bean<?>> reducible; 081 082 private final Selectable<AttributedType, Bean<?>> selectable; 083 084 085 /* 086 * Constructors. 087 */ 088 089 090 /** 091 * Creates a new {@link DefaultRequest}. 092 * 093 * @param selectable a {@link Selectable} that selects {@link Bean}s given an {@link AttributedType}; must not be {@code null} 094 * 095 * @param reducible a {@link Reducible} that reduces collections of {@link Bean}s; must not be {@code null} 096 * 097 * @param instances an {@link Instances} implementation; must not be {@code null} 098 * 099 * @param cp a {@link ClientProxier}; must not be {@code null} 100 * 101 * @exception NullPointerException if any argument is {@code null} 102 */ 103 public DefaultRequest(final Selectable<AttributedType, Bean<?>> selectable, 104 final Reducible<AttributedType, Bean<?>> reducible, // normally Reducible.of(selectable, RankedReducer.of()) 105 final Instances instances, 106 final ClientProxier cp) { // not nullable 107 this(selectable, 108 reducible, 109 instances, 110 null, 111 null, 112 cp, 113 null); 114 } 115 116 /** 117 * Creates a new {@link DefaultRequest}. 118 * 119 * @param selectable a {@link Selectable} that selects {@link Bean}s given an {@link AttributedType}; must not be {@code null} 120 * 121 * @param reducible a {@link Reducible} that reduces collections of {@link Bean}s; must not be {@code null} 122 * 123 * @param instances an {@link Instances} implementation; must not be {@code null} 124 * 125 * @param acr an {@link AutoCloseableRegistry}; may be {@code null} in which case a default implementation will be 126 * used instead 127 * 128 * @param c a {@link Creation} implementation; 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 selectable}, {@code reducible}, {@code instances} or {@code cp} is {@code 134 * null} 135 */ 136 public DefaultRequest(final Selectable<AttributedType, Bean<?>> selectable, 137 final Reducible<AttributedType, Bean<?>> reducible, // normally Reducible.of(selectable, RankedReducer.of()) 138 final Instances instances, 139 final AutoCloseableRegistry acr, // nullable 140 final Creation<I> c, // nullable 141 final ClientProxier cp) { // not nullable 142 this(selectable, 143 reducible, 144 instances, 145 acr, 146 c, 147 cp, 148 null); 149 } 150 151 private DefaultRequest(final Selectable<AttributedType, Bean<?>> selectable, 152 final Reducible<AttributedType, Bean<?>> reducible, // normally Reducible.of(selectable, RankedReducer.of()) 153 final Instances instances, 154 final AutoCloseableRegistry acr, // nullable 155 final Creation<I> c, // nullable 156 final ClientProxier cp, // not nullable 157 final BeanReduction<I> beanReduction) { // nullable, but only for the first one 158 super(); 159 this.selectable = Objects.requireNonNull(selectable, "selectable"); 160 this.reducible = Objects.requireNonNull(reducible, "reducible"); 161 this.instances = Objects.requireNonNull(instances, "instances"); 162 this.cp = Objects.requireNonNull(cp, "cp"); 163 this.beanReduction = beanReduction; 164 this.acr = acr == null ? new DefaultAutoCloseableRegistry() : acr; 165 this.c = c == null ? i -> {} : c; 166 } 167 168 169 /* 170 * Instance methods. 171 */ 172 173 174 @Override // Request<I> 175 public final BeanReduction<I> beanReduction() { 176 return this.beanReduction; // will be null for the first/primordial Request and no other 177 } 178 179 @Override // Request<I> 180 @SuppressWarnings("unchecked") 181 public <J> DefaultRequest<J> child(final BeanReduction<J> beanReduction) { 182 if (beanReduction.equals(this.beanReduction)) { 183 return (DefaultRequest<J>)this; 184 } 185 final DefaultRequest<J> child = 186 new DefaultRequest<J>(this.selectable, 187 this.reducible, 188 this.instances, 189 this.newChild(), // NOTE 190 (Creation<J>)this.c, 191 this.cp, 192 beanReduction); 193 if (!this.register(child)) { 194 throw new IllegalStateException(); 195 } 196 return child; 197 } 198 199 @Override // AutoCloseableRegistry, Instances (AutoCloseable), Request (AutoCloseable) 200 public void close() { 201 this.acr.close(); 202 this.instances.close(); 203 } 204 205 @Override // AutoCloseableRegistry 206 public boolean closed() { 207 return this.acr.closed(); 208 } 209 210 @Override // Creation<I> 211 public final void created(final I i) { 212 this.c.created(i); 213 } 214 215 @Override // AutoCloseableRegistry 216 public final AutoCloseableRegistry newChild() { 217 return this.acr.newChild(); 218 } 219 220 @Override // Reducible<AttributedType, Bean<?>> // mostly for convenience 221 public final Bean<?> reduce(final AttributedType attributedType) { 222 return this.reducible.reduce(attributedType); 223 } 224 225 @Override // Request<I> 226 @SuppressWarnings("unchecked") 227 public final <R> R reference(final AttributedType attributedType) { 228 return this.reference(new BeanReduction<>(attributedType, this.reduce(attributedType).cast()), (Request<R>)this); 229 } 230 231 @Override // ReferenceSelector 232 public final <R> R reference(final AttributedType attributedType, final Creation<R> c) { 233 return this.reference(new BeanReduction<>(attributedType, this.reduce(attributedType).cast()), this.toRequest(c)); 234 } 235 236 @Override // ReferenceSelector 237 public final <R> R reference(final AttributedType attributedType, final Bean<R> bean, final Creation<R> c) { 238 return this.reference(new BeanReduction<>(attributedType, bean == null ? this.reduce(attributedType).cast() : bean.cast()), this.toRequest(c)); 239 } 240 241 // Must not call any other reference() method 242 final <R> R reference(final BeanReduction<R> beanReduction, Request<R> r) { 243 final Request<R> request = (r == null ? this : r).child(beanReduction); // child(beanReduction) is critical 244 final Queue<Request<?>> requests = REQUESTS.get(); 245 requests.offer(request); // push 246 try { 247 return 248 this.instances.proxiable(request) ? 249 this.cp.clientProxy(request, this.instances.supplier(request)) : 250 this.instances.supplier(request).get(); 251 } finally { 252 requests.poll(); // pop 253 } 254 } 255 256 @Override // AutoCloseableRegistry 257 public boolean register(final AutoCloseable closeable) { 258 return this.acr.register(closeable); 259 } 260 261 @Override // Selectable<AttributedType, Bean<?>> // mostly for convenience 262 public final List<Bean<?>> select(final AttributedType attributedType) { 263 return this.selectable.select(attributedType); 264 } 265 266 @SuppressWarnings("unchecked") 267 private final <R> Request<R> toRequest(final Creation<R> c) { 268 // Note: deliberately no child/newChild semantics at all 269 return switch (c) { 270 case null -> null; 271 case Request<R> r -> r; 272 default -> new DefaultRequest<>(this.selectable, 273 this.reducible, 274 this.instances, 275 this.acr, 276 c, 277 this.cp, 278 (BeanReduction<R>)this.beanReduction); 279 }; 280 } 281 282}