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.named.support;
20  
21  import java.util.ArrayDeque;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.LinkedHashMap;
25  import java.util.Map;
26  import java.util.concurrent.TimeUnit;
27  
28  import org.eclipse.aether.named.NamedLock;
29  import org.eclipse.aether.named.NamedLockKey;
30  import org.slf4j.Logger;
31  import org.slf4j.LoggerFactory;
32  
33  /**
34   * Implementation of composite lock when "composition" is needed for locks that are naturally mapped as 1:1 name
35   * vs some backing implementation. Instances of these locks are "unique per call" and are not ref counted.
36   *
37   * @since 2.0.0
38   */
39  public final class CompositeNamedLock extends NamedLockSupport {
40      private static final Logger LOGGER = LoggerFactory.getLogger(CompositeNamedLock.class);
41  
42      private final Map<NamedLockKey, NamedLock> locks;
43  
44      private final ArrayDeque<ArrayDeque<NamedLock>> steps = new ArrayDeque<>();
45  
46      public CompositeNamedLock(NamedLockKey key, NamedLockFactorySupport factory, Collection<NamedLock> namedLocks) {
47          super(key, factory);
48          LinkedHashMap<NamedLockKey, NamedLock> map = new LinkedHashMap<>();
49          namedLocks.forEach(l -> map.put(l.key(), l));
50          this.locks = Collections.unmodifiableMap(map);
51      }
52  
53      @Override
54      protected boolean doLockShared(long time, TimeUnit unit) throws InterruptedException {
55          return lock(time, unit, true);
56      }
57  
58      @Override
59      protected boolean doLockExclusively(long time, TimeUnit unit) throws InterruptedException {
60          return lock(time, unit, false);
61      }
62  
63      private boolean lock(long time, TimeUnit timeUnit, boolean shared) throws InterruptedException {
64          final ArrayDeque<NamedLock> step = new ArrayDeque<>(locks.size());
65          final String timeStr = time + " " + timeUnit;
66          final String lockKind = shared ? "shared" : "exclusive";
67          LOGGER.trace(
68                  "{}: Need {} {} lock(s) of {} in {}", key().name(), locks.size(), lockKind, key().resources(), timeStr);
69          for (NamedLock namedLock : locks.values()) {
70              LOGGER.trace("{}: Acquiring {} lock for '{}'", key().name(), lockKind, namedLock.key());
71  
72              boolean locked;
73              if (shared) {
74                  locked = namedLock.lockShared(time, timeUnit);
75              } else {
76                  locked = namedLock.lockExclusively(time, timeUnit);
77              }
78  
79              if (!locked) {
80                  LOGGER.trace(
81                          "{}: Failed to acquire {} lock for '{}' in {}",
82                          key().name(),
83                          lockKind,
84                          namedLock.key(),
85                          timeStr);
86  
87                  unlockAll(step);
88                  break;
89              } else {
90                  step.push(namedLock);
91              }
92          }
93          if (step.size() == locks.size()) {
94              steps.push(step);
95              return true;
96          }
97          unlockAll(step);
98          return false;
99      }
100 
101     @Override
102     protected void doUnlock() {
103         unlockAll(steps.pop());
104     }
105 
106     @Override
107     protected void doClose() {
108         locks.values().forEach(NamedLock::close);
109     }
110 
111     private void unlockAll(final ArrayDeque<NamedLock> locks) {
112         if (locks.isEmpty()) {
113             return;
114         }
115 
116         // Release locks in reverse locking order
117         while (!locks.isEmpty()) {
118             NamedLock namedLock = locks.pop();
119             LOGGER.trace("{}: Releasing lock for '{}'", key().name(), namedLock.key());
120             namedLock.unlock();
121         }
122     }
123 }