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