gnodet-bot commented on code in PR #28239:
URL: https://github.com/apache/camel/pull/28239#discussion_r4336564722
##########
tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/MavenDownloaderImpl.java:
##########
@@ -116,6 +114,7 @@ public class MavenDownloaderImpl extends ServiceSupport
implements MavenDownload
// Maven Resolver components
private RepositorySystem repositorySystem;
private RepositorySystemSession repositorySystemSession;
+ private RepositorySystemSession preferLocalSession;
Review Comment:
💡 **Note:** `preferLocalSession` is written and read without
synchronization. The lazy init in `getPreferLocalSession()` is benign in
practice — concurrent calls produce equivalent sessions — but the JMM does not
guarantee visibility of the write without `volatile`. Marking the field
`volatile` would make the intent explicit and satisfy the memory model without
any performance cost (the fast path is a single `null` check).
##########
tooling/camel-tooling-maven/src/main/java/org/apache/camel/tooling/maven/TrackingKeyCompatLocalRepositoryManager.java:
##########
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.camel.tooling.maven;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.util.Properties;
+import java.util.regex.Pattern;
+
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.metadata.Metadata;
+import org.eclipse.aether.repository.LocalArtifactRegistration;
+import org.eclipse.aether.repository.LocalArtifactRequest;
+import org.eclipse.aether.repository.LocalArtifactResult;
+import org.eclipse.aether.repository.LocalMetadataRegistration;
+import org.eclipse.aether.repository.LocalMetadataRequest;
+import org.eclipse.aether.repository.LocalMetadataResult;
+import org.eclipse.aether.repository.LocalRepository;
+import org.eclipse.aether.repository.LocalRepositoryManager;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.util.ConfigUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A {@link LocalRepositoryManager} that also accepts the artifacts a newer
Maven Resolver tracked as downloaded from
+ * the requested repository.
+ * <p>
+ * Maven 3.10 (Maven Resolver 2.0.23+) records the origin of a downloaded
artifact in {@code _remote.repositories} with
+ * a URL-qualified key ({@code file>central-<sha1(url)>=}), while Maven
Resolver 1.9 only knows the ID-only key
+ * ({@code file>central=}). Without this, every artifact downloaded by Maven
3.10 is considered as not downloaded from
+ * the requested repository: it cannot be resolved offline and it is verified
again against the remote repositories.
+ * <p>
+ * An URL-qualified entry is accepted for a repository with the same id,
whatever the URL, which is the trust level of
+ * the ID-only entries (and what Maven Resolver 2.0.23 itself does when it
reads ID-only entries). What is written to
+ * the local repository is left untouched, so it stays readable by both old
and new Maven versions.
+ */
+class TrackingKeyCompatLocalRepositoryManager implements
LocalRepositoryManager {
+
+ private static final Logger LOG =
LoggerFactory.getLogger(TrackingKeyCompatLocalRepositoryManager.class);
+
+ private static final String CONFIG_PROP_TRACKING_FILENAME =
"aether.enhancedLocalRepository.trackingFilename";
+ private static final String DEFAULT_TRACKING_FILENAME =
"_remote.repositories";
+ // the SHA-1 of the URL (nid_hurl), optionally followed by the one of the
context for repository managers
+ private static final String URL_QUALIFIER =
"-[0-9a-f]{40}(-[0-9a-f]{40})?";
+
+ private final LocalRepositoryManager delegate;
+ private final String trackingFilename;
+
+ TrackingKeyCompatLocalRepositoryManager(RepositorySystemSession session,
LocalRepositoryManager delegate) {
+ this.delegate = delegate;
+ this.trackingFilename
+ = ConfigUtils.getString(session, DEFAULT_TRACKING_FILENAME,
CONFIG_PROP_TRACKING_FILENAME);
+ }
+
+ /**
+ * Wraps the local repository manager of the given session, unless it is
already wrapped.
+ */
+ static LocalRepositoryManager wrap(RepositorySystemSession session,
LocalRepositoryManager lrm) {
+ if (lrm == null || lrm instanceof
TrackingKeyCompatLocalRepositoryManager) {
+ return lrm;
+ }
+ return new TrackingKeyCompatLocalRepositoryManager(session, lrm);
+ }
+
+ @Override
+ public LocalArtifactResult find(RepositorySystemSession session,
LocalArtifactRequest request) {
+ LocalArtifactResult result = delegate.find(session, request);
+ if (!result.isAvailable() && result.getFile() != null &&
result.getFile().isFile()) {
+ File file = result.getFile();
+ Properties props = readTrackingFile(file);
+ for (RemoteRepository repository : request.getRepositories()) {
+ Pattern key = Pattern.compile(Pattern.quote(file.getName() +
'>' + repository.getId()) + URL_QUALIFIER);
+ if (props.keySet().stream().anyMatch(k ->
key.matcher(k.toString()).matches())) {
+ LOG.debug("Accepting {} tracked with an URL-qualified key
of repository {}", file,
+ repository.getId());
+ result.setAvailable(true);
+ result.setRepository(repository);
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ private Properties readTrackingFile(File artifactFile) {
+ Properties props = new Properties();
+ File trackingFile = new File(artifactFile.getParentFile(),
trackingFilename);
+ if (trackingFile.isFile()) {
+ try (InputStream is = Files.newInputStream(trackingFile.toPath()))
{
+ props.load(is);
+ } catch (IOException e) {
+ LOG.debug("Cannot read {}: {}", trackingFile, e.getMessage(),
e);
+ }
+ }
+ return props;
+ }
+
+ @Override
+ public LocalRepository getRepository() {
+ return delegate.getRepository();
+ }
+
+ @Override
+ public String getPathForLocalArtifact(Artifact artifact) {
+ return delegate.getPathForLocalArtifact(artifact);
+ }
+
+ @Override
+ public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository
repository, String context) {
+ return delegate.getPathForRemoteArtifact(artifact, repository,
context);
+ }
+
+ @Override
+ public String getPathForLocalMetadata(Metadata metadata) {
+ return delegate.getPathForLocalMetadata(metadata);
+ }
+
+ @Override
+ public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository
repository, String context) {
+ return delegate.getPathForRemoteMetadata(metadata, repository,
context);
+ }
+
+ @Override
+ public void add(RepositorySystemSession session, LocalArtifactRegistration
request) {
+ delegate.add(session, request);
+ }
+
+ @Override
+ public LocalMetadataResult find(RepositorySystemSession session,
LocalMetadataRequest request) {
+ return delegate.find(session, request);
Review Comment:
💡 **Note:** The `find(RepositorySystemSession, LocalMetadataRequest)`
override delegates straight through without applying the compat-key lookup.
This is intentional for the initial fix scope — metadata entries in
`_remote.repositories` use different naming conventions than artifact entries,
and the compat logic targets artifact resolution. Worth noting here in case a
future caller sees metadata unexpectedly re-fetched when `preferLocal` is
active and the metadata was downloaded by Maven 3.10.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]