View Javadoc
1   package org.eclipse.aether.internal.impl;
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.Collection;
24  import java.util.Collections;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Map;
28  
29  import static java.util.Objects.requireNonNull;
30  
31  import java.util.Objects;
32  import java.util.Properties;
33  
34  import org.eclipse.aether.RepositorySystemSession;
35  import org.eclipse.aether.artifact.Artifact;
36  import org.eclipse.aether.metadata.Metadata;
37  import org.eclipse.aether.repository.LocalArtifactRegistration;
38  import org.eclipse.aether.repository.LocalArtifactRequest;
39  import org.eclipse.aether.repository.LocalArtifactResult;
40  import org.eclipse.aether.repository.RemoteRepository;
41  
42  /**
43   * These are implementation details for enhanced local repository manager, subject to change without prior notice.
44   * Repositories from which a cached artifact was resolved are tracked in a properties file named
45   * <code>_remote.repositories</code>, with content key as filename&gt;repo_id and value as empty string. If a file has
46   * been installed in the repository, but not downloaded from a remote repository, it is tracked as empty repository id
47   * and always resolved. For example:
48   *
49   * <pre>
50   * artifact-1.0.pom>=
51   * artifact-1.0.jar>=
52   * artifact-1.0.pom>central=
53   * artifact-1.0.jar>central=
54   * artifact-1.0.zip>central=
55   * artifact-1.0-classifier.zip>central=
56   * artifact-1.0.pom>my_repo_id=
57   * </pre>
58   *
59   * @see EnhancedLocalRepositoryManagerFactory
60   */
61  class EnhancedLocalRepositoryManager
62          extends SimpleLocalRepositoryManager
63  {
64  
65      private static final String LOCAL_REPO_ID = "";
66  
67      private final String trackingFilename;
68  
69      private final TrackingFileManager trackingFileManager;
70  
71      private final LocalPathPrefixComposer localPathPrefixComposer;
72  
73      EnhancedLocalRepositoryManager( File basedir,
74                                      LocalPathComposer localPathComposer,
75                                      String trackingFilename,
76                                      TrackingFileManager trackingFileManager,
77                                      LocalPathPrefixComposer localPathPrefixComposer )
78      {
79          super( basedir, "enhanced", localPathComposer );
80          this.trackingFilename = requireNonNull( trackingFilename );
81          this.trackingFileManager = requireNonNull( trackingFileManager );
82          this.localPathPrefixComposer = requireNonNull( localPathPrefixComposer );
83      }
84  
85      private String concatPaths( String prefix, String artifactPath )
86      {
87          if ( prefix == null || prefix.isEmpty() )
88          {
89              return artifactPath;
90          }
91          return prefix + '/' + artifactPath;
92      }
93  
94      @Override
95      public String getPathForLocalArtifact( Artifact artifact )
96      {
97          return concatPaths(
98                  localPathPrefixComposer.getPathPrefixForLocalArtifact( artifact ),
99                  super.getPathForLocalArtifact( artifact )
100         );
101     }
102 
103     @Override
104     public String getPathForRemoteArtifact( Artifact artifact, RemoteRepository repository, String context )
105     {
106         return concatPaths(
107                 localPathPrefixComposer.getPathPrefixForRemoteArtifact( artifact, repository ),
108                 super.getPathForRemoteArtifact( artifact, repository, context )
109         );
110     }
111 
112     @Override
113     public String getPathForLocalMetadata( Metadata metadata )
114     {
115         return concatPaths(
116                 localPathPrefixComposer.getPathPrefixForLocalMetadata( metadata ),
117                 super.getPathForLocalMetadata( metadata )
118         );
119     }
120 
121     @Override
122     public String getPathForRemoteMetadata( Metadata metadata, RemoteRepository repository, String context )
123     {
124         return concatPaths(
125                 localPathPrefixComposer.getPathPrefixForRemoteMetadata( metadata, repository ),
126                 super.getPathForRemoteMetadata( metadata, repository, context )
127         );
128     }
129 
130     @Override
131     public LocalArtifactResult find( RepositorySystemSession session, LocalArtifactRequest request )
132     {
133         Artifact artifact = request.getArtifact();
134         LocalArtifactResult result = new LocalArtifactResult( request );
135 
136         String path;
137         File file;
138 
139         // Local repository CANNOT have timestamped installed, they are created only during deploy
140         if ( Objects.equals( artifact.getVersion(), artifact.getBaseVersion() ) )
141         {
142             path = getPathForLocalArtifact( artifact );
143             file = new File( getRepository().getBasedir(), path );
144             checkFind( file, result );
145         }
146 
147         if ( !result.isAvailable() )
148         {
149             for ( RemoteRepository repository : request.getRepositories() )
150             {
151                 path = getPathForRemoteArtifact( artifact, repository, request.getContext() );
152                 file = new File( getRepository().getBasedir(), path );
153 
154                 checkFind( file, result );
155 
156                 if ( result.isAvailable() )
157                 {
158                     break;
159                 }
160             }
161         }
162 
163         return result;
164     }
165 
166     private void checkFind( File file, LocalArtifactResult result )
167     {
168         if ( file.isFile() )
169         {
170             result.setFile( file );
171 
172             Properties props = readRepos( file );
173 
174             if ( props.get( getKey( file, LOCAL_REPO_ID ) ) != null )
175             {
176                 // artifact installed into the local repo is always accepted
177                 result.setAvailable( true );
178             }
179             else
180             {
181                 String context = result.getRequest().getContext();
182                 for ( RemoteRepository repository : result.getRequest().getRepositories() )
183                 {
184                     if ( props.get( getKey( file, getRepositoryKey( repository, context ) ) ) != null )
185                     {
186                         // artifact downloaded from remote repository is accepted only downloaded from request
187                         // repositories
188                         result.setAvailable( true );
189                         result.setRepository( repository );
190                         break;
191                     }
192                 }
193                 if ( !result.isAvailable() && !isTracked( props, file ) )
194                 {
195                     /*
196                      * NOTE: The artifact is present but not tracked at all, for inter-op with simple local repo, assume
197                      * the artifact was locally installed.
198                      */
199                     result.setAvailable( true );
200                 }
201             }
202         }
203     }
204 
205     @Override
206     public void add( RepositorySystemSession session, LocalArtifactRegistration request )
207     {
208         Collection<String> repositories;
209         if ( request.getRepository() == null )
210         {
211             repositories = Collections.singleton( LOCAL_REPO_ID );
212         }
213         else
214         {
215             repositories = getRepositoryKeys( request.getRepository(), request.getContexts() );
216         }
217         if ( request.getRepository() == null )
218         {
219             addArtifact( request.getArtifact(), repositories, null, null );
220         }
221         else
222         {
223             for ( String context : request.getContexts() )
224             {
225                 addArtifact( request.getArtifact(), repositories, request.getRepository(), context );
226             }
227         }
228     }
229 
230     private Collection<String> getRepositoryKeys( RemoteRepository repository, Collection<String> contexts )
231     {
232         Collection<String> keys = new HashSet<>();
233 
234         if ( contexts != null )
235         {
236             for ( String context : contexts )
237             {
238                 keys.add( getRepositoryKey( repository, context ) );
239             }
240         }
241 
242         return keys;
243     }
244 
245     private void addArtifact( Artifact artifact, Collection<String> repositories, RemoteRepository repository,
246                               String context )
247     {
248         requireNonNull( artifact, "artifact cannot be null" );
249         String path = repository == null ? getPathForLocalArtifact( artifact )
250                 : getPathForRemoteArtifact( artifact, repository, context );
251         File file = new File( getRepository().getBasedir(), path );
252         addRepo( file, repositories );
253     }
254 
255     private Properties readRepos( File artifactFile )
256     {
257         File trackingFile = getTrackingFile( artifactFile );
258 
259         Properties props = trackingFileManager.read( trackingFile );
260 
261         return ( props != null ) ? props : new Properties();
262     }
263 
264     private void addRepo( File artifactFile, Collection<String> repositories )
265     {
266         Map<String, String> updates = new HashMap<>();
267         for ( String repository : repositories )
268         {
269             updates.put( getKey( artifactFile, repository ), "" );
270         }
271 
272         File trackingFile = getTrackingFile( artifactFile );
273 
274         trackingFileManager.update( trackingFile, updates );
275     }
276 
277     private File getTrackingFile( File artifactFile )
278     {
279         return new File( artifactFile.getParentFile(), trackingFilename );
280     }
281 
282     private String getKey( File file, String repository )
283     {
284         return file.getName() + '>' + repository;
285     }
286 
287     private boolean isTracked( Properties props, File file )
288     {
289         if ( props != null )
290         {
291             String keyPrefix = file.getName() + '>';
292             for ( Object key : props.keySet() )
293             {
294                 if ( key.toString().startsWith( keyPrefix ) )
295                 {
296                     return true;
297                 }
298             }
299         }
300         return false;
301     }
302 
303 }