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