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