| /** |
| * 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.marmotta.maven.plugins.repochecker; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.http.HttpResponse; |
| import org.apache.http.client.ClientProtocolException; |
| import org.apache.http.client.ResponseHandler; |
| import org.apache.http.client.methods.HttpGet; |
| import org.apache.http.client.methods.HttpHead; |
| import org.apache.http.impl.client.CloseableHttpClient; |
| import org.apache.http.impl.client.HttpClients; |
| import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.repository.ArtifactRepository; |
| import org.apache.maven.plugin.AbstractMojo; |
| import org.apache.maven.plugin.MojoExecutionException; |
| import org.apache.maven.plugin.MojoFailureException; |
| import org.apache.maven.plugin.logging.Log; |
| import org.apache.maven.plugins.annotations.Component; |
| import org.apache.maven.plugins.annotations.Mojo; |
| import org.apache.maven.plugins.annotations.Parameter; |
| import org.apache.maven.plugins.annotations.ResolutionScope; |
| import org.apache.maven.project.MavenProject; |
| import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; |
| import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException; |
| import org.apache.maven.shared.dependency.graph.DependencyNode; |
| import org.codehaus.plexus.util.StringUtils; |
| import org.jdom2.Document; |
| import org.jdom2.Element; |
| import org.jdom2.JDOMException; |
| import org.jdom2.input.SAXBuilder; |
| |
| @Mojo(name = "matrix", requiresDependencyResolution = ResolutionScope.TEST, threadSafe = true) |
| public class RepositoryCheckerMojo extends AbstractMojo { |
| |
| private static final String META_XML = "maven-metadata.xml"; |
| |
| public enum Result { |
| FOUND, NOT_FOUND, IGNORED, ERROR |
| } |
| |
| /** |
| * The Maven project. |
| */ |
| @Component |
| private MavenProject project; |
| |
| /** |
| * The dependency tree builder to use. |
| */ |
| @Component(hint = "default") |
| private DependencyGraphBuilder dependencyGraphBuilder; |
| |
| @Parameter(property = "repositories") |
| private String[] repositories; |
| |
| @Parameter(property = "checkSnapshots", defaultValue = "true") |
| private boolean checkSnapshots; |
| |
| @Parameter(property = "depth", defaultValue = "1") |
| private int depth; |
| |
| @Parameter(property = "breakOnMissing", defaultValue = "false") |
| private boolean breakOnMissing; |
| |
| @Parameter(property = "silent", defaultValue = "false") |
| private boolean silent; |
| |
| private final ResponseHandler<Boolean> fileExistsHandler; |
| |
| public RepositoryCheckerMojo() { |
| fileExistsHandler = new ResponseHandler<Boolean>() { |
| public Boolean handleResponse(HttpResponse response) |
| throws ClientProtocolException, IOException { |
| return response.getStatusLine().getStatusCode() < 300; |
| } |
| }; |
| } |
| |
| public void execute() throws MojoExecutionException, MojoFailureException { |
| PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager(); |
| final CloseableHttpClient client = HttpClients.custom() |
| .setConnectionManager(manager) |
| .build(); |
| |
| final MatrixPrinter printer; |
| if (silent) { |
| printer = new SilentMatrixPrinter(); |
| } else |
| printer = new LogMatrixPrinter(getLog(), 1); |
| |
| try { |
| final Log log = getLog(); |
| final List<ArtifactRepository> reps; |
| if (repositories != null && repositories.length > 0) { |
| @SuppressWarnings("unchecked") |
| final LinkedList<ArtifactRepository> _tmp = new LinkedList<ArtifactRepository>( |
| project.getRemoteArtifactRepositories()); |
| reps = new LinkedList<ArtifactRepository>(); |
| |
| for (String rid : repositories) { |
| ArtifactRepository r = null; |
| for (ArtifactRepository ar : _tmp) { |
| if (rid.equals(ar.getId())) { |
| r = ar; |
| break; |
| } |
| } |
| if (r != null) |
| reps.add(r); |
| else |
| log.warn("Could not find artifact repository '" + rid |
| + "'"); |
| } |
| |
| if (reps.size() == 0) { |
| log.warn("No artifact repositories provided, skipping."); |
| return; |
| } |
| } else { |
| @SuppressWarnings("unchecked") |
| final LinkedList<ArtifactRepository> _tmp = new LinkedList<ArtifactRepository>( |
| project.getRemoteArtifactRepositories()); |
| reps = _tmp; |
| } |
| |
| printer.printHeader(reps); |
| |
| final DependencyNode rootNode = dependencyGraphBuilder |
| .buildDependencyGraph(project, null); |
| Set<Artifact> missingArtifacts = checkDepNode(rootNode, reps, 0, client, printer); |
| |
| printer.printFooter(reps); |
| |
| if (missingArtifacts.size() > 0) { |
| log.warn("unresolved dependencies:"); |
| for (Artifact missing : missingArtifacts) { |
| log.warn(" " + missing.getId()); |
| } |
| } |
| } catch (DependencyGraphBuilderException e) { |
| throw new MojoExecutionException( |
| "Cannot build project dependency graph", e); |
| } finally { |
| try { |
| manager.shutdown(); |
| client.close(); |
| } catch (IOException e) { |
| getLog().warn("IOExeption while closing the HttpClient: " + e.getMessage()); |
| } |
| } |
| } |
| |
| private Set<Artifact> checkDepNode(final DependencyNode rootNode, |
| final List<ArtifactRepository> repositories, final int level, |
| CloseableHttpClient client, MatrixPrinter printer) |
| throws MojoFailureException { |
| if (!(level < depth)) |
| return Collections.emptySet(); |
| |
| HashSet<Artifact> missingArts = new HashSet<Artifact>(); |
| for (DependencyNode dep : rootNode.getChildren()) { |
| Artifact artifact = dep.getArtifact(); |
| |
| if (!checkSnapshots && artifact.isSnapshot()) { |
| printer.printResult(artifact, level, Collections.nCopies(repositories.size(), Result.IGNORED)); |
| } else { |
| final LinkedList<Result> results = new LinkedList<RepositoryCheckerMojo.Result>(); |
| for (ArtifactRepository repo : repositories) { |
| Result result = lookupArtifact(artifact, repo, client); |
| results.add(result); |
| } |
| |
| if (!results.contains(Result.FOUND)) { |
| missingArts.add(artifact); |
| if (breakOnMissing) { |
| throw new MojoFailureException( |
| String.format( |
| "did not find artifact %s in any of the available repositories", |
| artifact.getId())); |
| } |
| } |
| |
| printer.printResult(artifact, level, results); |
| missingArts.addAll(checkDepNode(dep, repositories, level + 1, client, printer)); |
| } |
| } |
| |
| return missingArts; |
| } |
| |
| private Result lookupArtifact(Artifact artifact, ArtifactRepository rep, |
| CloseableHttpClient client) { |
| |
| if (artifact.isSnapshot() && !rep.getSnapshots().isEnabled()) { |
| return Result.NOT_FOUND; |
| } |
| if (artifact.isRelease() && !rep.getReleases().isEnabled()) { |
| return Result.NOT_FOUND; |
| } |
| |
| try { |
| final String baseUrl = rep.getUrl().replaceAll("/$", "") + "/"; |
| |
| if (client.execute(new HttpHead(baseUrl + buildRelUrl(artifact)), |
| fileExistsHandler)) { |
| return Result.FOUND; |
| } |
| if (artifact.isSnapshot()) { |
| // now check for a timestamp version |
| final String fName = client.execute(new HttpGet(baseUrl |
| + buildArtifactDir(artifact) + META_XML), |
| createTimestampHandler(artifact)); |
| if (fName != null |
| && client.execute(new HttpHead(baseUrl |
| + buildArtifactDir(artifact) + fName), |
| fileExistsHandler)) { |
| return Result.FOUND; |
| } else { |
| return Result.NOT_FOUND; |
| } |
| } else { |
| return Result.NOT_FOUND; |
| } |
| } catch (IOException e) { |
| return Result.ERROR; |
| } |
| } |
| |
| private ResponseHandler<String> createTimestampHandler( |
| final Artifact artifact) { |
| return new ResponseHandler<String>() { |
| |
| public String handleResponse(HttpResponse response) |
| throws ClientProtocolException, IOException { |
| if (response.getStatusLine().getStatusCode() >= 300) { |
| return null; |
| } |
| if (response.getEntity() == null) |
| return null; |
| InputStream content = response.getEntity().getContent(); |
| if (content == null) |
| return null; |
| |
| try { |
| Document doc = new SAXBuilder().build(content); |
| |
| Element meta = doc.getRootElement(); |
| if (!"metadata".equals(meta.getName())) |
| throw new IOException(); |
| Element vers = meta.getChild("versioning"); |
| if (vers == null) |
| throw new IOException(); |
| Element sVers = vers.getChild("snapshotVersions"); |
| if (sVers == null) |
| throw new IOException(); |
| |
| for (Element sv : sVers.getChildren("snapshotVersion")) { |
| // if there is a classifier, check if it's the right one |
| if (artifact.hasClassifier()) { |
| if (!artifact.getClassifier().equals( |
| sv.getChildText("classifier"))) { |
| continue; |
| } |
| } |
| if (!artifact.getType().equals( |
| sv.getChildText("extension"))) { |
| continue; |
| } |
| |
| // If we reach this, then it's the right snapshotVersion |
| StringBuilder sb = new StringBuilder( |
| artifact.getArtifactId()); |
| sb.append("-").append(sv.getChildText("value")); |
| if (artifact.hasClassifier()) |
| sb.append("-") |
| .append(sv.getChildText("classifier")); |
| sb.append(".").append(sv.getChildText("extension")); |
| |
| return sb.toString(); |
| } |
| return null; |
| } catch (JDOMException e) { |
| throw new IOException(e); |
| } |
| } |
| }; |
| } |
| |
| private String buildRelUrl(Artifact artifact) { |
| StringBuilder sb = new StringBuilder(buildArtifactDir(artifact)); |
| sb.append(buildArtifactFileName(artifact)); |
| return sb.toString(); |
| } |
| |
| private String buildArtifactFileName(Artifact artifact) { |
| StringBuilder sb = new StringBuilder(); |
| |
| sb.append(artifact.getArtifactId()); |
| sb.append("-").append(artifact.getVersion()); |
| |
| if (!StringUtils.isBlank(artifact.getClassifier())) |
| sb.append("-").append(artifact.getClassifier()); |
| |
| sb.append(".").append(artifact.getType()); |
| |
| return sb.toString(); |
| } |
| |
| private String buildArtifactDir(Artifact artifact) { |
| StringBuilder sb = new StringBuilder(artifact.getGroupId().replaceAll( |
| "\\.", "/")); |
| |
| sb.append("/").append(artifact.getArtifactId()); |
| sb.append("/").append(artifact.getVersion()); |
| sb.append("/"); |
| |
| return sb.toString(); |
| } |
| |
| } |