View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl;
20  
21  import java.nio.file.Files;
22  import java.nio.file.Path;
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 java.util.Objects;
29  import java.util.Properties;
30  import java.util.function.Function;
31  
32  import org.eclipse.aether.RepositorySystemSession;
33  import org.eclipse.aether.artifact.Artifact;
34  import org.eclipse.aether.metadata.Metadata;
35  import org.eclipse.aether.repository.ArtifactRepository;
36  import org.eclipse.aether.repository.LocalArtifactRegistration;
37  import org.eclipse.aether.repository.LocalArtifactRequest;
38  import org.eclipse.aether.repository.LocalArtifactResult;
39  import org.eclipse.aether.repository.RemoteRepository;
40  
41  import static java.util.Objects.requireNonNull;
42  
43  /**
44   * These are implementation details for enhanced local repository manager, subject to change without prior notice.
45   * Repositories from which a cached artifact was resolved are tracked in a properties file named
46   * <code>_remote.repositories</code>, with content key as filename&gt;repo_id and value as empty string. If a file has
47   * been installed in the repository, but not downloaded from a remote repository, it is tracked as empty repository id
48   * and always resolved. For example:
49   *
50   * <pre>
51   * artifact-1.0.pom>=
52   * artifact-1.0.jar>=
53   * artifact-1.0.pom>central=
54   * artifact-1.0.jar>central=
55   * artifact-1.0.zip>central=
56   * artifact-1.0-classifier.zip>central=
57   * artifact-1.0.pom>my_repo_id=
58   * </pre>
59   *
60   * @see EnhancedLocalRepositoryManagerFactory
61   */
62  class EnhancedLocalRepositoryManager extends SimpleLocalRepositoryManager {
63  
64      private static final String LOCAL_REPO_ID = "";
65  
66      private final String trackingFilename;
67  
68      private final TrackingFileManager trackingFileManager;
69  
70      private final LocalPathPrefixComposer localPathPrefixComposer;
71  
72      EnhancedLocalRepositoryManager(
73              Path basedir,
74              LocalPathComposer localPathComposer,
75              Function<ArtifactRepository, String> idToPathSegmentFunction,
76              String trackingFilename,
77              TrackingFileManager trackingFileManager,
78              LocalPathPrefixComposer localPathPrefixComposer) {
79          super(basedir, "enhanced", localPathComposer, idToPathSegmentFunction);
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          if (prefix == null || prefix.isEmpty()) {
87              return artifactPath;
88          }
89          return prefix + '/' + artifactPath;
90      }
91  
92      @Override
93      public String getPathForLocalArtifact(Artifact artifact) {
94          return concatPaths(
95                  localPathPrefixComposer.getPathPrefixForLocalArtifact(artifact),
96                  super.getPathForLocalArtifact(artifact));
97      }
98  
99      @Override
100     public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) {
101         return concatPaths(
102                 localPathPrefixComposer.getPathPrefixForRemoteArtifact(artifact, repository),
103                 super.getPathForRemoteArtifact(artifact, repository, context));
104     }
105 
106     @Override
107     public String getPathForLocalMetadata(Metadata metadata) {
108         return concatPaths(
109                 localPathPrefixComposer.getPathPrefixForLocalMetadata(metadata),
110                 super.getPathForLocalMetadata(metadata));
111     }
112 
113     @Override
114     public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) {
115         return concatPaths(
116                 localPathPrefixComposer.getPathPrefixForRemoteMetadata(metadata, repository),
117                 super.getPathForRemoteMetadata(metadata, repository, context));
118     }
119 
120     @Override
121     public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) {
122         Artifact artifact = request.getArtifact();
123         LocalArtifactResult result = new LocalArtifactResult(request);
124 
125         Path filePath;
126 
127         // Local repository CANNOT have timestamped installed, they are created only during deploy
128         if (Objects.equals(artifact.getVersion(), artifact.getBaseVersion())) {
129             filePath = getAbsolutePathForLocalArtifact(artifact);
130             checkFind(filePath, result);
131         }
132 
133         if (!result.isAvailable()) {
134             for (RemoteRepository repository : request.getRepositories()) {
135                 filePath = getAbsolutePathForRemoteArtifact(artifact, repository, request.getContext());
136 
137                 checkFind(filePath, result);
138 
139                 if (result.isAvailable()) {
140                     break;
141                 }
142             }
143         }
144 
145         return result;
146     }
147 
148     private void checkFind(Path path, LocalArtifactResult result) {
149         if (Files.isRegularFile(path)) {
150             result.setPath(path);
151 
152             Properties props = readRepos(path);
153 
154             if (props.get(getKey(path, LOCAL_REPO_ID)) != null) {
155                 // artifact installed into the local repo is always accepted
156                 result.setAvailable(true);
157             } else {
158                 String context = result.getRequest().getContext();
159                 for (RemoteRepository repository : result.getRequest().getRepositories()) {
160                     if (props.get(getKey(path, getRepositoryKey(repository, context))) != null) {
161                         // artifact downloaded from remote repository is accepted only downloaded from request
162                         // repositories
163                         result.setAvailable(true);
164                         result.setRepository(repository);
165                         break;
166                     }
167                 }
168                 if (!result.isAvailable() && !isTracked(props, path)) {
169                     /*
170                      * NOTE: The artifact is present but not tracked at all, for inter-op with simple local repo, assume
171                      * the artifact was locally installed.
172                      */
173                     result.setAvailable(true);
174                 }
175             }
176         }
177     }
178 
179     @Override
180     public void add(RepositorySystemSession session, LocalArtifactRegistration request) {
181         Collection<String> repositories;
182         if (request.getRepository() == null) {
183             repositories = Collections.singleton(LOCAL_REPO_ID);
184         } else {
185             repositories = getRepositoryKeys(request.getRepository(), request.getContexts());
186         }
187         if (request.getRepository() == null) {
188             addArtifact(request.getArtifact(), repositories, null, null);
189         } else {
190             for (String context : request.getContexts()) {
191                 addArtifact(request.getArtifact(), repositories, request.getRepository(), context);
192             }
193         }
194     }
195 
196     private Collection<String> getRepositoryKeys(RemoteRepository repository, Collection<String> contexts) {
197         Collection<String> keys = new HashSet<>();
198 
199         if (contexts != null) {
200             for (String context : contexts) {
201                 keys.add(getRepositoryKey(repository, context));
202             }
203         }
204 
205         return keys;
206     }
207 
208     private void addArtifact(
209             Artifact artifact, Collection<String> repositories, RemoteRepository repository, String context) {
210         requireNonNull(artifact, "artifact cannot be null");
211         Path file = repository == null
212                 ? getAbsolutePathForLocalArtifact(artifact)
213                 : getAbsolutePathForRemoteArtifact(artifact, repository, context);
214         addRepo(file, repositories);
215     }
216 
217     private Properties readRepos(Path artifactPath) {
218         Path trackingFile = getTrackingFile(artifactPath);
219 
220         Properties props = trackingFileManager.read(trackingFile);
221 
222         return (props != null) ? props : new Properties();
223     }
224 
225     private void addRepo(Path artifactPath, Collection<String> repositories) {
226         Map<String, String> updates = new HashMap<>();
227         for (String repository : repositories) {
228             updates.put(getKey(artifactPath, repository), "");
229         }
230 
231         Path trackingPath = getTrackingFile(artifactPath);
232 
233         trackingFileManager.update(trackingPath, updates);
234     }
235 
236     private Path getTrackingFile(Path artifactPath) {
237         return artifactPath.getParent().resolve(trackingFilename);
238     }
239 
240     private String getKey(Path path, String repository) {
241         return path.getFileName() + ">" + repository;
242     }
243 
244     private boolean isTracked(Properties props, Path path) {
245         if (props != null) {
246             String keyPrefix = path.getFileName() + ">";
247             for (Object key : props.keySet()) {
248                 if (key.toString().startsWith(keyPrefix)) {
249                     return true;
250                 }
251             }
252         }
253         return false;
254     }
255 }