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.util.repository;
20  
21  import java.nio.file.Files;
22  import java.nio.file.Path;
23  import java.util.List;
24  
25  import org.eclipse.aether.ConfigurationProperties;
26  import org.eclipse.aether.RepositorySystemSession;
27  import org.eclipse.aether.artifact.Artifact;
28  import org.eclipse.aether.metadata.Metadata;
29  import org.eclipse.aether.repository.LocalArtifactRegistration;
30  import org.eclipse.aether.repository.LocalArtifactRequest;
31  import org.eclipse.aether.repository.LocalArtifactResult;
32  import org.eclipse.aether.repository.LocalMetadataRegistration;
33  import org.eclipse.aether.repository.LocalMetadataRequest;
34  import org.eclipse.aether.repository.LocalMetadataResult;
35  import org.eclipse.aether.repository.LocalRepository;
36  import org.eclipse.aether.repository.LocalRepositoryManager;
37  import org.eclipse.aether.repository.RemoteRepository;
38  import org.eclipse.aether.util.ConfigUtils;
39  
40  import static java.util.Objects.requireNonNull;
41  import static java.util.stream.Collectors.toList;
42  
43  /**
44   * A local repository manager that chains multiple local repository managers: it directs all the write operations
45   * to chain head, while uses tail for {@link #find(RepositorySystemSession, LocalArtifactRequest)} and
46   * {@link #find(RepositorySystemSession, LocalMetadataRequest)} methods only. Hence, tail is used in resolving
47   * metadata and artifacts with or without (configurable) artifact availability tracking.
48   * <p>
49   * Implementation represents itself using the head local repository manager.
50   *
51   * @since 1.9.2
52   */
53  public final class ChainedLocalRepositoryManager implements LocalRepositoryManager {
54      private static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "chainedLocalRepository.";
55  
56      /**
57       * When using chained local repository, should be the artifact availability ignored in tail.
58       *
59       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
60       * @configurationType {@link java.lang.Boolean}
61       * @configurationDefaultValue {@link #DEFAULT_IGNORE_TAIL_AVAILABILITY}
62       */
63      public static final String CONFIG_PROP_IGNORE_TAIL_AVAILABILITY = CONFIG_PROPS_PREFIX + "ignoreTailAvailability";
64  
65      public static final boolean DEFAULT_IGNORE_TAIL_AVAILABILITY = true;
66  
67      private final LocalRepositoryManager head;
68  
69      private final List<LocalRepositoryManager> tail;
70  
71      private final boolean ignoreTailAvailability;
72  
73      private final int installTarget;
74  
75      private final int cacheTarget;
76  
77      public ChainedLocalRepositoryManager(
78              LocalRepositoryManager head, List<LocalRepositoryManager> tail, boolean ignoreTailAvailability) {
79          this(head, tail, ignoreTailAvailability, 0, 0);
80      }
81  
82      public ChainedLocalRepositoryManager(
83              LocalRepositoryManager head, List<LocalRepositoryManager> tail, RepositorySystemSession session) {
84          this(
85                  head,
86                  tail,
87                  ConfigUtils.getBoolean(session, DEFAULT_IGNORE_TAIL_AVAILABILITY, CONFIG_PROP_IGNORE_TAIL_AVAILABILITY),
88                  0,
89                  0);
90      }
91  
92      /**
93       * Warning: this is experimental feature of chained, is not recommended to be used/integrated into plain Maven.
94       *
95       * @param head The head LRM
96       * @param tail The tail LRMs
97       * @param ignoreTailAvailability Whether tail availability should be ignored (usually you do want this)
98       * @param installTarget The installation LRM index, integer from 0 to size of tail.
99       * @param cacheTarget The cache LRM index, integer from 0 to size of tail.
100      * @since 2.0.5
101      */
102     public ChainedLocalRepositoryManager(
103             LocalRepositoryManager head,
104             List<LocalRepositoryManager> tail,
105             boolean ignoreTailAvailability,
106             int installTarget,
107             int cacheTarget) {
108         this.head = requireNonNull(head, "head cannot be null");
109         this.tail = requireNonNull(tail, "tail cannot be null");
110         this.ignoreTailAvailability = ignoreTailAvailability;
111         if (installTarget < 0 || installTarget > tail.size()) {
112             throw new IllegalArgumentException("Illegal installTarget value");
113         }
114         this.installTarget = installTarget;
115         if (cacheTarget < 0 || cacheTarget > tail.size()) {
116             throw new IllegalArgumentException("Illegal cacheTarget value");
117         }
118         this.cacheTarget = cacheTarget;
119     }
120 
121     @Override
122     public LocalRepository getRepository() {
123         return head.getRepository();
124     }
125 
126     private LocalRepositoryManager getInstallTarget() {
127         if (installTarget == 0) {
128             return head;
129         } else {
130             return tail.get(installTarget - 1);
131         }
132     }
133 
134     private LocalRepositoryManager getCacheTarget() {
135         if (cacheTarget == 0) {
136             return head;
137         } else {
138             return tail.get(cacheTarget - 1);
139         }
140     }
141 
142     @Override
143     public Path getAbsolutePathForLocalArtifact(Artifact artifact) {
144         return getInstallTarget().getAbsolutePathForLocalArtifact(artifact);
145     }
146 
147     @Override
148     public Path getAbsolutePathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) {
149         return getCacheTarget().getAbsolutePathForRemoteArtifact(artifact, repository, context);
150     }
151 
152     @Override
153     public Path getAbsolutePathForLocalMetadata(Metadata metadata) {
154         return getInstallTarget().getAbsolutePathForLocalMetadata(metadata);
155     }
156 
157     @Override
158     public Path getAbsolutePathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) {
159         return getCacheTarget().getAbsolutePathForRemoteMetadata(metadata, repository, context);
160     }
161 
162     @Override
163     public String getPathForLocalArtifact(Artifact artifact) {
164         return getInstallTarget().getPathForLocalArtifact(artifact);
165     }
166 
167     @Override
168     public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) {
169         return getCacheTarget().getPathForRemoteArtifact(artifact, repository, context);
170     }
171 
172     @Override
173     public String getPathForLocalMetadata(Metadata metadata) {
174         return getInstallTarget().getPathForLocalMetadata(metadata);
175     }
176 
177     @Override
178     public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) {
179         return getCacheTarget().getPathForRemoteMetadata(metadata, repository, context);
180     }
181 
182     @Override
183     public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) {
184         LocalArtifactResult result = head.find(session, request);
185         if (result.isAvailable()) {
186             return result;
187         }
188 
189         for (LocalRepositoryManager lrm : tail) {
190             result = lrm.find(session, request);
191             if (result.getPath() != null) {
192                 if (ignoreTailAvailability) {
193                     result.setAvailable(true);
194                     return result;
195                 } else if (result.isAvailable()) {
196                     return result;
197                 }
198             }
199         }
200         return new LocalArtifactResult(request);
201     }
202 
203     @Override
204     public void add(RepositorySystemSession session, LocalArtifactRegistration request) {
205         String artifactPath;
206         LocalRepositoryManager target;
207         if (request.getRepository() != null) {
208             artifactPath = getPathForRemoteArtifact(request.getArtifact(), request.getRepository(), "check");
209             target = getCacheTarget();
210         } else {
211             artifactPath = getPathForLocalArtifact(request.getArtifact());
212             target = getInstallTarget();
213         }
214         Path file = target.getRepository().getBasePath().resolve(artifactPath);
215         if (Files.isRegularFile(file)) {
216             target.add(session, request);
217         }
218     }
219 
220     @Override
221     public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRequest request) {
222         LocalMetadataResult result = head.find(session, request);
223         if (result.getPath() != null) {
224             return result;
225         }
226 
227         for (LocalRepositoryManager lrm : tail) {
228             result = lrm.find(session, request);
229             if (result.getPath() != null) {
230                 return result;
231             }
232         }
233         return new LocalMetadataResult(request);
234     }
235 
236     @Override
237     public void add(RepositorySystemSession session, LocalMetadataRegistration request) {
238         String metadataPath;
239         LocalRepositoryManager target;
240         if (request.getRepository() != null) {
241             metadataPath = getPathForRemoteMetadata(request.getMetadata(), request.getRepository(), "check");
242             target = getCacheTarget();
243         } else {
244             metadataPath = getPathForLocalMetadata(request.getMetadata());
245             target = getInstallTarget();
246         }
247 
248         Path file = target.getRepository().getBasePath().resolve(metadataPath);
249         if (Files.isRegularFile(file)) {
250             target.add(session, request);
251         }
252     }
253 
254     @Override
255     public String toString() {
256         return head.getRepository().toString()
257                 + tail.stream().map(LocalRepositoryManager::getRepository).collect(toList());
258     }
259 }