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 javax.inject.Named;
22 import javax.inject.Singleton;
23
24 import java.io.ByteArrayOutputStream;
25 import java.io.File;
26 import java.io.IOException;
27 import java.io.UncheckedIOException;
28 import java.nio.ByteBuffer;
29 import java.nio.channels.Channels;
30 import java.nio.channels.FileChannel;
31 import java.nio.channels.FileLock;
32 import java.nio.channels.OverlappingFileLockException;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.nio.file.StandardOpenOption;
36 import java.util.Map;
37 import java.util.Properties;
38
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42
43
44
45
46
47
48
49
50 @Singleton
51 @Named
52 public final class DefaultTrackingFileManager implements TrackingFileManager {
53 private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTrackingFileManager.class);
54
55 @Deprecated
56 @Override
57 public Properties read(File file) {
58 return read(file.toPath());
59 }
60
61 @Override
62 public Properties read(Path path) {
63 if (Files.isReadable(path)) {
64 synchronized (getMutex(path)) {
65 try {
66 long fileSize = Files.size(path);
67 try (FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
68 FileLock unused = fileLock(fileChannel, Math.max(1, fileSize), true)) {
69 Properties props = new Properties();
70 props.load(Channels.newInputStream(fileChannel));
71 return props;
72 }
73 } catch (IOException e) {
74 LOGGER.warn("Failed to read tracking file '{}'", path, e);
75 throw new UncheckedIOException(e);
76 }
77 }
78 }
79 return null;
80 }
81
82 @Deprecated
83 @Override
84 public Properties update(File file, Map<String, String> updates) {
85 return update(file.toPath(), updates);
86 }
87
88 @Override
89 public Properties update(Path path, Map<String, String> updates) {
90 Properties props = new Properties();
91 try {
92 Files.createDirectories(path.getParent());
93 } catch (IOException e) {
94 LOGGER.warn("Failed to create tracking file parent '{}'", path, e);
95 throw new UncheckedIOException(e);
96 }
97
98 synchronized (getMutex(path)) {
99 try {
100 long fileSize;
101 try {
102 fileSize = Files.size(path);
103 } catch (IOException e) {
104 fileSize = 0L;
105 }
106 try (FileChannel fileChannel = FileChannel.open(
107 path, StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
108 FileLock unused = fileLock(fileChannel, Math.max(1, fileSize), false)) {
109 if (fileSize > 0) {
110 props.load(Channels.newInputStream(fileChannel));
111 }
112
113 for (Map.Entry<String, String> update : updates.entrySet()) {
114 if (update.getValue() == null) {
115 props.remove(update.getKey());
116 } else {
117 props.setProperty(update.getKey(), update.getValue());
118 }
119 }
120
121 LOGGER.debug("Writing tracking file '{}'", path);
122 ByteArrayOutputStream stream = new ByteArrayOutputStream(1024 * 2);
123 props.store(
124 stream,
125 "NOTE: This is a Maven Resolver internal implementation file"
126 + ", its format can be changed without prior notice.");
127 fileChannel.position(0);
128 int written = fileChannel.write(ByteBuffer.wrap(stream.toByteArray()));
129 fileChannel.truncate(written);
130 }
131 } catch (IOException e) {
132 LOGGER.warn("Failed to write tracking file '{}'", path, e);
133 throw new UncheckedIOException(e);
134 }
135 }
136
137 return props;
138 }
139
140 private Object getMutex(Path path) {
141
142
143
144
145
146
147
148 return path.toAbsolutePath().normalize().toString().intern();
149 }
150
151 @SuppressWarnings({"checkstyle:magicnumber"})
152 private FileLock fileLock(FileChannel channel, long size, boolean shared) throws IOException {
153 FileLock lock = null;
154 for (int attempts = 8; attempts >= 0; attempts--) {
155 try {
156 lock = channel.lock(0, size, shared);
157 break;
158 } catch (OverlappingFileLockException e) {
159 if (attempts <= 0) {
160 throw new IOException(e);
161 }
162 try {
163 Thread.sleep(50L);
164 } catch (InterruptedException e1) {
165 Thread.currentThread().interrupt();
166 }
167 }
168 }
169 if (lock == null) {
170 throw new IOException("Could not lock file");
171 }
172 return lock;
173 }
174 }