001package 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
022import java.io.File;
023import java.util.ArrayList;
024import java.util.Iterator;
025import java.util.LinkedHashMap;
026import java.util.LinkedHashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030import java.util.concurrent.ConcurrentHashMap;
031
032import org.apache.maven.artifact.Artifact;
033import org.apache.maven.artifact.ArtifactUtils;
034import org.apache.maven.artifact.metadata.ResolutionGroup;
035import org.apache.maven.artifact.repository.ArtifactRepository;
036import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
037import org.codehaus.plexus.component.annotations.Component;
038
039@Component( role = MavenMetadataCache.class )
040public 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}