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

org.wildfly.build.featurepack.FeaturePackBuilder Maven / Gradle / Ivy

/*
 * Copyright 2014 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.wildfly.build.featurepack;

import nu.xom.ParsingException;
import org.jboss.logging.Logger;
import org.wildfly.build.ArtifactFileResolver;
import org.wildfly.build.ArtifactResolver;
import org.wildfly.build.Locations;
import org.wildfly.build.featurepack.model.FeaturePackBuild;
import org.wildfly.build.common.model.CopyArtifact;
import org.wildfly.build.pack.model.Artifact;
import org.wildfly.build.pack.model.FeaturePack;
import org.wildfly.build.pack.model.FeaturePackArtifactResolver;
import org.wildfly.build.pack.model.FeaturePackDescription;
import org.wildfly.build.pack.model.FeaturePackDescriptionXMLWriter11;
import org.wildfly.build.pack.model.FeaturePackFactory;
import org.wildfly.build.common.model.FileFilter;
import org.wildfly.build.pack.model.ModuleIdentifier;
import org.wildfly.build.util.FileUtils;
import org.wildfly.build.util.ModuleParseResult;
import org.wildfly.build.util.ModuleParser;

import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Task that builds a feature pack. In general this task assumes that some other tool will copy the files from the build
 * to the target directory (e.g. maven).
 *
 * This tool will then verify the modules directory against the listed dependency feature packs, to make sure there
 * are no unresolved non-optional module references. It also resolves the versions of artifact and adds it to
 * the versions.properties file, and creates the feature-pack.xml file.
 *
 *
 * @author Stuart Douglas
 * @author Eduardo Martins
 */
public class FeaturePackBuilder {

    private static final Logger logger = Logger.getLogger(FeaturePackBuilder.class);

    public static void build(FeaturePackBuild build, File serverDirectory, ArtifactResolver artifactResolver, ArtifactFileResolver artifactFileResolver) {

        //List of errors that were encountered. These will be reported at the end so they are all reported in one go.
        final List errors = new ArrayList<>();
        final Set knownModules = new HashSet<>();
        final Map artifactVersionMap = new HashMap<>();
        final FeaturePackDescription featurePackDescription = new FeaturePackDescription(build.getDependencies(), build.getConfig(), build.getCopyArtifacts(), build.getFilePermissions());
        try {
            processDependencies(build.getDependencies(), knownModules, new HashSet(), artifactResolver, artifactFileResolver, artifactVersionMap);
            processModulesDirectory(knownModules, serverDirectory, artifactResolver, artifactVersionMap, errors);
            processVersions(featurePackDescription, artifactResolver, artifactVersionMap);
            processContentsDirectory(build, serverDirectory);
            writeFeaturePackXml(featurePackDescription, serverDirectory);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if(!errors.isEmpty()) {
                StringBuilder sb = new StringBuilder();
                sb.append("Some errors were encountered creating the feature pack\n");
                for(String error : errors) {
                    sb.append(error);
                    sb.append("\n");
                }
                throw new RuntimeException(sb.toString());
            }
        }
    }

    private static void processDependencies(List dependencies, Set knownModules, Set featurePacksProcessed, ArtifactResolver buildArtifactResolver, ArtifactFileResolver artifactFileResolver, final Map artifactVersionMap) {
        for (String dependency : dependencies) {
            if (!featurePacksProcessed.add(dependency)) {
                continue;
            }
            Artifact artifact = Artifact.parse(dependency);
            if(artifact.getPackaging() == null) {
                artifact = new Artifact(artifact.getGroupId(), artifact.getArtifactId(), "zip", artifact.getClassifier(), artifact.getVersion());
            }
            Artifact dependencyArtifact = buildArtifactResolver.getArtifact(artifact);
            if (dependencyArtifact == null) {
                throw new RuntimeException("Could not find artifact for " + dependency);
            }
            // load the dependency feature pack
            FeaturePack dependencyFeaturePack = FeaturePackFactory.createPack(dependencyArtifact, artifactFileResolver, new FeaturePackArtifactResolver(Collections.emptyList()));
            // put its artifact to the version map
            artifactVersionMap.put(dependencyFeaturePack.getArtifact().getUnversioned(), dependencyFeaturePack.getArtifact().getVersion());
            // process it
            processDependency(dependencyFeaturePack, knownModules, buildArtifactResolver, artifactVersionMap);
        }
    }

    private static void processDependency(FeaturePack dependencyFeaturePack, Set knownModules, ArtifactResolver buildArtifactResolver, Map artifactVersionMap) {
        // the new feature pack may override an artifact version for its dependencies, if that's the case it goes to the version map too
        for (Artifact dependencyVersionArtifact : dependencyFeaturePack.getDescription().getArtifactVersions()) {
            if (!artifactVersionMap.containsKey(dependencyVersionArtifact.getUnversioned())) {
                Artifact artifact = buildArtifactResolver.getArtifact(dependencyVersionArtifact.getUnversioned());
                if (artifact != null) {
                    artifactVersionMap.put(artifact.getUnversioned(), artifact.getVersion());
                }
            }
        }
        knownModules.addAll(dependencyFeaturePack.getFeaturePackModules().keySet());
        // process its dependencies too
        for (FeaturePack featurePack : dependencyFeaturePack.getDependencies()) {
            processDependency(featurePack, knownModules, buildArtifactResolver, artifactVersionMap);
        }
    }

    private static void processModulesDirectory(Set packProvidedModules, File serverDirectory, final ArtifactResolver artifactResolver, final Map artifactVersionMap,  final List errors) throws IOException {
        final Path modulesDir = Paths.get(new File(serverDirectory, Locations.MODULES).getAbsolutePath());
        if (Files.exists(modulesDir)) {
            final HashSet knownModules = new HashSet<>(packProvidedModules);
            final Map> requiredDepds = new HashMap<>();
            Files.walkFileTree(modulesDir, new SimpleFileVisitor() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    if (!file.getFileName().toString().equals("module.xml")) {
                        return FileVisitResult.CONTINUE;
                    }
                    try {
                        ModuleParseResult result = ModuleParser.parse(file);
                        knownModules.add(result.getIdentifier());
                        for (ModuleParseResult.ArtifactName artifactName : result.getArtifacts()) {

                            Artifact artifact;
                            boolean include = true;
                            if(artifactName.hasVersion()) {
                                include = false;
                                artifact = artifactName.getArtifact();
                            } else {
                                artifact = artifactResolver.getArtifact(artifactName.getArtifact());
                            }
                            if(artifact == null) {
                                errors.add("Could not determine version for artifact " + artifactName);
                            } else if(include) {
                                artifactVersionMap.put(artifact.getUnversioned(), artifact.getVersion());
                            }
                        }
                        for(ModuleParseResult.ModuleDependency dep : result.getDependencies()) {
                            if(!dep.isOptional()) {
                                Set dependees = requiredDepds.get(dep.getModuleId());
                                if(dependees == null) {
                                    requiredDepds.put(dep.getModuleId(), dependees = new HashSet<>());
                                }
                                dependees.add(result.getIdentifier());
                            }
                        }

                    } catch (ParsingException e) {
                        throw new RuntimeException(e);
                    }

                    return FileVisitResult.CONTINUE;
                }
            });

            //now look for unresolved dependencies
            for(Map.Entry> dep : requiredDepds.entrySet()) {
                if(!knownModules.contains(dep.getKey())) {
                    errors.add("Missing module " + dep.getKey() + ". Module was required by " + dep.getValue());
                }
            }
        }
    }

    private static void processVersions(FeaturePackDescription featurePackDescription, ArtifactResolver artifactResolver, Map artifactVersionMap) {
        // resolve copy-artifact versions and add to map
        for (CopyArtifact copyArtifact : featurePackDescription.getCopyArtifacts()) {
            if(copyArtifact.getArtifact().getVersion() == null) {
                Artifact artifact = copyArtifact.getArtifact(artifactResolver);
                artifactVersionMap.put(artifact.getUnversioned(), artifact.getVersion());
            }
        }
        // fill feature pack description versions
        for (Map.Entry mapEntry : artifactVersionMap.entrySet()) {
            featurePackDescription.getArtifactVersions().add(new Artifact(mapEntry.getKey(), mapEntry.getValue()));
        }
    }


    private static void processContentsDirectory(final FeaturePackBuild build, File serverDirectory) throws IOException {
        final File baseDir = new File(serverDirectory, Locations.CONTENT);
        // make dirs
        for (String dir : build.getMkDirs()) {
            File file = new File(baseDir, dir);
            if(!file.isDirectory()) {
                if(!file.mkdirs()) {
                    throw new RuntimeException("Could not create directory " + file);
                }
            }
        }
        // line endings
        final Path baseDirPath = Paths.get(baseDir.getAbsolutePath());
        if (!Files.exists(baseDirPath)){
            return;
        }
        Files.walkFileTree(baseDirPath, new SimpleFileVisitor() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                String relative = baseDirPath.relativize(file).toString();
                for (FileFilter fileFilter : build.getUnix()) {
                    if (fileFilter.matches(relative)) {
                        toUnixLineEndings(file);
                    }
                }
                for (FileFilter fileFilter : build.getWindows()) {
                    if (fileFilter.matches(relative)) {
                        toWindowsLineEndings(file);
                    }
                }
                return FileVisitResult.CONTINUE;
            }
        });
    }

    private static void writeFeaturePackXml(FeaturePackDescription featurePackDescription, File serverDirectory) throws IOException, XMLStreamException {
        final File outputFile = new File(serverDirectory, Locations.FEATURE_PACK_DESCRIPTION);
        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().mkdirs();
        }
        FeaturePackDescriptionXMLWriter11.INSTANCE.write(featurePackDescription, outputFile);
    }

    private static void toUnixLineEndings(Path file) throws IOException {
        Pattern pattern = Pattern.compile("\\r\\n", Pattern.MULTILINE);
        String content = FileUtils.readFile(file.toFile());
        Matcher matcher = pattern.matcher(content);
        content = matcher.replaceAll("\n");
        FileUtils.copyFile(new ByteArrayInputStream(content.getBytes("UTF-8")), file.toFile());
    }

    private static void toWindowsLineEndings(Path file) throws IOException {
        Pattern pattern = Pattern.compile("(?




© 2015 - 2025 Weber Informatics LLC | Privacy Policy