View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl.synccontext.named;
20  
21  import java.util.Collection;
22  import java.util.Comparator;
23  import java.util.TreeSet;
24  
25  import org.eclipse.aether.RepositorySystemSession;
26  import org.eclipse.aether.artifact.Artifact;
27  import org.eclipse.aether.metadata.Metadata;
28  import org.eclipse.aether.named.NamedLockKey;
29  import org.eclipse.aether.util.PathUtils;
30  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
31  
32  import static java.util.Objects.requireNonNull;
33  
34  /**
35   * Artifact GAV {@link NameMapper}, uses artifact and metadata coordinates to name their corresponding locks. Is not
36   * considering local repository, only the artifact coordinates. May use custom prefixes and suffixes and separators,
37   * hence this instance may or may not be filesystem friendly (depends on strings used).
38   * <p>
39   * Note: in earlier Resolver 1.9.x versions this mapper was the default, but it changed to {@link GAECVNameMapper}
40   * in 1.9.25.
41   */
42  public class GAVNameMapper implements NameMapper {
43      protected final boolean fileSystemFriendly;
44  
45      protected final String artifactPrefix;
46  
47      protected final String artifactSuffix;
48  
49      protected final String metadataPrefix;
50  
51      protected final String metadataSuffix;
52  
53      protected final String fieldSeparator;
54  
55      public GAVNameMapper(
56              boolean fileSystemFriendly,
57              String artifactPrefix,
58              String artifactSuffix,
59              String metadataPrefix,
60              String metadataSuffix,
61              String fieldSeparator) {
62          this.fileSystemFriendly = fileSystemFriendly;
63          this.artifactPrefix = requireNonNull(artifactPrefix);
64          this.artifactSuffix = requireNonNull(artifactSuffix);
65          this.metadataPrefix = requireNonNull(metadataPrefix);
66          this.metadataSuffix = requireNonNull(metadataSuffix);
67          this.fieldSeparator = requireNonNull(fieldSeparator);
68      }
69  
70      @Override
71      public boolean isFileSystemFriendly() {
72          return fileSystemFriendly;
73      }
74  
75      @Override
76      public Collection<NamedLockKey> nameLocks(
77              final RepositorySystemSession session,
78              final Collection<? extends Artifact> artifacts,
79              final Collection<? extends Metadata> metadatas) {
80          // Deadlock prevention: https://stackoverflow.com/a/16780988/696632
81          // We must acquire multiple locks always in the same order!
82          TreeSet<NamedLockKey> keys = new TreeSet<>(Comparator.comparing(NamedLockKey::name));
83          if (artifacts != null) {
84              for (Artifact artifact : artifacts) {
85                  keys.add(NamedLockKey.of(getArtifactName(artifact), ArtifactIdUtils.toBaseId(artifact)));
86              }
87          }
88  
89          if (metadatas != null) {
90              for (Metadata metadata : metadatas) {
91                  keys.add(NamedLockKey.of(getMetadataName(metadata), toMetadataId(metadata)));
92              }
93          }
94          return keys;
95      }
96  
97      protected String getArtifactName(Artifact artifact) {
98          return artifactPrefix
99                  + fieldToSegment(artifact.getGroupId())
100                 + fieldSeparator
101                 + fieldToSegment(artifact.getArtifactId())
102                 + fieldSeparator
103                 + fieldToSegment(artifact.getBaseVersion())
104                 + artifactSuffix;
105     }
106 
107     /**
108      * Returns the given coordinate field in a form that is safe to use as (part of) a file name: when this
109      * mapper is {@link #isFileSystemFriendly()}, characters that act as path separators or are otherwise
110      * illegal in file names are replaced via {@link PathUtils#stringToPathSegment(String)}. File-system
111      * friendly names are resolved against the locks base directory by {@link BasedirNameMapper}, so every
112      * field must be a valid path segment.
113      *
114      * @since 2.0.23
115      */
116     protected String fieldToSegment(String field) {
117         return fileSystemFriendly ? PathUtils.stringToPathSegment(field) : field;
118     }
119 
120     protected static final String MAVEN_METADATA = "maven-metadata.xml";
121 
122     protected String getMetadataName(Metadata metadata) {
123         String name = metadataPrefix;
124         if (!metadata.getGroupId().isEmpty()) {
125             name += fieldToSegment(metadata.getGroupId());
126             if (!metadata.getArtifactId().isEmpty()) {
127                 name += fieldSeparator + fieldToSegment(metadata.getArtifactId());
128                 if (!metadata.getVersion().isEmpty()) {
129                     name += fieldSeparator + fieldToSegment(metadata.getVersion());
130                 }
131             }
132             if (!MAVEN_METADATA.equals(metadata.getType())) {
133                 name += fieldSeparator + fieldToSegment(metadata.getType());
134             }
135         } else {
136             if (!MAVEN_METADATA.equals(metadata.getType())) {
137                 name += fieldToSegment(metadata.getType());
138             }
139         }
140         return name + metadataSuffix;
141     }
142 
143     protected String toMetadataId(Metadata metadata) {
144         String name = "";
145         if (!metadata.getGroupId().isEmpty()) {
146             name += metadata.getGroupId();
147             if (!metadata.getArtifactId().isEmpty()) {
148                 name += ":" + metadata.getArtifactId();
149                 if (!metadata.getVersion().isEmpty()) {
150                     name += ":" + metadata.getVersion();
151                 }
152             }
153         }
154         if (!metadata.getType().isEmpty()) {
155             name += (name.isEmpty() ? "" : ":") + metadata.getType();
156         }
157         return name;
158     }
159 
160     /**
161      * @deprecated Use {@link NameMappers} to create name mappers instead.
162      */
163     @Deprecated
164     public static NameMapper gav() {
165         return new GAVNameMapper(false, "artifact:", "", "metadata:", "", ":");
166     }
167 
168     /**
169      * @deprecated Use {@link NameMappers} to create name mappers instead.
170      */
171     @Deprecated
172     public static NameMapper fileGav() {
173         return new GAVNameMapper(true, "artifact~", ".lock", "metadata~", ".lock", "~");
174     }
175 }