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.Deque;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.concurrent.ConcurrentHashMap;
25  import java.util.concurrent.ConcurrentMap;
26  import java.util.concurrent.atomic.AtomicInteger;
27  
28  import org.eclipse.aether.named.NamedLockFactory;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  import static java.util.Objects.requireNonNull;
33  
34  /**
35   * Support class for {@link NamedLockFactory} implementations providing reference counting.
36   */
37  public abstract class NamedLockFactorySupport implements NamedLockFactory {
38      /**
39       * System property key to enable locking diagnostic collection.
40       *
41       * @since 1.9.11
42       */
43      private static final boolean DIAGNOSTIC_ENABLED = Boolean.getBoolean("aether.named.diagnostic.enabled");
44  
45      protected final Logger logger = LoggerFactory.getLogger(getClass());
46  
47      private final ConcurrentMap<String, NamedLockHolder> locks;
48  
49      private final boolean diagnosticEnabled;
50  
51      public NamedLockFactorySupport() {
52          this(DIAGNOSTIC_ENABLED);
53      }
54  
55      public NamedLockFactorySupport(boolean diagnosticEnabled) {
56          this.locks = new ConcurrentHashMap<>();
57          this.diagnosticEnabled = diagnosticEnabled;
58      }
59  
60      /**
61       * Returns {@code true} if factory diagnostic collection is enabled.
62       *
63       * @since 1.9.11
64       */
65      public boolean isDiagnosticEnabled() {
66          return diagnosticEnabled;
67      }
68  
69      @Override
70      public NamedLockSupport getLock(final String name) {
71          return locks.compute(name, (k, v) -> {
72                      if (v == null) {
73                          v = new NamedLockHolder(createLock(k));
74                      }
75                      v.incRef();
76                      return v;
77                  })
78                  .namedLock;
79      }
80  
81      @Override
82      public void shutdown() {
83          // override if needed
84      }
85  
86      @Override
87      public <E extends Throwable> E onFailure(E failure) {
88          if (isDiagnosticEnabled()) {
89              Map<String, NamedLockHolder> locks = new HashMap<>(this.locks); // copy
90              int activeLocks = locks.size();
91              logger.info("Diagnostic dump of lock factory");
92              logger.info("===============================");
93              logger.info("Implementation: {}", getClass().getName());
94              logger.info("Active locks: {}", activeLocks);
95              logger.info("");
96              if (activeLocks > 0) {
97                  for (Map.Entry<String, NamedLockHolder> entry : locks.entrySet()) {
98                      String name = entry.getKey();
99                      int refCount = entry.getValue().referenceCount.get();
100                     NamedLockSupport lock = entry.getValue().namedLock;
101                     logger.info("Name: {}", name);
102                     logger.info("RefCount: {}", refCount);
103                     Map<Thread, Deque<String>> diag = lock.diagnosticState();
104                     diag.forEach((key, value) -> logger.info("  {} -> {}", key, value));
105                 }
106                 logger.info("");
107             }
108         }
109         return failure;
110     }
111 
112     public void closeLock(final String name) {
113         locks.compute(name, (k, v) -> {
114             if (v != null && v.decRef() == 0) {
115                 destroyLock(v.namedLock.name());
116                 return null;
117             }
118             return v;
119         });
120     }
121 
122     /**
123      * Implementations shall create and return {@link NamedLockSupport} for given {@code name}, this method must never
124      * return {@code null}.
125      */
126     protected abstract NamedLockSupport createLock(String name);
127 
128     /**
129      * Implementation may override this (empty) method to perform some sort of implementation specific cleanup for
130      * given lock name. Invoked when reference count for given name drops to zero and named lock was removed.
131      */
132     protected void destroyLock(final String name) {
133         // override if needed
134     }
135 
136     private static final class NamedLockHolder {
137         private final NamedLockSupport namedLock;
138 
139         private final AtomicInteger referenceCount;
140 
141         private NamedLockHolder(final NamedLockSupport namedLock) {
142             this.namedLock = requireNonNull(namedLock);
143             this.referenceCount = new AtomicInteger(0);
144         }
145 
146         private int incRef() {
147             return referenceCount.incrementAndGet();
148         }
149 
150         private int decRef() {
151             return referenceCount.decrementAndGet();
152         }
153 
154         @Override
155         public String toString() {
156             return "[refCount=" + referenceCount.get() + ", lock=" + namedLock + "]";
157         }
158     }
159 }