org.wildfly.plugin.provision.AbstractProvisionServerMojo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wildfly-maven-plugin Show documentation
Show all versions of wildfly-maven-plugin Show documentation
A maven plugin that allows various management operations to be executed on WildFly Application
Server.
/*
* Copyright 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.plugin.provision;
import static org.wildfly.plugin.core.Constants.PLUGIN_PROVISIONING_FILE;
import static org.wildfly.plugin.core.Constants.STANDALONE_XML;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.xml.stream.XMLStreamException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.repository.RemoteRepository;
import org.jboss.galleon.ProvisioningDescriptionException;
import org.jboss.galleon.ProvisioningException;
import org.jboss.galleon.ProvisioningManager;
import org.jboss.galleon.config.ProvisioningConfig;
import org.jboss.galleon.maven.plugin.util.MavenArtifactRepositoryManager;
import org.jboss.galleon.maven.plugin.util.MvnMessageWriter;
import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
import org.jboss.galleon.util.IoUtils;
import org.jboss.galleon.xml.ProvisioningXmlWriter;
import org.wildfly.channel.UnresolvedMavenArtifactException;
import org.wildfly.plugin.common.PropertyNames;
import org.wildfly.plugin.common.Utils;
import org.wildfly.plugin.core.FeaturePack;
import org.wildfly.plugin.core.GalleonUtils;
import org.wildfly.plugin.core.MavenRepositoriesEnricher;
import org.wildfly.plugin.core.PluginProgressTracker;
/**
* Provision a server
*
* @author jfdenise
*/
abstract class AbstractProvisionServerMojo extends AbstractMojo {
@Component
RepositorySystem repoSystem;
@Component
MavenProjectHelper projectHelper;
@Parameter(defaultValue = "${repositorySystemSession}", readonly = true)
RepositorySystemSession repoSession;
@Parameter(defaultValue = "${project.remoteProjectRepositories}", readonly = true, required = true)
List repositories;
@Parameter(defaultValue = "${project}", readonly = true, required = true)
MavenProject project;
@Parameter(defaultValue = "${session}", readonly = true, required = true)
MavenSession session;
/**
* Arbitrary Galleon options used when provisioning the server. In case you
* are building a large amount of server in the same maven session, it
* is strongly advised to set 'jboss-fork-embedded' option to 'true' in
* order to fork Galleon provisioning and CLI scripts execution in dedicated
* processes. For example:
*
*
* <galleon-options>
* <jboss-fork-embedded>true</jboss-fork-embedded>
* </galleon-options>
*
*/
@Parameter(required = false, alias = "galleon-options")
Map galleonOptions = Collections.emptyMap();
/**
* Whether to use offline mode when the plugin resolves an artifact. In
* offline mode the plugin will only use the local Maven repository for an
* artifact resolution.
*/
@Parameter(alias = "offline-provisioning", defaultValue = "false", property = PropertyNames.WILDFLY_PROVISIONING_OFFLINE)
boolean offlineProvisioning;
/**
* Whether to log provisioning time at the end
*/
@Parameter(alias = "log-provisioning-time", defaultValue = "false", property = PropertyNames.WILDFLY_PROVISIONING_LOG_TIME)
boolean logProvisioningTime;
/**
* Whether to record provisioning state in .galleon directory.
*/
@Parameter(alias = "record-provisioning-state", defaultValue = "false", property = PropertyNames.WILDFLY_PROVISIONING_RECORD_STATE)
boolean recordProvisioningState;
/**
* Set to {@code true} if you want the goal to be skipped, otherwise
* {@code false}.
*/
@Parameter(defaultValue = "false", property = PropertyNames.SKIP_PROVISION)
private boolean skip;
/**
* The path to the directory where to provision the server. Can be an absolute path or a path relative to the buildDir.
* By default the server is provisioned into the {@code target/server} directory.
*/
@Parameter(alias = "provisioning-dir", property = PropertyNames.WILDFLY_PROVISIONING_DIR, defaultValue = Utils.WILDFLY_DEFAULT_DIR)
protected String provisioningDir;
/**
* Set to {@code true} if you want to delete the existing server referenced from the {@code provisioningDir} and provision a
* new one,
* otherwise {@code false}.
*/
@Parameter(alias = "overwrite-provisioned-server", defaultValue = "false", property = PropertyNames.WILDFLY_PROVISIONING_OVERWRITE_PROVISIONED_SERVER)
private boolean overwriteProvisionedServer;
/**
* A list of feature-pack configurations to install, can be combined with layers.
* Use the System property {@code wildfly.provisioning.feature-packs} to provide a comma separated list of feature-packs.
*/
@Parameter(required = false, alias = "feature-packs", property = PropertyNames.WILDFLY_PROVISIONING_FEATURE_PACKS)
List featurePacks = Collections.emptyList();
/**
* A list of Galleon layers to provision. Can be used when
* feature-pack-location or feature-packs are set.
* Use the System property {@code wildfly.provisioning.layers} to provide a comma separated list of layers.
*/
@Parameter(alias = "layers", required = false, property = PropertyNames.WILDFLY_PROVISIONING_LAYERS)
List layers = Collections.emptyList();
/**
* A list of Galleon layers to exclude. Can be used when
* feature-pack-location or feature-packs are set.
* Use the System property {@code wildfly.provisioning.layers.excluded} to provide a comma separated list of layers to
* exclude.
*/
@Parameter(alias = "excluded-layers", required = false, property = PropertyNames.WILDFLY_PROVISIONING_LAYERS_EXCLUDED)
List excludedLayers = Collections.emptyList();
/**
* The path to the {@code provisioning.xml} file to use. Note that this cannot be used with the {@code feature-packs}
* or {@code configurations}.
* If the provisioning file is not absolute, it has to be relative to the project base directory.
*/
@Parameter(alias = "provisioning-file", property = PropertyNames.WILDFLY_PROVISIONING_FILE, defaultValue = "${project.basedir}/galleon/provisioning.xml")
private File provisioningFile;
/**
* The name of the configuration file generated from layers. Default value is {@code standalone.xml}.
* If no {@code layers} have been configured, setting this parameter is invalid.
*/
@Parameter(alias = "layers-configuration-file-name", property = PropertyNames.WILDFLY_LAYERS_CONFIGURATION_FILE_NAME, defaultValue = STANDALONE_XML)
String layersConfigurationFileName;
/**
* A list of channels used for resolving artifacts while provisioning.
*
* Defining a channel:
*
*
*
*
*
* org.wildfly.channels
* wildfly-30.0
*
*
*
*
* https://example.example.org/channel/30
*
*
*
*
*
*
* The {@code wildfly.channels} property can be used pass a comma delimited string for the channels. The channel
* can be a URL or a Maven GAV. If a Maven GAV is used, the groupId and artifactId are required.
*
* Examples:
*
*
* -Dwildfly.channels="https://channels.example.org/30"
* -Dwildfly.channels="https://channels.example.org/30,org.example.channel:updates-30"
* -Dwildfly.channels="https://channels.example.org/30,org.example.channel:updates-30:1.0.2"
*
*
*/
@Parameter(alias = "channels", property = PropertyNames.CHANNELS)
List channels;
private Path wildflyDir;
protected MavenRepoManager artifactResolver;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
if (skip) {
getLog().debug(String.format("Skipping " + getGoal() + " of %s:%s", project.getGroupId(), project.getArtifactId()));
return;
}
Path targetPath = Paths.get(project.getBuild().getDirectory());
wildflyDir = targetPath.resolve(provisioningDir).normalize();
if (!overwriteProvisionedServer && Files.exists(wildflyDir)) {
getLog().info(String.format("A server already exists in " + wildflyDir + ", skipping " + getGoal() +
" of %s:%s", project.getGroupId(), project.getArtifactId()));
return;
}
enrichRepositories();
if (channels == null || channels.isEmpty()) {
artifactResolver = offlineProvisioning ? new MavenArtifactRepositoryManager(repoSystem, repoSession)
: new MavenArtifactRepositoryManager(repoSystem, repoSession, repositories);
} else {
try {
artifactResolver = new ChannelMavenArtifactRepositoryManager(channels,
repoSystem, repoSession, repositories,
getLog(), offlineProvisioning);
} catch (MalformedURLException | UnresolvedMavenArtifactException ex) {
throw new MojoExecutionException(ex.getLocalizedMessage(), ex);
}
}
if (!Paths.get(provisioningDir).isAbsolute() && (targetPath.equals(wildflyDir) || !wildflyDir.startsWith(targetPath))) {
throw new MojoExecutionException("provisioning-dir " + provisioningDir
+ " must be an absolute path or a child directory relative to the project build directory.");
}
IoUtils.recursiveDelete(wildflyDir);
try {
try {
provisionServer(wildflyDir);
if (artifactResolver instanceof ChannelMavenArtifactRepositoryManager) {
((ChannelMavenArtifactRepositoryManager) artifactResolver).done(wildflyDir);
}
} catch (ProvisioningException | IOException | XMLStreamException ex) {
throw new MojoExecutionException("Provisioning failed", ex);
}
serverProvisioned(wildflyDir);
} finally {
// Although cli and embedded are run in their own classloader,
// the module.path system property has been set and needs to be cleared for
// in same JVM next execution.
System.clearProperty("module.path");
}
}
protected void enrichRepositories() throws MojoExecutionException {
MavenRepositoriesEnricher.enrich(session, project, repositories);
}
protected abstract String getGoal();
protected abstract void serverProvisioned(Path jbossHome) throws MojoExecutionException, MojoFailureException;
private void provisionServer(Path home) throws ProvisioningException,
MojoExecutionException, IOException, XMLStreamException {
try (ProvisioningManager pm = ProvisioningManager.builder().addArtifactResolver(artifactResolver)
.setInstallationHome(home)
.setMessageWriter(new MvnMessageWriter(getLog()))
.setLogTime(logProvisioningTime)
.setRecordState(recordProvisioningState)
.build()) {
ProvisioningConfig config = null;
Path resolvedProvisioningFile = resolvePath(project, provisioningFile.toPath());
boolean provisioningFileExists = Files.exists(resolvedProvisioningFile);
if (featurePacks.isEmpty()) {
if (provisioningFileExists) {
getLog().info("Provisioning server using " + resolvedProvisioningFile + " file.");
config = GalleonUtils.buildConfig(resolvedProvisioningFile);
} else {
config = getDefaultConfig();
if (config == null) {
throw new MojoExecutionException("No feature-pack has been configured, can't provision a server.");
}
}
} else {
if (provisioningFileExists) {
getLog().warn(
"Galleon provisioning file " + provisioningFile + " is ignored, plugin configuration is used.");
}
if (layers.isEmpty() && !STANDALONE_XML.equals(layersConfigurationFileName)) {
throw new MojoExecutionException(
"layers-configuration-file-name has been set although no layers are defined.");
}
config = GalleonUtils.buildConfig(pm, featurePacks, layers, excludedLayers, galleonOptions,
layersConfigurationFileName);
}
getLog().info("Provisioning server in " + home);
PluginProgressTracker.initTrackers(pm, getLog());
pm.provision(config);
// Check that at least the standalone or domain directories have been generated.
if (!Files.exists(home.resolve("standalone")) && !Files.exists(home.resolve("domain"))) {
getLog().error("Invalid galleon provisioning, no server provisioned in " + home + ". Make sure "
+ "that the list of Galleon feature-packs and Galleon layers are properly configured.");
throw new MojoExecutionException("Invalid plugin configuration, no server provisioned.");
}
if (!recordProvisioningState) {
Path file = home.resolve(PLUGIN_PROVISIONING_FILE);
try (FileWriter writer = new FileWriter(file.toFile())) {
ProvisioningXmlWriter.getInstance().write(config, writer);
}
}
}
}
protected ProvisioningConfig getDefaultConfig() throws ProvisioningDescriptionException {
return GalleonUtils.buildDefaultConfig();
}
static Path resolvePath(MavenProject project, Path path) {
if (!path.isAbsolute()) {
path = Paths.get(project.getBasedir().getAbsolutePath()).resolve(path);
}
return path;
}
}