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  import static java.util.Objects.requireNonNull;
29  import java.util.Properties;
30  
31  import org.eclipse.aether.RepositorySystemSession;
32  import org.eclipse.aether.artifact.Artifact;
33  import org.eclipse.aether.repository.LocalArtifactRegistration;
34  import org.eclipse.aether.repository.LocalArtifactRequest;
35  import org.eclipse.aether.repository.LocalArtifactResult;
36  import org.eclipse.aether.repository.RemoteRepository;
37  import org.eclipse.aether.spi.log.Logger;
38  import org.eclipse.aether.util.ConfigUtils;
39  
40  /**
41   * These are implementation details for enhanced local repository manager, subject to change without prior notice.
42   * Repositories from which a cached artifact was resolved are tracked in a properties file named
43   * <code>_remote.repositories</code>, with content key as filename&gt;repo_id and value as empty string. If a file has
44   * been installed in the repository, but not downloaded from a remote repository, it is tracked as empty repository id
45   * and always resolved. For example:
46   * 
47   * <pre>
48   * artifact-1.0.pom>=
49   * artifact-1.0.jar>=
50   * artifact-1.0.pom>central=
51   * artifact-1.0.jar>central=
52   * artifact-1.0.zip>central=
53   * artifact-1.0-classifier.zip>central=
54   * artifact-1.0.pom>my_repo_id=
55   * </pre>
56   * 
57   * @see EnhancedLocalRepositoryManagerFactory
58   */
59  class EnhancedLocalRepositoryManager
60      extends SimpleLocalRepositoryManager
61  {
62  
63      private static final String LOCAL_REPO_ID = "";
64  
65      private final String trackingFilename;
66  
67      private final TrackingFileManager trackingFileManager;
68  
69      public EnhancedLocalRepositoryManager( File basedir, RepositorySystemSession session )
70      {
71          super( basedir, "enhanced" );
72          String filename = ConfigUtils.getString( session, "", "aether.enhancedLocalRepository.trackingFilename" );
73          if ( filename.length() <= 0 || filename.contains( "/" ) || filename.contains( "\\" )
74              || filename.contains( ".." ) )
75          {
76              filename = "_remote.repositories";
77          }
78          trackingFilename = filename;
79          trackingFileManager = new TrackingFileManager();
80      }
81  
82      @Override
83      public EnhancedLocalRepositoryManager setLogger( Logger logger )
84      {
85          super.setLogger( logger );
86          trackingFileManager.setLogger( logger );
87          return this;
88      }
89  
90      @Override
91      public LocalArtifactResult find( RepositorySystemSession session, LocalArtifactRequest request )
92      {
93          String path = getPathForArtifact( request.getArtifact(), false );
94          File file = new File( getRepository().getBasedir(), path );
95  
96          LocalArtifactResult result = new LocalArtifactResult( request );
97  
98          if ( file.isFile() )
99          {
100             result.setFile( file );
101 
102             Properties props = readRepos( file );
103 
104             if ( props.get( getKey( file, LOCAL_REPO_ID ) ) != null )
105             {
106                 // artifact installed into the local repo is always accepted
107                 result.setAvailable( true );
108             }
109             else
110             {
111                 String context = request.getContext();
112                 for ( RemoteRepository repository : request.getRepositories() )
113                 {
114                     if ( props.get( getKey( file, getRepositoryKey( repository, context ) ) ) != null )
115                     {
116                         // artifact downloaded from remote repository is accepted only downloaded from request
117                         // repositories
118                         result.setAvailable( true );
119                         result.setRepository( repository );
120                         break;
121                     }
122                 }
123                 if ( !result.isAvailable() && !isTracked( props, file ) )
124                 {
125                     /*
126                      * NOTE: The artifact is present but not tracked at all, for inter-op with simple local repo, assume
127                      * the artifact was locally installed.
128                      */
129                     result.setAvailable( true );
130                 }
131             }
132         }
133 
134         return result;
135     }
136 
137     @Override
138     public void add( RepositorySystemSession session, LocalArtifactRegistration request )
139     {
140         Collection<String> repositories;
141         if ( request.getRepository() == null )
142         {
143             repositories = Collections.singleton( LOCAL_REPO_ID );
144         }
145         else
146         {
147             repositories = getRepositoryKeys( request.getRepository(), request.getContexts() );
148         }
149         addArtifact( request.getArtifact(), repositories, request.getRepository() == null );
150     }
151 
152     private Collection<String> getRepositoryKeys( RemoteRepository repository, Collection<String> contexts )
153     {
154         Collection<String> keys = new HashSet<String>();
155 
156         if ( contexts != null )
157         {
158             for ( String context : contexts )
159             {
160                 keys.add( getRepositoryKey( repository, context ) );
161             }
162         }
163 
164         return keys;
165     }
166 
167     private void addArtifact( Artifact artifact, Collection<String> repositories, boolean local )
168     {
169         String path = getPathForArtifact( requireNonNull( artifact, "artifact cannot be null" ), local );
170         File file = new File( getRepository().getBasedir(), path );
171         addRepo( file, repositories );
172     }
173 
174     private Properties readRepos( File artifactFile )
175     {
176         File trackingFile = getTrackingFile( artifactFile );
177 
178         Properties props = trackingFileManager.read( trackingFile );
179 
180         return ( props != null ) ? props : new Properties();
181     }
182 
183     private void addRepo( File artifactFile, Collection<String> repositories )
184     {
185         Map<String, String> updates = new HashMap<String, String>();
186         for ( String repository : repositories )
187         {
188             updates.put( getKey( artifactFile, repository ), "" );
189         }
190 
191         File trackingFile = getTrackingFile( artifactFile );
192 
193         trackingFileManager.update( trackingFile, updates );
194     }
195 
196     private File getTrackingFile( File artifactFile )
197     {
198         return new File( artifactFile.getParentFile(), trackingFilename );
199     }
200 
201     private String getKey( File file, String repository )
202     {
203         return file.getName() + '>' + repository;
204     }
205 
206     private boolean isTracked( Properties props, File file )
207     {
208         if ( props != null )
209         {
210             String keyPrefix = file.getName() + '>';
211             for ( Object key : props.keySet() )
212             {
213                 if ( key.toString().startsWith( keyPrefix ) )
214                 {
215                     return true;
216                 }
217             }
218         }
219         return false;
220     }
221 
222 }