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.Objects;
25 import java.util.concurrent.TimeUnit;
26
27 import org.eclipse.aether.RepositorySystemSession;
28 import org.eclipse.aether.SyncContext;
29 import org.eclipse.aether.artifact.Artifact;
30 import org.eclipse.aether.metadata.Metadata;
31 import org.eclipse.aether.named.NamedLock;
32 import org.eclipse.aether.named.NamedLockFactory;
33 import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
34 import org.eclipse.aether.util.ConfigUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38
39
40
41 public final class NamedLockFactoryAdapter {
42 public static final String TIME_KEY = "aether.syncContext.named.time";
43
44 public static final long DEFAULT_TIME = 30L;
45
46 public static final String TIME_UNIT_KEY = "aether.syncContext.named.time.unit";
47
48 public static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;
49
50 private final NameMapper nameMapper;
51
52 private final NamedLockFactory namedLockFactory;
53
54 public NamedLockFactoryAdapter(final NameMapper nameMapper, final NamedLockFactory namedLockFactory) {
55 this.nameMapper = Objects.requireNonNull(nameMapper);
56 this.namedLockFactory = Objects.requireNonNull(namedLockFactory);
57
58 if (this.namedLockFactory instanceof FileLockNamedLockFactory && !this.nameMapper.isFileSystemFriendly()) {
59 throw new IllegalArgumentException(
60 "Misconfiguration: FileLockNamedLockFactory lock factory requires FS friendly NameMapper");
61 }
62 }
63
64 public SyncContext newInstance(final RepositorySystemSession session, final boolean shared) {
65 return new AdaptedLockSyncContext(session, shared, nameMapper, namedLockFactory);
66 }
67
68
69
70
71 public NameMapper getNameMapper() {
72 return nameMapper;
73 }
74
75
76
77
78 public NamedLockFactory getNamedLockFactory() {
79 return namedLockFactory;
80 }
81
82 public String toString() {
83 return getClass().getSimpleName()
84 + "(nameMapper=" + nameMapper
85 + ", namedLockFactory=" + namedLockFactory
86 + ")";
87 }
88
89 private static class AdaptedLockSyncContext implements SyncContext {
90 private static final Logger LOGGER = LoggerFactory.getLogger(AdaptedLockSyncContext.class);
91
92 private final RepositorySystemSession session;
93
94 private final boolean shared;
95
96 private final NameMapper lockNaming;
97
98 private final NamedLockFactory namedLockFactory;
99
100 private final long time;
101
102 private final TimeUnit timeUnit;
103
104 private final Deque<NamedLock> locks;
105
106 private AdaptedLockSyncContext(
107 final RepositorySystemSession session,
108 final boolean shared,
109 final NameMapper lockNaming,
110 final NamedLockFactory namedLockFactory) {
111 this.session = session;
112 this.shared = shared;
113 this.lockNaming = lockNaming;
114 this.namedLockFactory = namedLockFactory;
115 this.time = getTime(session);
116 this.timeUnit = getTimeUnit(session);
117 this.locks = new ArrayDeque<>();
118
119 if (time < 0L) {
120 throw new IllegalArgumentException("time cannot be negative");
121 }
122 }
123
124 private long getTime(final RepositorySystemSession session) {
125 return ConfigUtils.getLong(session, DEFAULT_TIME, TIME_KEY);
126 }
127
128 private TimeUnit getTimeUnit(final RepositorySystemSession session) {
129 return TimeUnit.valueOf(ConfigUtils.getString(session, DEFAULT_TIME_UNIT.name(), TIME_UNIT_KEY));
130 }
131
132 @Override
133 public void acquire(Collection<? extends Artifact> artifacts, Collection<? extends Metadata> metadatas) {
134 Collection<String> keys = lockNaming.nameLocks(session, artifacts, metadatas);
135 if (keys.isEmpty()) {
136 return;
137 }
138
139 LOGGER.trace("Need {} {} lock(s) for {}", keys.size(), shared ? "read" : "write", keys);
140 int acquiredLockCount = 0;
141 for (String key : keys) {
142 NamedLock namedLock = namedLockFactory.getLock(key);
143 try {
144 LOGGER.trace("Acquiring {} lock for '{}'", shared ? "read" : "write", key);
145
146 boolean locked;
147 if (shared) {
148 locked = namedLock.lockShared(time, timeUnit);
149 } else {
150 locked = namedLock.lockExclusively(time, timeUnit);
151 }
152
153 if (!locked) {
154 LOGGER.trace("Failed to acquire {} lock for '{}'", shared ? "read" : "write", key);
155
156 namedLock.close();
157 throw new IllegalStateException("Could not acquire " + (shared ? "read" : "write")
158 + " lock for '" + namedLock.name() + "'");
159 }
160
161 locks.push(namedLock);
162 acquiredLockCount++;
163 } catch (InterruptedException e) {
164 Thread.currentThread().interrupt();
165 throw new RuntimeException(e);
166 }
167 }
168 LOGGER.trace("Total locks acquired: {}", acquiredLockCount);
169 }
170
171 @Override
172 public void close() {
173 if (locks.isEmpty()) {
174 return;
175 }
176
177
178 int released = 0;
179 while (!locks.isEmpty()) {
180 try (NamedLock namedLock = locks.pop()) {
181 LOGGER.trace("Releasing {} lock for '{}'", shared ? "read" : "write", namedLock.name());
182 namedLock.unlock();
183 released++;
184 }
185 }
186 LOGGER.trace("Total locks released: {}", released);
187 }
188 }
189 }