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.producer;
015
016import java.util.LinkedHashSet;
017import java.util.SequencedSet;
018
019import java.util.function.Function;
020
021import org.microbean.bean.Aggregate;
022import org.microbean.bean.Assignment;
023import org.microbean.bean.AttributedElement;
024import org.microbean.bean.AttributedType;
025import org.microbean.bean.Creation;
026import org.microbean.bean.Destruction;
027import org.microbean.bean.Id;
028import org.microbean.bean.ReferencesSelector;
029
030import static java.util.Collections.unmodifiableSequencedSet;
031
032/**
033 * An interface whose implementations {@linkplain #produce(Creation) produce} and commonly initialize contextual
034 * instances.
035 *
036 * <p>{@link Producer}s are used to implement {@link org.microbean.bean.Factory} instances' {@link
037 * org.microbean.bean.Factory#create(Creation) create(Creation)} and {@link org.microbean.bean.Factory#destroy(Object,
038 * Destruction) destroy(Object, Destruction)} methods.</p>
039 *
040 * <p>A {@link Producer} normally initializes the contextual instances it produces as part of its {@link
041 * #produce(Creation)} method implementation, but is not required to.</p>
042 *
043 * @param <I> the contextual instance type
044 *
045 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
046 *
047 * @see #produce(Creation)
048 *
049 * @see #dependencies()
050 *
051 * @see #dispose(Object, Destruction)
052 *
053 * @see org.microbean.bean.Factory#create(Creation)
054 */
055// Subordinate to Factory<I> (but looking more and more like it every day)
056// Akin to CDI's Producer.
057// Handles instance production, interception and disposal, *including intercepted production*.
058//
059// Also handles initialization. We may want to revisit this. See for example
060// https://github.com/search?q=repo%3Aweld%2Fcore+%22.produce%28%29%22+language%3AJava&type=code
061//
062// See also: InterceptingProducer
063public interface Producer<I> extends Aggregate {
064
065  /**
066   * A convenience method that returns an immutable, determinate {@link SequencedSet} of {@link AttributedElement}s
067   * consisting of this {@link Producer}'s {@linkplain #productionDependencies() production dependencies} followed by
068   * its {@linkplain #initializationDependencies() initialization dependencies}.
069   *
070   * <p>There is normally no need to override the default implementation of this method.</p>
071   *
072   * @return a non-{@code null}, immutable, determinate {@link SequencedSet} of {@link AttributedElement}s consisting of
073   * this {@link Producer}'s {@linkplain #productionDependencies() production dependencies} followed by its {@linkplain
074   * #initializationDependencies() initialization dependencies}
075   *
076   * @see #productionDependencies()
077   *
078   * @see #initializationDependencies()
079   */
080  @Override // Aggregate
081  public default SequencedSet<AttributedElement> dependencies() {
082    final SequencedSet<AttributedElement> productionDependencies = this.productionDependencies();
083    final SequencedSet<AttributedElement> initializationDependencies = this.initializationDependencies();
084    if (productionDependencies.isEmpty()) {
085      return initializationDependencies;
086    } else if (initializationDependencies.isEmpty()) {
087      return productionDependencies;
088    }
089    final LinkedHashSet<AttributedElement> d = new LinkedHashSet<>();
090    d.addAll(productionDependencies);
091    d.addAll(initializationDependencies);
092    return unmodifiableSequencedSet(d);
093  }
094
095  /**
096   * Disposes of the supplied contextual instance.
097   *
098   * <p>The default implementation of this method checks to see if {@code i} is an instance of {@link AutoCloseable},
099   * and, if so, calls {@link AutoCloseable#close() close()} on it, throwing any resulting exception as a {@link
100   * DisposalException}.</p>
101   *
102   * @param i a contextual instance {@linkplain #produce(Creation) produced} by this {@link Producer}; may be {@code
103   * null}
104   *
105   * @param r the {@link Creation} that was {@linkplain #produce(Creation) present at production time}; must not be {@code
106   * null}
107   *
108   * @exception NullPointerException if {@code r} is {@code null}
109   *
110   * @exception DisposalException if {@code i} is an {@link AutoCloseable} instance, and if its {@link
111   * AutoCloseable#close() close()} method throws a checked exception
112   */
113  public default void dispose(final I i, final Destruction r) {
114    if (i instanceof AutoCloseable ac) {
115      try {
116        ac.close();
117      } catch (final RuntimeException | Error e) {
118        throw e;
119      } catch (final InterruptedException e) {
120        Thread.currentThread().interrupt();
121        throw new DisposalException(e.getMessage(), e);
122      } catch (final Exception e) {
123        throw new DisposalException(e.getMessage(), e);
124      }
125    }
126  }
127
128  /**
129   * Returns an immutable, determinate {@link SequencedSet} of {@link AttributedElement}s representing dependencies
130   * required for initialization.
131   *
132   * <p>Such dependencies may represent initialization method parameters and/or fields.</p>
133   *
134   * <p>Contrast initialization dependencies with <dfn>production dependencies</dfn>.</p>
135   *
136   * @return a non-{@code null}, immutable, determinate {@link SequencedSet} of {@link AttributedElement}s representing
137   * dependencies required for initialization
138   *
139   * @see #productionDependencies()
140   */
141  public SequencedSet<AttributedElement> initializationDependencies();
142
143  /**
144   * Produces a new contextual instance and returns it by calling the {@link #produce(Id, SequencedSet)} method with the
145   * return value of an invocation of the {@link #assign(Function)} method with a reference to the supplied {@link
146   * Creation}'s {@link ReferencesSelector#reference(AttributedType) reference(AttributedType)} method.
147   *
148   * @param c a {@link Creation}; must not be {@code null}
149   *
150   * @return a new contextual instance, or {@code null}
151   *
152   * @exception NullPointerException if {@code c} is {@code null}
153   *
154   * @see #produce(Id, SequencedSet)
155   *
156   * @see #dependencies()
157   */
158  public default I produce(final Creation<I> c) {
159    return this.produce(c.id(), this.assign(c::reference));
160  }
161
162  /**
163   * Produces a new contextual instance and returns it, possibly (often) making use of the supplied assignments.
164   *
165   * <p>Implementations of this method must not call {@link #produce(Creation)} or an infinite loop may result.</p>
166   *
167   * @param id an {@link Id} for which production is occurring; must not be {@code null}
168   *
169   * @param assignments a {@link SequencedSet} of {@link Assignment}s this {@link Producer} needs to complete production
170   * and possibly initialization; must not be {@code null}
171   *
172   * @return a new contextual instance, or {@code null}
173   *
174   * @exception NullPointerException if {@code assignments} is {@code null}
175   */
176  public I produce(final Id id, final SequencedSet<? extends Assignment<?>> assignments);
177
178  /**
179   * Returns an immutable, determinate {@link SequencedSet} of {@link AttributedElement}s representing dependencies
180   * required for production.
181   *
182   * <p>Such dependencies normally represent constructor parameters.</p>
183   *
184   * <p>Contrast production dependencies with <dfn>initialization dependencies</dfn>.</p>
185   *
186   * @return a non-{@code null}, immutable, determinate {@link SequencedSet} of {@link AttributedElement}s representing
187   * dependencies required for production
188   *
189   * @see #initializationDependencies()
190   */
191  public SequencedSet<AttributedElement> productionDependencies();
192
193}