View Javadoc
1   package org.apache.maven.project.artifact;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.LinkedHashMap;
26  import java.util.LinkedHashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.concurrent.ConcurrentHashMap;
31  
32  import org.apache.maven.artifact.Artifact;
33  import org.apache.maven.artifact.ArtifactUtils;
34  import org.apache.maven.artifact.metadata.ResolutionGroup;
35  import org.apache.maven.artifact.repository.ArtifactRepository;
36  import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
37  import org.codehaus.plexus.component.annotations.Component;
38  
39  /**
40   * DefaultMavenMetadataCache
41   */
42  @Component( role = MavenMetadataCache.class )
43  public class DefaultMavenMetadataCache
44      implements MavenMetadataCache
45  {
46  
47      protected final Map<CacheKey, CacheRecord> cache = new ConcurrentHashMap<>();
48  
49      /**
50       * CacheKey
51       */
52      public static class CacheKey
53      {
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( Artifact artifact, boolean resolveManagedVersions, ArtifactRepository localRepository,
61                           List<ArtifactRepository> remoteRepositories )
62          {
63              File file = artifact.getFile();
64              this.artifact = ArtifactUtils.copyArtifact( artifact );
65              if ( "pom".equals( artifact.getType() ) && file != null )
66              {
67                  pomHash = file.getPath().hashCode() + file.lastModified();
68              }
69              else
70              {
71                  pomHash = 0;
72              }
73              this.resolveManagedVersions = resolveManagedVersions;
74              this.repositories.add( localRepository );
75              this.repositories.addAll( remoteRepositories );
76  
77              int hash = 17;
78              hash = hash * 31 + artifactHashCode( artifact );
79              hash = hash * 31 + ( resolveManagedVersions ? 1 : 2 );
80              hash = hash * 31 + repositoriesHashCode( repositories );
81              this.hashCode = hash;
82          }
83  
84          @Override
85          public int hashCode()
86          {
87              return hashCode;
88          }
89  
90          @Override
91          public boolean equals( Object o )
92          {
93              if ( o == this )
94              {
95                  return true;
96              }
97  
98              if ( !( o instanceof CacheKey ) )
99              {
100                 return false;
101             }
102 
103             CacheKey other = (CacheKey) o;
104 
105             return pomHash == other.pomHash && artifactEquals( artifact, other.artifact )
106                 && resolveManagedVersions == other.resolveManagedVersions
107                 && repositoriesEquals( repositories, other.repositories );
108         }
109     }
110 
111     private static int artifactHashCode( Artifact a )
112     {
113         int result = 17;
114         result = 31 * result + a.getGroupId().hashCode();
115         result = 31 * result + a.getArtifactId().hashCode();
116         result = 31 * result + a.getType().hashCode();
117         if ( a.getVersion() != null )
118         {
119             result = 31 * result + a.getVersion().hashCode();
120         }
121         result = 31 * result + ( a.getClassifier() != null ? a.getClassifier().hashCode() : 0 );
122         result = 31 * result + ( a.getScope() != null ? a.getScope().hashCode() : 0 );
123         result = 31 * result + ( a.getDependencyFilter() != null ? a.getDependencyFilter().hashCode() : 0 );
124         result = 31 * result + ( a.isOptional() ? 1 : 0 );
125         return result;
126     }
127 
128     private static boolean artifactEquals( Artifact a1, Artifact a2 )
129     {
130         if ( a1 == a2 )
131         {
132             return true;
133         }
134 
135         return eq( a1.getGroupId(), a2.getGroupId() )
136             && eq( a1.getArtifactId(), a2.getArtifactId() )
137             && eq( a1.getType(), a2.getType() )
138             && eq( a1.getVersion(), a2.getVersion() )
139             && eq( a1.getClassifier(), a2.getClassifier() )
140             && eq( a1.getScope(), a2.getScope() )
141             && eq( a1.getDependencyFilter(), a2.getDependencyFilter() )
142             && a1.isOptional() == a2.isOptional();
143     }
144 
145     private static int repositoryHashCode( ArtifactRepository repository )
146     {
147         int result = 17;
148         result = 31 * result + ( repository.getId() != null ? repository.getId().hashCode() : 0 );
149         return result;
150     }
151 
152     private static int repositoriesHashCode( List<ArtifactRepository> repositories )
153     {
154         int result = 17;
155         for ( ArtifactRepository repository : repositories )
156         {
157             result = 31 * result + repositoryHashCode( repository );
158         }
159         return result;
160     }
161 
162     private static boolean repositoryEquals( ArtifactRepository r1, ArtifactRepository r2 )
163     {
164         if ( r1 == r2 )
165         {
166             return true;
167         }
168 
169         return eq( r1.getId(), r2.getId() ) && eq( r1.getUrl(), r2.getUrl() )
170             && repositoryPolicyEquals( r1.getReleases(), r2.getReleases() )
171             && repositoryPolicyEquals( r1.getSnapshots(), r2.getSnapshots() );
172     }
173 
174     private static boolean repositoryPolicyEquals( ArtifactRepositoryPolicy p1, ArtifactRepositoryPolicy p2 )
175     {
176         if ( p1 == p2 )
177         {
178             return true;
179         }
180 
181         return p1.isEnabled() == p2.isEnabled() && eq( p1.getUpdatePolicy(), p2.getUpdatePolicy() );
182     }
183 
184     private static boolean repositoriesEquals( List<ArtifactRepository> r1, List<ArtifactRepository> r2 )
185     {
186         if ( r1.size() != r2.size() )
187         {
188             return false;
189         }
190 
191         for ( Iterator<ArtifactRepository> it1 = r1.iterator(), it2 = r2.iterator(); it1.hasNext(); )
192         {
193             if ( !repositoryEquals( it1.next(), it2.next() ) )
194             {
195                 return false;
196             }
197         }
198 
199         return true;
200     }
201 
202     private static <T> boolean eq( T s1, T s2 )
203     {
204         return s1 != null ? s1.equals( s2 ) : s2 == null;
205     }
206 
207     /**
208      * CacheRecord
209      */
210     public class CacheRecord
211     {
212         private Artifact pomArtifact;
213         private Artifact relocatedArtifact;
214         private List<Artifact> artifacts;
215         private Map<String, Artifact> managedVersions;
216         private List<ArtifactRepository> remoteRepositories;
217 
218         private long length;
219         private long timestamp;
220 
221         CacheRecord( Artifact pomArtifact, Artifact relocatedArtifact, Set<Artifact> artifacts,
222                      Map<String, Artifact> managedVersions, List<ArtifactRepository> remoteRepositories )
223         {
224             this.pomArtifact = ArtifactUtils.copyArtifact( pomArtifact );
225             this.relocatedArtifact = ArtifactUtils.copyArtifactSafe( relocatedArtifact );
226             this.artifacts = ArtifactUtils.copyArtifacts( artifacts, new ArrayList<Artifact>() );
227             this.remoteRepositories = new ArrayList<>( remoteRepositories );
228 
229             this.managedVersions = managedVersions;
230             if ( managedVersions != null )
231             {
232                 this.managedVersions =
233                     ArtifactUtils.copyArtifacts( managedVersions, new LinkedHashMap<String, Artifact>() );
234             }
235 
236             File pomFile = pomArtifact.getFile();
237             if ( pomFile != null && pomFile.canRead() )
238             {
239                 this.length = pomFile.length();
240                 this.timestamp = pomFile.lastModified();
241             }
242             else
243             {
244                 this.length = -1;
245                 this.timestamp = -1;
246             }
247         }
248 
249         public Artifact getArtifact()
250         {
251             return pomArtifact;
252         }
253 
254         public Artifact getRelocatedArtifact()
255         {
256             return relocatedArtifact;
257         }
258 
259         public List<Artifact> getArtifacts()
260         {
261             return artifacts;
262         }
263 
264         public Map<String, Artifact> getManagedVersions()
265         {
266             return managedVersions;
267         }
268 
269         public List<ArtifactRepository> getRemoteRepositories()
270         {
271             return remoteRepositories;
272         }
273 
274         public boolean isStale()
275         {
276             File pomFile = pomArtifact.getFile();
277             if ( pomFile != null )
278             {
279                 if ( pomFile.canRead() )
280                 {
281                     return length != pomFile.length() || timestamp != pomFile.lastModified();
282                 }
283                 else
284                 {
285                     // if the POM didn't exist, retry if any repo is configured to always update
286                     boolean snapshot = pomArtifact.isSnapshot();
287                     for ( ArtifactRepository repository : remoteRepositories )
288                     {
289                         ArtifactRepositoryPolicy policy =
290                             snapshot ? repository.getSnapshots() : repository.getReleases();
291                         if ( ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS.equals( policy.getUpdatePolicy() ) )
292                         {
293                             return true;
294                         }
295                     }
296                 }
297             }
298 
299             return length != -1 || timestamp != -1;
300         }
301     }
302 
303 
304     public ResolutionGroup get( Artifact artifact, boolean resolveManagedVersions, ArtifactRepository localRepository,
305                                 List<ArtifactRepository> remoteRepositories )
306     {
307         CacheKey cacheKey = newCacheKey( artifact, resolveManagedVersions, localRepository, remoteRepositories );
308 
309         CacheRecord cacheRecord = cache.get( cacheKey );
310 
311         if ( cacheRecord != null && !cacheRecord.isStale() )
312         {
313             Artifact pomArtifact = ArtifactUtils.copyArtifact( cacheRecord.getArtifact() );
314             Artifact relocatedArtifact = ArtifactUtils.copyArtifactSafe( cacheRecord.getRelocatedArtifact() );
315             Set<Artifact> artifacts =
316                 ArtifactUtils.copyArtifacts( cacheRecord.getArtifacts(), new LinkedHashSet<Artifact>() );
317             Map<String, Artifact> managedVersions = cacheRecord.getManagedVersions();
318             if ( managedVersions != null )
319             {
320                 managedVersions = ArtifactUtils.copyArtifacts( managedVersions, new LinkedHashMap<String, Artifact>() );
321             }
322             return new ResolutionGroup( pomArtifact, relocatedArtifact, artifacts, managedVersions,
323                                         cacheRecord.getRemoteRepositories() );
324         }
325 
326         cache.remove( cacheKey );
327 
328         return null;
329     }
330 
331     public void put( Artifact artifact, boolean resolveManagedVersions, ArtifactRepository localRepository,
332                      List<ArtifactRepository> remoteRepositories, ResolutionGroup result )
333     {
334         put( newCacheKey( artifact, resolveManagedVersions, localRepository, remoteRepositories ), result );
335     }
336 
337     protected CacheKey newCacheKey( Artifact artifact, boolean resolveManagedVersions,
338                                     ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories )
339     {
340         return new CacheKey( artifact, resolveManagedVersions, localRepository, remoteRepositories );
341     }
342 
343     protected void put( CacheKey cacheKey, ResolutionGroup result )
344     {
345         CacheRecord cacheRecord =
346             new CacheRecord( result.getPomArtifact(), result.getRelocatedArtifact(), result.getArtifacts(),
347                              result.getManagedVersions(), result.getResolutionRepositories() );
348 
349         cache.put( cacheKey, cacheRecord );
350     }
351 
352     public void flush()
353     {
354         cache.clear();
355     }
356 }