001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.util.repository; 020 021import java.nio.file.Files; 022import java.nio.file.Path; 023import java.util.List; 024 025import org.eclipse.aether.ConfigurationProperties; 026import org.eclipse.aether.RepositorySystemSession; 027import org.eclipse.aether.artifact.Artifact; 028import org.eclipse.aether.metadata.Metadata; 029import org.eclipse.aether.repository.LocalArtifactRegistration; 030import org.eclipse.aether.repository.LocalArtifactRequest; 031import org.eclipse.aether.repository.LocalArtifactResult; 032import org.eclipse.aether.repository.LocalMetadataRegistration; 033import org.eclipse.aether.repository.LocalMetadataRequest; 034import org.eclipse.aether.repository.LocalMetadataResult; 035import org.eclipse.aether.repository.LocalRepository; 036import org.eclipse.aether.repository.LocalRepositoryManager; 037import org.eclipse.aether.repository.RemoteRepository; 038import org.eclipse.aether.util.ConfigUtils; 039 040import static java.util.Objects.requireNonNull; 041import static java.util.stream.Collectors.toList; 042 043/** 044 * A local repository manager that chains multiple local repository managers: it directs all the write operations 045 * to chain head, while uses tail for {@link #find(RepositorySystemSession, LocalArtifactRequest)} and 046 * {@link #find(RepositorySystemSession, LocalMetadataRequest)} methods only. Hence, tail is used in resolving 047 * metadata and artifacts with or without (configurable) artifact availability tracking. 048 * <p> 049 * Implementation represents itself using the head local repository manager. 050 * 051 * @since 1.9.2 052 */ 053public final class ChainedLocalRepositoryManager implements LocalRepositoryManager { 054 private static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_AETHER + "chainedLocalRepository."; 055 056 /** 057 * When using chained local repository, should be the artifact availability ignored in tail. 058 * 059 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 060 * @configurationType {@link java.lang.Boolean} 061 * @configurationDefaultValue {@link #DEFAULT_IGNORE_TAIL_AVAILABILITY} 062 */ 063 public static final String CONFIG_PROP_IGNORE_TAIL_AVAILABILITY = CONFIG_PROPS_PREFIX + "ignoreTailAvailability"; 064 065 public static final boolean DEFAULT_IGNORE_TAIL_AVAILABILITY = true; 066 067 private final LocalRepositoryManager head; 068 069 private final List<LocalRepositoryManager> tail; 070 071 private final boolean ignoreTailAvailability; 072 073 private final int installTarget; 074 075 private final int cacheTarget; 076 077 public ChainedLocalRepositoryManager( 078 LocalRepositoryManager head, List<LocalRepositoryManager> tail, boolean ignoreTailAvailability) { 079 this(head, tail, ignoreTailAvailability, 0, 0); 080 } 081 082 public ChainedLocalRepositoryManager( 083 LocalRepositoryManager head, List<LocalRepositoryManager> tail, RepositorySystemSession session) { 084 this( 085 head, 086 tail, 087 ConfigUtils.getBoolean(session, DEFAULT_IGNORE_TAIL_AVAILABILITY, CONFIG_PROP_IGNORE_TAIL_AVAILABILITY), 088 0, 089 0); 090 } 091 092 /** 093 * Warning: this is experimental feature of chained, is not recommended to be used/integrated into plain Maven. 094 * 095 * @param head The head LRM 096 * @param tail The tail LRMs 097 * @param ignoreTailAvailability Whether tail availability should be ignored (usually you do want this) 098 * @param installTarget The installation LRM index, integer from 0 to size of tail. 099 * @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}