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}