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.constant.ClassDesc;
017import java.lang.constant.Constable;
018import java.lang.constant.ConstantDesc;
019import java.lang.constant.DynamicConstantDesc;
020import java.lang.constant.MethodHandleDesc;
021
022import java.util.List;
023import java.util.Objects;
024import java.util.Optional;
025
026import org.microbean.bean.AutoCloseableRegistry;
027import org.microbean.bean.DisposableReference;
028import org.microbean.bean.Factory;
029import org.microbean.bean.Id;
030import org.microbean.bean.Request;
031
032import org.microbean.constant.Constables;
033
034import org.microbean.construct.Domain;
035
036import static java.lang.constant.ConstantDescs.BSM_INVOKE;
037
038import static org.microbean.assign.Qualifiers.anyQualifier;
039
040import static org.microbean.scope.Scope.NONE_ID;
041import static org.microbean.scope.Scope.SINGLETON_ID;
042
043/**
044 * A {@link Scopelet} implementation that does not cache objects at all.
045 *
046 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
047 */
048public class NoneScopelet extends Scopelet<NoneScopelet> implements Constable {
049
050  private static final boolean useDisposableReferences =
051    Boolean.parseBoolean(System.getProperty("useDisposableReferences", "false"));
052
053  private final Domain domain;
054
055  /**
056   * Creates a new {@link NoneScopelet}.
057   *
058   * @param domain a {@link Domain}; must not be {@code null}
059   *
060   * @exception NullPointerException if {@code domain} is {@code null}
061   */
062  public NoneScopelet(final Domain domain) {
063    super(NONE_ID); // the scope we implement
064    this.domain = Objects.requireNonNull(domain, "domain");
065  }
066
067  @Override // Scopelet<NoneScopelet>
068  public Id id() {
069    return
070      new Id(List.of(this.domain.declaredType(NoneScopelet.class.getName()),
071                     this.domain.declaredType(null,
072                                              this.domain.typeElement(Scopelet.class.getName()),
073                                              this.domain.declaredType(NoneScopelet.class.getName()))),
074             List.of(NONE_ID, anyQualifier()), // qualifiers
075             SINGLETON_ID); // the scope we belong to
076  }
077
078  // All parameters are nullable.
079  // Non-final to permit subclasses to, e.g., add logging.
080  @Override // Scopelet<NoneScopelet>
081  public <I> I instance(final Object ignoredBeanId,
082                        final Factory<I> factory,
083                        final Request<I> request) {
084    if (!this.active()) {
085      throw new InactiveScopeletException();
086    } else if (factory == null) {
087      return null;
088    }
089    final I returnValue = factory.create(request);
090    if (factory.destroys()) {
091      if (useDisposableReferences) {
092        // Merely creating a DisposableReference will cause it to get disposed IF garbage collection runs (which is not guaranteed).
093        new DisposableReference<>(returnValue, referent -> factory.destroy(referent, request));
094      } else if (request instanceof AutoCloseableRegistry acr) {
095        acr.register(new Instance<>(returnValue, factory::destroy, request));
096      } else {
097        // TODO: warn or otherwise point out that dependencies will not be destroyed
098      }
099    }
100    return returnValue;
101  }
102
103  @Override // Constable
104  public Optional<? extends ConstantDesc> describeConstable() {
105    return (this.domain instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty())
106      .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE,
107                                             MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()),
108                                                                            ClassDesc.of(Domain.class.getName())),
109                                             domainDesc));
110  }
111
112}