1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.internal.impl.synccontext.named;
20
21 import java.util.ArrayDeque;
22 import java.util.Collection;
23 import java.util.Deque;
24 import java.util.concurrent.TimeUnit;
25 import java.util.stream.Collectors;
26
27 import org.eclipse.aether.ConfigurationProperties;
28 import org.eclipse.aether.RepositorySystemSession;
29 import org.eclipse.aether.SyncContext;
30 import org.eclipse.aether.artifact.Artifact;
31 import org.eclipse.aether.internal.impl.named.DefaultNamedLockFactorySelector;
32 import org.eclipse.aether.metadata.Metadata;
33 import org.eclipse.aether.named.NamedLock;
34 import org.eclipse.aether.named.NamedLockFactory;
35 import org.eclipse.aether.named.NamedLockKey;
36 import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
37 import org.eclipse.aether.util.ConfigUtils;
38 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import static java.util.Objects.requireNonNull;
43
44
45
46
47 public final class NamedLockFactoryAdapter {
48 public static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_SYNC_CONTEXT + "named.";
49
50
51
52
53
54
55
56
57
58
59 @Deprecated
60 public static final String CONFIG_PROP_TIME = CONFIG_PROPS_PREFIX + "time";
61
62 @Deprecated
63 public static final long DEFAULT_TIME = DefaultNamedLockFactorySelector.DEFAULT_LOCK_WAIT_TIME;
64
65
66
67
68
69
70
71
72
73
74 @Deprecated
75 public static final String CONFIG_PROP_TIME_UNIT = CONFIG_PROPS_PREFIX + "time.unit";
76
77 @Deprecated
78 public static final String DEFAULT_TIME_UNIT = DefaultNamedLockFactorySelector.DEFAULT_LOCK_WAIT_TIME_UNIT;
79
80
81
82
83
84
85
86
87
88 public static final String CONFIG_PROP_RETRY = CONFIG_PROPS_PREFIX + "retry";
89
90 public static final int DEFAULT_RETRY = 1;
91
92
93
94
95
96
97
98
99
100 public static final String CONFIG_PROP_RETRY_WAIT = CONFIG_PROPS_PREFIX + "retry.wait";
101
102 public static final long DEFAULT_RETRY_WAIT = 200L;
103
104 private final NameMapper nameMapper;
105
106 private final NamedLockFactory namedLockFactory;
107
108 private final long lockWait;
109
110 private final TimeUnit lockWaitUnit;
111
112 public NamedLockFactoryAdapter(
113 final NameMapper nameMapper,
114 final NamedLockFactory namedLockFactory,
115 long lockWait,
116 TimeUnit lockWaitUnit) {
117 this.nameMapper = requireNonNull(nameMapper);
118 this.namedLockFactory = requireNonNull(namedLockFactory);
119 this.lockWait = lockWait;
120 this.lockWaitUnit = requireNonNull(lockWaitUnit);
121
122 if (this.namedLockFactory instanceof FileLockNamedLockFactory && !this.nameMapper.isFileSystemFriendly()) {
123 throw new IllegalArgumentException(
124 "Misconfiguration: FileLockNamedLockFactory lock factory requires FS friendly NameMapper");
125 }
126 }
127
128 public SyncContext newInstance(final RepositorySystemSession session, final boolean shared) {
129 return new AdaptedLockSyncContext(session, shared, nameMapper, namedLockFactory, lockWait, lockWaitUnit);
130 }
131
132
133
134
135 public NameMapper getNameMapper() {
136 return nameMapper;
137 }
138
139
140
141
142 public NamedLockFactory getNamedLockFactory() {
143 return namedLockFactory;
144 }
145
146 public String toString() {
147 return getClass().getSimpleName()
148 + "(nameMapper=" + nameMapper
149 + ", namedLockFactory=" + namedLockFactory
150 + ")";
151 }
152
153 private static class AdaptedLockSyncContext implements SyncContext {
154 private static final Logger LOGGER = LoggerFactory.getLogger(AdaptedLockSyncContext.class);
155
156 private final RepositorySystemSession session;
157
158 private final boolean shared;
159
160 private final NameMapper lockNaming;
161
162 private final NamedLockFactory namedLockFactory;
163
164 private final long time;
165
166 private final TimeUnit timeUnit;
167
168 private final int retry;
169
170 private final long retryWait;
171
172 private final Deque<NamedLock> locks;
173
174 private AdaptedLockSyncContext(
175 final RepositorySystemSession session,
176 final boolean shared,
177 final NameMapper lockNaming,
178 final NamedLockFactory namedLockFactory,
179 final long lockWait,
180 final TimeUnit lockWaitUnit) {
181 this.session = session;
182 this.shared = shared;
183 this.lockNaming = lockNaming;
184 this.namedLockFactory = namedLockFactory;
185 this.time = lockWait;
186 this.timeUnit = lockWaitUnit;
187 this.retry = getRetry(session);
188 this.retryWait = getRetryWait(session);
189 this.locks = new ArrayDeque<>();
190
191 if (retry < 0L) {
192 throw new IllegalArgumentException(CONFIG_PROP_RETRY + " value cannot be negative");
193 }
194 if (retryWait < 0L) {
195 throw new IllegalArgumentException(CONFIG_PROP_RETRY_WAIT + " value cannot be negative");
196 }
197 }
198
199 private int getRetry(final RepositorySystemSession session) {
200 return ConfigUtils.getInteger(session, DEFAULT_RETRY, CONFIG_PROP_RETRY);
201 }
202
203 private long getRetryWait(final RepositorySystemSession session) {
204 return ConfigUtils.getLong(session, DEFAULT_RETRY_WAIT, CONFIG_PROP_RETRY_WAIT);
205 }
206
207 @Override
208 public void acquire(Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas) {
209 Collection<NamedLockKey> keys = lockNaming.nameLocks(session, artifacts, metadatas);
210 if (keys.isEmpty()) {
211 return;
212 }
213
214 final String timeStr = time + " " + timeUnit;
215 final String lockKind = shared ? "shared" : "exclusive";
216 final NamedLock namedLock = namedLockFactory.getLock(keys);
217 if (LOGGER.isTraceEnabled()) {
218 LOGGER.trace(
219 "Need {} lock for {} from {}",
220 lockKind,
221 namedLock.key().resources(),
222 namedLock.key().name());
223 }
224
225 final int attempts = retry + 1;
226 for (int attempt = 1; attempt <= attempts; attempt++) {
227 if (LOGGER.isTraceEnabled()) {
228 LOGGER.trace(
229 "Attempt {}: Acquire {} lock from {}",
230 attempt,
231 lockKind,
232 namedLock.key().name());
233 }
234 try {
235 if (attempt > 1) {
236 Thread.sleep(retryWait);
237 }
238 boolean locked;
239 if (shared) {
240 locked = namedLock.lockShared(time, timeUnit);
241 } else {
242 locked = namedLock.lockExclusively(time, timeUnit);
243 }
244
245 if (locked) {
246
247 locks.push(namedLock);
248 return;
249 }
250
251
252 if (LOGGER.isTraceEnabled()) {
253 LOGGER.trace(
254 "Failed to acquire {} lock for '{}' in {}",
255 lockKind,
256 namedLock.key().name(),
257 timeStr);
258 }
259 } catch (InterruptedException e) {
260
261 close();
262 Thread.currentThread().interrupt();
263 throw new RuntimeException(e);
264 }
265 }
266
267 close();
268 String message = "Could not acquire " + lockKind + " lock for "
269 + lockSubjects(artifacts, metadatas) + " in " + timeStr
270 + "; consider using '" + CONFIG_PROP_TIME
271 + "' property to increase lock timeout to a value that fits your environment";
272 FailedToAcquireLockException ex = new FailedToAcquireLockException(shared, message);
273 throw namedLockFactory.onFailure(ex);
274 }
275
276 private String lockSubjects(
277 Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas) {
278 StringBuilder builder = new StringBuilder();
279 if (artifacts != null && !artifacts.isEmpty()) {
280 builder.append("artifacts: ")
281 .append(artifacts.stream().map(ArtifactIdUtils::toId).collect(Collectors.joining(", ")));
282 }
283 if (metadatas != null && !metadatas.isEmpty()) {
284 if (builder.length() != 0) {
285 builder.append("; ");
286 }
287 builder.append("metadata: ")
288 .append(metadatas.stream().map(this::metadataSubjects).collect(Collectors.joining(", ")));
289 }
290 return builder.toString();
291 }
292
293 private String metadataSubjects(Metadata metadata) {
294 String name = "";
295 if (!metadata.getGroupId().isEmpty()) {
296 name += metadata.getGroupId();
297 if (!metadata.getArtifactId().isEmpty()) {
298 name += ":" + metadata.getArtifactId();
299 if (!metadata.getVersion().isEmpty()) {
300 name += ":" + metadata.getVersion();
301 }
302 }
303 }
304 if (!metadata.getType().isEmpty()) {
305 name += (name.isEmpty() ? "" : ":") + metadata.getType();
306 }
307 return name;
308 }
309
310 @Override
311 public void close() {
312 while (!locks.isEmpty()) {
313 try (NamedLock namedLock = locks.pop()) {
314 namedLock.unlock();
315 if (LOGGER.isTraceEnabled()) {
316 LOGGER.trace(
317 "Unlocked and closed {} lock of {}", shared ? "shared" : "exclusive", namedLock.key());
318 }
319 }
320 }
321 }
322 }
323 }