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