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.internal.impl.synccontext.named;
020
021import java.util.Collection;
022import java.util.Comparator;
023import java.util.TreeSet;
024
025import org.eclipse.aether.RepositorySystemSession;
026import org.eclipse.aether.artifact.Artifact;
027import org.eclipse.aether.metadata.Metadata;
028import org.eclipse.aether.named.NamedLockKey;
029import org.eclipse.aether.util.PathUtils;
030
031import static java.util.Objects.requireNonNull;
032
033/**
034 * Artifact GAV {@link NameMapper}, uses artifact and metadata coordinates to name their corresponding locks. Is not
035 * considering local repository, only the artifact coordinates. May use custom prefixes and suffixes and separators,
036 * hence this instance may or may not be filesystem friendly (depends on strings used).
037 */
038public class GAVNameMapper implements NameMapper {
039    private final boolean fileSystemFriendly;
040
041    private final String artifactPrefix;
042
043    private final String artifactSuffix;
044
045    private final String metadataPrefix;
046
047    private final String metadataSuffix;
048
049    private final String fieldSeparator;
050
051    public GAVNameMapper(
052            boolean fileSystemFriendly,
053            String artifactPrefix,
054            String artifactSuffix,
055            String metadataPrefix,
056            String metadataSuffix,
057            String fieldSeparator) {
058        this.fileSystemFriendly = fileSystemFriendly;
059        this.artifactPrefix = requireNonNull(artifactPrefix);
060        this.artifactSuffix = requireNonNull(artifactSuffix);
061        this.metadataPrefix = requireNonNull(metadataPrefix);
062        this.metadataSuffix = requireNonNull(metadataSuffix);
063        this.fieldSeparator = requireNonNull(fieldSeparator);
064    }
065
066    @Override
067    public boolean isFileSystemFriendly() {
068        return fileSystemFriendly;
069    }
070
071    @Override
072    public Collection<NamedLockKey> nameLocks(
073            final RepositorySystemSession session,
074            final Collection<? extends Artifact> artifacts,
075            final Collection<? extends Metadata> metadatas) {
076        // Deadlock prevention: https://stackoverflow.com/a/16780988/696632
077        // We must acquire multiple locks always in the same order!
078        TreeSet<NamedLockKey> keys = new TreeSet<>(Comparator.comparing(NamedLockKey::name));
079        if (artifacts != null) {
080            for (Artifact artifact : artifacts) {
081                keys.add(NamedLockKey.of(
082                        getArtifactName(artifact, artifactPrefix, fieldSeparator, artifactSuffix),
083                        getArtifactName(artifact, "", ":", "")));
084            }
085        }
086
087        if (metadatas != null) {
088            for (Metadata metadata : metadatas) {
089                keys.add(NamedLockKey.of(
090                        getMetadataName(metadata, metadataPrefix, fieldSeparator, metadataSuffix),
091                        getMetadataName(metadata, "", ":", "")));
092            }
093        }
094        return keys;
095    }
096
097    private static String getArtifactName(Artifact artifact, String prefix, String separator, String suffix) {
098        return prefix
099                + artifact.getGroupId()
100                + separator
101                + artifact.getArtifactId()
102                + separator
103                + artifact.getBaseVersion()
104                + suffix;
105    }
106
107    private static final String MAVEN_METADATA = "maven-metadata.xml";
108
109    private static String getMetadataName(Metadata metadata, String prefix, String separator, String suffix) {
110        String name = prefix;
111        if (!metadata.getGroupId().isEmpty()) {
112            name += metadata.getGroupId();
113            if (!metadata.getArtifactId().isEmpty()) {
114                name += separator + metadata.getArtifactId();
115                if (!metadata.getVersion().isEmpty()) {
116                    name += separator + metadata.getVersion();
117                }
118            }
119            if (!MAVEN_METADATA.equals(metadata.getType())) {
120                name += separator + PathUtils.stringToPathSegment(metadata.getType());
121            }
122        } else {
123            if (!MAVEN_METADATA.equals(metadata.getType())) {
124                name += PathUtils.stringToPathSegment(metadata.getType());
125            }
126        }
127        return name + suffix;
128    }
129
130    public static NameMapper gav() {
131        return new GAVNameMapper(false, "artifact:", "", "metadata:", "", ":");
132    }
133
134    public static NameMapper fileGav() {
135        return new GAVNameMapper(true, "artifact~", ".lock", "metadata~", ".lock", "~");
136    }
137}