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