diff --git a/doc-plugin/pom.xml b/doc-plugin/pom.xml
index 6d224a4e..eea65a8c 100644
--- a/doc-plugin/pom.xml
+++ b/doc-plugin/pom.xml
@@ -49,6 +49,14 @@
org.apache.maven.shared
maven-shared-utils
+
+ org.wildfly.channel
+ channel-core
+
+
+ org.wildfly.channel
+ maven-resolver
+
diff --git a/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ChannelConfiguration.java b/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ChannelConfiguration.java
new file mode 100644
index 00000000..734367dd
--- /dev/null
+++ b/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ChannelConfiguration.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2023 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.glow.plugin.doc;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.maven.plugin.MojoExecutionException;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.wildfly.channel.Channel;
+import org.wildfly.channel.ChannelManifestCoordinate;
+import org.wildfly.channel.Repository;
+
+/**
+ * A channel configuration. Contains a {@code manifest} composed of a {@code groupId}, an {@code artifactId}
+ * an optional {@code version} or a {@code url}.
+ *
+ * @author jdenise
+ */
+public class ChannelConfiguration {
+ private static final Pattern FILE_MATCHER = Pattern.compile("^(file|http|https)://.*");
+
+ private ChannelManifestCoordinate manifest;
+
+ /**
+ * @return the manifest
+ */
+ public ChannelManifestCoordinate getManifest() {
+ return manifest;
+ }
+
+ public void set(final String channel) {
+ // Is this a URL?
+ if (FILE_MATCHER.matcher(channel).matches()) {
+ try {
+ this.manifest = new ChannelManifestCoordinate(new URL(channel));
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException("Failed to parse URL for " + channel, e);
+ }
+ } else {
+ // Treat as a Maven GAV
+ final String[] coords = channel.split(":");
+ if (coords.length > 2) {
+ this.manifest = new ChannelManifestCoordinate(coords[0], coords[1], coords[2]);
+ } else if (coords.length == 2) {
+ this.manifest = new ChannelManifestCoordinate(coords[0], coords[1]);
+ } else {
+ throw new IllegalArgumentException(
+ "A channel must be a Maven GAV in the format groupId:artifactId:version. The groupId and artifactId are both required.");
+ }
+ }
+ }
+
+ void setManifest(ChannelManifestCoordinate manifest) {
+ this.manifest = manifest;
+ }
+
+ private void validate() throws MojoExecutionException {
+ if (getManifest() == null) {
+ throw new MojoExecutionException("Invalid Channel. No manifest specified.");
+ }
+ ChannelManifestCoordinate coordinates = getManifest();
+ if (coordinates.getUrl() == null && coordinates.getGroupId() == null && coordinates.getArtifactId() == null) {
+ throw new MojoExecutionException(
+ "Invalid Channel. Manifest must contain a groupId, artifactId and (optional) version or an url.");
+ }
+ if (coordinates.getUrl() == null) {
+ if (coordinates.getGroupId() == null) {
+ throw new MojoExecutionException("Invalid Channel. Manifest groupId is null.");
+ }
+ if (coordinates.getArtifactId() == null) {
+ throw new MojoExecutionException("Invalid Channel. Manifest artifactId is null.");
+ }
+ } else {
+ if (coordinates.getGroupId() != null) {
+ throw new MojoExecutionException("Invalid Channel. Manifest groupId is set although an URL is provided.");
+ }
+ if (coordinates.getArtifactId() != null) {
+ throw new MojoExecutionException("Invalid Channel. Manifest artifactId is set although an URL is provided.");
+ }
+ if (coordinates.getVersion() != null) {
+ throw new MojoExecutionException("Invalid Channel. Manifest version is set although an URL is provided.");
+ }
+ }
+ }
+
+ public Channel toChannel(List repositories) throws MojoExecutionException {
+ validate();
+ List repos = new ArrayList<>();
+ for (RemoteRepository r : repositories) {
+ repos.add(new Repository(r.getId(), r.getUrl()));
+ }
+ return new Channel(null, null, null, repos, getManifest(), null, null);
+ }
+}
diff --git a/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ChannelMavenArtifactRepositoryManager.java b/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ChannelMavenArtifactRepositoryManager.java
new file mode 100644
index 00000000..d36563b4
--- /dev/null
+++ b/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ChannelMavenArtifactRepositoryManager.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2024 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.glow.plugin.doc;
+
+import static org.wildfly.channel.maven.VersionResolverFactory.DEFAULT_REPOSITORY_MAPPER;
+
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.regex.Pattern;
+
+import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
+import org.eclipse.aether.DefaultRepositorySystemSession;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.resolution.VersionRangeRequest;
+import org.eclipse.aether.resolution.VersionRangeResolutionException;
+import org.eclipse.aether.resolution.VersionRangeResult;
+import org.jboss.galleon.api.MavenStreamResolver;
+import org.jboss.galleon.universe.maven.MavenArtifact;
+import org.jboss.galleon.universe.maven.MavenUniverseException;
+import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
+import org.wildfly.channel.ArtifactTransferException;
+import org.wildfly.channel.Channel;
+import org.wildfly.channel.ChannelSession;
+import org.wildfly.channel.NoStreamFoundException;
+import org.wildfly.channel.Repository;
+import org.wildfly.channel.UnresolvedMavenArtifactException;
+import org.wildfly.channel.VersionResult;
+import org.wildfly.channel.maven.VersionResolverFactory;
+import org.wildfly.channel.spi.ChannelResolvable;
+
+public class ChannelMavenArtifactRepositoryManager implements MavenRepoManager, ChannelResolvable, MavenStreamResolver {
+
+ private final ChannelSession channelSession;
+ private final RepositorySystem system;
+ private final DefaultRepositorySystemSession session;
+ private final List repositories;
+
+ public ChannelMavenArtifactRepositoryManager(List channels,
+ RepositorySystem system,
+ RepositorySystemSession contextSession,
+ List repositories)
+ throws Exception {
+ session = MavenRepositorySystemUtils.newSession();
+ this.repositories = repositories;
+ session.setLocalRepositoryManager(contextSession.getLocalRepositoryManager());
+ Map mapping = new HashMap<>();
+ for (RemoteRepository r : repositories) {
+ mapping.put(r.getId(), r);
+ }
+ Function mapper = r -> {
+ RemoteRepository rep = mapping.get(r.getId());
+ if (rep == null) {
+ rep = DEFAULT_REPOSITORY_MAPPER.apply(r);
+ }
+ return rep;
+ };
+ VersionResolverFactory factory = new VersionResolverFactory(system, session, mapper);
+ channelSession = new ChannelSession(channels, factory);
+ this.system = system;
+ }
+
+ public ChannelSession getChannelSession() {
+ return channelSession;
+ }
+
+ @Override
+ public void resolve(MavenArtifact artifact) throws MavenUniverseException {
+ try {
+ resolveFromChannels(artifact);
+ } catch (ArtifactTransferException ex) {
+ throw new MavenUniverseException(ex.getLocalizedMessage(), ex);
+ } catch (NoStreamFoundException ex) {
+ // unable to resolve the artifact through the channel.
+ // if the version is defined, let's resolve it directly
+ if (artifact.getVersion() == null || artifact.getVersion().isEmpty()) {
+ throw new MavenUniverseException(ex.getLocalizedMessage(), ex);
+ }
+ try {
+ org.wildfly.channel.MavenArtifact mavenArtifact = channelSession.resolveDirectMavenArtifact(
+ artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(), artifact.getClassifier(),
+ artifact.getVersion());
+ artifact.setPath(mavenArtifact.getFile().toPath());
+ } catch (UnresolvedMavenArtifactException e) {
+ // if the artifact can not be resolved directly either, we abort
+ throw new MavenUniverseException(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+
+ private void resolveFromChannels(MavenArtifact artifact) throws UnresolvedMavenArtifactException {
+ org.wildfly.channel.MavenArtifact result = channelSession.resolveMavenArtifact(artifact.getGroupId(),
+ artifact.getArtifactId(), artifact.getExtension(), artifact.getClassifier(), artifact.getVersion());
+ artifact.setVersion(result.getVersion());
+ artifact.setPath(result.getFile().toPath());
+ }
+
+ @Override
+ public void resolveLatestVersion(MavenArtifact artifact) throws MavenUniverseException {
+ throw new MavenUniverseException("Channel resolution can't be applied to Galleon universe");
+ }
+
+ @Override
+ public boolean isResolved(MavenArtifact artifact) throws MavenUniverseException {
+ throw new MavenUniverseException("Channel resolution can't be applied to Galleon universe");
+ }
+
+ @Override
+ public boolean isLatestVersionResolved(MavenArtifact artifact, String lowestQualifier) throws MavenUniverseException {
+ throw new MavenUniverseException("Channel resolution can't be applied to Galleon universe");
+ }
+
+ @Override
+ public void resolveLatestVersion(MavenArtifact artifact, String lowestQualifier, Pattern includeVersion,
+ Pattern excludeVersion) throws MavenUniverseException {
+ resolveLatestVersion(artifact, null, false);
+ }
+
+ @Override
+ public void resolveLatestVersion(MavenArtifact artifact, String lowestQualifier, boolean locallyAvailable)
+ throws MavenUniverseException {
+ artifact.setVersion(getLatestVersion(artifact));
+ resolve(artifact);
+ }
+
+ @Override
+ public String getLatestVersion(MavenArtifact artifact) throws MavenUniverseException {
+ return getLatestVersion(artifact, null, null, null);
+ }
+
+ @Override
+ public String getLatestVersion(MavenArtifact artifact, String lowestQualifier) throws MavenUniverseException {
+ return getLatestVersion(artifact, lowestQualifier, null, null);
+ }
+
+ @Override
+ public String getLatestVersion(MavenArtifact artifact, String lowestQualifier, Pattern includeVersion,
+ Pattern excludeVersion) throws MavenUniverseException {
+ try {
+ return channelSession.resolveMavenArtifact(artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
+ artifact.getClassifier(), null).getVersion();
+ } catch (UnresolvedMavenArtifactException e) {
+ VersionRangeResult res = getVersionRange(new DefaultArtifact(artifact.getGroupId(),
+ artifact.getArtifactId(), artifact.getExtension(), artifact.getVersionRange()));
+ return res.getHighestVersion().toString();
+ }
+ }
+
+ @Override
+ public List getAllVersions(MavenArtifact artifact) throws MavenUniverseException {
+ throw new MavenUniverseException("Channel resolution can't be applied to Galleon universe");
+ }
+
+ @Override
+ public List getAllVersions(MavenArtifact artifact, Pattern includeVersion, Pattern excludeVersion)
+ throws MavenUniverseException {
+ throw new MavenUniverseException("Channel resolution can't be applied to Galleon universe");
+ }
+
+ @Override
+ public void install(MavenArtifact artifact, Path path) throws MavenUniverseException {
+ throw new MavenUniverseException("Channel resolution can't be applied to Galleon universe");
+ }
+
+ @Override
+ public String getLatestVersion(String groupId, String artifactId, String extension, String classifier, String baseVersion) {
+ VersionResult res = channelSession.findLatestMavenArtifactVersion(groupId, artifactId, extension, classifier,
+ baseVersion);
+ return res.getVersion();
+ }
+
+ private VersionRangeResult getVersionRange(Artifact artifact) throws MavenUniverseException {
+ VersionRangeRequest rangeRequest = new VersionRangeRequest();
+ rangeRequest.setArtifact(artifact);
+ rangeRequest.setRepositories(repositories);
+ VersionRangeResult rangeResult;
+ try {
+ rangeResult = system.resolveVersionRange(session, rangeRequest);
+ } catch (VersionRangeResolutionException ex) {
+ throw new MavenUniverseException(ex.getLocalizedMessage(), ex);
+ }
+ return rangeResult;
+ }
+
+}
diff --git a/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ScanDocMojo.java b/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ScanDocMojo.java
index b81fde0e..c5d116ee 100644
--- a/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ScanDocMojo.java
+++ b/doc-plugin/src/main/java/org/wildfly/glow/plugin/doc/ScanDocMojo.java
@@ -37,6 +37,7 @@
import org.wildfly.glow.Layer;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -50,6 +51,7 @@
import org.jboss.galleon.api.config.GalleonProvisioningConfig;
import org.jboss.galleon.universe.UniverseResolver;
import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
+import org.wildfly.channel.Channel;
import org.wildfly.glow.AddOn;
import org.wildfly.glow.FeaturePacks;
import org.wildfly.glow.LayerMapping;
@@ -95,6 +97,37 @@ public class ScanDocMojo extends AbstractMojo {
@Parameter(required = false)
String repoPath;
+ /**
+ * 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
+ *
+ *
+ *
+ *
+ *
+ */
+ @Parameter(required = false)
+ List channels;
+
+ @Parameter(required = false, defaultValue = "true")
+ boolean preview;
+
+ @Parameter(required = false, defaultValue = "WildFly")
+ String serverType;
+
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
try {
@@ -134,8 +167,17 @@ public void execute() throws MojoExecutionException, MojoFailureException {
}
try {
//Typically under target
- Path outputFolder = Paths.get(project.getBuild().getDirectory());
- MavenRepoManager artifactResolver = new MavenArtifactRepositoryManager(repoSystem, repoSession, repositories);
+ MavenRepoManager artifactResolver;
+ if (channels != null && !channels.isEmpty()) {
+ getLog().debug("WildFly channel enabled.");
+ List lst = new ArrayList<>();
+ for (ChannelConfiguration conf : channels) {
+ lst.add(conf.toChannel(repositories));
+ }
+ artifactResolver = new ChannelMavenArtifactRepositoryManager(lst, repoSystem, repoSession, repositories);
+ } else {
+ artifactResolver = new MavenArtifactRepositoryManager(repoSystem, repoSession, repositories);
+ }
UniverseResolver universeResolver = UniverseResolver.builder().addArtifactResolver(artifactResolver).build();
GalleonBuilder provider = new GalleonBuilder();
provider.addArtifactResolver(artifactResolver);
@@ -144,15 +186,14 @@ public void execute() throws MojoExecutionException, MojoFailureException {
getRules(provider, "bare-metal", universeResolver, rules);
Map> cloudRules = new TreeMap<>();
getRules(provider,"cloud", universeResolver, cloudRules);
- rulesBuilder.append("## Support for WildFly " + FeaturePacks.getLatestVersion() + "\n\n");
-
+ rulesBuilder.append("## Support for " + serverType + " " + FeaturePacks.getLatestVersion() + "\n\n");
rulesBuilder.append(buildTable(provider,"bare-metal", rules, false));
rulesBuilder.append(buildTable(provider,"cloud", cloudRules, false));
-
- rulesBuilder.append("## Support for WildFly Preview " + FeaturePacks.getLatestVersion() + "\n\n");
-
- rulesBuilder.append(buildTable(provider, "bare-metal", rules, true));
- rulesBuilder.append(buildTable(provider, "cloud", cloudRules, true));
+ if (preview) {
+ rulesBuilder.append("## Support for WildFly Preview " + FeaturePacks.getLatestVersion() + "\n\n");
+ rulesBuilder.append(buildTable(provider, "bare-metal", rules, true));
+ rulesBuilder.append(buildTable(provider, "cloud", cloudRules, true));
+ }
} finally {
System.clearProperty(FeaturePacks.URL_PROPERTY);
}