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.project.artifact;
20  
21  import java.io.File;
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.LinkedHashMap;
25  import java.util.LinkedHashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Objects;
29  import java.util.Set;
30  import java.util.concurrent.ConcurrentHashMap;
31  import javax.inject.Named;
32  import javax.inject.Singleton;
33  import org.apache.maven.artifact.Artifact;
34  import org.apache.maven.artifact.ArtifactUtils;
35  import org.apache.maven.artifact.metadata.ResolutionGroup;
36  import org.apache.maven.artifact.repository.ArtifactRepository;
37  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
38  
39  /**
40   * DefaultMavenMetadataCache
41   */
42  @Named
43  @Singleton
44  public class DefaultMavenMetadataCache implements MavenMetadataCache {
45  
46      protected final Map<CacheKey, CacheRecord> cache = new ConcurrentHashMap<>();
47  
48      /**
49       * CacheKey
50       */
51      public static class CacheKey {
52          private final Artifact artifact;
53          private final long pomHash;
54          private final boolean resolveManagedVersions;
55          private final List<ArtifactRepository> repositories = new ArrayList<>();
56          private final int hashCode;
57  
58          public CacheKey(
59                  Artifact artifact,
60                  boolean resolveManagedVersions,
61                  ArtifactRepository localRepository,
62                  List<ArtifactRepository> remoteRepositories) {
63              File file = artifact.getFile();
64              this.artifact = ArtifactUtils.copyArtifact(artifact);
65              if ("pom".equals(artifact.getType()) && file != null) {
66                  pomHash = file.getPath().hashCode() + file.lastModified();
67              } else {
68                  pomHash = 0;
69              }
70              this.resolveManagedVersions = resolveManagedVersions;
71              this.repositories.add(localRepository);
72              this.repositories.addAll(remoteRepositories);
73  
74              int hash = 17;
75              hash = hash * 31 + artifactHashCode(artifact);
76              hash = hash * 31 + (resolveManagedVersions ? 1 : 2);
77              hash = hash * 31 + repositoriesHashCode(repositories);
78              this.hashCode = hash;
79          }
80  
81          @Override
82          public int hashCode() {
83              return hashCode;
84          }
85  
86          @Override
87          public boolean equals(Object o) {
88              if (o == this) {
89                  return true;
90              }
91  
92              if (!(o instanceof CacheKey)) {
93                  return false;
94              }
95  
96              CacheKey other = (CacheKey) o;
97  
98              return pomHash == other.pomHash
99                      && artifactEquals(artifact, other.artifact)
100                     && resolveManagedVersions == other.resolveManagedVersions
101                     && repositoriesEquals(repositories, other.repositories);
102         }
103     }
104 
105     private static int artifactHashCode(Artifact a) {
106         int result = 17;
107         result = 31 * result + a.getGroupId().hashCode();
108         result = 31 * result + a.getArtifactId().hashCode();
109         result = 31 * result + a.getType().hashCode();
110         if (a.getVersion() != null) {
111             result = 31 * result + a.getVersion().hashCode();
112         }
113         result = 31 * result + (a.getClassifier() != null ? a.getClassifier().hashCode() : 0);
114         result = 31 * result + (a.getScope() != null ? a.getScope().hashCode() : 0);
115         result = 31 * result
116                 + (a.getDependencyFilter() != null ? a.getDependencyFilter().hashCode() : 0);
117         result = 31 * result + (a.isOptional() ? 1 : 0);
118         return result;
119     }
120 
121     private static boolean artifactEquals(Artifact a1, Artifact a2) {
122         if (a1 == a2) {
123             return true;
124         }
125 
126         return Objects.equals(a1.getGroupId(), a2.getGroupId())
127                 && Objects.equals(a1.getArtifactId(), a2.getArtifactId())
128                 && Objects.equals(a1.getType(), a2.getType())
129                 && Objects.equals(a1.getVersion(), a2.getVersion())
130                 && Objects.equals(a1.getClassifier(), a2.getClassifier())
131                 && Objects.equals(a1.getScope(), a2.getScope())
132                 && Objects.equals(a1.getDependencyFilter(), a2.getDependencyFilter())
133                 && a1.isOptional() == a2.isOptional();
134     }
135 
136     private static int repositoryHashCode(ArtifactRepository repository) {
137         int result = 17;
138         result = 31 * result + (repository.getId() != null ? repository.getId().hashCode() : 0);
139         return result;
140     }
141 
142     private static int repositoriesHashCode(List<ArtifactRepository> repositories) {
143         int result = 17;
144         for (ArtifactRepository repository : repositories) {
145             result = 31 * result + repositoryHashCode(repository);
146         }
147         return result;
148     }
149 
150     private static boolean repositoryEquals(ArtifactRepository r1, ArtifactRepository r2) {
151         if (r1 == r2) {
152             return true;
153         }
154 
155         return Objects.equals(r1.getId(), r2.getId())
156                 && Objects.equals(r1.getUrl(), r2.getUrl())
157                 && repositoryPolicyEquals(r1.getReleases(), r2.getReleases())
158                 && repositoryPolicyEquals(r1.getSnapshots(), r2.getSnapshots());
159     }
160 
161     private static boolean repositoryPolicyEquals(ArtifactRepositoryPolicy p1, ArtifactRepositoryPolicy p2) {
162         if (p1 == p2) {
163             return true;
164         }
165 
166         return p1.isEnabled() == p2.isEnabled() && Objects.equals(p1.getUpdatePolicy(), p2.getUpdatePolicy());
167     }
168 
169     private static boolean repositoriesEquals(List<ArtifactRepository> r1, List<ArtifactRepository> r2) {
170         if (r1.size() != r2.size()) {
171             return false;
172         }
173 
174         for (Iterator<ArtifactRepository> it1 = r1.iterator(), it2 = r2.iterator(); it1.hasNext(); ) {
175             if (!repositoryEquals(it1.next(), it2.next())) {
176                 return false;
177             }
178         }
179 
180         return true;
181     }
182 
183     /**
184      * CacheRecord
185      */
186     public class CacheRecord {
187         private Artifact pomArtifact;
188         private Artifact relocatedArtifact;
189         private List<Artifact> artifacts;
190         private Map<String, Artifact> managedVersions;
191         private List<ArtifactRepository> remoteRepositories;
192 
193         private long length;
194         private long timestamp;
195 
196         CacheRecord(
197                 Artifact pomArtifact,
198                 Artifact relocatedArtifact,
199                 Set<Artifact> artifacts,
200                 Map<String, Artifact> managedVersions,
201                 List<ArtifactRepository> remoteRepositories) {
202             this.pomArtifact = ArtifactUtils.copyArtifact(pomArtifact);
203             this.relocatedArtifact = ArtifactUtils.copyArtifactSafe(relocatedArtifact);
204             this.artifacts = ArtifactUtils.copyArtifacts(artifacts, new ArrayList<>());
205             this.remoteRepositories = new ArrayList<>(remoteRepositories);
206 
207             this.managedVersions = managedVersions;
208             if (managedVersions != null) {
209                 this.managedVersions = ArtifactUtils.copyArtifacts(managedVersions, new LinkedHashMap<>());
210             }
211 
212             File pomFile = pomArtifact.getFile();
213             if (pomFile != null && pomFile.canRead()) {
214                 this.length = pomFile.length();
215                 this.timestamp = pomFile.lastModified();
216             } else {
217                 this.length = -1;
218                 this.timestamp = -1;
219             }
220         }
221 
222         public Artifact getArtifact() {
223             return pomArtifact;
224         }
225 
226         public Artifact getRelocatedArtifact() {
227             return relocatedArtifact;
228         }
229 
230         public List<Artifact> getArtifacts() {
231             return artifacts;
232         }
233 
234         public Map<String, Artifact> getManagedVersions() {
235             return managedVersions;
236         }
237 
238         public List<ArtifactRepository> getRemoteRepositories() {
239             return remoteRepositories;
240         }
241 
242         public boolean isStale() {
243             File pomFile = pomArtifact.getFile();
244             if (pomFile != null) {
245                 if (pomFile.canRead()) {
246                     return length != pomFile.length() || timestamp != pomFile.lastModified();
247                 } else {
248                     // if the POM didn't exist, retry if any repo is configured to always update
249                     boolean snapshot = pomArtifact.isSnapshot();
250                     for (ArtifactRepository repository : remoteRepositories) {
251                         ArtifactRepositoryPolicy policy =
252                                 snapshot ? repository.getSnapshots() : repository.getReleases();
253                         if (ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS.equals(policy.getUpdatePolicy())) {
254                             return true;
255                         }
256                     }
257                 }
258             }
259 
260             return length != -1 || timestamp != -1;
261         }
262     }
263 
264     public ResolutionGroup get(
265             Artifact artifact,
266             boolean resolveManagedVersions,
267             ArtifactRepository localRepository,
268             List<ArtifactRepository> remoteRepositories) {
269         CacheKey cacheKey = newCacheKey(artifact, resolveManagedVersions, localRepository, remoteRepositories);
270 
271         CacheRecord cacheRecord = cache.get(cacheKey);
272 
273         if (cacheRecord != null && !cacheRecord.isStale()) {
274             Artifact pomArtifact = ArtifactUtils.copyArtifact(cacheRecord.getArtifact());
275             Artifact relocatedArtifact = ArtifactUtils.copyArtifactSafe(cacheRecord.getRelocatedArtifact());
276             Set<Artifact> artifacts = ArtifactUtils.copyArtifacts(cacheRecord.getArtifacts(), new LinkedHashSet<>());
277             Map<String, Artifact> managedVersions = cacheRecord.getManagedVersions();
278             if (managedVersions != null) {
279                 managedVersions = ArtifactUtils.copyArtifacts(managedVersions, new LinkedHashMap<>());
280             }
281             return new ResolutionGroup(
282                     pomArtifact, relocatedArtifact, artifacts, managedVersions, cacheRecord.getRemoteRepositories());
283         }
284 
285         cache.remove(cacheKey);
286 
287         return null;
288     }
289 
290     public void put(
291             Artifact artifact,
292             boolean resolveManagedVersions,
293             ArtifactRepository localRepository,
294             List<ArtifactRepository> remoteRepositories,
295             ResolutionGroup result) {
296         put(newCacheKey(artifact, resolveManagedVersions, localRepository, remoteRepositories), result);
297     }
298 
299     protected CacheKey newCacheKey(
300             Artifact artifact,
301             boolean resolveManagedVersions,
302             ArtifactRepository localRepository,
303             List<ArtifactRepository> remoteRepositories) {
304         return new CacheKey(artifact, resolveManagedVersions, localRepository, remoteRepositories);
305     }
306 
307     protected void put(CacheKey cacheKey, ResolutionGroup result) {
308         CacheRecord cacheRecord = new CacheRecord(
309                 result.getPomArtifact(),
310                 result.getRelocatedArtifact(),
311                 result.getArtifacts(),
312                 result.getManagedVersions(),
313                 result.getResolutionRepositories());
314 
315         cache.put(cacheKey, cacheRecord);
316     }
317 
318     public void flush() {
319         cache.clear();
320     }
321 }