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.util.ChecksumUtils;
26  import org.eclipse.aether.util.ConfigUtils;
27  import org.slf4j.Logger;
28  import org.slf4j.LoggerFactory;
29  
30  import javax.inject.Inject;
31  import javax.inject.Named;
32  import javax.inject.Singleton;
33  import java.io.File;
34  import java.net.InetAddress;
35  import java.net.UnknownHostException;
36  import java.nio.charset.StandardCharsets;
37  import java.util.Collection;
38  import java.util.Collections;
39  import java.util.Map;
40  import java.util.Objects;
41  
42  import static java.util.stream.Collectors.toList;
43  
44  /**
45   * Discriminating {@link NameMapper}, that wraps another {@link NameMapper} and adds a "discriminator" as prefix, that
46   * makes lock names unique including the hostname and local repository (by default). The discriminator may be passed
47   * in via {@link RepositorySystemSession} or is automatically calculated based on the local hostname and repository
48   * path. The implementation retains order of collection elements as it got it from
49   * {@link NameMapper#nameLocks(RepositorySystemSession, Collection, Collection)} method.
50   * <p>
51   * The default setup wraps {@link GAVNameMapper}, but manually may be created any instance needed.
52   */
53  @Singleton
54  @Named( DiscriminatingNameMapper.NAME )
55  public class DiscriminatingNameMapper implements NameMapper
56  {
57      public static final String NAME = "discriminating";
58  
59      /**
60       * Configuration property to pass in discriminator
61       */
62      private static final String CONFIG_PROP_DISCRIMINATOR = "aether.syncContext.named.discriminating.discriminator";
63  
64      /**
65       * Configuration property to pass in hostname
66       */
67      private static final String CONFIG_PROP_HOSTNAME = "aether.syncContext.named.discriminating.hostname";
68  
69      private static final String DEFAULT_DISCRIMINATOR_DIGEST = "da39a3ee5e6b4b0d3255bfef95601890afd80709";
70  
71      private static final String DEFAULT_HOSTNAME = "localhost";
72  
73      private static final Logger LOGGER = LoggerFactory.getLogger( DiscriminatingNameMapper.class );
74  
75      private final NameMapper nameMapper;
76  
77      private final String hostname;
78  
79      @Inject
80      public DiscriminatingNameMapper( @Named( GAVNameMapper.NAME ) final NameMapper nameMapper )
81      {
82          this.nameMapper = Objects.requireNonNull( nameMapper );
83          this.hostname = getHostname();
84      }
85  
86      @Override
87      public Collection<String> nameLocks( final RepositorySystemSession session,
88                                           final Collection<? extends Artifact> artifacts,
89                                           final Collection<? extends Metadata> metadatas )
90      {
91          String discriminator = createDiscriminator( session );
92          return nameMapper.nameLocks( session, artifacts, metadatas ).stream().map( s -> discriminator + ":" + s )
93                           .collect( toList() );
94      }
95  
96      private String getHostname()
97      {
98          try
99          {
100             return InetAddress.getLocalHost().getHostName();
101         }
102         catch ( UnknownHostException e )
103         {
104             LOGGER.warn( "Failed to get hostname, using '{}'", DEFAULT_HOSTNAME, e );
105             return DEFAULT_HOSTNAME;
106         }
107     }
108 
109     private String createDiscriminator( final RepositorySystemSession session )
110     {
111         String discriminator = ConfigUtils.getString( session, null, CONFIG_PROP_DISCRIMINATOR );
112 
113         if ( discriminator == null || discriminator.isEmpty() )
114         {
115             String hostname = ConfigUtils.getString( session, this.hostname, CONFIG_PROP_HOSTNAME );
116             File basedir = session.getLocalRepository().getBasedir();
117             discriminator = hostname + ":" + basedir;
118             try
119             {
120                 Map<String, Object> checksums = ChecksumUtils
121                         .calc( discriminator.getBytes( StandardCharsets.UTF_8 ), Collections.singletonList( "SHA-1" ) );
122                 Object checksum = checksums.get( "SHA-1" );
123 
124                 if ( checksum instanceof Exception )
125                 {
126                     throw (Exception) checksum;
127                 }
128 
129                 return String.valueOf( checksum );
130             }
131             catch ( Exception e )
132             {
133                 LOGGER.warn( "Failed to calculate discriminator digest, using '{}'", DEFAULT_DISCRIMINATOR_DIGEST, e );
134                 return DEFAULT_DISCRIMINATOR_DIGEST;
135             }
136         }
137         return discriminator;
138     }
139 }