Skip to content

Commit

Permalink
Merge pull request #24 from jfdenise/main
Browse files Browse the repository at this point in the history
Add support for WildFly Channel in arquillian plugin
  • Loading branch information
jfdenise authored Nov 21, 2023
2 parents a4b1d74 + 2727c6d commit 4edc0fd
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 19 deletions.
8 changes: 8 additions & 0 deletions arquillian-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@
<groupId>org.jboss.shrinkwrap</groupId>
<artifactId>shrinkwrap-impl-base</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly.channel</groupId>
<artifactId>channel-core</artifactId>
</dependency>
<dependency>
<groupId>org.wildfly.channel</groupId>
<artifactId>maven-resolver</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -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.arquillian;

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<RemoteRepository> repositories) throws MojoExecutionException {
validate();
List<Repository> 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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2022 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.arquillian;

import static org.wildfly.channel.maven.VersionResolverFactory.DEFAULT_REPOSITORY_MAPPER;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
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.repository.RemoteRepository;
import org.wildfly.channel.Channel;
import org.wildfly.channel.ChannelSession;
import org.wildfly.channel.Repository;
import org.wildfly.channel.UnresolvedMavenArtifactException;
import org.wildfly.channel.maven.VersionResolverFactory;

public class ConfiguredChannels {

private final ChannelSession channelSession;
public ConfiguredChannels(List<ChannelConfiguration> channels,
RepositorySystem system,
RepositorySystemSession contextSession,
List<RemoteRepository> repositories, Log log, boolean offline)
throws MalformedURLException, UnresolvedMavenArtifactException, MojoExecutionException {
if (channels.isEmpty()) {
throw new MojoExecutionException("No channel specified.");
}
DefaultRepositorySystemSession session = MavenRepositorySystemUtils.newSession();
session.setLocalRepositoryManager(contextSession.getLocalRepositoryManager());
session.setOffline(offline);
Map<String, RemoteRepository> mapping = new HashMap<>();
for (RemoteRepository r : repositories) {
mapping.put(r.getId(), r);
}
List<Channel> channelDefinitions = new ArrayList<>();
for (ChannelConfiguration channelConfiguration : channels) {
channelDefinitions.add(channelConfiguration.toChannel(repositories));
}
Function<Repository, RemoteRepository> 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(channelDefinitions, factory);
}

ChannelSession getChannelSession() {
return channelSession;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ public String getMavenCoords() {
builder.append(":").append(getClassifier() == null ? "" : getClassifier()).append(":")
.append(type == null ? "" : type);
}
// For WildFly Channel we need the Maven GA to be ended by a ':' if no version.
builder.append(":");
if (getVersion() != null) {
builder.append(":").append(getVersion());
builder.append(getVersion());
}
return builder.toString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
Expand All @@ -66,6 +67,9 @@
import java.util.Map;
import java.util.Set;
import org.apache.maven.plugin.logging.Log;
import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
import org.wildfly.channel.UnresolvedMavenArtifactException;
import org.wildfly.channel.VersionResult;
import org.wildfly.glow.error.IdentifiedError;
import static org.wildfly.glow.plugin.arquillian.GlowArquillianDeploymentExporter.TEST_CLASSPATH;
import static org.wildfly.glow.plugin.arquillian.GlowArquillianDeploymentExporter.TEST_PATHS;
Expand Down Expand Up @@ -203,6 +207,43 @@ public void trace(Object s) {
@Parameter(property = "org.wildfly.glow.verbose")
private boolean verbose = false;

/**
* A list of channels used for resolving artifacts while provisioning.
* <p>
* Defining a channel:
*
* <pre>
* <channels>
* <channel>
* <manifest>
* <groupId>org.wildfly.channels</groupId>
* <artifactId>wildfly-30.0</artifactId>
* </manifest>
* </channel>
* <channel>
* <manifest>
* <url>https://example.example.org/channel/30</url>
* </manifest>
* </channel>
* </channels>
* </pre>
* </p>
* <p>
* 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.
* <br>
* Examples:
*
* <pre>
* -Dorg.wildfly.glow.channels=&quot;https://channels.example.org/30&quot;
* -Dorg.wildfly.glow.channels=&quot;https://channels.example.org/30,org.example.channel:updates-30&quot;
* -Dorg.wildfly.glow.channels=&quot;https://channels.example.org/30,org.example.channel:updates-30:1.0.2&quot;
* </pre>
* </p>
*/
@Parameter(alias = "channels", property = "org.wildfly.glow.channels")
List<ChannelConfiguration> channels;

@Override
public void execute() throws MojoExecutionException, MojoFailureException {
// Make sure that the 'hidden' properties used by the Arguments class come from the Maven configuration
Expand Down Expand Up @@ -232,6 +273,27 @@ public void execute() throws MojoExecutionException, MojoFailureException {
for (String s : project.getTestClasspathElements()) {
paths.add(new File(s).getAbsolutePath());
}
MavenRepoManager artifactResolver = new MavenArtifactRepositoryManager(repoSystem, repoSession, repositories);
if (channels != null && !channels.isEmpty()) {
getLog().debug("WildFly channel enabled, feature-pack versions are retrieved from channels (if stream known).");
try {
ConfiguredChannels cr = new ConfiguredChannels(channels,
repoSystem, repoSession, repositories,
getLog(), true);
for (FeaturePack fp : featurePacks) {
try {
VersionResult res = cr.getChannelSession().findLatestMavenArtifactVersion(fp.getGroupId(), fp.getArtifactId(),
fp.getExtension(), fp.getClassifier(), null);
getLog().debug(fp.getGroupId() +":"+fp.getArtifactId() + ", Channel resolved version " + res.getVersion());
fp.setVersion(res.getVersion());
} catch (Exception ex) {
getLog().debug("Got exception trying to resolve " + fp.getGroupId() +":"+fp.getArtifactId(), ex);
}
}
} catch (MalformedURLException | UnresolvedMavenArtifactException ex) {
throw new MojoExecutionException(ex.getLocalizedMessage(), ex);
}
}
Arguments arguments = Arguments.scanBuilder().
setExecutionProfiles(profiles).
setBinaries(retrieveDeployments(paths, classesRootFolder, outputFolder)).
Expand All @@ -242,8 +304,6 @@ public void execute() throws MojoExecutionException, MojoFailureException {
setJndiLayers(layersForJndi).
setVerbose(verbose || getLog().isDebugEnabled()).
setOutput(OutputFormat.PROVISIONING_XML).build();
MavenArtifactRepositoryManager artifactResolver
= new MavenArtifactRepositoryManager(repoSystem, repoSession, repositories);
ScanResults results = GlowSession.scan(artifactResolver,
arguments, writer);
if (expectedDiscovery != null) {
Expand Down
33 changes: 26 additions & 7 deletions core/src/main/java/org/wildfly/glow/GlowSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.jboss.galleon.universe.FeaturePackLocation.ProducerSpec;
import static org.wildfly.glow.OutputFormat.BOOTABLE_JAR;
import static org.wildfly.glow.OutputFormat.DOCKER_IMAGE;

Expand Down Expand Up @@ -809,16 +810,34 @@ private static ProvisioningConfig buildProvisioningConfig(ProvisioningConfig inp
Map<FPID, FeaturePackConfig> map = new HashMap<>();
Map<FPID, FPID> universeToGav = new HashMap<>();
for (FeaturePackConfig cfg : input.getFeaturePackDeps()) {
FeaturePackLocation.FPID fpid = Utils.toMavenCoordinates(cfg.getLocation().getFPID(), universeResolver);
map.put(fpid, cfg);
universeToGav.put(cfg.getLocation().getFPID(), fpid);
FeaturePackLocation.FPID loc = null;
for (FeaturePackLocation.FPID f : fpDependencies.keySet()) {
if (cfg.getLocation().getProducer().equals(f.getProducer())) {
loc = f;
break;
}
}
if(loc == null) {
throw new ProvisioningException("Input fp "+ cfg.getLocation() + " not found in resolved feature-packs " + fpDependencies.keySet());
}
map.put(loc, cfg);
universeToGav.put(cfg.getLocation().getFPID(), loc);
}
Set<FeaturePackLocation.FPID> activeFeaturePacks = new LinkedHashSet<>();
// Add WildFly first.
Map<ProducerSpec, FeaturePackLocation.FPID> tmpFps = new HashMap<>();
FeaturePackLocation.FPID baseFPID = universeToGav.get(input.getFeaturePackDeps().iterator().next().getLocation().getFPID());
activeFeaturePacks.add(baseFPID);
tmpFps.put(baseFPID.getProducer(), baseFPID);
for (Layer l : allBaseLayers) {
activeFeaturePacks.addAll(l.getFeaturePacks());
for(FPID fpid : l.getFeaturePacks()) {
tmpFps.put(fpid.getProducer(), fpid);
}
}
Set<FeaturePackLocation.FPID> activeFeaturePacks = new LinkedHashSet<>();
// Order follow the one from the input
for(FeaturePackConfig cfg : input.getFeaturePackDeps()) {
FeaturePackLocation.FPID fpid = tmpFps.get(cfg.getLocation().getProducer());
if (fpid != null) {
activeFeaturePacks.add(fpid);
}
}
// Remove dependencies that are not Main FP...
//System.out.println("Active FP " + activeFeaturePacks);
Expand Down
7 changes: 1 addition & 6 deletions core/src/main/java/org/wildfly/glow/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,8 @@ public static Map<String, Layer> getAllLayers(UniverseResolver universeResolver,
}
l.getFeaturePacks().add(fpid);
}

Set<ProducerSpec> producers = fpDependencies.computeIfAbsent(fpid, (value) -> new HashSet<>());
for (FeaturePackConfig cfg : fp.getSpec().getFeaturePackDeps()) {
Set<ProducerSpec> producers = fpDependencies.get(fpid);
if (producers == null) {
producers = new HashSet<>();
fpDependencies.put(fpid, producers);
}
FPID fpidDep = toMavenCoordinates(cfg.getLocation().getFPID(), universeResolver);
producers.add(fpidDep.getProducer());
}
Expand Down
Loading

0 comments on commit 4edc0fd

Please sign in to comment.