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.SyncContext;
24  import org.eclipse.aether.artifact.Artifact;
25  import org.eclipse.aether.metadata.Metadata;
26  import org.eclipse.aether.named.NamedLock;
27  import org.eclipse.aether.named.NamedLockFactory;
28  import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
29  import org.eclipse.aether.util.ConfigUtils;
30  
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import java.util.ArrayDeque;
35  import java.util.Collection;
36  import java.util.Deque;
37  import java.util.Objects;
38  import java.util.concurrent.TimeUnit;
39  
40  /**
41   * Adapter to adapt {@link NamedLockFactory} and {@link NamedLock} to {@link SyncContext}.
42   */
43  public final class NamedLockFactoryAdapter
44  {
45      public static final String TIME_KEY = "aether.syncContext.named.time";
46  
47      public static final long DEFAULT_TIME = 30L;
48  
49      public static final String TIME_UNIT_KEY = "aether.syncContext.named.time.unit";
50  
51      public static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
52  
53      private final NameMapper nameMapper;
54  
55      private final NamedLockFactory namedLockFactory;
56  
57      public NamedLockFactoryAdapter( final NameMapper nameMapper, final NamedLockFactory namedLockFactory )
58      {
59          this.nameMapper = Objects.requireNonNull( nameMapper );
60          this.namedLockFactory = Objects.requireNonNull( namedLockFactory );
61          // TODO: this is ad-hoc "validation", experimental and likely to change
62          if ( this.namedLockFactory instanceof FileLockNamedLockFactory
63                  && !this.nameMapper.isFileSystemFriendly() )
64          {
65              throw new IllegalArgumentException(
66                      "Misconfiguration: FileLockNamedLockFactory lock factory requires FS friendly NameMapper"
67              );
68          }
69      }
70  
71      public SyncContext newInstance( final RepositorySystemSession session, final boolean shared )
72      {
73          return new AdaptedLockSyncContext( session, shared, nameMapper, namedLockFactory );
74      }
75  
76      public void shutdown()
77      {
78          namedLockFactory.shutdown();
79      }
80  
81      public String toString()
82      {
83          return getClass().getSimpleName()
84                  + "(nameMapper=" + nameMapper
85                  + ", namedLockFactory=" + namedLockFactory
86                  + ")";
87      }
88  
89      private static class AdaptedLockSyncContext implements SyncContext
90      {
91          private static final Logger LOGGER = LoggerFactory.getLogger( AdaptedLockSyncContext.class );
92  
93          private final RepositorySystemSession session;
94  
95          private final boolean shared;
96  
97          private final NameMapper lockNaming;
98  
99          private final NamedLockFactory namedLockFactory;
100 
101         private final long time;
102 
103         private final TimeUnit timeUnit;
104 
105         private final Deque<NamedLock> locks;
106 
107         private AdaptedLockSyncContext( final RepositorySystemSession session, final boolean shared,
108                                         final NameMapper lockNaming, final NamedLockFactory namedLockFactory )
109         {
110             this.session = session;
111             this.shared = shared;
112             this.lockNaming = lockNaming;
113             this.namedLockFactory = namedLockFactory;
114             this.time = getTime( session );
115             this.timeUnit = getTimeUnit( session );
116             this.locks = new ArrayDeque<>();
117 
118             if ( time < 0L )
119             {
120                 throw new IllegalArgumentException( "time cannot be negative" );
121             }
122         }
123 
124         private long getTime( final RepositorySystemSession session )
125         {
126             return ConfigUtils.getLong( session, DEFAULT_TIME, TIME_KEY );
127         }
128 
129         private TimeUnit getTimeUnit( final RepositorySystemSession session )
130         {
131             return TimeUnit.valueOf( ConfigUtils.getString(
132                 session, DEFAULT_TIME_UNIT.name(), TIME_UNIT_KEY
133             ) );
134         }
135 
136         @Override
137         public void acquire( Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas )
138         {
139             Collection<String> keys = lockNaming.nameLocks( session, artifacts, metadatas );
140             if ( keys.isEmpty() )
141             {
142                 return;
143             }
144 
145             LOGGER.trace( "Need {} {} lock(s) for {}", keys.size(), shared ? "read" : "write", keys );
146             int acquiredLockCount = 0;
147             for ( String key : keys )
148             {
149                 NamedLock namedLock = namedLockFactory.getLock( key );
150                 try
151                 {
152                      LOGGER.trace( "Acquiring {} lock for '{}'",
153                              shared ? "read" : "write", key );
154 
155                     boolean locked;
156                     if ( shared )
157                     {
158                         locked = namedLock.lockShared( time, timeUnit );
159                     }
160                     else
161                     {
162                         locked = namedLock.lockExclusively( time, timeUnit );
163                     }
164 
165                     if ( !locked )
166                     {
167                         LOGGER.trace( "Failed to acquire {} lock for '{}'",
168                                 shared ? "read" : "write", key );
169 
170                         namedLock.close();
171                         throw new IllegalStateException(
172                                 "Could not acquire " + ( shared ? "read" : "write" )
173                                 + " lock for '" + namedLock.name() + "'" );
174                     }
175 
176                     locks.push( namedLock );
177                     acquiredLockCount++;
178                 }
179                 catch ( InterruptedException e )
180                 {
181                     Thread.currentThread().interrupt();
182                     throw new RuntimeException( e );
183                 }
184             }
185             LOGGER.trace( "Total locks acquired: {}", acquiredLockCount );
186         }
187 
188         @Override
189         public void close()
190         {
191             if ( locks.isEmpty() )
192             {
193                 return;
194             }
195 
196             // Release locks in reverse insertion order
197             int released = 0;
198             while ( !locks.isEmpty() )
199             {
200                 try ( NamedLock namedLock = locks.pop() )
201                 {
202                     LOGGER.trace( "Releasing {} lock for '{}'",
203                             shared ? "read" : "write", namedLock.name() );
204                     namedLock.unlock();
205                     released++;
206                 }
207             }
208             LOGGER.trace( "Total locks released: {}", released );
209         }
210     }
211 }