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.bean;
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.Optional;
023
024import org.microbean.assign.Aggregate;
025
026import static java.lang.constant.ConstantDescs.BSM_INVOKE;
027
028/**
029 * A source of (normally new) contextual instances.
030 *
031 * @param <I> the contextual instance type
032 *
033 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
034 *
035 * @see #create(Creation)
036 *
037 * @see Aggregate
038 */
039public interface Factory<I> extends Aggregate, Constable {
040
041  /**
042   * Returns a (normally new) contextual instance, which may be {@code null}.
043   *
044   * @param creation a {@link Creation}; may be {@code null}
045   *
046   * @return a contextual instance, which may be {@code null}
047   *
048   * @see Creation
049   */
050  public I create(final Creation<I> creation);
051
052  /**
053   * Returns an {@link Optional} containing the nominal descriptor for this instance, if one can be constructed, or an
054   * {@linkplain Optional#isEmpty() empty <code>Optional</code>} if one cannot be constructed.
055   *
056   * <p>The default implementation of this method returns an {@link Optional} that contains a dynamic constant
057   * representing an invocation of the implementation's constructor that takes no arguments.  <strong>The resolution of
058   * this dynamic constant is undefined if the implementation does not declare such a constructor.</strong></p>
059   *
060   * @return an {@link Optional} containing the nominal descriptor for this instance, if one can be constructed, or an
061   * {@linkplain Optional#isEmpty() empty <code>Optional</code>} if one cannot be constructed
062   *
063   * @microbean.threadsafety This method is safe for concurrent use by multiple threads.
064   *
065   * @microbean.idempotency This method is neither idempotent nor deterministic.
066   */
067  @Override // Constable
068  public default Optional<? extends ConstantDesc> describeConstable() {
069    return
070      Optional.of(DynamicConstantDesc.of(BSM_INVOKE,
071                                         MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getCanonicalName()))));
072  }
073
074  /**
075   * Destroys the supplied contextual instance.
076   *
077   * <p>The default implementation of this method {@linkplain AutoCloseable#close() closes} the supplied contextual
078   * instance if it is an instance of {@link AutoCloseable}, and {@linkplain AutoCloseable#close() closes} the supplied
079   * {@link Destruction} if it is non-{@code null}.</p>
080   *
081   * @param i the contextual instance to destroy; may be {@code null} in which case no action must be taken
082   *
083   * @param creation the object supplied to the {@link #create(Creation)} method represented here as a {@link
084   * Destruction}; may be {@code null}; must have an idempotent {@link AutoCloseable#close() close()} method
085   *
086   * @see #create(Creation)
087   *
088   * @see Destruction
089   *
090   * @see Creation
091   */
092  @SuppressWarnings("try")
093  public default void destroy(final I i, final Destruction creation) {
094    if (creation == null) {
095      if (i instanceof AutoCloseable ac) {
096        try {
097          ac.close();
098        } catch (final RuntimeException | Error e) {
099          throw e;
100        } catch (final InterruptedException e) {
101          Thread.currentThread().interrupt();
102          throw new DestructionException(e.getMessage(), e);
103        } catch (final Exception e) {
104          throw new DestructionException(e.getMessage(), e);
105        }
106      }
107    } else if (!(creation instanceof Creation<I>)) {
108      throw new IllegalArgumentException("creation: " + creation);
109    } else if (creation instanceof AutoCloseable cac) {
110      try (cac) {
111        if (i instanceof AutoCloseable iac) {
112          iac.close();
113        }
114      } catch (final RuntimeException | Error e) {
115        throw e;
116      } catch (final InterruptedException e) {
117        Thread.currentThread().interrupt();
118        throw new DestructionException(e.getMessage(), e);
119      } catch (final Exception e) {
120        throw new DestructionException(e.getMessage(), e);
121      }
122    } else if (i instanceof AutoCloseable ac) {
123      try {
124        ac.close();
125      } catch (final RuntimeException | Error e) {
126        throw e;
127      } catch (final InterruptedException e) {
128        Thread.currentThread().interrupt();
129        throw new DestructionException(e.getMessage(), e);
130      } catch (final Exception e) {
131        throw new DestructionException(e.getMessage(), e);
132      }
133    }
134  }
135
136  /**
137   * Returns {@code true} if this {@link Factory} implementation {@linkplain #destroy(Object, Destruction) destroys} its
138   * {@linkplain #create(Creation) created} contextual instances in some way, or {@code false} if it does not.
139   *
140   * <p>The default implementation of this method returns {@code true}.</p>
141   *
142   * <p>Overrides of this method must be idempotent and return a determinate value.</p>
143   *
144   * @return {@code true} if this {@link Factory} implementation {@linkplain #destroy(Object, Destruction) destroys} its
145   * {@linkplain #create(Creation) created} contextual instances in some way; {@code false} otherwise
146   *
147   * @see #destroy(Object, Destruction)
148   */
149  public default boolean destroys() {
150    return true;
151  }
152
153  /**
154   * Returns the sole contextual instance of this {@link Factory}'s type, if there is one, or {@code null} in the very
155   * common case that there is not.
156   *
157   * <p>The default implementation of this method returns {@code null}.</p>
158   *
159   * <p>Overrides of this method should not call {@link #create(Creation)}.</p>
160   *
161   * <p>Overrides of this method must be idempotent and must return a determinate value.</p>
162   *
163   * @return the sole contextual instance of this {@link Factory}'s type, or (commonly) {@code null}
164   */
165  public default I singleton() {
166    return null;
167  }
168
169}