|
18 | 18 | */ |
19 | 19 | package org.apache.maven.cling.invoker.mvnup.goals; |
20 | 20 |
|
| 21 | +import java.io.File; |
| 22 | +import java.nio.file.Files; |
21 | 23 | import java.nio.file.Path; |
| 24 | +import java.util.Comparator; |
22 | 25 | import java.util.HashMap; |
23 | 26 | import java.util.HashSet; |
| 27 | +import java.util.List; |
24 | 28 | import java.util.Map; |
25 | 29 | import java.util.Set; |
26 | 30 |
|
27 | 31 | import eu.maveniverse.domtrip.Document; |
28 | 32 | import eu.maveniverse.domtrip.Element; |
29 | 33 | import eu.maveniverse.domtrip.maven.Coordinates; |
30 | 34 | import eu.maveniverse.domtrip.maven.MavenPomElements; |
| 35 | +import org.apache.maven.api.RemoteRepository; |
| 36 | +import org.apache.maven.api.Session; |
31 | 37 | import org.apache.maven.api.cli.mvnup.UpgradeOptions; |
| 38 | +import org.apache.maven.api.di.Named; |
| 39 | +import org.apache.maven.api.di.Provides; |
| 40 | +import org.apache.maven.api.model.Repository; |
| 41 | +import org.apache.maven.api.model.RepositoryPolicy; |
| 42 | +import org.apache.maven.api.services.ModelBuilder; |
| 43 | +import org.apache.maven.api.services.ModelBuilderRequest; |
| 44 | +import org.apache.maven.api.services.ModelBuilderResult; |
| 45 | +import org.apache.maven.api.services.RepositoryFactory; |
| 46 | +import org.apache.maven.api.services.Sources; |
32 | 47 | import org.apache.maven.cling.invoker.mvnup.UpgradeContext; |
| 48 | +import org.apache.maven.impl.standalone.ApiRunner; |
| 49 | +import org.codehaus.plexus.components.secdispatcher.Dispatcher; |
| 50 | +import org.codehaus.plexus.components.secdispatcher.internal.dispatchers.LegacyDispatcher; |
| 51 | +import org.eclipse.aether.spi.connector.transport.TransporterFactory; |
| 52 | +import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor; |
| 53 | +import org.eclipse.aether.spi.io.PathProcessor; |
| 54 | +import org.eclipse.aether.transport.file.FileTransporterFactory; |
| 55 | +import org.eclipse.aether.transport.jdk.JdkTransporterFactory; |
33 | 56 |
|
34 | 57 | import static eu.maveniverse.domtrip.maven.MavenPomElements.Elements.PARENT; |
35 | 58 |
|
|
47 | 70 | */ |
48 | 71 | public abstract class AbstractUpgradeStrategy implements UpgradeStrategy { |
49 | 72 |
|
| 73 | + private Session session; |
| 74 | + |
50 | 75 | /** |
51 | 76 | * Template method that handles common logging and error handling. |
52 | 77 | * Subclasses implement the actual upgrade logic in doApply(). |
@@ -185,4 +210,115 @@ public static Set<Coordinates> computeAllArtifactCoordinates(UpgradeContext cont |
185 | 210 | context.info("Computed " + coordinatesByGAV.size() + " unique artifact(s) for inference"); |
186 | 211 | return new HashSet<>(coordinatesByGAV.values()); |
187 | 212 | } |
| 213 | + |
| 214 | + protected Session getSession() { |
| 215 | + if (session == null) { |
| 216 | + session = createMaven4Session(); |
| 217 | + } |
| 218 | + return session; |
| 219 | + } |
| 220 | + |
| 221 | + private Session createMaven4Session() { |
| 222 | + Session session = ApiRunner.createSession(injector -> { |
| 223 | + injector.bindInstance(Dispatcher.class, new LegacyDispatcher()); |
| 224 | + injector.bindImplicit(TransporterFactoryConfig.class); |
| 225 | + }); |
| 226 | + |
| 227 | + // TODO: we should read settings |
| 228 | + RemoteRepository central = |
| 229 | + session.createRemoteRepository(RemoteRepository.CENTRAL_ID, "https://repo.maven.apache.org/maven2"); |
| 230 | + RemoteRepository snapshots = session.getService(RepositoryFactory.class) |
| 231 | + .createRemote(Repository.newBuilder() |
| 232 | + .id("apache-snapshots") |
| 233 | + .url("https://repository.apache.org/content/repositories/snapshots/") |
| 234 | + .releases(RepositoryPolicy.newBuilder().enabled("false").build()) |
| 235 | + .snapshots(RepositoryPolicy.newBuilder().enabled("true").build()) |
| 236 | + .build()); |
| 237 | + |
| 238 | + return session.withRemoteRepositories(List.of(central, snapshots)); |
| 239 | + } |
| 240 | + |
| 241 | + protected Path createTempProjectStructure(UpgradeContext context, Map<Path, Document> pomMap) throws Exception { |
| 242 | + Path tempDir = Files.createTempDirectory("mvnup-project-"); |
| 243 | + context.debug("Created temp project directory: " + tempDir); |
| 244 | + |
| 245 | + Path commonRoot = findCommonRoot(pomMap.keySet()); |
| 246 | + context.debug("Common root: " + commonRoot); |
| 247 | + |
| 248 | + for (Map.Entry<Path, Document> entry : pomMap.entrySet()) { |
| 249 | + Path originalPath = entry.getKey(); |
| 250 | + Document document = entry.getValue(); |
| 251 | + |
| 252 | + Path relativePath = commonRoot.relativize(originalPath); |
| 253 | + Path tempPomPath = tempDir.resolve(relativePath); |
| 254 | + |
| 255 | + Files.createDirectories(tempPomPath.getParent()); |
| 256 | + Files.writeString(tempPomPath, document.toXml()); |
| 257 | + context.debug("Wrote POM to temp location: " + tempPomPath); |
| 258 | + } |
| 259 | + |
| 260 | + return tempDir; |
| 261 | + } |
| 262 | + |
| 263 | + protected Path findCommonRoot(Set<Path> pomPaths) { |
| 264 | + Path commonRoot = null; |
| 265 | + for (Path pomPath : pomPaths) { |
| 266 | + Path parent = pomPath.getParent(); |
| 267 | + if (parent == null) { |
| 268 | + parent = Path.of("."); |
| 269 | + } |
| 270 | + if (commonRoot == null) { |
| 271 | + commonRoot = parent; |
| 272 | + } else { |
| 273 | + while (!parent.startsWith(commonRoot)) { |
| 274 | + commonRoot = commonRoot.getParent(); |
| 275 | + if (commonRoot == null) { |
| 276 | + break; |
| 277 | + } |
| 278 | + } |
| 279 | + } |
| 280 | + } |
| 281 | + return commonRoot; |
| 282 | + } |
| 283 | + |
| 284 | + protected void cleanupTempDirectory(Path tempDir) { |
| 285 | + try { |
| 286 | + Files.walk(tempDir) |
| 287 | + .sorted(Comparator.reverseOrder()) |
| 288 | + .map(Path::toFile) |
| 289 | + .forEach(File::delete); |
| 290 | + } catch (Exception e) { |
| 291 | + // Best effort cleanup |
| 292 | + } |
| 293 | + } |
| 294 | + |
| 295 | + protected org.apache.maven.api.model.Model buildEffectiveModel(Path pomPath) { |
| 296 | + Session session = getSession(); |
| 297 | + ModelBuilder modelBuilder = session.getService(ModelBuilder.class); |
| 298 | + |
| 299 | + ModelBuilderRequest request = ModelBuilderRequest.builder() |
| 300 | + .session(session) |
| 301 | + .source(Sources.buildSource(pomPath)) |
| 302 | + .requestType(ModelBuilderRequest.RequestType.BUILD_EFFECTIVE) |
| 303 | + .recursive(false) |
| 304 | + .build(); |
| 305 | + |
| 306 | + ModelBuilderResult result = modelBuilder.newSession().build(request); |
| 307 | + return result.getEffectiveModel(); |
| 308 | + } |
| 309 | + |
| 310 | + static class TransporterFactoryConfig { |
| 311 | + @Provides |
| 312 | + @Named(JdkTransporterFactory.NAME) |
| 313 | + static TransporterFactory jdkTransporterFactory( |
| 314 | + ChecksumExtractor checksumExtractor, PathProcessor pathProcessor) { |
| 315 | + return new JdkTransporterFactory(checksumExtractor, pathProcessor); |
| 316 | + } |
| 317 | + |
| 318 | + @Provides |
| 319 | + @Named(FileTransporterFactory.NAME) |
| 320 | + static TransporterFactory fileTransporterFactory() { |
| 321 | + return new FileTransporterFactory(); |
| 322 | + } |
| 323 | + } |
188 | 324 | } |
0 commit comments