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