001    package org.apache.maven.project.artifact;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *  http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.File;
023    import java.util.ArrayList;
024    import java.util.Iterator;
025    import java.util.LinkedHashMap;
026    import java.util.LinkedHashSet;
027    import java.util.List;
028    import java.util.Map;
029    import java.util.Set;
030    import java.util.concurrent.ConcurrentHashMap;
031    
032    import org.apache.maven.artifact.Artifact;
033    import org.apache.maven.artifact.ArtifactUtils;
034    import org.apache.maven.artifact.metadata.ResolutionGroup;
035    import org.apache.maven.artifact.repository.ArtifactRepository;
036    import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
037    import org.codehaus.plexus.component.annotations.Component;
038    
039    @Component( role = MavenMetadataCache.class )
040    public class DefaultMavenMetadataCache
041        implements MavenMetadataCache
042    {
043    
044        protected final Map<CacheKey, CacheRecord> cache = new ConcurrentHashMap<CacheKey, CacheRecord>();
045    
046        public static class CacheKey
047        {
048            private final Artifact artifact;
049            private final long pomHash;
050            private final boolean resolveManagedVersions;
051            private final List<ArtifactRepository> repositories = new ArrayList<ArtifactRepository>();
052            private final int hashCode;
053    
054            public CacheKey( Artifact artifact, boolean resolveManagedVersions, ArtifactRepository localRepository,
055                             List<ArtifactRepository> remoteRepositories )
056            {
057                File file = artifact.getFile();
058                this.artifact = ArtifactUtils.copyArtifact( artifact );
059                if ( "pom".equals( artifact.getType() ) && file != null )
060                {
061                    pomHash = file.getPath().hashCode() + file.lastModified();
062                }
063                else
064                {
065                    pomHash = 0;
066                }
067                this.resolveManagedVersions = resolveManagedVersions;
068                this.repositories.add( localRepository );
069                this.repositories.addAll( remoteRepositories );
070    
071                int hash = 17;
072                hash = hash * 31 + artifactHashCode( artifact );
073                hash = hash * 31 + ( resolveManagedVersions ? 1 : 2 );
074                hash = hash * 31 + repositoriesHashCode( repositories );
075                this.hashCode = hash;
076            }
077    
078            @Override
079            public int hashCode()
080            {
081                return hashCode;
082            }
083    
084            @Override
085            public boolean equals( Object o )
086            {
087                if ( o == this )
088                {
089                    return true;
090                }
091    
092                if ( !( o instanceof CacheKey ) )
093                {
094                    return false;
095                }
096    
097                CacheKey other = (CacheKey) o;
098    
099                return pomHash == other.pomHash && artifactEquals( artifact, other.artifact )
100                    && resolveManagedVersions == other.resolveManagedVersions
101                    && repositoriesEquals( repositories, other.repositories );
102            }
103        }
104    
105        private static int artifactHashCode( Artifact a )
106        {
107            int result = 17;
108            result = 31 * result + a.getGroupId().hashCode();
109            result = 31 * result + a.getArtifactId().hashCode();
110            result = 31 * result + a.getType().hashCode();
111            if ( a.getVersion() != null )
112            {
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 + ( a.getDependencyFilter() != null ? a.getDependencyFilter().hashCode() : 0 );
118            result = 31 * result + ( a.isOptional() ? 1 : 0 );
119            return result;
120        }
121    
122        private static boolean artifactEquals( Artifact a1, Artifact a2 )
123        {
124            if ( a1 == a2 )
125            {
126                return true;
127            }
128    
129            return eq( a1.getGroupId(), a2.getGroupId() )
130                && eq( a1.getArtifactId(), a2.getArtifactId() )
131                && eq( a1.getType(), a2.getType() )
132                && eq( a1.getVersion(), a2.getVersion() )
133                && eq( a1.getClassifier(), a2.getClassifier() )
134                && eq( a1.getScope(), a2.getScope() )
135                && eq( a1.getDependencyFilter(), a2.getDependencyFilter() )
136                && a1.isOptional() == a2.isOptional();
137        }
138    
139        private static int repositoryHashCode( ArtifactRepository repository )
140        {
141            int result = 17;
142            result = 31 * result + ( repository.getId() != null ? repository.getId().hashCode() : 0 );
143            return result;
144        }
145    
146        private static int repositoriesHashCode( List<ArtifactRepository> repositories )
147        {
148            int result = 17;
149            for ( ArtifactRepository repository : repositories )
150            {
151                result = 31 * result + repositoryHashCode( repository );
152            }
153            return result;
154        }
155    
156        private static boolean repositoryEquals( ArtifactRepository r1, ArtifactRepository r2 )
157        {
158            if ( r1 == r2 )
159            {
160                return true;
161            }
162    
163            return eq( r1.getId(), r2.getId() ) && eq( r1.getUrl(), r2.getUrl() )
164                && repositoryPolicyEquals( r1.getReleases(), r2.getReleases() )
165                && repositoryPolicyEquals( r1.getSnapshots(), r2.getSnapshots() );
166        }
167    
168        private static boolean repositoryPolicyEquals( ArtifactRepositoryPolicy p1, ArtifactRepositoryPolicy p2 )
169        {
170            if ( p1 == p2 )
171            {
172                return true;
173            }
174    
175            return p1.isEnabled() == p2.isEnabled() && eq( p1.getUpdatePolicy(), p2.getUpdatePolicy() );
176        }
177    
178        private static boolean repositoriesEquals( List<ArtifactRepository> r1, List<ArtifactRepository> r2 )
179        {
180            if ( r1.size() != r2.size() )
181            {
182                return false;
183            }
184    
185            for ( Iterator<ArtifactRepository> it1 = r1.iterator(), it2 = r2.iterator(); it1.hasNext(); )
186            {
187                if ( !repositoryEquals( it1.next(), it2.next() ) )
188                {
189                    return false;
190                }
191            }
192    
193            return true;
194        }
195    
196        private static <T> boolean eq( T s1, T s2 )
197        {
198            return s1 != null ? s1.equals( s2 ) : s2 == null;
199        }
200    
201        public class CacheRecord
202        {
203            private Artifact pomArtifact;
204            private Artifact relocatedArtifact;
205            private List<Artifact> artifacts;
206            private Map<String, Artifact> managedVersions;
207            private List<ArtifactRepository> remoteRepositories;
208    
209            private long length;
210            private long timestamp;
211    
212            CacheRecord( Artifact pomArtifact, Artifact relocatedArtifact, Set<Artifact> artifacts,
213                         Map<String, Artifact> managedVersions, List<ArtifactRepository> remoteRepositories )
214            {
215                this.pomArtifact = ArtifactUtils.copyArtifact( pomArtifact );
216                this.relocatedArtifact = ArtifactUtils.copyArtifactSafe( relocatedArtifact );
217                this.artifacts = ArtifactUtils.copyArtifacts( artifacts, new ArrayList<Artifact>() );
218                this.remoteRepositories = new ArrayList<ArtifactRepository>( remoteRepositories );
219    
220                this.managedVersions = managedVersions;
221                if ( managedVersions != null )
222                {
223                    this.managedVersions =
224                        ArtifactUtils.copyArtifacts( managedVersions, new LinkedHashMap<String, Artifact>() );
225                }
226    
227                File pomFile = pomArtifact.getFile();
228                if ( pomFile != null && pomFile.canRead() )
229                {
230                    this.length = pomFile.length();
231                    this.timestamp = pomFile.lastModified();
232                }
233                else
234                {
235                    this.length = -1;
236                    this.timestamp = -1;
237                }
238            }
239    
240            public Artifact getArtifact()
241            {
242                return pomArtifact;
243            }
244    
245            public Artifact getRelocatedArtifact()
246            {
247                return relocatedArtifact;
248            }
249    
250            public List<Artifact> getArtifacts()
251            {
252                return artifacts;
253            }
254    
255            public Map<String, Artifact> getManagedVersions()
256            {
257                return managedVersions;
258            }
259    
260            public List<ArtifactRepository> getRemoteRepositories()
261            {
262                return remoteRepositories;
263            }
264    
265            public boolean isStale()
266            {
267                File pomFile = pomArtifact.getFile();
268                if ( pomFile != null )
269                {
270                    if ( pomFile.canRead() )
271                    {
272                        return length != pomFile.length() || timestamp != pomFile.lastModified();
273                    }
274                    else
275                    {
276                        // if the POM didn't exist, retry if any repo is configured to always update
277                        boolean snapshot = pomArtifact.isSnapshot();
278                        for ( ArtifactRepository repository : remoteRepositories )
279                        {
280                            ArtifactRepositoryPolicy policy =
281                                snapshot ? repository.getSnapshots() : repository.getReleases();
282                            if ( ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS.equals( policy.getUpdatePolicy() ) )
283                            {
284                                return true;
285                            }
286                        }
287                    }
288                }
289    
290                return length != -1 || timestamp != -1;
291            }
292        }
293    
294    
295        public ResolutionGroup get( Artifact artifact, boolean resolveManagedVersions, ArtifactRepository localRepository,
296                                    List<ArtifactRepository> remoteRepositories )
297        {
298            CacheKey cacheKey = newCacheKey( artifact, resolveManagedVersions, localRepository, remoteRepositories );
299    
300            CacheRecord cacheRecord = cache.get( cacheKey );
301    
302            if ( cacheRecord != null && !cacheRecord.isStale() )
303            {
304                Artifact pomArtifact = ArtifactUtils.copyArtifact( cacheRecord.getArtifact() );
305                Artifact relocatedArtifact = ArtifactUtils.copyArtifactSafe( cacheRecord.getRelocatedArtifact() );
306                Set<Artifact> artifacts =
307                    ArtifactUtils.copyArtifacts( cacheRecord.getArtifacts(), new LinkedHashSet<Artifact>() );
308                Map<String, Artifact> managedVersions = cacheRecord.getManagedVersions();
309                if ( managedVersions != null )
310                {
311                    managedVersions = ArtifactUtils.copyArtifacts( managedVersions, new LinkedHashMap<String, Artifact>() );
312                }
313                return new ResolutionGroup( pomArtifact, relocatedArtifact, artifacts, managedVersions,
314                                            cacheRecord.getRemoteRepositories() );
315            }
316    
317            cache.remove( cacheKey );
318    
319            return null;
320        }
321    
322        public void put( Artifact artifact, boolean resolveManagedVersions, ArtifactRepository localRepository,
323                         List<ArtifactRepository> remoteRepositories, ResolutionGroup result )
324        {
325            put( newCacheKey( artifact, resolveManagedVersions, localRepository, remoteRepositories ), result );
326        }
327    
328        protected CacheKey newCacheKey( Artifact artifact, boolean resolveManagedVersions,
329                                        ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories )
330        {
331            return new CacheKey( artifact, resolveManagedVersions, localRepository, remoteRepositories );
332        }
333    
334        protected void put( CacheKey cacheKey, ResolutionGroup result )
335        {
336            CacheRecord cacheRecord =
337                new CacheRecord( result.getPomArtifact(), result.getRelocatedArtifact(), result.getArtifacts(),
338                                 result.getManagedVersions(), result.getResolutionRepositories() );
339    
340            cache.put( cacheKey, cacheRecord );
341        }
342    
343        public void flush()
344        {
345            cache.clear();
346        }
347    }