Skip to content

Commit aa48bce

Browse files
authored
Merge pull request #41 from jfdenise/JBEAP-35648
Fix for JBEAP-35648, offline handling with maven 3.10.0
2 parents 9699099 + 8fc2517 commit aa48bce

4 files changed

Lines changed: 191 additions & 22 deletions

File tree

‎plugin/src/main/java/org/wildfly/plugin/provision/AbstractProvisionServerMojo.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,8 @@ public void execute() throws MojoExecutionException, MojoFailureException {
236236
}
237237
enrichRepositories();
238238
if (channels == null || channels.isEmpty()) {
239-
artifactResolver = offlineProvisioning ? new MavenArtifactRepositoryManager(repoSystem, repoSession)
239+
artifactResolver = offlineProvisioning
240+
? MavenArtifactRepositoryManager.offline(repoSystem, repoSession, repositories)
240241
: new MavenArtifactRepositoryManager(repoSystem, repoSession, repositories);
241242
} else {
242243
try {

‎plugin/src/main/java/org/wildfly/plugin/provision/ChannelMavenArtifactRepositoryManager.java‎

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
import java.nio.file.Path;
1515
import java.util.ArrayList;
1616
import java.util.Collection;
17-
import java.util.Collections;
1817
import java.util.HashMap;
1918
import java.util.List;
2019
import java.util.Map;
@@ -78,28 +77,30 @@ public ChannelMavenArtifactRepositoryManager(List<ChannelConfiguration> channels
7877
this.log = log;
7978
session = new DefaultRepositorySystemSession(contextSession);
8079
this.repositories = repositories;
81-
session.setOffline(offline);
80+
// Do not override an offline Maven session (e.g. mvn -o) when offline provisioning is not requested
81+
session.setOffline(offline || contextSession.isOffline());
82+
// The remote repositories must be passed even in offline mode as artifacts in the local repository are tracked
83+
// with the id of the remote repository they were downloaded from and are only considered available if a
84+
// repository with the same id is part of the request. The offline session prevents any network access.
85+
// The channels recorded in the provisioned server do not include the repositories in offline mode.
86+
List<Channel> resolutionChannels = new ArrayList<>();
8287
for (ChannelConfiguration channelConfiguration : channels) {
83-
this.channels.add(channelConfiguration.toChannel(offline ? Collections.emptyList() : repositories));
88+
resolutionChannels.add(channelConfiguration.toChannel(repositories));
89+
this.channels.add(channelConfiguration.toChannel(offline ? List.of() : repositories));
8490
}
85-
VersionResolverFactory factory;
86-
if (offline) {
87-
factory = new VersionResolverFactory(system, session);
88-
} else {
89-
Map<String, RemoteRepository> mapping = new HashMap<>();
90-
for (RemoteRepository r : repositories) {
91-
mapping.put(r.getId(), r);
92-
}
93-
Function<Repository, RemoteRepository> mapper = r -> {
94-
RemoteRepository rep = mapping.get(r.getId());
95-
if (rep == null) {
96-
rep = DEFAULT_REPOSITORY_MAPPER.apply(r);
97-
}
98-
return rep;
99-
};
100-
factory = new VersionResolverFactory(system, session, mapper);
91+
Map<String, RemoteRepository> mapping = new HashMap<>();
92+
for (RemoteRepository remoteRepository : repositories) {
93+
mapping.put(remoteRepository.getId(), remoteRepository);
10194
}
102-
channelSession = new ChannelSession(this.channels, factory);
95+
Function<Repository, RemoteRepository> mapper = repository -> {
96+
RemoteRepository remoteRepository = mapping.get(repository.getId());
97+
if (remoteRepository == null) {
98+
remoteRepository = DEFAULT_REPOSITORY_MAPPER.apply(repository);
99+
}
100+
return remoteRepository;
101+
};
102+
VersionResolverFactory factory = new VersionResolverFactory(system, session, mapper);
103+
channelSession = new ChannelSession(resolutionChannels, factory);
103104
localCachePath = contextSession.getLocalRepositoryManager().getRepository().getBasedir().toPath();
104105
this.system = system;
105106
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
* Copyright The WildFly Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
package org.wildfly.plugin.provision;
6+
7+
import java.io.IOException;
8+
import java.nio.file.Files;
9+
import java.nio.file.Path;
10+
import java.util.List;
11+
12+
import org.apache.maven.plugin.logging.SystemStreamLog;
13+
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
14+
import org.eclipse.aether.DefaultRepositorySystemSession;
15+
import org.eclipse.aether.RepositorySystem;
16+
import org.eclipse.aether.artifact.Artifact;
17+
import org.eclipse.aether.impl.DefaultServiceLocator;
18+
import org.eclipse.aether.impl.RemoteRepositoryFilterManager;
19+
import org.eclipse.aether.metadata.Metadata;
20+
import org.eclipse.aether.repository.LocalRepository;
21+
import org.eclipse.aether.repository.RemoteRepository;
22+
import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
23+
import org.jboss.galleon.maven.plugin.util.MavenArtifactRepositoryManager;
24+
import org.jboss.galleon.universe.maven.MavenArtifact;
25+
import org.junit.jupiter.api.Assertions;
26+
import org.junit.jupiter.api.BeforeEach;
27+
import org.junit.jupiter.api.Test;
28+
import org.junit.jupiter.api.io.TempDir;
29+
import org.wildfly.channel.ChannelManifestCoordinate;
30+
31+
/**
32+
* Verifies that offline provisioning, with and without channels, resolves artifacts from the local Maven repository
33+
* which were downloaded from a remote repository. Such artifacts are tracked with the id of the remote repository they
34+
* were downloaded from and are only considered available if a repository with the same id is part of the request.
35+
*/
36+
public class OfflineResolutionTestCase {
37+
38+
private static final String REPOSITORY_ID = "test-repository";
39+
private static final String GROUP_ID = "org.wildfly.plugin.test";
40+
private static final RemoteRepositoryFilter.Result ACCEPTED = new RemoteRepositoryFilter.Result() {
41+
@Override
42+
public boolean isAccepted() {
43+
return true;
44+
}
45+
46+
@Override
47+
public String reasoning() {
48+
return "accepted";
49+
}
50+
};
51+
private static final RemoteRepositoryFilter ACCEPT_ALL = new RemoteRepositoryFilter() {
52+
@Override
53+
public Result acceptArtifact(final RemoteRepository remoteRepository, final Artifact artifact) {
54+
return ACCEPTED;
55+
}
56+
57+
@Override
58+
public Result acceptMetadata(final RemoteRepository remoteRepository, final Metadata metadata) {
59+
return ACCEPTED;
60+
}
61+
};
62+
63+
@TempDir
64+
private Path localRepository;
65+
66+
private RepositorySystem system;
67+
private DefaultRepositorySystemSession session;
68+
private List<RemoteRepository> repositories;
69+
70+
@BeforeEach
71+
public void setUp() throws Exception {
72+
final DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
73+
// Maven Resolver 1.x only enforces the availability of artifacts in the local repository when a remote
74+
// repository filter is active, while Maven Resolver 2 always enforces it. Use a filter accepting everything
75+
// to get the same behavior as Maven Resolver 2.
76+
locator.setServices(RemoteRepositoryFilterManager.class, s -> ACCEPT_ALL);
77+
system = locator.getService(RepositorySystem.class);
78+
session = MavenRepositorySystemUtils.newSession();
79+
session.setLocalRepositoryManager(system.newLocalRepositoryManager(session,
80+
new LocalRepository(localRepository.toFile())));
81+
// The repository is never accessed, all artifacts are already present in the local repository
82+
repositories = List.of(new RemoteRepository.Builder(REPOSITORY_ID, "default", "https://localhost.invalid/").build());
83+
84+
install("test-manifest", "1.0.0", "manifest", "yaml", """
85+
schemaVersion: "1.0.0"
86+
streams:
87+
- groupId: "%s"
88+
artifactId: "channel-artifact"
89+
version: "1.0.0"
90+
""".formatted(GROUP_ID));
91+
install("channel-artifact", "1.0.0", null, "jar", "channel-artifact");
92+
install("direct-artifact", "2.0.0", null, "jar", "direct-artifact");
93+
}
94+
95+
@Test
96+
public void testOfflineResolutionFromChannel(@TempDir final Path home) throws Exception {
97+
final MavenArtifact artifact = artifact("channel-artifact", null);
98+
final ChannelMavenArtifactRepositoryManager repositoryManager = createRepositoryManager();
99+
repositoryManager.resolve(artifact);
100+
Assertions.assertEquals("1.0.0", artifact.getVersion());
101+
assertResolved(artifact, "channel-artifact");
102+
103+
// The repositories are only used for resolution and are not recorded in the provisioned server
104+
repositoryManager.done(home);
105+
final String recordedChannels = Files.readString(home.resolve(".installation").resolve("installer-channels.yaml"));
106+
Assertions.assertTrue(recordedChannels.contains("test-manifest"), recordedChannels);
107+
Assertions.assertFalse(recordedChannels.contains(REPOSITORY_ID), recordedChannels);
108+
}
109+
110+
@Test
111+
public void testOfflineDirectResolution() throws Exception {
112+
// Not part of the channel manifest, resolved directly with its original version
113+
final MavenArtifact artifact = artifact("direct-artifact", "2.0.0");
114+
createRepositoryManager().resolve(artifact);
115+
assertResolved(artifact, "direct-artifact");
116+
}
117+
118+
@Test
119+
public void testOfflineResolveAll() throws Exception {
120+
final MavenArtifact channelArtifact = artifact("channel-artifact", null);
121+
final MavenArtifact directArtifact = artifact("direct-artifact", "2.0.0");
122+
createRepositoryManager().resolveAll(List.of(channelArtifact, directArtifact));
123+
assertResolved(channelArtifact, "channel-artifact");
124+
assertResolved(directArtifact, "direct-artifact");
125+
}
126+
127+
@Test
128+
public void testOfflineResolutionWithoutChannel() throws Exception {
129+
final MavenArtifact artifact = artifact("direct-artifact", "2.0.0");
130+
MavenArtifactRepositoryManager.offline(system, session, repositories).resolve(artifact);
131+
assertResolved(artifact, "direct-artifact");
132+
}
133+
134+
private ChannelMavenArtifactRepositoryManager createRepositoryManager() throws Exception {
135+
final ChannelConfiguration channel = new ChannelConfiguration();
136+
channel.setName("test-channel");
137+
channel.setManifest(new ChannelManifestCoordinate(GROUP_ID, "test-manifest", "1.0.0"));
138+
return new ChannelMavenArtifactRepositoryManager(List.of(channel), system, session, repositories,
139+
new SystemStreamLog(), true);
140+
}
141+
142+
private static MavenArtifact artifact(final String artifactId, final String version) {
143+
return new MavenArtifact()
144+
.setGroupId(GROUP_ID)
145+
.setArtifactId(artifactId)
146+
.setVersion(version)
147+
.setExtension("jar");
148+
}
149+
150+
private static void assertResolved(final MavenArtifact artifact, final String expectedContent) throws IOException {
151+
Assertions.assertNotNull(artifact.getPath(), () -> artifact + " was not resolved");
152+
Assertions.assertEquals(expectedContent, Files.readString(artifact.getPath()));
153+
}
154+
155+
/**
156+
* Installs an artifact into the local repository the same way the resolver does when downloading it from the
157+
* remote repository, i.e. tracking the id of the remote repository it was downloaded from.
158+
*/
159+
private void install(final String artifactId, final String version, final String classifier, final String extension,
160+
final String content) throws IOException {
161+
final Path dir = localRepository.resolve(GROUP_ID.replace('.', '/')).resolve(artifactId).resolve(version);
162+
Files.createDirectories(dir);
163+
final String fileName = artifactId + "-" + version + (classifier == null ? "" : "-" + classifier) + "." + extension;
164+
Files.writeString(dir.resolve(fileName), content);
165+
Files.writeString(dir.resolve("_remote.repositories"), fileName + ">" + REPOSITORY_ID + "=\n");
166+
}
167+
}

‎pom.xml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
<maven.compiler.release>17</maven.compiler.release>
118118

119119
<!-- galleon properties -->
120-
<version.org.jboss.galleon>7.0.7.Final</version.org.jboss.galleon>
120+
<version.org.jboss.galleon>7.0.11.Final</version.org.jboss.galleon>
121121
<version.org.wildfly.glow>2.2.0.Final-redhat-00001</version.org.wildfly.glow>
122122
<plugin.fork.embedded>true</plugin.fork.embedded>
123123
<!-- EAP specific properties -->

0 commit comments

Comments
 (0)