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