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;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.OutputStream;
25 import java.io.UncheckedIOException;
26 import java.nio.file.Files;
27 import java.nio.file.NoSuchFileException;
28 import java.nio.file.Path;
29 import java.nio.file.StandardOpenOption;
30 import java.util.Map;
31 import java.util.Properties;
32 import java.util.concurrent.TimeUnit;
33
34 import org.eclipse.aether.named.NamedLock;
35 import org.eclipse.aether.named.NamedLockFactory;
36 import org.eclipse.aether.named.NamedLockKey;
37 import org.eclipse.aether.util.StringDigestUtil;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47
48
49
50
51 public final class NamedLocksTrackingFileManager implements TrackingFileManager {
52 private static final Logger LOGGER = LoggerFactory.getLogger(NamedLocksTrackingFileManager.class);
53
54 private final NamedLockFactory namedLockFactory;
55 private final long time;
56 private final TimeUnit unit;
57
58 public NamedLocksTrackingFileManager(NamedLockFactory namedLockFactory, long time, TimeUnit unit) {
59 this.namedLockFactory = namedLockFactory;
60 this.time = time;
61 this.unit = unit;
62 }
63
64 @Deprecated
65 @Override
66 public Properties read(File file) {
67 return read(file.toPath());
68 }
69
70 @Override
71 public Properties read(Path path) {
72 try (NamedLock namedLock = namedLock(path)) {
73 if (namedLock.lockShared(time, unit)) {
74 try {
75 Properties props = new Properties();
76 try (InputStream in = Files.newInputStream(path)) {
77 props.load(in);
78 }
79 return props;
80 } catch (NoSuchFileException e) {
81 LOGGER.debug("No such file to read {}: {}", path, e.getMessage());
82 return null;
83 } catch (IOException e) {
84 LOGGER.warn("Failed to read tracking file '{}'", path, e);
85 throw new UncheckedIOException(e);
86 } finally {
87 namedLock.unlock();
88 }
89 }
90 throw new IllegalStateException("Failed to lock for read the tracking file " + path);
91 } catch (InterruptedException e) {
92 Thread.currentThread().interrupt();
93 throw new IllegalStateException("Interrupted while reading tracking file " + path, e);
94 }
95 }
96
97 @Deprecated
98 @Override
99 public Properties update(File file, Map<String, String> updates) {
100 return update(file.toPath(), updates);
101 }
102
103 @Override
104 public Properties update(Path path, Map<String, String> updates) {
105 try {
106 Path parent = path.getParent();
107 if (parent != null) {
108 Files.createDirectories(parent);
109 }
110 } catch (IOException e) {
111 LOGGER.warn("Failed to create tracking file parent '{}'", path, e);
112 throw new UncheckedIOException(e);
113 }
114 try (NamedLock lock = namedLock(path)) {
115 if (lock.lockExclusively(time, unit)) {
116 try {
117 Properties props = new Properties();
118 if (Files.isRegularFile(path)) {
119 try (InputStream stream = Files.newInputStream(path, StandardOpenOption.READ)) {
120 props.load(stream);
121 }
122 }
123 for (Map.Entry<String, String> update : updates.entrySet()) {
124 if (update.getValue() == null) {
125 props.remove(update.getKey());
126 } else {
127 props.setProperty(update.getKey(), update.getValue());
128 }
129 }
130 LOGGER.debug("Writing tracking file '{}'", path);
131 try (OutputStream out = Files.newOutputStream(path)) {
132 props.store(
133 out,
134 "NOTE: This is a Maven Resolver internal implementation file"
135 + ", its format can be changed without prior notice.");
136 }
137 return props;
138 } catch (IOException e) {
139 LOGGER.warn("Failed to write tracking file '{}'", path, e);
140 throw new UncheckedIOException(e);
141 } finally {
142 lock.unlock();
143 }
144 }
145 throw new IllegalStateException("Failed to lock for update the tracking file " + path);
146 } catch (InterruptedException e) {
147 Thread.currentThread().interrupt();
148 throw new IllegalStateException("Interrupted while updating tracking file " + path, e);
149 }
150 }
151
152 @Deprecated
153 @Override
154 public boolean delete(File file) {
155 return delete(file.toPath());
156 }
157
158 @Override
159 public boolean delete(Path path) {
160 try (NamedLock lock = namedLock(path)) {
161 if (lock.lockExclusively(time, unit)) {
162 try {
163 return Files.deleteIfExists(path);
164 } catch (NoSuchFileException e) {
165 LOGGER.debug("No such file to delete {}: {}", path, e.getMessage());
166 return false;
167 } catch (IOException e) {
168 LOGGER.warn("Failed to delete tracking file '{}'", path, e);
169 throw new UncheckedIOException(e);
170 } finally {
171 lock.unlock();
172 }
173 }
174 throw new IllegalStateException("Failed to lock for delete the tracking file " + path);
175 } catch (InterruptedException e) {
176 Thread.currentThread().interrupt();
177 throw new IllegalStateException("Interrupted while deleting tracking file " + path, e);
178 }
179 }
180
181
182
183
184
185
186
187
188
189 private NamedLock namedLock(Path path) {
190 Path canonical = canonicalPath(path);
191
192 Path lockPath = canonical.resolveSibling("tracking-" + StringDigestUtil.sha1(canonical.toString()) + ".lock");
193 return namedLockFactory.getLock(
194 NamedLockKey.of(lockPath.toAbsolutePath().toUri().toASCIIString(), path.toString()));
195 }
196
197
198
199
200
201 private static Path canonicalPath(Path path) {
202 try {
203 return path.toRealPath();
204 } catch (IOException e) {
205 return path.getParent() != null
206 ? canonicalPath(path.getParent()).resolve(path.getFileName())
207 : path.toAbsolutePath();
208 }
209 }
210 }