View Javadoc
1   package org.eclipse.aether.internal.impl.synccontext.named;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.eclipse.aether.RepositorySystemSession;
23  import org.eclipse.aether.artifact.Artifact;
24  import org.eclipse.aether.metadata.Metadata;
25  import org.eclipse.aether.named.support.FileSystemFriendly;
26  
27  import javax.inject.Named;
28  import javax.inject.Singleton;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.UncheckedIOException;
33  import java.nio.file.Path;
34  import java.util.Collection;
35  import java.util.TreeSet;
36  import java.util.concurrent.ConcurrentHashMap;
37  import java.util.concurrent.ConcurrentMap;
38  
39  /**
40   * A {@link NameMapper} that creates same name mapping as Takari Local Repository does, with
41   * {@code baseDir} (local repo). Part of code blatantly copies parts of the Takari
42   * {@code LockingSyncContext}.
43   *
44   * @see <a href="https://github.com/takari/takari-local-repository/blob/24133e50a0478dccb5620ac2f2255187608f165b/src/main/java/io/takari/aether/concurrency/LockingSyncContext.java">Takari
45   * LockingSyncContext.java</a>
46   */
47  @Singleton
48  @Named( FileGAVNameMapper.NAME )
49  public class FileGAVNameMapper
50      implements NameMapper, FileSystemFriendly
51  {
52      public static final String NAME = "file-gav";
53  
54      private static final String LOCK_SUFFIX = ".resolverlock";
55  
56      private static final char SEPARATOR = '~';
57  
58      private final ConcurrentMap<String, Path> baseDirs;
59  
60      public FileGAVNameMapper()
61      {
62          this.baseDirs = new ConcurrentHashMap<>();
63      }
64  
65      @Override
66      public TreeSet<String> nameLocks( final RepositorySystemSession session,
67                                        final Collection<? extends Artifact> artifacts,
68                                        final Collection<? extends Metadata> metadatas )
69      {
70          File localRepositoryBasedir = session.getLocalRepository().getBasedir();
71          // here we abuse concurrent hash map to make sure costly getCanonicalFile is invoked only once
72          Path baseDir = baseDirs.computeIfAbsent(
73              localRepositoryBasedir.getPath(), k ->
74              {
75                  try
76                  {
77                      return new File( localRepositoryBasedir, ".locks" ).getCanonicalFile().toPath();
78                  }
79                  catch ( IOException e )
80                  {
81                      throw new UncheckedIOException( e );
82                  }
83              }
84          );
85  
86          TreeSet<String> paths = new TreeSet<>();
87          if ( artifacts != null )
88          {
89              for ( Artifact artifact : artifacts )
90              {
91                  paths.add( getPath( baseDir, artifact ) + LOCK_SUFFIX );
92              }
93          }
94          if ( metadatas != null )
95          {
96              for ( Metadata metadata : metadatas )
97              {
98                  paths.add( getPath( baseDir, metadata ) + LOCK_SUFFIX );
99              }
100         }
101         return paths;
102     }
103 
104     private String getPath( final Path baseDir, final Artifact artifact )
105     {
106         // NOTE: Don't use LRM.getPath*() as those paths could be different across processes, e.g. due to staging LRMs.
107         String path = artifact.getGroupId()
108             + SEPARATOR + artifact.getArtifactId()
109             + SEPARATOR + artifact.getBaseVersion();
110         return baseDir.resolve( path ).toAbsolutePath().toString();
111     }
112 
113     private String getPath( final Path baseDir, final Metadata metadata )
114     {
115         // NOTE: Don't use LRM.getPath*() as those paths could be different across processes, e.g. due to staging.
116         String path = "";
117         if ( metadata.getGroupId().length() > 0 )
118         {
119             path += metadata.getGroupId();
120             if ( metadata.getArtifactId().length() > 0 )
121             {
122                 path += SEPARATOR + metadata.getArtifactId();
123                 if ( metadata.getVersion().length() > 0 )
124                 {
125                     path += SEPARATOR + metadata.getVersion();
126                 }
127             }
128         }
129         return baseDir.resolve( path ).toAbsolutePath().toString();
130     }
131 }