org.wildfly.galleon.plugin.WfInstallPlugin Maven / Gradle / Ivy
/*
* Copyright 2016-2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* 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.galleon.plugin;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitOption;
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.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import nu.xom.Attribute;
import nu.xom.Builder;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Elements;
import nu.xom.ParsingException;
import nu.xom.Serializer;
import org.jboss.galleon.Errors;
import org.jboss.galleon.MessageWriter;
import org.jboss.galleon.ProvisioningException;
import org.jboss.galleon.ProvisioningManager;
import org.jboss.galleon.ProvisioningOption;
import org.jboss.galleon.config.ConfigId;
import org.jboss.galleon.config.ConfigModel;
import org.jboss.galleon.config.FeaturePackConfig;
import org.jboss.galleon.config.ProvisioningConfig;
import org.jboss.galleon.diff.FsDiff;
import org.jboss.galleon.layout.ProvisioningLayoutFactory;
import org.jboss.galleon.plugin.InstallPlugin;
import org.jboss.galleon.plugin.ProvisioningPluginWithOptions;
import org.jboss.galleon.progresstracking.ProgressTracker;
import org.jboss.galleon.runtime.FeaturePackRuntime;
import org.jboss.galleon.runtime.PackageRuntime;
import org.jboss.galleon.runtime.ProvisioningRuntime;
import org.jboss.galleon.universe.FeaturePackLocation.FPID;
import org.jboss.galleon.universe.FeaturePackLocation.ProducerSpec;
import org.jboss.galleon.universe.maven.MavenArtifact;
import org.jboss.galleon.universe.maven.MavenUniverseException;
import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
import org.jboss.galleon.util.IoUtils;
import org.jboss.galleon.util.CollectionUtils;
import org.jboss.galleon.util.ZipUtils;
import org.wildfly.galleon.plugin.config.CopyArtifact;
import org.wildfly.galleon.plugin.config.CopyPath;
import org.wildfly.galleon.plugin.config.DeletePath;
import org.wildfly.galleon.plugin.config.ExampleFpConfigs;
import org.wildfly.galleon.plugin.config.FileFilter;
import org.wildfly.galleon.plugin.config.XslTransform;
import org.wildfly.galleon.plugin.server.CliScriptRunner;
import org.wildfly.galleon.plugin.transformer.JakartaTransformer;
import org.wildfly.galleon.plugin.transformer.TransformedArtifact;
/**
*
* @author Alexey Loubyansky
*/
public class WfInstallPlugin extends ProvisioningPluginWithOptions implements InstallPlugin {
private static final String CONFIG_GEN_METHOD = "generate";
private static final String CONFIG_GEN_PATH = "wildfly/wildfly-config-gen.jar";
private static final String CONFIG_GEN_CLASS = "org.wildfly.galleon.plugin.config.generator.WfConfigGenerator";
private static final String MAVEN_REPO_LOCAL = "maven.repo.local";
private static final ProvisioningOption OPTION_MVN_DIST = ProvisioningOption.builder("jboss-maven-dist")
.setBooleanValueSet()
.build();
public static final ProvisioningOption OPTION_DUMP_CONFIG_SCRIPTS = ProvisioningOption.builder("jboss-dump-config-scripts").setPersistent(false).build();
private static final ProvisioningOption OPTION_FORK_EMBEDDED = ProvisioningOption.builder("jboss-fork-embedded")
.setBooleanValueSet()
.build();
private static final ProvisioningOption OPTION_MVN_REPO = ProvisioningOption.builder("jboss-maven-repo")
.setPersistent(false)
.build();
private static final ProvisioningOption OPTION_JAKARTA_TRANSFORM_ARTIFACTS = ProvisioningOption.builder("jboss-jakarta-transform-artifacts")
.setBooleanValueSet()
.build();
private static final ProvisioningOption OPTION_MVN_PROVISIONING_REPO = ProvisioningOption.builder("jboss-maven-provisioning-repo")
.setPersistent(false)
.build();
private static final ProvisioningOption OPTION_JAKARTA_TRANSFORM_ARTIFACTS_VERBOSE = ProvisioningOption.builder("jboss-jakarta-transform-artifacts-verbose")
.setBooleanValueSet()
.build();
private ProvisioningRuntime runtime;
private MessageWriter log;
private Map mergedArtifactVersions = new HashMap<>();
private Map> fpArtifactVersions = new HashMap<>();
private Map> fpTasksProps = Collections.emptyMap();
private Map mergedTaskProps = new HashMap<>();
private PropertyResolver mergedTaskPropsResolver;
private boolean thinServer;
private Path jakartaTransformConfigsDir;
private boolean jakartaTransformVerbose;
private boolean jakartaTransform;
private JakartaTransformer.LogHandler logHandler;
private Set schemaGroups = Collections.emptySet();
private List finalizingTasks = Collections.emptyList();
private List finalizingTasksPkgs = Collections.emptyList();
private DocumentBuilderFactory docBuilderFactory;
private TransformerFactory xsltFactory;
private Map xslTransformers = Collections.emptyMap();
private Map exampleConfigs = Collections.emptyMap();
private ProgressTracker pkgProgressTracker;
private MavenRepoManager maven;
private Map jbossModules = new LinkedHashMap<>();
private Path transformationMavenRepo;
private Set transformExcluded = new HashSet<>();
private Path provisioningMavenRepo;
@Override
protected List initPluginOptions() {
return Arrays.asList(OPTION_MVN_DIST, OPTION_DUMP_CONFIG_SCRIPTS,
OPTION_FORK_EMBEDDED, OPTION_MVN_REPO, OPTION_JAKARTA_TRANSFORM_ARTIFACTS,
OPTION_MVN_PROVISIONING_REPO, OPTION_JAKARTA_TRANSFORM_ARTIFACTS_VERBOSE);
}
public ProvisioningRuntime getRuntime() {
return runtime;
}
private boolean isThinServer() throws ProvisioningException {
if(!runtime.isOptionSet(OPTION_MVN_DIST)) {
return false;
}
final String value = runtime.getOptionValue(OPTION_MVN_DIST);
return value == null ? true : Boolean.parseBoolean(value);
}
private Path getProvisioningMavenRepo() throws ProvisioningException {
if (!runtime.isOptionSet(OPTION_MVN_PROVISIONING_REPO)) {
return null;
}
final String value = runtime.getOptionValue(OPTION_MVN_PROVISIONING_REPO);
return value == null ? null : Paths.get(value);
}
private boolean isTransformationEnabled() throws ProvisioningException {
if (!runtime.isOptionSet(OPTION_JAKARTA_TRANSFORM_ARTIFACTS)) {
return true;
}
final String value = runtime.getOptionValue(OPTION_JAKARTA_TRANSFORM_ARTIFACTS);
return value == null ? true : Boolean.parseBoolean(value);
}
private boolean isVerboseTransformation() throws ProvisioningException {
if (!runtime.isOptionSet(OPTION_JAKARTA_TRANSFORM_ARTIFACTS_VERBOSE)) {
return false;
}
final String value = runtime.getOptionValue(OPTION_JAKARTA_TRANSFORM_ARTIFACTS_VERBOSE);
return value == null ? true : Boolean.parseBoolean(value);
}
@Override
public void preInstall(ProvisioningRuntime runtime) throws ProvisioningException {
final FsDiff fsDiff = runtime.getFsDiff();
if(fsDiff == null) {
return;
}
final String runningMode = fsDiff.getEntry("standalone/tmp/startup-marker") != null ? WfConstants.STANDALONE
: fsDiff.getEntry("domain/tmp/startup-marker") != null ? WfConstants.DOMAIN : null;
if (runningMode != null) {
throw new ProvisioningException("The server appears to be running (" + runningMode + " mode).");
}
}
/* (non-Javadoc)
* @see org.jboss.galleon.util.plugin.ProvisioningPlugin#execute()
*/
@Override
public void postInstall(ProvisioningRuntime runtime) throws ProvisioningException {
final long startTime = runtime.isLogTime() ? System.nanoTime() : -1;
this.runtime = runtime;
log = runtime.getMessageWriter();
log.verbose("WildFly Galleon Installation Plugin");
thinServer = isThinServer();
maven = (MavenRepoManager) runtime.getArtifactResolver(MavenRepoManager.REPOSITORY_ID);
provisioningMavenRepo = getProvisioningMavenRepo();
if (provisioningMavenRepo != null && Files.notExists(provisioningMavenRepo)) {
throw new ProvisioningException("Local maven repository " + provisioningMavenRepo.toAbsolutePath().toString()
+ " used to provision the server doesn't exist.");
}
for(FeaturePackRuntime fp : runtime.getFeaturePacks()) {
final Path wfRes = fp.getResource(WfConstants.WILDFLY);
if(!Files.exists(wfRes)) {
continue;
}
final Path artifactProps = wfRes.resolve(WfConstants.ARTIFACT_VERSIONS_PROPS);
if(Files.exists(artifactProps)) {
final Map versionProps = Utils.readProperties(artifactProps);
fpArtifactVersions.put(fp.getFPID().getProducer(), versionProps);
mergedArtifactVersions.putAll(versionProps);
}
final Path tasksPropsPath = wfRes.resolve(WfConstants.WILDFLY_TASKS_PROPS);
if(Files.exists(tasksPropsPath)) {
final Map fpProps = Utils.readProperties(tasksPropsPath);
fpTasksProps = CollectionUtils.put(fpTasksProps, fp.getFPID().getProducer(), fpProps);
mergedTaskProps.putAll(fpProps);
}
if(fp.containsPackage(WfConstants.DOCS_SCHEMA)) {
final Path schemaGroupsTxt = fp.getPackage(WfConstants.DOCS_SCHEMA).getResource(
WfConstants.PM, WfConstants.WILDFLY, WfConstants.SCHEMA_GROUPS_TXT);
try(BufferedReader reader = Files.newBufferedReader(schemaGroupsTxt)) {
String line = reader.readLine();
while(line != null) {
schemaGroups = CollectionUtils.add(schemaGroups, line);
line = reader.readLine();
}
} catch (IOException e) {
throw new ProvisioningException(Errors.readFile(schemaGroupsTxt), e);
}
}
final Path excludedArtifacts = wfRes.resolve(WfConstants.WILDFLY_JAKARTA_TRANSFORM_EXCLUDES);
if (Files.exists(excludedArtifacts)) {
try (BufferedReader reader = Files.newBufferedReader(excludedArtifacts, StandardCharsets.UTF_8)) {
String line = reader.readLine();
while (line != null) {
transformExcluded = CollectionUtils.add(transformExcluded, line);
line = reader.readLine();
}
} catch (IOException e) {
throw new ProvisioningException(Errors.readFile(excludedArtifacts), e);
}
}
}
mergedTaskPropsResolver = new MapPropertyResolver(mergedTaskProps);
final ProvisioningLayoutFactory layoutFactory = runtime.getLayout().getFactory();
pkgProgressTracker = layoutFactory.getProgressTracker(ProvisioningLayoutFactory.TRACK_PACKAGES);
long pkgsTotal = 0;
for(FeaturePackRuntime fp : runtime.getFeaturePacks()) {
pkgsTotal += fp.getPackageNames().size();
}
pkgProgressTracker.starting(pkgsTotal);
for(FeaturePackRuntime fp : runtime.getFeaturePacks()) {
processPackages(fp);
}
pkgProgressTracker.complete();
if (!jbossModules.isEmpty()) {
final ProgressTracker modulesTracker = layoutFactory.getProgressTracker("JBMODULES");
modulesTracker.starting(jbossModules.size());
Path optionMavenRepo = null;
if (runtime.isOptionSet(OPTION_MVN_REPO)) {
String path = runtime.getOptionValue(OPTION_MVN_REPO);
optionMavenRepo = Paths.get(path);
IoUtils.recursiveDelete(optionMavenRepo);
}
// initialize jakarta transformation properties
jakartaTransform = isTransformationEnabled() && Boolean.valueOf(mergedTaskProps.getOrDefault(JakartaTransformer.TRANSFORM_ARTIFACTS, "false"));
jakartaTransformVerbose = isVerboseTransformation();
final String jakartaConfigsDir = mergedTaskProps.get(JakartaTransformer.TRANSFORM_CONFIGS_DIR);
if (jakartaConfigsDir != null) {
jakartaTransformConfigsDir = Paths.get(jakartaConfigsDir);
}
// continue
if (jakartaTransform) {
if (thinServer && optionMavenRepo == null) {
// This trace is printed when generating example config. Make it verbose.
log.verbose("WARNING: EE9 transformation disabled for thin server");
} else {
log.print("EE9 transforming server jar artifacts");
logHandler = new JakartaTransformer.LogHandler() {
@Override
public void print(String format, Object... args) {
log.print(format, args);
}
};
if (!exampleConfigs.isEmpty()) {
// Track where we store transformed artifacts so we can configure the embedded
// server to point there when we generate example configs
transformationMavenRepo = optionMavenRepo != null ? optionMavenRepo : runtime.getTmpPath("jakarta-transform-repo");
}
}
}
for (Map.Entry entry : jbossModules.entrySet()) {
final PackageRuntime pkg = entry.getValue();
modulesTracker.processing(pkg);
try {
processModuleTemplate(pkg, entry.getKey());
} catch (IOException e) {
throw new ProvisioningException("Failed to process JBoss module XML template for feature-pack "
+ pkg.getFeaturePackRuntime().getFPID() + " package " + pkg.getName(), e);
}
modulesTracker.processed(pkg);
}
modulesTracker.complete();
}
final Path layersConf = runtime.getStagedDir().resolve(WfConstants.MODULES).resolve(WfConstants.LAYERS_CONF);
if (Files.exists(layersConf)) {
mergeLayerConfs(runtime);
}
String originalMavenRepoLocal = System.getProperty(MAVEN_REPO_LOCAL);
if (thinServer) {
Path localRepo = null;
// When provisioning a slim server, we must re-use the generated local repo
// That is required in case this repo already contains transformed artifact.
if (runtime.isOptionSet(OPTION_MVN_REPO)) {
String path = runtime.getOptionValue(OPTION_MVN_REPO);
localRepo = Paths.get(path).toAbsolutePath();
} else {
// If we have a provisioning repo, we must use that, can contain transformed artifacts
if (provisioningMavenRepo != null) {
localRepo = provisioningMavenRepo.toAbsolutePath();
}
}
if (localRepo != null) {
System.setProperty(MAVEN_REPO_LOCAL, localRepo.toString());
}
if (log.isVerboseEnabled()) {
log.verbose("Generating configs with local maven repository: " + System.getProperty(MAVEN_REPO_LOCAL));
}
}
try {
generateConfigs(runtime);
} finally {
if (originalMavenRepoLocal == null) {
System.clearProperty(MAVEN_REPO_LOCAL);
} else {
System.setProperty(MAVEN_REPO_LOCAL, originalMavenRepoLocal);
}
}
// TODO this needs to be revisited
for(FeaturePackRuntime fp : runtime.getFeaturePacks()) {
final Path finalizeCli = fp.getResource(WfConstants.WILDFLY, WfConstants.SCRIPTS, "finalize.cli");
if(Files.exists(finalizeCli)) {
CliScriptRunner.runCliScript(runtime.getStagedDir(), finalizeCli, log);
}
}
if(!finalizingTasks.isEmpty()) {
for(int i = 0; i < finalizingTasks.size(); ++i) {
finalizingTasks.get(i).execute(this, finalizingTasksPkgs.get(i));
}
}
if(!exampleConfigs.isEmpty()) {
provisionExampleConfigs();
}
if (startTime > 0) {
log.print(Errors.tookTime("Overall WildFly Galleon Plugin", startTime));
}
}
private void setupLayerDirectory(Path layersConf, Path layersDir) throws ProvisioningException {
log.verbose("Creating layers directories if needed.");
try (BufferedReader reader = Files.newBufferedReader(layersConf)) {
Properties props = new Properties();
props.load(reader);
String layersProp = props.getProperty(WfConstants.LAYERS);
if (layersProp == null || (layersProp = layersProp.trim()).length() == 0) {
return;
}
final String[] layerNames = layersProp.split(",");
for (String layerName : layerNames) {
log.verbose("Found layer %s", layerName);
Path layerDir = layersDir.resolve(layerName);
if (!Files.exists(layerDir)) {
log.verbose("Creating directory %s", layerDir);
Files.createDirectories(layerDir);
}
}
} catch (IOException ex) {
throw new ProvisioningException("Failed to setup layers directory in " + layersDir, ex);
}
}
private void mergeLayerConfs(ProvisioningRuntime runtime) throws ProvisioningException {
final List layersConfs = Utils.collectLayersConf(runtime.getLayout());
// The list contains all layer confs, even the one that will be not provisioned.
// create directories for all of them.
for (Path p : layersConfs) {
setupLayerDirectory(p, runtime.getStagedDir().resolve(WfConstants.MODULES).
resolve(WfConstants.SYSTEM).resolve(WfConstants.LAYERS));
}
if(layersConfs.size() < 2) {
return;
}
Utils.mergeLayersConfs(layersConfs, runtime.getStagedDir());
}
private void provisionExampleConfigs() throws ProvisioningException {
final Path examplesTmp = runtime.getTmpPath("example-configs");
final ProvisioningManager pm = ProvisioningManager.builder()
.setInstallationHome(examplesTmp)
.setMessageWriter(log)
.setLayoutFactory(runtime.getLayout().getFactory())
.setRecordState(false)
.build();
List configPaths = new ArrayList<>();
final ProvisioningConfig.Builder configBuilder = ProvisioningConfig.builder();
for(Map.Entry example : exampleConfigs.entrySet()) {
final FeaturePackConfig.Builder fpBuilder = FeaturePackConfig.builder(example.getKey().getLocation())
.setInheritConfigs(false)
.setInheritPackages(false);
final ExampleFpConfigs fpExampleConfigs = example.getValue();
if(fpExampleConfigs != null) {
for(Map.Entry config : fpExampleConfigs.getConfigs().entrySet()) {
final ConfigId configId = config.getKey();
final ConfigModel configModel = config.getValue();
String configName = null;
if(configModel != null) {
fpBuilder.addConfig(configModel);
if(configModel.hasProperties()) {
if(WfConstants.STANDALONE.equals(configId.getModel())) {
configName = configModel.getProperties().get(WfConstants.EMBEDDED_ARG_SERVER_CONFIG);
} else if(WfConstants.HOST.equals(configId.getModel())) {
configName = configModel.getProperties().get(WfConstants.EMBEDDED_ARG_HOST_CONFIG);
} else {
configName = configModel.getProperties().get(WfConstants.EMBEDDED_ARG_DOMAIN_CONFIG);
}
}
if(configName == null) {
configName = configId.getName();
}
} else {
fpBuilder.includeDefaultConfig(configId);
configName = configId.getName();
}
if(WfConstants.HOST.equals(configId.getModel())) {
configPaths.add(examplesTmp.resolve(WfConstants.DOMAIN).resolve(WfConstants.CONFIGURATION).resolve(configName));
} else {
configPaths.add(examplesTmp.resolve(configId.getModel()).resolve(WfConstants.CONFIGURATION).resolve(configName));
}
}
}
configBuilder.addFeaturePackDep(fpBuilder.build());
}
String originalMavenRepoLocal = System.getProperty(MAVEN_REPO_LOCAL);
Path localRepo = null;
if (transformationMavenRepo != null) {
// We're about to provision a thin server and use it to generate a config.
// Have the thin server resolve against the transformed artifacts in transformationMavenRepo
localRepo = transformationMavenRepo.toAbsolutePath();
} else {
// Use the generated repo, could contain transformed artifacts.
if (thinServer && runtime.isOptionSet(OPTION_MVN_REPO)) {
String path = runtime.getOptionValue(OPTION_MVN_REPO);
Path repo = Paths.get(path);
localRepo = repo.toAbsolutePath();
}
}
if (localRepo != null) {
System.setProperty(MAVEN_REPO_LOCAL, localRepo.toString());
}
try {
log.verbose("Generating example configs");
ProvisioningConfig config = configBuilder.build();
Map options = runtime.getLayout().getOptions();
if(!options.containsKey(OPTION_MVN_DIST.getName()) || options.containsKey(OPTION_MVN_REPO.getName())) {
final Map tmp = new HashMap<>(options.size() + 1);
tmp.putAll(options);
options = tmp;
options.put(OPTION_MVN_DIST.getName(), null);
// Remove OPTION_MVN_REPO so we don't waste time populating it again
// as it was already populated by the main postInstall provisioning.
// It would be a waste of time regardless, but if jakartaTransform is true,
// trying to populate it again will fail provisioning
options.remove(OPTION_MVN_REPO.getName());
}
pm.provision(config, options);
} catch(ProvisioningException e) {
throw new ProvisioningException("Failed to generate example configs", e);
} finally {
if (originalMavenRepoLocal == null) {
System.clearProperty(MAVEN_REPO_LOCAL);
} else {
System.setProperty(MAVEN_REPO_LOCAL, originalMavenRepoLocal);
}
}
final Path exampleConfigsDir = runtime.getStagedDir().resolve(WfConstants.DOCS).resolve("examples").resolve("configs");
for(Path configPath : configPaths) {
try {
IoUtils.copy(configPath, exampleConfigsDir.resolve(configPath.getFileName()));
} catch (IOException e) {
throw new ProvisioningException(Errors.copyFile(configPath, exampleConfigsDir.resolve(configPath.getFileName())), e);
}
}
}
private void generateConfigs(ProvisioningRuntime runtime) throws ProvisioningException {
if(!runtime.hasConfigs()) {
return;
}
final long startTime = runtime.isLogTime() ? System.nanoTime() : -1;
final Path configGenJar = runtime.getResource(CONFIG_GEN_PATH);
if(!Files.exists(configGenJar)) {
throw new ProvisioningException(Errors.pathDoesNotExist(configGenJar));
}
final URL[] cp = new URL[3];
try {
cp[0] = configGenJar.toUri().toURL();
MavenArtifact artifact = Utils.toArtifactCoords(mergedArtifactVersions, "org.jboss.modules:jboss-modules", false);
resolve(artifact);
cp[1] = artifact.getPath().toUri().toURL();
artifact = Utils.toArtifactCoords(mergedArtifactVersions, "org.wildfly.core:wildfly-cli::client", false);
resolve(artifact);
cp[2] = artifact.getPath().toUri().toURL();
} catch (IOException e) {
throw new ProvisioningException("Failed to init classpath for " + runtime.getStagedDir(), e);
}
if(log.isVerboseEnabled()) {
log.verbose("Config generator classpath:");
for(int i = 0; i < cp.length; ++i) {
log.verbose(i+1 + ". " + cp[i]);
}
}
final ClassLoader originalCl = Thread.currentThread().getContextClassLoader();
final URLClassLoader configGenCl = new URLClassLoader(cp, originalCl);
Thread.currentThread().setContextClassLoader(configGenCl);
try {
final Class> configHandlerCls = configGenCl.loadClass(CONFIG_GEN_CLASS);
final Constructor> ctor = configHandlerCls.getConstructor();
final Method m = configHandlerCls.getMethod(CONFIG_GEN_METHOD, ProvisioningRuntime.class, boolean.class);
final Object generator = ctor.newInstance();
boolean forkEmbedded = false;
if(runtime.isOptionSet(OPTION_FORK_EMBEDDED)) {
final String value = runtime.getOptionValue(OPTION_FORK_EMBEDDED);
forkEmbedded = value == null ? true : Boolean.parseBoolean(value);
}
m.invoke(generator, runtime, forkEmbedded);
if(startTime > 0) {
log.print(Errors.tookTime("WildFly configuration generation", startTime));
}
} catch(InvocationTargetException e) {
final Throwable cause = e.getCause();
if(cause instanceof ProvisioningException) {
throw (ProvisioningException)cause;
} else {
throw new ProvisioningException("Failed to invoke config generator " + CONFIG_GEN_CLASS, cause);
}
} catch (Throwable e) {
throw new ProvisioningException("Failed to initialize config generator " + CONFIG_GEN_CLASS, e);
} finally {
Thread.currentThread().setContextClassLoader(originalCl);
try {
configGenCl.close();
} catch (IOException e) {
}
}
}
private void processPackages(final FeaturePackRuntime fp) throws ProvisioningException {
log.verbose("Processing %s packages", fp.getFPID());
for(PackageRuntime pkg : fp.getPackages()) {
pkgProgressTracker.processing(pkg);
final Path pmWfDir = pkg.getResource(WfConstants.PM, WfConstants.WILDFLY);
if(!Files.exists(pmWfDir)) {
pkgProgressTracker.processed(pkg);
continue;
}
final Path moduleDir = pmWfDir.resolve(WfConstants.MODULE);
if(Files.exists(moduleDir)) {
processModules(pkg, moduleDir);
}
final Path tasksXml = pmWfDir.resolve(WfConstants.TASKS_XML);
if (Files.exists(tasksXml)) {
final WildFlyPackageTasks pkgTasks = WildFlyPackageTasks.load(tasksXml);
if (pkgTasks.hasTasks()) {
log.verbose("Processing %s package %s tasks", fp.getFPID(), pkg.getName());
for (WildFlyPackageTask task : pkgTasks.getTasks()) {
if (task.getPhase() == WildFlyPackageTask.Phase.PROCESSING) {
task.execute(this, pkg);
} else {
finalizingTasks = CollectionUtils.add(finalizingTasks, task);
finalizingTasksPkgs = CollectionUtils.add(finalizingTasksPkgs, pkg);
}
}
}
if (pkgTasks.hasMkDirs()) {
mkdirs(pkgTasks, this.runtime.getStagedDir());
}
changeLineEndings(pkgTasks, this.runtime.getStagedDir());
}
pkgProgressTracker.processed(pkg);
}
}
public void xslTransform(PackageRuntime pkg, XslTransform xslt) throws ProvisioningException {
final Path src = runtime.getStagedDir().resolve(xslt.getSrc());
if (!Files.exists(src)) {
throw new ProvisioningException(Errors.pathDoesNotExist(src));
}
final Path output = runtime.getStagedDir().resolve(xslt.getOutput());
if (Files.exists(output)) {
throw new ProvisioningException(Errors.pathAlreadyExists(output));
}
try (InputStream srcInput = Files.newInputStream(src); OutputStream outStream = Files.newOutputStream(output)) {
final org.w3c.dom.Document document = getXmlDocumentBuilderFactory().newDocumentBuilder().parse(srcInput);
final Transformer transformer = getXslTransformer(xslt.getStylesheet());
if (xslt.hasParams()) {
for (Map.Entry param : xslt.getParams().entrySet()) {
transformer.setParameter(param.getKey(), param.getValue());
}
}
final Map taskProps = xslt.isFeaturePackProperties() ? fpTasksProps.get(pkg.getFeaturePackRuntime().getFPID().getProducer()) : mergedTaskProps;
if (taskProps != null) {
for (Map.Entry prop : taskProps.entrySet()) {
transformer.setParameter(prop.getKey(), prop.getValue());
}
}
final DOMSource source = new DOMSource(document);
final StreamResult result = new StreamResult(outStream);
transformer.transform(source, result);
} catch (ProvisioningException e) {
throw e;
} catch (Exception e) {
throw new ProvisioningException(
"Failed to transform " + xslt.getSrc() + " with " + xslt.getStylesheet() + " to " + xslt.getOutput(), e);
}
}
private Transformer getXslTransformer(String stylesheet) throws ProvisioningException {
Transformer transformer = xslTransformers.get(stylesheet);
if(transformer != null) {
return transformer;
}
transformer = getXslTransformer(runtime.getStagedDir().resolve(stylesheet));
xslTransformers = CollectionUtils.put(xslTransformers, stylesheet, transformer);
return transformer;
}
public DocumentBuilderFactory getXmlDocumentBuilderFactory() {
if(docBuilderFactory == null) {
docBuilderFactory = DocumentBuilderFactory.newInstance();
}
return docBuilderFactory;
}
public Transformer getXslTransformer(Path p) throws ProvisioningException {
if(!Files.exists(p)) {
throw new ProvisioningException(Errors.pathDoesNotExist(p));
}
try (InputStream styleInput = Files.newInputStream(p)) {
final StreamSource stylesource = new StreamSource(styleInput);
if(xsltFactory == null) {
xsltFactory = TransformerFactory.newInstance();
}
return xsltFactory.newTransformer(stylesource);
} catch (Exception e) {
throw new ProvisioningException("Failed to initialize a transformer for " + p, e);
}
}
private void processModules(PackageRuntime pkg, Path fpModuleDir) throws ProvisioningException {
try {
final Path stagedDir = runtime.getStagedDir();
if(!Files.exists(stagedDir)) {
Files.createDirectories(stagedDir);
}
Files.walkFileTree(fpModuleDir, new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
final Path targetDir = stagedDir.resolve(fpModuleDir.relativize(dir).toString());
try {
Files.copy(dir, targetDir);
} catch (FileAlreadyExistsException e) {
if (!Files.isDirectory(targetDir)) {
throw e;
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
if(file.getFileName().toString().equals(WfConstants.MODULE_XML)) {
final PackageRuntime overridenPkg = jbossModules.put(fpModuleDir.relativize(file), pkg);
if(overridenPkg != null) {
if(log.isVerboseEnabled()) {
log.verbose("Feature-pack " + pkg.getFeaturePackRuntime().getFPID() + " package " + pkg.getName() +
" overrode jboss-module from feature-pack " + overridenPkg.getFeaturePackRuntime().getFPID() +
" package " + overridenPkg.getName());
}
}
} else {
Files.copy(file, stagedDir.resolve(fpModuleDir.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new ProvisioningException("Failed to process modules from package " + pkg.getName() + " from feature-pack " + pkg.getFeaturePackRuntime().getFPID(), e);
}
}
private void processModuleTemplate(PackageRuntime pkg, Path moduleXmlRelativePath) throws ProvisioningException, IOException {
final Path moduleTemplate = pkg.getResource(WfConstants.PM, WfConstants.WILDFLY, WfConstants.MODULE).resolve(moduleXmlRelativePath);
final Path targetPath = runtime.getStagedDir().resolve(moduleXmlRelativePath.toString());
final Builder builder = new Builder(false);
final Document document;
try (BufferedReader reader = Files.newBufferedReader(moduleTemplate, StandardCharsets.UTF_8)) {
document = builder.build(reader);
} catch (ParsingException e) {
throw new IOException("Failed to parse document", e);
}
final Element rootElement = document.getRootElement();
if (! rootElement.getLocalName().equals("module") &&
// module-alias files don't need to be processed
// the only reason their are parsed and serialized is to match the processing in the legacy build tools
// this fixes the difference in line endings between the two builds
!rootElement.getLocalName().equals("module-alias")) {
// just copy the content and leave
Files.copy(moduleTemplate, targetPath, StandardCopyOption.REPLACE_EXISTING);
return;
}
final Map versionProps = fpArtifactVersions.get(pkg.getFeaturePackRuntime().getFPID().getProducer());
// replace version, if any
final Attribute versionAttribute = rootElement.getAttribute("version");
if (versionAttribute != null) {
final String versionExpr = versionAttribute.getValue();
if (versionExpr.startsWith("${") && versionExpr.endsWith("}")) {
final String exprBody = versionExpr.substring(2, versionExpr.length() - 1);
final int optionsIndex = exprBody.indexOf('?');
final String artifactName;
if (optionsIndex > 0) {
artifactName = exprBody.substring(0, optionsIndex);
} else {
artifactName = exprBody;
}
final MavenArtifact artifact = Utils.toArtifactCoords(versionProps, artifactName, false);
if (artifact != null) {
versionAttribute.setValue(artifact.getVersion());
}
}
}
// replace all artifact declarations
final Element resourcesElement = rootElement.getFirstChildElement("resources", rootElement.getNamespaceURI());
if (resourcesElement != null) {
final Elements artifacts = resourcesElement.getChildElements("artifact", rootElement.getNamespaceURI());
final int artifactCount = artifacts.size();
for (int i = 0; i < artifactCount; i ++) {
final Element element = artifacts.get(i);
assert element.getLocalName().equals("artifact");
final Attribute attribute = element.getAttribute("name");
String coordsStr = attribute.getValue();
boolean jandex = false;
if (coordsStr.startsWith("${") && coordsStr.endsWith("}")) {
coordsStr = coordsStr.substring(2, coordsStr.length() - 1);
final int optionsIndex = coordsStr.indexOf('?');
if (optionsIndex >= 0) {
jandex = coordsStr.indexOf("jandex", optionsIndex) >= 0;
coordsStr = coordsStr.substring(0, optionsIndex);
}
coordsStr = versionProps.get(coordsStr);
}
if(coordsStr == null) {
continue;
}
MavenArtifact artifact;
try {
artifact = Utils.toArtifactCoords(versionProps, coordsStr, false);
} catch (ProvisioningException e) {
throw new IOException("Failed to resolve full coordinates for " + coordsStr, e);
}
Path moduleArtifact;
log.verbose("Resolving %s", artifact);
try {
resolve(artifact);
} catch (ProvisioningException e) {
throw new IOException("Failed to resolve artifact " + artifact, e);
}
moduleArtifact = artifact.getPath();
if (thinServer) {
// ignore jandex variable, just resolve coordinates to a string
final StringBuilder buf = new StringBuilder();
buf.append(artifact.getGroupId());
buf.append(':');
buf.append(artifact.getArtifactId());
buf.append(':');
buf.append(artifact.getVersion());
if(!artifact.getClassifier().isEmpty()) {
buf.append(':');
buf.append(artifact.getClassifier());
}
attribute.setValue(buf.toString());
if (runtime.isOptionSet(OPTION_MVN_REPO)) {
String path = runtime.getOptionValue(OPTION_MVN_REPO);
Path repo = Paths.get(path);
Path versionPath = getLocalRepoPath(artifact, repo);
Path actualTarget = versionPath.resolve(moduleArtifact.getFileName().toString());
if (jakartaTransform && !isExcludedFromTransformation(artifact)) {
// The artifact could already exist in case of redefined module as alias (eg: javax.security.jacc.api).
// The transformer doesn't accept existing target, so skip transformation.
if (!Files.exists(actualTarget)) {
transform(artifact, versionPath);
}
} else {
Files.copy(moduleArtifact, actualTarget, StandardCopyOption.REPLACE_EXISTING);
}
Path pomFile = getPomArtifactPath(artifact);
Files.copy(pomFile, versionPath.resolve(pomFile.getFileName().toString()), StandardCopyOption.REPLACE_EXISTING);
}
} else {
final Path targetDir = targetPath.getParent();
final String artifactFileName = moduleArtifact.getFileName().toString();
final String finalFileName;
if (jandex) {
final int lastDot = artifactFileName.lastIndexOf(".");
final File target = new File(targetDir.toFile(),
new StringBuilder().append(artifactFileName.substring(0, lastDot)).append("-jandex")
.append(artifactFileName.substring(lastDot)).toString());
JandexIndexer.createIndex(moduleArtifact.toFile(), new FileOutputStream(target), log);
finalFileName = target.getName();
} else {
finalFileName = artifactFileName;
if (jakartaTransform) {
if (!isExcludedFromTransformation(artifact)) {
transform(artifact, targetDir);
} else {
Files.copy(moduleArtifact, targetDir.resolve(artifactFileName), StandardCopyOption.REPLACE_EXISTING);
}
if (transformationMavenRepo != null) {
// Copy to the transformationMavenRepo for future use
Path versionPath = getLocalRepoPath(artifact, transformationMavenRepo);
Path pomFile = getPomArtifactPath(artifact);
Files.copy(pomFile, versionPath.resolve(pomFile.getFileName().toString()), StandardCopyOption.REPLACE_EXISTING);
Files.copy(targetDir.resolve(artifactFileName), versionPath.resolve(artifactFileName), StandardCopyOption.REPLACE_EXISTING);
}
} else {
Files.copy(moduleArtifact, targetDir.resolve(artifactFileName), StandardCopyOption.REPLACE_EXISTING);
}
}
element.setLocalName("resource-root");
attribute.setLocalName("path");
attribute.setValue(finalFileName);
}
if (schemaGroups.contains(artifact.getGroupId())) {
extractSchemas(moduleArtifact);
}
}
}
// now serialize the result
try (OutputStream outputStream = Files.newOutputStream(targetPath)) {
new Serializer(outputStream).write(document);
} catch (Throwable t) {
try {
Files.deleteIfExists(targetPath);
} catch (Throwable t2) {
t2.addSuppressed(t);
throw t2;
}
throw t;
}
}
private boolean isExcludedFromTransformation(MavenArtifact artifact) {
if (transformExcluded.contains(ArtifactCoords.newGav(artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion()).toString())) {
if (log.isVerboseEnabled()) {
log.verbose("Excluding " + artifact + " from EE9 transformation");
}
return true;
}
return false;
}
private Path transform(MavenArtifact artifact, Path targetDir) throws IOException {
TransformedArtifact a = JakartaTransformer.transform(jakartaTransformConfigsDir, artifact.getPath(), targetDir, jakartaTransformVerbose, logHandler);
return a.getTarget();
}
public void addExampleConfigs(FeaturePackRuntime fp, ExampleFpConfigs exampleConfigs) throws ProvisioningException {
final FPID originFpId;
if(exampleConfigs.getOrigin() != null) {
originFpId = fp.getSpec().getFeaturePackDep(exampleConfigs.getOrigin()).getLocation().getFPID();
} else {
originFpId = fp.getFPID();
}
ExampleFpConfigs existingConfigs = this.exampleConfigs.get(originFpId);
if(existingConfigs == null) {
this.exampleConfigs = CollectionUtils.put(this.exampleConfigs, originFpId, exampleConfigs);
} else {
existingConfigs.addAll(exampleConfigs);
}
}
private void extractSchemas(Path moduleArtifact) throws IOException {
final Path targetSchemasDir = this.runtime.getStagedDir().resolve(WfConstants.DOCS).resolve(WfConstants.SCHEMA);
Files.createDirectories(targetSchemasDir);
try (FileSystem jarFS = FileSystems.newFileSystem(moduleArtifact, null)) {
final Path schemaSrc = jarFS.getPath(WfConstants.SCHEMA);
if (Files.exists(schemaSrc)) {
ZipUtils.copyFromZip(schemaSrc.toAbsolutePath(), targetSchemasDir);
}
}
}
public void copyArtifact(CopyArtifact copyArtifact, PackageRuntime pkg) throws ProvisioningException {
final MavenArtifact artifact = Utils.toArtifactCoords(
copyArtifact.isFeaturePackVersion() ? fpArtifactVersions.get(pkg.getFeaturePackRuntime().getFPID().getProducer())
: mergedArtifactVersions,
copyArtifact.getArtifact(), copyArtifact.isOptional());
if(artifact == null) {
return;
}
try {
log.verbose("Resolving artifact %s ", artifact);
resolve(artifact);
final Path jarSrc = artifact.getPath();
String location = copyArtifact.getToLocation();
if (!location.isEmpty() && location.charAt(location.length() - 1) == '/') {
// if the to location ends with a / then it is a directory
// so we need to append the artifact name
location += jarSrc.getFileName();
}
final Path jarTarget = runtime.getStagedDir().resolve(location);
Files.createDirectories(jarTarget.getParent());
log.verbose("Copying artifact %s to %s", jarSrc, jarTarget);
if (copyArtifact.isExtract()) {
Utils.extractArtifact(jarSrc, jarTarget, copyArtifact);
} else {
IoUtils.copy(jarSrc, jarTarget);
}
if(schemaGroups.contains(artifact.getGroupId())) {
extractSchemas(jarSrc);
}
} catch (IOException e) {
throw new ProvisioningException("Failed to copy artifact " + artifact, e);
}
}
public void copyPath(final Path relativeTo, CopyPath copyPath) throws ProvisioningException {
final Path src = relativeTo.resolve(copyPath.getSrc());
if (!Files.exists(src)) {
throw new ProvisioningException(Errors.pathDoesNotExist(src));
}
final Path target = copyPath.getTarget() == null ? runtime.getStagedDir() : runtime.getStagedDir().resolve(copyPath.getTarget());
if (copyPath.isReplaceProperties()) {
if (!Files.exists(target.getParent())) {
try {
Files.createDirectories(target.getParent());
} catch (IOException e) {
throw new ProvisioningException(Errors.mkdirs(target.getParent()), e);
}
}
try {
Files.walkFileTree(src, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
new SimpleFileVisitor() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
final Path targetDir = target.resolve(src.relativize(dir).toString());
try {
Files.copy(dir, targetDir);
} catch (FileAlreadyExistsException e) {
if (!Files.isDirectory(targetDir)) {
throw e;
}
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
PropertyReplacer.copy(file, target.resolve(src.relativize(file).toString()), mergedTaskPropsResolver);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
throw new ProvisioningException(Errors.copyFile(src, target), e);
}
} else {
try {
IoUtils.copy(src, target);
} catch (IOException e) {
throw new ProvisioningException(Errors.copyFile(src, target));
}
}
}
public void deletePath(DeletePath deletePath) throws ProvisioningException {
final Path path = runtime.getStagedDir().resolve(deletePath.getPath());
if (!Files.exists(path)) {
return;
}
if(deletePath.isRecursive()) {
IoUtils.recursiveDelete(path);
return;
}
if(deletePath.isIfEmpty()) {
if(!Files.isDirectory(path)) {
throw new ProvisioningException(Errors.notADir(path));
}
try(Stream stream = Files.list(path)) {
if(stream.iterator().hasNext()) {
return;
}
} catch (IOException e) {
throw new ProvisioningException(Errors.readDirectory(path));
}
}
try {
Files.delete(path);
} catch (IOException e) {
throw new ProvisioningException(Errors.deletePath(path), e);
}
}
private static void mkdirs(final WildFlyPackageTasks tasks, Path installDir) throws ProvisioningException {
// make dirs
for (String dirName : tasks.getMkDirs()) {
try {
Files.createDirectories(installDir.resolve(dirName));
} catch (IOException e) {
throw new ProvisioningException(Errors.mkdirs(installDir.resolve(dirName)));
}
}
}
private static void changeLineEndings(final WildFlyPackageTasks tasks, final Path installDir) throws ProvisioningException {
// If not line end filters are present no need to walk the directory
if (tasks.getUnixLineEndFilters().isEmpty() && tasks.getWindowsLineEndFilters().isEmpty()) {
return;
}
try {
Files.walkFileTree(installDir, new SimpleFileVisitor() {
@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) {
final String relative = installDir.relativize(file).toString();
for (FileFilter filter : tasks.getUnixLineEndFilters()) {
if (filter.matches(relative)) {
try {
changeLineEndings(file, false);
} catch (IOException e) {
throw new UncheckedIOException(String.format("Failed to convert %s to Unix line endings.", file), e);
}
}
}
for (FileFilter filter : tasks.getWindowsLineEndFilters()) {
if (filter.matches(relative)) {
try {
changeLineEndings(file, true);
} catch (IOException e) {
throw new UncheckedIOException(String.format("Failed to convert %s to Windows line endings.", file), e);
}
}
}
return FileVisitResult.CONTINUE;
}
});
} catch (UncheckedIOException e) {
throw new ProvisioningException(e.getMessage(), e.getCause());
} catch (IOException e) {
throw new ProvisioningException(String.format("Failed to process %s for files that require line ending changes.", installDir), e);
}
}
private static void changeLineEndings(final Path file, final boolean isWindows) throws IOException {
final String eol = (isWindows ? "\r\n" : "\n");
final Path temp = Files.createTempFile(file.getFileName().toString(), ".tmp");
// Copy the original file to the temporary file, replacing it and copying the attributes. Note that the order of
// REPLACE_EXISTING and COPY_ATTRIBUTES is important.
Files.copy(file, temp, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES);
try (
BufferedReader reader = Files.newBufferedReader(temp, StandardCharsets.UTF_8);
BufferedWriter writer = Files.newBufferedWriter(file, StandardCharsets.UTF_8, StandardOpenOption.TRUNCATE_EXISTING)
) {
// Process each line and write a eol string
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.write(eol);
}
} finally {
Files.delete(temp);
}
}
private Path getPomArtifactPath(MavenArtifact artifact) throws MavenUniverseException, IOException {
MavenArtifact pomArtifact = new MavenArtifact();
pomArtifact.setGroupId(artifact.getGroupId());
pomArtifact.setArtifactId(artifact.getArtifactId());
pomArtifact.setVersion(artifact.getVersion());
pomArtifact.setExtension("pom");
resolve(pomArtifact);
return pomArtifact.getPath();
}
private Path getLocalRepoPath(MavenArtifact artifact, Path repo) throws IOException {
String grpid = artifact.getGroupId().replaceAll("\\.", File.separator);
Path grpidPath = repo.resolve(grpid);
Path artifactidPath = grpidPath.resolve(artifact.getArtifactId());
Path versionPath = artifactidPath.resolve(artifact.getVersion());
Files.createDirectories(versionPath);
return versionPath;
}
private void resolve(MavenArtifact artifact) throws MavenUniverseException, IOException {
if (provisioningMavenRepo == null) {
maven.resolve(artifact);
} else {
String grpid = artifact.getGroupId().replaceAll("\\.", File.separator);
Path grpidPath = provisioningMavenRepo.resolve(grpid);
Path artifactidPath = grpidPath.resolve(artifact.getArtifactId());
Path versionPath = artifactidPath.resolve(artifact.getVersion());
String classifier = (artifact.getClassifier() == null || artifact.getClassifier().isEmpty()) ? null : artifact.getClassifier();
Path localPath = versionPath.resolve(artifact.getArtifactId() + "-"
+ artifact.getVersion()
+ (classifier == null ? "" : "-" + classifier)
+ "." + artifact.getExtension());
if (Files.exists(localPath)) {
artifact.setPath(localPath);
} else {
maven.resolve(artifact);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy