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}