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