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.io.File;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.HashSet;
26  import java.util.Map;
27  import java.util.Objects;
28  import java.util.Properties;
29  
30  import org.eclipse.aether.RepositorySystemSession;
31  import org.eclipse.aether.artifact.Artifact;
32  import org.eclipse.aether.metadata.Metadata;
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  
38  import static java.util.Objects.requireNonNull;
39  
40  /**
41   * These are implementation details for enhanced local repository manager, subject to change without prior notice.
42   * Repositories from which a cached artifact was resolved are tracked in a properties file named
43   * <code>_remote.repositories</code>, with content key as filename&gt;repo_id and value as empty string. If a file has
44   * been installed in the repository, but not downloaded from a remote repository, it is tracked as empty repository id
45   * and always resolved. For example:
46   *
47   * <pre>
48   * artifact-1.0.pom>=
49   * artifact-1.0.jar>=
50   * artifact-1.0.pom>central=
51   * artifact-1.0.jar>central=
52   * artifact-1.0.zip>central=
53   * artifact-1.0-classifier.zip>central=
54   * artifact-1.0.pom>my_repo_id=
55   * </pre>
56   *
57   * @see EnhancedLocalRepositoryManagerFactory
58   */
59  class EnhancedLocalRepositoryManager extends SimpleLocalRepositoryManager {
60  
61      private static final String LOCAL_REPO_ID = "";
62  
63      private final String trackingFilename;
64  
65      private final TrackingFileManager trackingFileManager;
66  
67      private final LocalPathPrefixComposer localPathPrefixComposer;
68  
69      EnhancedLocalRepositoryManager(
70              File basedir,
71              LocalPathComposer localPathComposer,
72              String trackingFilename,
73              TrackingFileManager trackingFileManager,
74              LocalPathPrefixComposer localPathPrefixComposer) {
75          super(basedir, "enhanced", localPathComposer);
76          this.trackingFilename = requireNonNull(trackingFilename);
77          this.trackingFileManager = requireNonNull(trackingFileManager);
78          this.localPathPrefixComposer = requireNonNull(localPathPrefixComposer);
79      }
80  
81      private String concatPaths(String prefix, String artifactPath) {
82          if (prefix == null || prefix.isEmpty()) {
83              return artifactPath;
84          }
85          return prefix + '/' + artifactPath;
86      }
87  
88      @Override
89      public String getPathForLocalArtifact(Artifact artifact) {
90          return concatPaths(
91                  localPathPrefixComposer.getPathPrefixForLocalArtifact(artifact),
92                  super.getPathForLocalArtifact(artifact));
93      }
94  
95      @Override
96      public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) {
97          return concatPaths(
98                  localPathPrefixComposer.getPathPrefixForRemoteArtifact(artifact, repository),
99                  super.getPathForRemoteArtifact(artifact, repository, context));
100     }
101 
102     @Override
103     public String getPathForLocalMetadata(Metadata metadata) {
104         return concatPaths(
105                 localPathPrefixComposer.getPathPrefixForLocalMetadata(metadata),
106                 super.getPathForLocalMetadata(metadata));
107     }
108 
109     @Override
110     public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) {
111         return concatPaths(
112                 localPathPrefixComposer.getPathPrefixForRemoteMetadata(metadata, repository),
113                 super.getPathForRemoteMetadata(metadata, repository, context));
114     }
115 
116     @Override
117     public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) {
118         Artifact artifact = request.getArtifact();
119         LocalArtifactResult result = new LocalArtifactResult(request);
120 
121         String path;
122         File file;
123 
124         // Local repository CANNOT have timestamped installed, they are created only during deploy
125         if (Objects.equals(artifact.getVersion(), artifact.getBaseVersion())) {
126             path = getPathForLocalArtifact(artifact);
127             file = new File(getRepository().getBasedir(), path);
128             checkFind(file, result);
129         }
130 
131         if (!result.isAvailable()) {
132             for (RemoteRepository repository : request.getRepositories()) {
133                 path = getPathForRemoteArtifact(artifact, repository, request.getContext());
134                 file = new File(getRepository().getBasedir(), path);
135 
136                 checkFind(file, result);
137 
138                 if (result.isAvailable()) {
139                     break;
140                 }
141             }
142         }
143 
144         return result;
145     }
146 
147     private void checkFind(File file, LocalArtifactResult result) {
148         if (file.isFile()) {
149             result.setFile(file);
150 
151             Properties props = readRepos(file);
152 
153             if (props.get(getKey(file, 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(file, 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, file)) {
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         String path = repository == null
211                 ? getPathForLocalArtifact(artifact)
212                 : getPathForRemoteArtifact(artifact, repository, context);
213         File file = new File(getRepository().getBasedir(), path);
214         addRepo(file, repositories);
215     }
216 
217     private Properties readRepos(File artifactFile) {
218         File trackingFile = getTrackingFile(artifactFile);
219 
220         Properties props = trackingFileManager.read(trackingFile);
221 
222         return (props != null) ? props : new Properties();
223     }
224 
225     private void addRepo(File artifactFile, Collection<String> repositories) {
226         Map<String, String> updates = new HashMap<>();
227         for (String repository : repositories) {
228             updates.put(getKey(artifactFile, repository), "");
229         }
230 
231         File trackingFile = getTrackingFile(artifactFile);
232 
233         trackingFileManager.update(trackingFile, updates);
234     }
235 
236     private File getTrackingFile(File artifactFile) {
237         return new File(artifactFile.getParentFile(), trackingFilename);
238     }
239 
240     private String getKey(File file, String repository) {
241         return file.getName() + '>' + repository;
242     }
243 
244     private boolean isTracked(Properties props, File file) {
245         if (props != null) {
246             String keyPrefix = file.getName() + '>';
247             for (Object key : props.keySet()) {
248                 if (key.toString().startsWith(keyPrefix)) {
249                     return true;
250                 }
251             }
252         }
253         return false;
254     }
255 }