View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.impl.resolver;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.util.List;
26  import java.util.Objects;
27  import java.util.concurrent.atomic.AtomicReference;
28  import java.util.function.Consumer;
29  import java.util.stream.Collectors;
30  
31  import org.apache.maven.api.ArtifactCoordinates;
32  import org.apache.maven.api.DownloadedArtifact;
33  import org.apache.maven.api.RemoteRepository;
34  import org.apache.maven.api.Session;
35  import org.apache.maven.api.Version;
36  import org.apache.maven.api.annotations.Nonnull;
37  import org.apache.maven.api.annotations.Nullable;
38  import org.apache.maven.api.di.Named;
39  import org.apache.maven.api.di.Singleton;
40  import org.apache.maven.api.model.Dependency;
41  import org.apache.maven.api.model.InputLocation;
42  import org.apache.maven.api.model.Parent;
43  import org.apache.maven.api.services.ArtifactResolverException;
44  import org.apache.maven.api.services.ModelSource;
45  import org.apache.maven.api.services.Source;
46  import org.apache.maven.api.services.VersionRangeResolverException;
47  import org.apache.maven.api.services.model.ModelResolver;
48  import org.apache.maven.api.services.model.ModelResolverException;
49  
50  /**
51   * A model resolver to assist building of dependency POMs.
52   *
53   * @see DefaultArtifactDescriptorReader
54   */
55  @Named
56  @Singleton
57  public class DefaultModelResolver implements ModelResolver {
58  
59      @Nonnull
60      @Override
61      public ModelSource resolveModel(
62              @Nonnull Session session,
63              @Nullable List<RemoteRepository> repositories,
64              @Nonnull Parent parent,
65              @Nonnull AtomicReference<Parent> modified)
66              throws ModelResolverException {
67          return resolveModel(
68                  session,
69                  repositories,
70                  parent.getGroupId(),
71                  parent.getArtifactId(),
72                  parent.getVersion(),
73                  "parent",
74                  null,
75                  parent.getLocation("version"),
76                  version -> modified.set(parent.withVersion(version)));
77      }
78  
79      @Nonnull
80      public ModelSource resolveModel(
81              @Nonnull Session session,
82              @Nullable List<RemoteRepository> repositories,
83              @Nonnull Dependency dependency,
84              @Nonnull AtomicReference<Dependency> modified)
85              throws ModelResolverException {
86          return resolveModel(
87                  session,
88                  repositories,
89                  dependency.getGroupId(),
90                  dependency.getArtifactId(),
91                  dependency.getVersion(),
92                  "dependency",
93                  dependency.getClassifier(),
94                  dependency.getLocation("version"),
95                  version -> modified.set(dependency.withVersion(version)));
96      }
97  
98      @Override
99      public ModelSource resolveModel(
100             @Nonnull Session session,
101             @Nullable List<RemoteRepository> repositories,
102             @Nonnull String groupId,
103             @Nonnull String artifactId,
104             @Nonnull String version,
105             @Nullable String classifier,
106             @Nonnull Consumer<String> resolvedVersion)
107             throws ModelResolverException {
108         return resolveModel(
109                 session, repositories, groupId, artifactId, version, null, classifier, null, resolvedVersion);
110     }
111 
112     @SuppressWarnings("checkstyle:ParameterNumber")
113     public ModelSource resolveModel(
114             Session session,
115             List<RemoteRepository> repositories,
116             String groupId,
117             String artifactId,
118             String version,
119             String type,
120             String classifier,
121             InputLocation location,
122             Consumer<String> resolvedVersion)
123             throws ModelResolverException {
124         try {
125             ArtifactCoordinates coords =
126                     session.createArtifactCoordinates(groupId, artifactId, version, classifier, "pom", null);
127             if (coords.getVersionConstraint().getVersionRange() != null
128                     && coords.getVersionConstraint().getVersionRange().getUpperBoundary() == null) {
129                 // Message below is checked for in the MNG-2199 core IT.
130                 throw new ModelResolverException(
131                         "The requested " + (type != null ? type + " " : "") + "version range '" + version + "'"
132                                 + (location != null ? " (at " + location + ")" : "")
133                                 + " does not specify an upper bound",
134                         groupId,
135                         artifactId,
136                         version);
137             }
138             List<Version> versions = session.resolveVersionRange(coords, repositories);
139             if (versions.isEmpty()) {
140                 throw new ModelResolverException(
141                         "No versions matched the requested " + (type != null ? type + " " : "") + "version range '"
142                                 + version + "'",
143                         groupId,
144                         artifactId,
145                         version);
146             }
147             String newVersion = versions.get(versions.size() - 1).asString();
148             if (!version.equals(newVersion)) {
149                 resolvedVersion.accept(newVersion);
150             }
151 
152             Path path = getPath(session, repositories, groupId, artifactId, newVersion, classifier);
153             return new ResolverModelSource(path, groupId + ":" + artifactId + ":" + newVersion);
154         } catch (VersionRangeResolverException | ArtifactResolverException e) {
155             throw new ModelResolverException(
156                     e.getMessage() + " (remote repositories: "
157                             + (repositories != null ? repositories : session.getRemoteRepositories())
158                                     .stream().map(Object::toString).collect(Collectors.joining(", "))
159                             + ")",
160                     groupId,
161                     artifactId,
162                     version,
163                     e);
164         }
165     }
166 
167     protected Path getPath(
168             Session session,
169             List<RemoteRepository> repositories,
170             String groupId,
171             String artifactId,
172             String version,
173             String classifier) {
174         DownloadedArtifact resolved = session.resolveArtifact(
175                 session.createArtifactCoordinates(groupId, artifactId, version, classifier, "pom", null), repositories);
176         return resolved.getPath();
177     }
178 
179     protected static class ResolverModelSource implements ModelSource {
180         private final Path path;
181         private final String location;
182 
183         ResolverModelSource(Path path, String location) {
184             this.path = path;
185             this.location = location;
186         }
187 
188         @Override
189         public ModelSource resolve(ModelLocator modelLocator, String relative) {
190             return null;
191         }
192 
193         @Override
194         public Path getPath() {
195             return null;
196         }
197 
198         @Override
199         public InputStream openStream() throws IOException {
200             return Files.newInputStream(path);
201         }
202 
203         @Override
204         public String getLocation() {
205             return location;
206         }
207 
208         @Override
209         public Source resolve(String relative) {
210             return null;
211         }
212 
213         @Override
214         public boolean equals(Object o) {
215             if (this == o) {
216                 return true;
217             }
218             if (o == null || getClass() != o.getClass()) {
219                 return false;
220             }
221             ResolverModelSource that = (ResolverModelSource) o;
222             return Objects.equals(path, that.path) && Objects.equals(location, that.location);
223         }
224 
225         @Override
226         public int hashCode() {
227             return Objects.hash(path, location);
228         }
229     }
230 }