1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.named.support;
20
21 import java.util.Collection;
22 import java.util.Deque;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
27 import java.util.concurrent.atomic.AtomicBoolean;
28 import java.util.concurrent.atomic.AtomicInteger;
29 import java.util.function.Supplier;
30 import java.util.stream.Collectors;
31
32 import org.eclipse.aether.named.NamedLock;
33 import org.eclipse.aether.named.NamedLockFactory;
34 import org.eclipse.aether.named.NamedLockKey;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import static java.util.Objects.requireNonNull;
39
40
41
42
43 public abstract class NamedLockFactorySupport implements NamedLockFactory {
44
45
46
47
48
49
50
51
52 public static final String SYSTEM_PROP_DIAGNOSTIC_ENABLED = "aether.named.diagnostic.enabled";
53
54 private static final boolean DIAGNOSTIC_ENABLED = Boolean.getBoolean(SYSTEM_PROP_DIAGNOSTIC_ENABLED);
55
56 protected final Logger logger = LoggerFactory.getLogger(getClass());
57
58 private final ConcurrentMap<NamedLockKey, NamedLockHolder> locks;
59
60 private final AtomicInteger compositeCounter;
61
62 private final boolean diagnosticEnabled;
63
64 private final AtomicBoolean shutdown = new AtomicBoolean(false);
65
66 public NamedLockFactorySupport() {
67 this(DIAGNOSTIC_ENABLED);
68 }
69
70 public NamedLockFactorySupport(boolean diagnosticEnabled) {
71 this.locks = new ConcurrentHashMap<>();
72 this.compositeCounter = new AtomicInteger(0);
73 this.diagnosticEnabled = diagnosticEnabled;
74 }
75
76
77
78
79
80
81 public boolean isDiagnosticEnabled() {
82 return diagnosticEnabled;
83 }
84
85 @Override
86 public final NamedLock getLock(final Collection<NamedLockKey> keys) {
87 requireNonNull(keys, "keys");
88 if (shutdown.get()) {
89 throw new IllegalStateException("factory already shut down");
90 }
91 if (keys.isEmpty()) {
92 throw new IllegalArgumentException("empty keys");
93 } else {
94 return doGetLock(keys);
95 }
96 }
97
98 protected NamedLock doGetLock(final Collection<NamedLockKey> keys) {
99 if (keys.size() == 1) {
100 NamedLockKey key = keys.iterator().next();
101 return getLockAndRefTrack(key, () -> createLock(key));
102 } else {
103 return new CompositeNamedLock(
104 NamedLockKey.of(
105 "composite-" + compositeCounter.incrementAndGet(),
106 keys.stream()
107 .map(NamedLockKey::resources)
108 .flatMap(Collection::stream)
109 .collect(Collectors.toList())),
110 this,
111 keys.stream()
112 .map(k -> getLockAndRefTrack(k, () -> createLock(k)))
113 .collect(Collectors.toList()));
114 }
115 }
116
117 protected NamedLock getLockAndRefTrack(final NamedLockKey key, Supplier<NamedLockSupport> supplier) {
118 return locks.compute(key, (k, v) -> {
119 if (v == null) {
120 v = new NamedLockHolder(supplier.get());
121 }
122 return v.incRef();
123 })
124 .namedLock;
125 }
126
127 @Override
128 public void shutdown() {
129 if (shutdown.compareAndSet(false, true)) {
130 doShutdown();
131 }
132 }
133
134 protected void doShutdown() {
135
136 }
137
138 @Override
139 public <E extends Throwable> E onFailure(E failure) {
140 if (isDiagnosticEnabled()) {
141 Map<NamedLockKey, NamedLockHolder> locks = new HashMap<>(this.locks);
142 int activeLocks = locks.size();
143 logger.info("Diagnostic dump of lock factory");
144 logger.info("===============================");
145 logger.info("Implementation: {}", getClass().getName());
146 logger.info("Active locks: {}", activeLocks);
147 logger.info("");
148 if (activeLocks > 0) {
149 for (Map.Entry<NamedLockKey, NamedLockHolder> entry : locks.entrySet()) {
150 NamedLockKey key = entry.getKey();
151 int refCount = entry.getValue().referenceCount.get();
152 NamedLockSupport lock = entry.getValue().namedLock;
153 logger.info("Name: {}", key.name());
154 logger.info("RefCount: {}", refCount);
155 logger.info("Resources:");
156 key.resources().forEach(r -> logger.info(" - {}", r));
157 Map<Thread, Deque<String>> diag = lock.diagnosticState();
158 logger.info("State:");
159 diag.forEach((k, v) -> logger.info(" {} -> {}", k, v));
160 }
161 logger.info("");
162 }
163 }
164 return failure;
165 }
166
167 public void closeLock(final NamedLockKey key) {
168 locks.compute(key, (k, v) -> {
169 if (v != null && v.decRef() == 0) {
170 destroyLock(v.namedLock);
171 return null;
172 }
173 return v;
174 });
175 }
176
177
178
179
180
181 protected abstract NamedLockSupport createLock(NamedLockKey key);
182
183
184
185
186
187 protected void destroyLock(final NamedLock namedLock) {
188
189 }
190
191 private static final class NamedLockHolder {
192 private final NamedLockSupport namedLock;
193
194 private final AtomicInteger referenceCount;
195
196 private NamedLockHolder(final NamedLockSupport namedLock) {
197 this.namedLock = requireNonNull(namedLock);
198 this.referenceCount = new AtomicInteger(0);
199 }
200
201 private NamedLockHolder incRef() {
202 referenceCount.incrementAndGet();
203 return this;
204 }
205
206 private int decRef() {
207 return referenceCount.decrementAndGet();
208 }
209
210 @Override
211 public String toString() {
212 return "[refCount=" + referenceCount.get() + ", lock=" + namedLock + "]";
213 }
214 }
215 }