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 if (shutdown.get()) {
119 throw new IllegalStateException("factory already shut down");
120 }
121
122
123
124
125 NamedLockHolder holder = locks.get(key);
126 if (holder != null && holder.tryIncRef()) {
127 return holder.namedLock;
128 }
129
130
131 return locks.compute(key, (k, v) -> {
132 if (shutdown.get()) {
133 throw new IllegalStateException("factory already shut down");
134 }
135 if (v == null || !v.tryIncRef()) {
136 v = new NamedLockHolder(supplier.get());
137 v.incRef();
138 }
139 return v;
140 })
141 .namedLock;
142 }
143
144 @Override
145 public void shutdown() {
146 if (shutdown.compareAndSet(false, true)) {
147 doShutdown();
148 }
149 }
150
151 protected void doShutdown() {
152
153 }
154
155 @Override
156 public <E extends Throwable> E onFailure(E failure) {
157 if (isDiagnosticEnabled()) {
158 Map<NamedLockKey, NamedLockHolder> locks = new HashMap<>(this.locks);
159 int activeLocks = locks.size();
160 logger.info("Diagnostic dump of lock factory");
161 logger.info("===============================");
162 logger.info("Implementation: {}", getClass().getName());
163 logger.info("Active locks: {}", activeLocks);
164 logger.info("");
165 if (activeLocks > 0) {
166 for (Map.Entry<NamedLockKey, NamedLockHolder> entry : locks.entrySet()) {
167 NamedLockKey key = entry.getKey();
168 int refCount = entry.getValue().referenceCount.get();
169 NamedLockSupport lock = entry.getValue().namedLock;
170 logger.info("Name: {}", key.name());
171 logger.info("RefCount: {}", refCount);
172 logger.info("Resources:");
173 key.resources().forEach(r -> logger.info(" - {}", r));
174 Map<Thread, Deque<String>> diag = lock.diagnosticState();
175 logger.info("State:");
176 diag.forEach((k, v) -> logger.info(" {} -> {}", k, v));
177 }
178 logger.info("");
179 }
180 }
181 return failure;
182 }
183
184 public void closeLock(final NamedLockKey key) {
185 locks.compute(key, (k, v) -> {
186 if (v != null && v.decRef() == 0) {
187
188
189
190 if (v.referenceCount.compareAndSet(0, Integer.MIN_VALUE)) {
191 destroyLock(v.namedLock);
192 return null;
193 }
194
195 }
196 return v;
197 });
198 }
199
200
201
202
203
204 protected abstract NamedLockSupport createLock(NamedLockKey key);
205
206
207
208
209
210 protected void destroyLock(final NamedLock namedLock) {
211
212 }
213
214 private static final class NamedLockHolder {
215 private final NamedLockSupport namedLock;
216
217 private final AtomicInteger referenceCount;
218
219 private NamedLockHolder(final NamedLockSupport namedLock) {
220 this.namedLock = requireNonNull(namedLock);
221 this.referenceCount = new AtomicInteger(0);
222 }
223
224 private NamedLockHolder incRef() {
225 referenceCount.incrementAndGet();
226 return this;
227 }
228
229
230
231
232
233
234 private boolean tryIncRef() {
235 while (true) {
236 int current = referenceCount.get();
237 if (current <= 0) {
238 return false;
239 }
240 if (referenceCount.compareAndSet(current, current + 1)) {
241 return true;
242 }
243 }
244 }
245
246 private int decRef() {
247 return referenceCount.decrementAndGet();
248 }
249
250 @Override
251 public String toString() {
252 return "[refCount=" + referenceCount.get() + ", lock=" + namedLock + "]";
253 }
254 }
255 }