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