[ 
https://issues.apache.org/jira/browse/MNG-6869?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17686619#comment-17686619
 ] 

ASF GitHub Bot commented on MNG-6869:
-------------------------------------

michael-o commented on code in PR #995:
URL: https://github.com/apache/maven/pull/995#discussion_r1101671449


##########
maven-embedder/src/main/java/org/apache/maven/cli/MavenStatusCommand.java:
##########
@@ -0,0 +1,217 @@
+/*
+ * 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.maven.cli;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.api.ArtifactCoordinate;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.services.ArtifactResolver;
+import org.apache.maven.api.services.ArtifactResolverException;
+import org.apache.maven.api.services.ArtifactResolverResult;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.bridge.MavenRepositorySystem;
+import org.apache.maven.execution.DefaultMavenExecutionResult;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequestPopulationException;
+import org.apache.maven.execution.MavenExecutionRequestPopulator;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
+import org.apache.maven.internal.impl.DefaultArtifactCoordinate;
+import org.apache.maven.internal.impl.DefaultSession;
+import org.apache.maven.internal.impl.DefaultSessionFactory;
+import org.apache.maven.session.scope.internal.SessionScope;
+import org.codehaus.plexus.PlexusContainer;
+import 
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MavenStatusCommand {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(MavenStatusCommand.class);
+
+    /**
+     * In order to verify artifacts can be downloaded from the remote 
repositories we want to resolve an actual
+     * artifact. The Apache Maven artifact was chosen as it eventually, be it 
by proxy, mirror or directly, will be
+     * gathered from the central repository. The version is chosen arbitrarily 
since any listed should work.
+     */
+    public static final Artifact APACHE_MAVEN_ARTIFACT =
+            new DefaultArtifact("org.apache.maven", "apache-maven", null, 
"pom", "3.8.6");
+
+    private final MavenExecutionRequestPopulator 
mavenExecutionRequestPopulator;
+    private final ArtifactResolver artifactResolver;
+    private final RemoteRepositoryConnectionVerifier 
remoteRepositoryConnectionVerifier;
+    private final DefaultSessionFactory defaultSessionFactory;
+    private final DefaultRepositorySystemSessionFactory repoSession;
+    private final MavenRepositorySystem repositorySystem;
+    private final PlexusContainer container;
+    private final SessionScope sessionScope;
+    private Path tempLocalRepository;
+
+    public MavenStatusCommand(final PlexusContainer container) throws 
ComponentLookupException {
+        this.container = container;
+        this.remoteRepositoryConnectionVerifier = new 
RemoteRepositoryConnectionVerifier(container);
+        this.mavenExecutionRequestPopulator = 
container.lookup(MavenExecutionRequestPopulator.class);
+        this.artifactResolver = container.lookup(ArtifactResolver.class);
+        this.defaultSessionFactory = 
container.lookup(DefaultSessionFactory.class);
+        this.repoSession = 
container.lookup(DefaultRepositorySystemSessionFactory.class);
+        this.sessionScope = container.lookup(SessionScope.class);
+        this.repositorySystem = container.lookup(MavenRepositorySystem.class);
+    }
+
+    public List<String> verify(final MavenExecutionRequest cliRequest) throws 
MavenExecutionRequestPopulationException {
+        final MavenExecutionRequest mavenExecutionRequest = 
mavenExecutionRequestPopulator.populateDefaults(cliRequest);
+
+        final ArtifactRepository localRepository = 
cliRequest.getLocalRepository();
+
+        final List<String> localRepositoryIssues =
+                
verifyLocalRepository(Paths.get(URI.create(localRepository.getUrl())));
+
+        // We overwrite the local repository with a temporary folder to avoid 
using a cached version of the artifact.
+        setTemporaryLocalRepositoryPathOnRequest(cliRequest);
+
+        final List<String> remoteRepositoryIssues =
+                
verifyRemoteRepositoryConnections(cliRequest.getRemoteRepositories(), 
mavenExecutionRequest);
+        final List<String> artifactResolutionIssues = 
verifyArtifactResolution(mavenExecutionRequest);
+
+        cleanupTempFiles();
+
+        // Collect all issues into a single list
+        return Stream.of(localRepositoryIssues, remoteRepositoryIssues, 
artifactResolutionIssues)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toList());
+    }
+
+    private void cleanupTempFiles() {
+        if (tempLocalRepository != null) {
+            try (Stream<Path> files = Files.walk(tempLocalRepository)) {
+                files.sorted(Comparator.reverseOrder()) // Sort in reverse 
order so that directories are deleted last
+                        .map(Path::toFile)
+                        .forEach(File::delete);
+            } catch (IOException ioe) {
+                LOGGER.debug("Failed to delete temporary local repository", 
ioe);
+            }
+        }
+    }
+
+    private void setTemporaryLocalRepositoryPathOnRequest(final 
MavenExecutionRequest request) {
+        try {
+            tempLocalRepository = 
Files.createTempDirectory("mvn-status").toAbsolutePath();
+            request.setLocalRepositoryPath(tempLocalRepository.toString());
+            
request.setLocalRepository(repositorySystem.createLocalRepository(request, 
tempLocalRepository.toFile()));
+        } catch (Exception ex) {
+            LOGGER.debug("Could not create temporary local repository", ex);
+            LOGGER.warn("Artifact resolution test is less accurate as it may 
use earlier resolution results.");
+        }
+    }
+
+    private List<String> verifyRemoteRepositoryConnections(
+            final List<ArtifactRepository> remoteRepositories, final 
MavenExecutionRequest mavenExecutionRequest) {
+        final List<String> issues = new ArrayList<>();
+
+        for (ArtifactRepository remoteRepository : remoteRepositories) {
+            final RepositorySystemSession repositorySession = 
repoSession.newRepositorySession(mavenExecutionRequest);
+            remoteRepositoryConnectionVerifier
+                    .verifyConnectionToRemoteRepository(repositorySession, 
remoteRepository)
+                    .ifPresent(issues::add);
+        }
+
+        return issues;
+    }
+
+    private List<String> verifyArtifactResolution(final MavenExecutionRequest 
mavenExecutionRequest) {
+        final Session session = this.defaultSessionFactory.getSession(new 
MavenSession(
+                container,
+                repoSession.newRepositorySession(mavenExecutionRequest),
+                mavenExecutionRequest,
+                new DefaultMavenExecutionResult()));
+
+        sessionScope.enter();
+        try {
+            sessionScope.seed(DefaultSession.class, (DefaultSession) session);
+
+            ArtifactCoordinate artifactCoordinate = new 
DefaultArtifactCoordinate(session, APACHE_MAVEN_ARTIFACT);
+            ArtifactResolverResult resolverResult =
+                    artifactResolver.resolve(session, 
Collections.singleton(artifactCoordinate));
+
+            resolverResult
+                    .getArtifacts()
+                    .forEach((key, value) ->
+                            LOGGER.debug("Successfully resolved {} to {}", 
key.toString(), value.toString()));

Review Comment:
   No need for toString() here



##########
maven-embedder/src/main/java/org/apache/maven/cli/MavenStatusCommand.java:
##########
@@ -0,0 +1,217 @@
+/*
+ * 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.maven.cli;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.api.ArtifactCoordinate;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.services.ArtifactResolver;
+import org.apache.maven.api.services.ArtifactResolverException;
+import org.apache.maven.api.services.ArtifactResolverResult;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.bridge.MavenRepositorySystem;
+import org.apache.maven.execution.DefaultMavenExecutionResult;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequestPopulationException;
+import org.apache.maven.execution.MavenExecutionRequestPopulator;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
+import org.apache.maven.internal.impl.DefaultArtifactCoordinate;
+import org.apache.maven.internal.impl.DefaultSession;
+import org.apache.maven.internal.impl.DefaultSessionFactory;
+import org.apache.maven.session.scope.internal.SessionScope;
+import org.codehaus.plexus.PlexusContainer;
+import 
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MavenStatusCommand {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(MavenStatusCommand.class);
+
+    /**
+     * In order to verify artifacts can be downloaded from the remote 
repositories we want to resolve an actual
+     * artifact. The Apache Maven artifact was chosen as it eventually, be it 
by proxy, mirror or directly, will be
+     * gathered from the central repository. The version is chosen arbitrarily 
since any listed should work.
+     */
+    public static final Artifact APACHE_MAVEN_ARTIFACT =
+            new DefaultArtifact("org.apache.maven", "apache-maven", null, 
"pom", "3.8.6");
+
+    private final MavenExecutionRequestPopulator 
mavenExecutionRequestPopulator;
+    private final ArtifactResolver artifactResolver;
+    private final RemoteRepositoryConnectionVerifier 
remoteRepositoryConnectionVerifier;
+    private final DefaultSessionFactory defaultSessionFactory;
+    private final DefaultRepositorySystemSessionFactory repoSession;
+    private final MavenRepositorySystem repositorySystem;
+    private final PlexusContainer container;
+    private final SessionScope sessionScope;
+    private Path tempLocalRepository;
+
+    public MavenStatusCommand(final PlexusContainer container) throws 
ComponentLookupException {
+        this.container = container;
+        this.remoteRepositoryConnectionVerifier = new 
RemoteRepositoryConnectionVerifier(container);
+        this.mavenExecutionRequestPopulator = 
container.lookup(MavenExecutionRequestPopulator.class);
+        this.artifactResolver = container.lookup(ArtifactResolver.class);
+        this.defaultSessionFactory = 
container.lookup(DefaultSessionFactory.class);
+        this.repoSession = 
container.lookup(DefaultRepositorySystemSessionFactory.class);
+        this.sessionScope = container.lookup(SessionScope.class);
+        this.repositorySystem = container.lookup(MavenRepositorySystem.class);
+    }
+
+    public List<String> verify(final MavenExecutionRequest cliRequest) throws 
MavenExecutionRequestPopulationException {
+        final MavenExecutionRequest mavenExecutionRequest = 
mavenExecutionRequestPopulator.populateDefaults(cliRequest);
+
+        final ArtifactRepository localRepository = 
cliRequest.getLocalRepository();
+
+        final List<String> localRepositoryIssues =
+                
verifyLocalRepository(Paths.get(URI.create(localRepository.getUrl())));
+
+        // We overwrite the local repository with a temporary folder to avoid 
using a cached version of the artifact.

Review Comment:
   directory



##########
maven-embedder/src/main/java/org/apache/maven/cli/RemoteRepositoryConnectionVerifier.java:
##########
@@ -0,0 +1,130 @@
+/*
+ * 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.maven.cli;
+
+import java.net.URI;
+import java.util.Optional;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.codehaus.plexus.PlexusContainer;
+import 
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.transport.GetTask;
+import org.eclipse.aether.spi.connector.transport.Transporter;
+import org.eclipse.aether.spi.connector.transport.TransporterProvider;
+import org.eclipse.aether.transfer.NoTransporterException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class to verify connection to a remote repository.
+ */
+public class RemoteRepositoryConnectionVerifier {
+    private static final Artifact APACHE_MAVEN_ARTIFACT = 
MavenStatusCommand.APACHE_MAVEN_ARTIFACT;
+    private final Logger logger;
+    private final TransporterProvider transporterProvider;
+
+    public RemoteRepositoryConnectionVerifier(final PlexusContainer container) 
throws ComponentLookupException {
+        this.logger = 
LoggerFactory.getILoggerFactory().getLogger(RemoteRepositoryConnectionVerifier.class.getName());
+        this.transporterProvider = container.lookup(TransporterProvider.class);
+    }
+
+    private boolean isCentralOrMirrorOfCentral(final RemoteRepository 
remoteRepository) {
+        return "central".equals(remoteRepository.getId())
+                || remoteRepository.getMirroredRepositories().stream()
+                        .map(RemoteRepository::getId)
+                        .anyMatch("central"::equals);
+    }

Review Comment:
   This can be deceving when blocked OR when Central is moved out of Core and 
removed from settings.



##########
maven-embedder/src/main/java/org/apache/maven/cli/RemoteRepositoryConnectionVerifier.java:
##########
@@ -0,0 +1,130 @@
+/*
+ * 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.maven.cli;
+
+import java.net.URI;
+import java.util.Optional;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.codehaus.plexus.PlexusContainer;
+import 
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.transport.GetTask;
+import org.eclipse.aether.spi.connector.transport.Transporter;
+import org.eclipse.aether.spi.connector.transport.TransporterProvider;
+import org.eclipse.aether.transfer.NoTransporterException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class to verify connection to a remote repository.
+ */
+public class RemoteRepositoryConnectionVerifier {
+    private static final Artifact APACHE_MAVEN_ARTIFACT = 
MavenStatusCommand.APACHE_MAVEN_ARTIFACT;
+    private final Logger logger;
+    private final TransporterProvider transporterProvider;
+
+    public RemoteRepositoryConnectionVerifier(final PlexusContainer container) 
throws ComponentLookupException {
+        this.logger = 
LoggerFactory.getILoggerFactory().getLogger(RemoteRepositoryConnectionVerifier.class.getName());
+        this.transporterProvider = container.lookup(TransporterProvider.class);
+    }
+
+    private boolean isCentralOrMirrorOfCentral(final RemoteRepository 
remoteRepository) {
+        return "central".equals(remoteRepository.getId())
+                || remoteRepository.getMirroredRepositories().stream()
+                        .map(RemoteRepository::getId)
+                        .anyMatch("central"::equals);
+    }
+
+    public Optional<String> verifyConnectionToRemoteRepository(
+            final RepositorySystemSession session, final ArtifactRepository 
artifactRepository) {
+        final RemoteRepository repository = 
RepositoryUtils.toRepo(artifactRepository);
+
+        final String artifactPath;
+
+        if (isCentralOrMirrorOfCentral(repository)) {
+            // We can be sure the Apache Maven artifact should be resolvable.
+            artifactPath = 
artifactRepository.getLayout().pathOf(RepositoryUtils.toArtifact(APACHE_MAVEN_ARTIFACT));
+        } else {
+            // We cannot be sure about any artifact that lives here.
+            artifactPath = "";
+        }
+
+        try {
+            final Transporter transporter = 
transporterProvider.newTransporter(session, repository);
+            final Optional<String> maybeIssue = 
verifyConnectionUsingTransport(transporter, repository, artifactPath);
+
+            if (!maybeIssue.isPresent()) {
+                logger.info("Connection check for {} [{}] completed", 
repository.getId(), repository.getUrl());
+            }
+
+            return maybeIssue;
+        } catch (final NoTransporterException nte) {
+            final String message = String.format(
+                    "There is no compatible transport for remote repository %s 
with location %s",
+                    repository.getId(), repository.getUrl());
+            return Optional.of(message);
+        }
+    }
+
+    private Optional<String> verifyConnectionUsingTransport(
+            final Transporter transporter, final RemoteRepository 
remoteRepository, final String artifactPath) {
+        try {
+            final GetTask task = new GetTask(URI.create(artifactPath));
+            transporter.get(task);
+            return Optional.empty();
+        } catch (final Exception e) {
+            return classifyException(remoteRepository, e);
+        }
+    }
+
+    private Optional<String> classifyException(final RemoteRepository 
remoteRepository, final Exception e) {
+        final String message = e.getMessage();
+        final String repositoryId = remoteRepository.getId();
+        final String repositoryUrl = remoteRepository.getUrl();
+        final String repository = String.format("%s [%s]", repositoryId, 
repositoryUrl);
+
+        final boolean notFound = StringUtils.contains(message, "status code: 
404");
+        final boolean unauthorized = StringUtils.contains(message, "status 
code: 401");
+        final boolean forbidden = StringUtils.contains(message, "status code: 
403");

Review Comment:
   Uh, that really hurts. No better way? You assume that transport is always 
HTTP? Either in Wagon or Resolver there is a classify method, maybe that can 
help...



##########
maven-embedder/src/main/java/org/apache/maven/cli/RemoteRepositoryConnectionVerifier.java:
##########
@@ -0,0 +1,130 @@
+/*
+ * 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.maven.cli;
+
+import java.net.URI;
+import java.util.Optional;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.codehaus.plexus.PlexusContainer;
+import 
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.repository.RemoteRepository;
+import org.eclipse.aether.spi.connector.transport.GetTask;
+import org.eclipse.aether.spi.connector.transport.Transporter;
+import org.eclipse.aether.spi.connector.transport.TransporterProvider;
+import org.eclipse.aether.transfer.NoTransporterException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Helper class to verify connection to a remote repository.
+ */
+public class RemoteRepositoryConnectionVerifier {
+    private static final Artifact APACHE_MAVEN_ARTIFACT = 
MavenStatusCommand.APACHE_MAVEN_ARTIFACT;
+    private final Logger logger;
+    private final TransporterProvider transporterProvider;
+
+    public RemoteRepositoryConnectionVerifier(final PlexusContainer container) 
throws ComponentLookupException {
+        this.logger = 
LoggerFactory.getILoggerFactory().getLogger(RemoteRepositoryConnectionVerifier.class.getName());
+        this.transporterProvider = container.lookup(TransporterProvider.class);
+    }
+
+    private boolean isCentralOrMirrorOfCentral(final RemoteRepository 
remoteRepository) {
+        return "central".equals(remoteRepository.getId())
+                || remoteRepository.getMirroredRepositories().stream()
+                        .map(RemoteRepository::getId)
+                        .anyMatch("central"::equals);
+    }
+
+    public Optional<String> verifyConnectionToRemoteRepository(
+            final RepositorySystemSession session, final ArtifactRepository 
artifactRepository) {
+        final RemoteRepository repository = 
RepositoryUtils.toRepo(artifactRepository);
+
+        final String artifactPath;
+
+        if (isCentralOrMirrorOfCentral(repository)) {
+            // We can be sure the Apache Maven artifact should be resolvable.
+            artifactPath = 
artifactRepository.getLayout().pathOf(RepositoryUtils.toArtifact(APACHE_MAVEN_ARTIFACT));
+        } else {
+            // We cannot be sure about any artifact that lives here.
+            artifactPath = "";
+        }
+
+        try {
+            final Transporter transporter = 
transporterProvider.newTransporter(session, repository);
+            final Optional<String> maybeIssue = 
verifyConnectionUsingTransport(transporter, repository, artifactPath);
+
+            if (!maybeIssue.isPresent()) {
+                logger.info("Connection check for {} [{}] completed", 
repository.getId(), repository.getUrl());
+            }
+
+            return maybeIssue;
+        } catch (final NoTransporterException nte) {
+            final String message = String.format(
+                    "There is no compatible transport for remote repository %s 
with location %s",

Review Comment:
   single quotes



##########
maven-embedder/src/main/java/org/apache/maven/cli/MavenStatusCommand.java:
##########
@@ -0,0 +1,217 @@
+/*
+ * 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.maven.cli;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.maven.api.ArtifactCoordinate;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.services.ArtifactResolver;
+import org.apache.maven.api.services.ArtifactResolverException;
+import org.apache.maven.api.services.ArtifactResolverResult;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.bridge.MavenRepositorySystem;
+import org.apache.maven.execution.DefaultMavenExecutionResult;
+import org.apache.maven.execution.MavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionRequestPopulationException;
+import org.apache.maven.execution.MavenExecutionRequestPopulator;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory;
+import org.apache.maven.internal.impl.DefaultArtifactCoordinate;
+import org.apache.maven.internal.impl.DefaultSession;
+import org.apache.maven.internal.impl.DefaultSessionFactory;
+import org.apache.maven.session.scope.internal.SessionScope;
+import org.codehaus.plexus.PlexusContainer;
+import 
org.codehaus.plexus.component.repository.exception.ComponentLookupException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.resolution.ArtifactResolutionException;
+import org.eclipse.aether.resolution.ArtifactResult;
+import org.eclipse.aether.transfer.ArtifactNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class MavenStatusCommand {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(MavenStatusCommand.class);
+
+    /**
+     * In order to verify artifacts can be downloaded from the remote 
repositories we want to resolve an actual
+     * artifact. The Apache Maven artifact was chosen as it eventually, be it 
by proxy, mirror or directly, will be
+     * gathered from the central repository. The version is chosen arbitrarily 
since any listed should work.
+     */
+    public static final Artifact APACHE_MAVEN_ARTIFACT =
+            new DefaultArtifact("org.apache.maven", "apache-maven", null, 
"pom", "3.8.6");
+
+    private final MavenExecutionRequestPopulator 
mavenExecutionRequestPopulator;
+    private final ArtifactResolver artifactResolver;
+    private final RemoteRepositoryConnectionVerifier 
remoteRepositoryConnectionVerifier;
+    private final DefaultSessionFactory defaultSessionFactory;
+    private final DefaultRepositorySystemSessionFactory repoSession;
+    private final MavenRepositorySystem repositorySystem;
+    private final PlexusContainer container;
+    private final SessionScope sessionScope;
+    private Path tempLocalRepository;
+
+    public MavenStatusCommand(final PlexusContainer container) throws 
ComponentLookupException {
+        this.container = container;
+        this.remoteRepositoryConnectionVerifier = new 
RemoteRepositoryConnectionVerifier(container);
+        this.mavenExecutionRequestPopulator = 
container.lookup(MavenExecutionRequestPopulator.class);
+        this.artifactResolver = container.lookup(ArtifactResolver.class);
+        this.defaultSessionFactory = 
container.lookup(DefaultSessionFactory.class);
+        this.repoSession = 
container.lookup(DefaultRepositorySystemSessionFactory.class);
+        this.sessionScope = container.lookup(SessionScope.class);
+        this.repositorySystem = container.lookup(MavenRepositorySystem.class);
+    }
+
+    public List<String> verify(final MavenExecutionRequest cliRequest) throws 
MavenExecutionRequestPopulationException {
+        final MavenExecutionRequest mavenExecutionRequest = 
mavenExecutionRequestPopulator.populateDefaults(cliRequest);
+
+        final ArtifactRepository localRepository = 
cliRequest.getLocalRepository();
+
+        final List<String> localRepositoryIssues =
+                
verifyLocalRepository(Paths.get(URI.create(localRepository.getUrl())));
+
+        // We overwrite the local repository with a temporary folder to avoid 
using a cached version of the artifact.
+        setTemporaryLocalRepositoryPathOnRequest(cliRequest);
+
+        final List<String> remoteRepositoryIssues =
+                
verifyRemoteRepositoryConnections(cliRequest.getRemoteRepositories(), 
mavenExecutionRequest);
+        final List<String> artifactResolutionIssues = 
verifyArtifactResolution(mavenExecutionRequest);
+
+        cleanupTempFiles();
+
+        // Collect all issues into a single list
+        return Stream.of(localRepositoryIssues, remoteRepositoryIssues, 
artifactResolutionIssues)
+                .flatMap(Collection::stream)
+                .collect(Collectors.toList());
+    }
+
+    private void cleanupTempFiles() {
+        if (tempLocalRepository != null) {
+            try (Stream<Path> files = Files.walk(tempLocalRepository)) {
+                files.sorted(Comparator.reverseOrder()) // Sort in reverse 
order so that directories are deleted last
+                        .map(Path::toFile)
+                        .forEach(File::delete);
+            } catch (IOException ioe) {
+                LOGGER.debug("Failed to delete temporary local repository", 
ioe);
+            }
+        }
+    }
+
+    private void setTemporaryLocalRepositoryPathOnRequest(final 
MavenExecutionRequest request) {
+        try {
+            tempLocalRepository = 
Files.createTempDirectory("mvn-status").toAbsolutePath();
+            request.setLocalRepositoryPath(tempLocalRepository.toString());
+            
request.setLocalRepository(repositorySystem.createLocalRepository(request, 
tempLocalRepository.toFile()));
+        } catch (Exception ex) {
+            LOGGER.debug("Could not create temporary local repository", ex);
+            LOGGER.warn("Artifact resolution test is less accurate as it may 
use earlier resolution results.");
+        }
+    }
+
+    private List<String> verifyRemoteRepositoryConnections(
+            final List<ArtifactRepository> remoteRepositories, final 
MavenExecutionRequest mavenExecutionRequest) {
+        final List<String> issues = new ArrayList<>();
+
+        for (ArtifactRepository remoteRepository : remoteRepositories) {
+            final RepositorySystemSession repositorySession = 
repoSession.newRepositorySession(mavenExecutionRequest);
+            remoteRepositoryConnectionVerifier
+                    .verifyConnectionToRemoteRepository(repositorySession, 
remoteRepository)
+                    .ifPresent(issues::add);
+        }
+
+        return issues;
+    }
+
+    private List<String> verifyArtifactResolution(final MavenExecutionRequest 
mavenExecutionRequest) {
+        final Session session = this.defaultSessionFactory.getSession(new 
MavenSession(
+                container,
+                repoSession.newRepositorySession(mavenExecutionRequest),
+                mavenExecutionRequest,
+                new DefaultMavenExecutionResult()));
+
+        sessionScope.enter();
+        try {
+            sessionScope.seed(DefaultSession.class, (DefaultSession) session);
+
+            ArtifactCoordinate artifactCoordinate = new 
DefaultArtifactCoordinate(session, APACHE_MAVEN_ARTIFACT);
+            ArtifactResolverResult resolverResult =
+                    artifactResolver.resolve(session, 
Collections.singleton(artifactCoordinate));
+
+            resolverResult
+                    .getArtifacts()
+                    .forEach((key, value) ->
+                            LOGGER.debug("Successfully resolved {} to {}", 
key.toString(), value.toString()));
+
+            return Collections.emptyList();
+        } catch (ArtifactResolverException are) {
+            return extractIssuesFromArtifactResolverException(are);
+        } finally {
+            sessionScope.exit();
+            LOGGER.info("Artifact resolution check completed");
+        }
+    }
+
+    private List<String> extractIssuesFromArtifactResolverException(final 
Exception exception) {
+        final boolean isArtifactResolutionException = exception.getCause() 
instanceof ArtifactResolutionException;
+        if (isArtifactResolutionException) {
+            final ArtifactResolutionException are = 
(ArtifactResolutionException) exception.getCause();
+            return are.getResults().stream()
+                    .map(ArtifactResult::getExceptions)
+                    .flatMap(List::stream)
+                    .map(ArtifactNotFoundException.class::cast)
+                    .map(Throwable::getMessage)
+                    .collect(Collectors.toList());
+        } else {
+            return Collections.singletonList(exception.getMessage());
+        }
+    }
+
+    private List<String> verifyLocalRepository(final Path localRepositoryPath) 
{
+        final List<String> issues = new ArrayList<>();
+
+        if (!Files.isDirectory(localRepositoryPath)) {
+            issues.add(String.format("Local repository path %s is not a 
directory.", localRepositoryPath));
+        }
+
+        if (!Files.isReadable(localRepositoryPath)) {
+            issues.add(String.format("No read permissions on local repository 
%s.", localRepositoryPath));
+        }
+
+        if (!Files.isWritable(localRepositoryPath)) {
+            issues.add(String.format("No write permissions on local repository 
%s.", localRepositoryPath));
+        }
+

Review Comment:
   Put the placeholders in single quotes





> New flag to verify the status
> -----------------------------
>
>                 Key: MNG-6869
>                 URL: https://issues.apache.org/jira/browse/MNG-6869
>             Project: Maven
>          Issue Type: New Feature
>            Reporter: Robert Scholte
>            Assignee: Maarten Mulders
>            Priority: Major
>
> While working on INFRA-19861 we had issues with invalid changes in the 
> settings.xml.
> This was detected too late. After installation {{mvn --version}} is called, 
> but it will only show the version of Maven.
> It would be better to have a flag that verifies it a bit more:
> - can Maven read/write to the local repository
> - can Maven access all predefined repositories? (does the proxy work?)
> This gives a much better feedback if Maven can do its job.
> Current workaround: call something like {{mvn help:evaluate 
> -Dexpression=settings.localRepository -q -DforceStdout}}



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to