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