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      public ChainedLocalRepositoryManager(
74              LocalRepositoryManager head, List<LocalRepositoryManager> tail, boolean ignoreTailAvailability) {
75          this.head = requireNonNull(head, "head cannot be null");
76          this.tail = requireNonNull(tail, "tail cannot be null");
77          this.ignoreTailAvailability = ignoreTailAvailability;
78      }
79  
80      public ChainedLocalRepositoryManager(
81              LocalRepositoryManager head, List<LocalRepositoryManager> tail, RepositorySystemSession session) {
82          this.head = requireNonNull(head, "head cannot be null");
83          this.tail = requireNonNull(tail, "tail cannot be null");
84          this.ignoreTailAvailability =
85                  ConfigUtils.getBoolean(session, DEFAULT_IGNORE_TAIL_AVAILABILITY, CONFIG_PROP_IGNORE_TAIL_AVAILABILITY);
86      }
87  
88      @Override
89      public LocalRepository getRepository() {
90          return head.getRepository();
91      }
92  
93      @Override
94      public String getPathForLocalArtifact(Artifact artifact) {
95          return head.getPathForLocalArtifact(artifact);
96      }
97  
98      @Override
99      public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) {
100         return head.getPathForRemoteArtifact(artifact, repository, context);
101     }
102 
103     @Override
104     public String getPathForLocalMetadata(Metadata metadata) {
105         return head.getPathForLocalMetadata(metadata);
106     }
107 
108     @Override
109     public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) {
110         return head.getPathForRemoteMetadata(metadata, repository, context);
111     }
112 
113     @Override
114     public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) {
115         LocalArtifactResult result = head.find(session, request);
116         if (result.isAvailable()) {
117             return result;
118         }
119 
120         for (LocalRepositoryManager lrm : tail) {
121             result = lrm.find(session, request);
122             if (result.getPath() != null) {
123                 if (ignoreTailAvailability) {
124                     result.setAvailable(true);
125                     return result;
126                 } else if (result.isAvailable()) {
127                     return result;
128                 }
129             }
130         }
131         return new LocalArtifactResult(request);
132     }
133 
134     @Override
135     public void add(RepositorySystemSession session, LocalArtifactRegistration request) {
136         String artifactPath;
137         if (request.getRepository() != null) {
138             artifactPath = getPathForRemoteArtifact(request.getArtifact(), request.getRepository(), "check");
139         } else {
140             artifactPath = getPathForLocalArtifact(request.getArtifact());
141         }
142 
143         Path file = head.getRepository().getBasePath().resolve(artifactPath);
144         if (Files.isRegularFile(file)) {
145             head.add(session, request);
146         }
147     }
148 
149     @Override
150     public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRequest request) {
151         LocalMetadataResult result = head.find(session, request);
152         if (result.getPath() != null) {
153             return result;
154         }
155 
156         for (LocalRepositoryManager lrm : tail) {
157             result = lrm.find(session, request);
158             if (result.getPath() != null) {
159                 return result;
160             }
161         }
162         return new LocalMetadataResult(request);
163     }
164 
165     @Override
166     public void add(RepositorySystemSession session, LocalMetadataRegistration request) {
167         String metadataPath;
168         if (request.getRepository() != null) {
169             metadataPath = getPathForRemoteMetadata(request.getMetadata(), request.getRepository(), "check");
170         } else {
171             metadataPath = getPathForLocalMetadata(request.getMetadata());
172         }
173 
174         Path file = head.getRepository().getBasePath().resolve(metadataPath);
175         if (Files.isRegularFile(file)) {
176             head.add(session, request);
177         }
178     }
179 
180     @Override
181     public String toString() {
182         return head.getRepository().toString()
183                 + tail.stream().map(LocalRepositoryManager::getRepository).collect(toList());
184     }
185 }