001/* 002 * JDrupes Builder 003 * Copyright (C) 2025 Michael N. Lipp 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <https://www.gnu.org/licenses/>. 017 */ 018 019package org.jdrupes.builder.api; 020 021import java.util.List; 022import java.util.Optional; 023import java.util.ServiceLoader; 024import java.util.stream.StreamSupport; 025 026/// Defines both an interface for factories that create [Resource]s and 027/// factory methods for invoking an appropriate factory. 028/// 029@SuppressWarnings("PMD.ImplicitFunctionalInterface") 030public interface ResourceFactory { 031 032 /// The factories as found by the [ServiceLoader]. 033 List<ResourceFactory> FACTORIES = StreamSupport.stream( 034 ServiceLoader.load(ResourceFactory.class).spliterator(), false) 035 .toList(); 036 037 /// Returns a new resource with the given type, passing the given 038 /// arguments to the constructor of the resource. The implementation 039 /// uses [ServiceLoader] to find a [ResourceFactory] that creates the 040 /// resource, i.e. that does not return `Optional.empty()` when 041 /// [newResource] is called. 042 /// 043 /// @param <T> the generic resource type 044 /// @param type the resource type 045 /// @param project the project 046 /// @param args the additional arguments 047 /// @return the resource 048 /// 049 static <T extends Resource> T create(ResourceType<T> type, 050 Project project, Object... args) { 051 return FACTORIES.stream().map(f -> f.newResource(type, project, args)) 052 .filter(Optional::isPresent).map(Optional::get).findFirst() 053 .orElseThrow(() -> new ConfigurationException() 054 .message("No resource factory for %s", type)); 055 } 056 057 /// Short for `create(type, null, args)`. 058 /// 059 /// @param <T> the generic resource type 060 /// @param type the resource type 061 /// @param args the additional arguments 062 /// @return the resource 063 /// 064 static <T extends Resource> T create(ResourceType<T> type, 065 Object... args) { 066 return create(type, null, args); 067 } 068 069 /// Returns a new resource of the given type if the factory instance 070 /// can create it. 071 /// 072 /// @param <T> the generic resource type 073 /// @param type the resource type 074 /// @param project the project 075 /// @param args the additional arguments 076 /// @return the result. `Optional.empty()` if the resource cannot 077 /// be created by the factory instance. 078 /// 079 <T extends Resource> Optional<T> newResource(ResourceType<T> type, 080 Project project, Object... args); 081 082}