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.Collections;
23  import java.util.Deque;
24  import java.util.Map;
25  import java.util.concurrent.ConcurrentHashMap;
26  import java.util.concurrent.TimeUnit;
27  
28  import org.eclipse.aether.named.NamedLock;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  /**
33   * Support class for {@link NamedLock} implementations providing reference counting.
34   */
35  public abstract class NamedLockSupport implements NamedLock {
36      protected final Logger logger = LoggerFactory.getLogger(getClass());
37  
38      private final String name;
39  
40      private final NamedLockFactorySupport factory;
41  
42      private final ConcurrentHashMap<Thread, Deque<String>> diagnosticState; // non-null only if diag enabled
43  
44      public NamedLockSupport(final String name, final NamedLockFactorySupport factory) {
45          this.name = name;
46          this.factory = factory;
47          this.diagnosticState = factory.isDiagnosticEnabled() ? new ConcurrentHashMap<>() : null;
48      }
49  
50      @Override
51      public String name() {
52          return name;
53      }
54  
55      @Override
56      public boolean lockShared(long time, TimeUnit unit) throws InterruptedException {
57          Deque<String> steps = null;
58          if (diagnosticState != null) {
59              steps = diagnosticState.computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>());
60          }
61          if (steps != null) {
62              steps.push("wait-shared");
63          }
64          boolean result = doLockShared(time, unit);
65          if (steps != null) {
66              steps.pop();
67              if (result) {
68                  steps.push("shared");
69              }
70          }
71          return result;
72      }
73  
74      protected abstract boolean doLockShared(long time, TimeUnit unit) throws InterruptedException;
75  
76      @Override
77      public boolean lockExclusively(long time, TimeUnit unit) throws InterruptedException {
78          Deque<String> steps = null;
79          if (diagnosticState != null) {
80              steps = diagnosticState.computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>());
81          }
82          if (steps != null) {
83              steps.push("wait-exclusive");
84          }
85          boolean result = doLockExclusively(time, unit);
86          if (steps != null) {
87              steps.pop();
88              if (result) {
89                  steps.push("exclusive");
90              }
91          }
92          return result;
93      }
94  
95      protected abstract boolean doLockExclusively(long time, TimeUnit unit) throws InterruptedException;
96  
97      @Override
98      public void unlock() {
99          doUnlock();
100         if (diagnosticState != null) {
101             diagnosticState
102                     .computeIfAbsent(Thread.currentThread(), k -> new ArrayDeque<>())
103                     .pop();
104         }
105     }
106 
107     protected abstract void doUnlock();
108 
109     @Override
110     public void close() {
111         doClose();
112     }
113 
114     protected void doClose() {
115         factory.closeLock(name);
116     }
117 
118     /**
119      * Returns the diagnostic state (if collected) or empty map, never {@code null}.
120      *
121      * @since 1.9.11
122      */
123     public Map<Thread, Deque<String>> diagnosticState() {
124         if (diagnosticState != null) {
125             return diagnosticState;
126         } else {
127             return Collections.emptyMap();
128         }
129     }
130 
131     @Override
132     public String toString() {
133         return getClass().getSimpleName() + "{" + "name='" + name + '\'' + '}';
134     }
135 }