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.io.File;
022import java.nio.file.Files;
023import java.nio.file.Path;
024import java.util.List;
025
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    public static final String IGNORE_TAIL_AVAILABILITY = "aether.chainedLocalRepository.ignoreTailAvailability";
055
056    private static final boolean DEFAULT_IGNORE_TAIL_AVAILABILITY = true;
057
058    private final LocalRepositoryManager head;
059
060    private final List<LocalRepositoryManager> tail;
061
062    private final boolean ignoreTailAvailability;
063
064    public ChainedLocalRepositoryManager(
065            LocalRepositoryManager head, List<LocalRepositoryManager> tail, boolean ignoreTailAvailability) {
066        this.head = requireNonNull(head, "head cannot be null");
067        this.tail = requireNonNull(tail, "tail cannot be null");
068        this.ignoreTailAvailability = ignoreTailAvailability;
069    }
070
071    public ChainedLocalRepositoryManager(
072            LocalRepositoryManager head, List<LocalRepositoryManager> tail, RepositorySystemSession session) {
073        this.head = requireNonNull(head, "head cannot be null");
074        this.tail = requireNonNull(tail, "tail cannot be null");
075        this.ignoreTailAvailability =
076                ConfigUtils.getBoolean(session, DEFAULT_IGNORE_TAIL_AVAILABILITY, IGNORE_TAIL_AVAILABILITY);
077    }
078
079    @Override
080    public LocalRepository getRepository() {
081        return head.getRepository();
082    }
083
084    @Override
085    public String getPathForLocalArtifact(Artifact artifact) {
086        return head.getPathForLocalArtifact(artifact);
087    }
088
089    @Override
090    public String getPathForRemoteArtifact(Artifact artifact, RemoteRepository repository, String context) {
091        return head.getPathForRemoteArtifact(artifact, repository, context);
092    }
093
094    @Override
095    public String getPathForLocalMetadata(Metadata metadata) {
096        return head.getPathForLocalMetadata(metadata);
097    }
098
099    @Override
100    public String getPathForRemoteMetadata(Metadata metadata, RemoteRepository repository, String context) {
101        return head.getPathForRemoteMetadata(metadata, repository, context);
102    }
103
104    @Override
105    public LocalArtifactResult find(RepositorySystemSession session, LocalArtifactRequest request) {
106        LocalArtifactResult result = head.find(session, request);
107        if (result.isAvailable()) {
108            return result;
109        }
110
111        for (LocalRepositoryManager lrm : tail) {
112            result = lrm.find(session, request);
113            if (result.getFile() != null) {
114                if (ignoreTailAvailability) {
115                    result.setAvailable(true);
116                    return result;
117                } else if (result.isAvailable()) {
118                    return result;
119                }
120            }
121        }
122        return new LocalArtifactResult(request);
123    }
124
125    @Override
126    public void add(RepositorySystemSession session, LocalArtifactRegistration request) {
127        String artifactPath;
128        if (request.getRepository() != null) {
129            artifactPath = getPathForRemoteArtifact(request.getArtifact(), request.getRepository(), "check");
130        } else {
131            artifactPath = getPathForLocalArtifact(request.getArtifact());
132        }
133
134        Path file = new File(head.getRepository().getBasedir(), artifactPath).toPath();
135        if (Files.isRegularFile(file)) {
136            head.add(session, request);
137        }
138    }
139
140    @Override
141    public LocalMetadataResult find(RepositorySystemSession session, LocalMetadataRequest request) {
142        LocalMetadataResult result = head.find(session, request);
143        if (result.getFile() != null) {
144            return result;
145        }
146
147        for (LocalRepositoryManager lrm : tail) {
148            result = lrm.find(session, request);
149            if (result.getFile() != null) {
150                return result;
151            }
152        }
153        return new LocalMetadataResult(request);
154    }
155
156    @Override
157    public void add(RepositorySystemSession session, LocalMetadataRegistration request) {
158        String metadataPath;
159        if (request.getRepository() != null) {
160            metadataPath = getPathForRemoteMetadata(request.getMetadata(), request.getRepository(), "check");
161        } else {
162            metadataPath = getPathForLocalMetadata(request.getMetadata());
163        }
164
165        Path file = new File(head.getRepository().getBasedir(), metadataPath).toPath();
166        if (Files.isRegularFile(file)) {
167            head.add(session, request);
168        }
169    }
170
171    @Override
172    public String toString() {
173        return head.getRepository().toString()
174                + tail.stream().map(LocalRepositoryManager::getRepository).collect(toList());
175    }
176}