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    public ChainedLocalRepositoryManager(
074            LocalRepositoryManager head, List<LocalRepositoryManager> tail, boolean ignoreTailAvailability) {
075        this.head = requireNonNull(head, "head cannot be null");
076        this.tail = requireNonNull(tail, "tail cannot be null");
077        this.ignoreTailAvailability = ignoreTailAvailability;
078    }
079
080    public ChainedLocalRepositoryManager(
081            LocalRepositoryManager head, List<LocalRepositoryManager> tail, RepositorySystemSession session) {
082        this.head = requireNonNull(head, "head cannot be null");
083        this.tail = requireNonNull(tail, "tail cannot be null");
084        this.ignoreTailAvailability =
085                ConfigUtils.getBoolean(session, DEFAULT_IGNORE_TAIL_AVAILABILITY, CONFIG_PROP_IGNORE_TAIL_AVAILABILITY);
086    }
087
088    @Override
089    public LocalRepository getRepository() {
090        return head.getRepository();
091    }
092
093    @Override
094    public String getPathForLocalArtifact(Artifact artifact) {
095        return head.getPathForLocalArtifact(artifact);
096    }
097
098    @Override
099    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}