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