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         String path;
123         Path filePath;
124 
125         // Local repository CANNOT have timestamped installed, they are created only during deploy
126         if (Objects.equals(artifact.getVersion(), artifact.getBaseVersion())) {
127             path = getPathForLocalArtifact(artifact);
128             filePath = getRepository().getBasePath().resolve(path);
129             checkFind(filePath, result);
130         }
131 
132         if (!result.isAvailable()) {
133             for (RemoteRepository repository : request.getRepositories()) {
134                 path = getPathForRemoteArtifact(artifact, repository, request.getContext());
135                 filePath = getRepository().getBasePath().resolve(path);
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         String path = repository == null
212                 ? getPathForLocalArtifact(artifact)
213                 : getPathForRemoteArtifact(artifact, repository, context);
214         Path file = getRepository().getBasePath().resolve(path);
215         addRepo(file, repositories);
216     }
217 
218     private Properties readRepos(Path artifactPath) {
219         Path trackingFile = getTrackingFile(artifactPath);
220 
221         Properties props = trackingFileManager.read(trackingFile);
222 
223         return (props != null) ? props : new Properties();
224     }
225 
226     private void addRepo(Path artifactPath, Collection<String> repositories) {
227         Map<String, String> updates = new HashMap<>();
228         for (String repository : repositories) {
229             updates.put(getKey(artifactPath, repository), "");
230         }
231 
232         Path trackingPath = getTrackingFile(artifactPath);
233 
234         trackingFileManager.update(trackingPath, updates);
235     }
236 
237     private Path getTrackingFile(Path artifactPath) {
238         return artifactPath.getParent().resolve(trackingFilename);
239     }
240 
241     private String getKey(Path path, String repository) {
242         return path.getFileName() + ">" + repository;
243     }
244 
245     private boolean isTracked(Properties props, Path path) {
246         if (props != null) {
247             String keyPrefix = path.getFileName() + ">";
248             for (Object key : props.keySet()) {
249                 if (key.toString().startsWith(keyPrefix)) {
250                     return true;
251                 }
252             }
253         }
254         return false;
255     }
256 }