All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.quarkiverse.web.bundler.deployment.devui.WebBundlerDevUIWebDependenciesProcessor Maven / Gradle / Ivy

There is a newer version: 1.8.0
Show newest version
package io.quarkiverse.web.bundler.deployment.devui;

import static io.quarkiverse.web.bundler.deployment.devui.DevUIWebDependenciesBuildItem.DevUIWebDependency;
import static io.quarkiverse.web.bundler.deployment.devui.DevUIWebDependenciesBuildItem.WebDependencyAsset;
import static io.quarkiverse.web.bundler.deployment.web.GeneratedWebResourcesProcessor.resolveFromRootPath;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.jboss.logging.Logger;

import io.quarkiverse.web.bundler.deployment.items.WebDependenciesBuildItem;
import io.quarkiverse.web.bundler.deployment.util.PathUtils;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.maven.dependency.ArtifactKey;
import io.quarkus.maven.dependency.ResolvedDependency;
import io.quarkus.vertx.http.runtime.HttpBuildTimeConfig;

public class WebBundlerDevUIWebDependenciesProcessor {

    private static final String PREFIX = "META-INF/resources/";
    private static final String WEBJARS_PATH = "webjars";
    private static final String MVNPM_PATH = "_static";

    private static final Logger log = Logger.getLogger(WebBundlerDevUIWebDependenciesProcessor.class.getName());

    @BuildStep(onlyIf = IsDevelopment.class)
    public DevUIWebDependenciesBuildItem findWebDependenciesAssets(
            HttpBuildTimeConfig httpConfig,
            LiveReloadBuildItem liveReload,
            WebDependenciesBuildItem webDependencies) {
        final DevUIWebDependenciesContext webDependenciesContext = liveReload
                .getContextObject(DevUIWebDependenciesContext.class);

        if (liveReload.isLiveReload() && webDependenciesContext != null
                && webDependenciesContext.dependencies().equals(webDependencies.list())) {
            return new DevUIWebDependenciesBuildItem(webDependenciesContext.devUIWebDependencies());
        }

        final List providers = new ArrayList<>();
        providers.addAll(QuarkusClassLoader.getElements(PREFIX + MVNPM_PATH, false));
        providers.addAll(QuarkusClassLoader.getElements(PREFIX + WEBJARS_PATH, false));

        if (!providers.isEmpty()) {
            // Map of webDependency artifact keys to class path elements
            final Map providersByKeys = providers.stream()
                    .filter(provider -> provider.getDependencyKey() != null)
                    .collect(Collectors.toMap(ClassPathElement::getDependencyKey, provider -> provider, (a, b) -> b,
                            () -> new HashMap<>(providers.size())));

            final List webJarDeps = new ArrayList<>(webDependencies.list().size());
            for (WebDependenciesBuildItem.Dependency dependency : webDependencies.list()) {
                final DevUIWebDependency dep = getDep(httpConfig, providersByKeys, dependency);
                if (dep != null) {
                    webJarDeps.add(dep);
                }
            }
            liveReload.setContextObject(DevUIWebDependenciesContext.class,
                    new DevUIWebDependenciesContext(webDependencies.list(), webJarDeps));
            return new DevUIWebDependenciesBuildItem(webJarDeps);
        }
        liveReload.setContextObject(DevUIWebDependenciesContext.class, new DevUIWebDependenciesContext());
        return new DevUIWebDependenciesBuildItem(List.of());
    }

    private DevUIWebDependency getDep(HttpBuildTimeConfig httpConfig, Map providersByKeys,
            WebDependenciesBuildItem.Dependency webDependency) {
        String path = getTypePath(webDependency);
        final String webDependencyRootPath = PathUtils.addTrailingSlash(resolveFromRootPath(httpConfig, path));

        return createWebDependency(webDependency, webDependencyRootPath, providersByKeys, path);
    }

    private static String getTypePath(WebDependenciesBuildItem.Dependency webDependency) {
        return switch (webDependency.type()) {
            case MVNPM -> MVNPM_PATH;
            case WEBJARS -> WEBJARS_PATH;
        };
    }

    private DevUIWebDependency createWebDependency(WebDependenciesBuildItem.Dependency webDep,
            String webDependencyRootPath,
            Map providersByKeys,
            String path) {
        final ResolvedDependency dep = webDep.resolvedDependency();
        final ClassPathElement provider = providersByKeys.get(dep.getKey());
        if (provider == null) {
            return null;
        }
        return provider.apply(tree -> {
            final Path webDependenciesDir = tree.getPath(PREFIX + path);
            final Path nameDir;
            try (Stream webDependenciesDirPaths = Files.list(webDependenciesDir)) {
                nameDir = webDependenciesDirPaths.filter(Files::isDirectory).findFirst().orElseThrow(() -> new IOException(
                        "Could not find name directory for " + dep.getKey().getArtifactId() + " in " + webDependenciesDir));
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
            final Path versionDir;
            Path root = nameDir;
            // The base URL for the Web Dependency
            final StringBuilder urlBase = new StringBuilder(webDependencyRootPath);
            boolean appendRootPart = true;
            try {
                // If the version directory exists, use it as a root, otherwise use the name directory
                versionDir = nameDir.resolve(dep.getVersion());
                root = Files.isDirectory(versionDir) ? versionDir : nameDir;
                urlBase.append(nameDir.getFileName().toString())
                        .append("/");
                appendRootPart = false;
            } catch (InvalidPathException e) {
                log.warn("Could not find version directory for " + dep.getKey().getArtifactId() + " "
                        + dep.getVersion() + " in " + nameDir + ", falling back to name directory");
            }
            try {
                // Create the asset tree for the web dependency and set it as the root asset
                var asset = createAssetForDep(root, urlBase.toString(), appendRootPart);
                return new DevUIWebDependency(webDep.type().toString(),
                        provider.getDependencyKey().getGroupId() + ":" + provider.getDependencyKey().getArtifactId(),
                        dep.getVersion(), asset);
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        });
    }

    private WebDependencyAsset createAssetForDep(Path rootPath, String urlBase, boolean appendRootPart)
            throws IOException {
        //If it is a directory, go deeper, otherwise add the file
        urlBase = appendRootPart ? urlBase + rootPath.getFileName().toString() + "/" : urlBase;
        var root = new WebDependencyAsset(rootPath.getFileName().toString(),
                new LinkedList<>(),
                false,
                urlBase);

        try (DirectoryStream directoryStream = Files.newDirectoryStream(rootPath)) {
            for (Path childPath : directoryStream) {
                if (Files.isDirectory(childPath)) { // If it is a directory, go deeper, otherwise add the file
                    var childDir = createAssetForDep(childPath, urlBase, true);
                    root.children().add(childDir);
                } else {
                    var childFile = new WebDependencyAsset(childPath.getFileName().toString(),
                            null,
                            true,
                            urlBase + childPath.getFileName());
                    root.children().add(childFile);
                }
            }
        }
        // Sort the children by name
        root.children().sort(Comparator.comparing(WebDependencyAsset::fileAsset).thenComparing(WebDependencyAsset::name));
        return root;
    }

    record DevUIWebDependenciesContext(List dependencies,
            List devUIWebDependencies) {

        DevUIWebDependenciesContext() {
            this(null, null);
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy