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.core; 020 021import java.nio.file.Path; 022import java.util.Arrays; 023import java.util.EnumSet; 024import java.util.concurrent.ExecutorService; 025import java.util.concurrent.Executors; 026import java.util.stream.Stream; 027import org.apache.commons.cli.CommandLine; 028import org.jdrupes.builder.api.BuildContext; 029import org.jdrupes.builder.api.Intent; 030import org.jdrupes.builder.api.Project; 031import org.jdrupes.builder.api.Resource; 032import org.jdrupes.builder.api.ResourceProvider; 033import org.jdrupes.builder.api.ResourceRequest; 034import org.jdrupes.builder.core.FutureStreamCache.Key; 035 036/// A context for building. 037/// 038public class DefaultBuildContext implements BuildContext { 039 040 /// The key for specifying the builder directory in the properties file. 041 public static final String JDBLD_DIRECTORY = "jdbldDirectory"; 042 private final FutureStreamCache cache; 043 private ExecutorService executor 044 = Executors.newVirtualThreadPerTaskExecutor(); 045 046 /// Instantiates a new default build. By default, the build uses 047 /// a virtual thread per task executor. 048 /// 049 /* default */ DefaultBuildContext() { 050 cache = new FutureStreamCache(); 051 } 052 053 /// Returns the executor service used by this build to create futures. 054 /// 055 /// @return the executor service 056 /// 057 public ExecutorService executor() { 058 return executor; 059 } 060 061 /// Sets the executor service used by this build to create futures. 062 /// 063 /// @param executor the executor 064 /// 065 public void executor(ExecutorService executor) { 066 this.executor = executor; 067 } 068 069 @Override 070 public <T extends Resource> Stream<T> resources(ResourceProvider provider, 071 ResourceRequest<T> requested) { 072 if (provider instanceof Project project) { 073 var defReq = (DefaultResourceRequest<T>) requested; 074 if (Arrays.asList(defReq.queried()).contains(provider)) { 075 return Stream.empty(); 076 } 077 // Log invocation 078 requested = defReq.queried(project); 079 // As a project's provide only delegates to other providers 080 // it is inefficient to invoke it asynchronously. Besides, it 081 // leads to recursive invocations of the project's deploy 082 // method too easily and results in a loop detection without 083 // there really being a loop. 084 return ((AbstractProvider) provider).doProvide(requested); 085 } 086 var req = requested; 087 if (!req.uses().isEmpty()) { 088 req = requested.using(EnumSet.noneOf(Intent.class)); 089 } 090 return cache.computeIfAbsent(new Key<>(provider, req), 091 k -> new FutureStream<T>(executor, k.provider(), k.request())) 092 .stream(); 093 } 094 095 @Override 096 public Path jdbldDirectory() { 097 return Path 098 .of(LauncherSupport.jdbldProperties().getProperty(JDBLD_DIRECTORY)); 099 } 100 101 @Override 102 public CommandLine commandLine() { 103 return LauncherSupport.commandLine(); 104 } 105 106 @Override 107 public String property(String name, String defaultValue) { 108 return LauncherSupport.jdbldProperties().getProperty(name, 109 defaultValue); 110 } 111 112}