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