1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.repository.legacy;
20
21 import javax.inject.Named;
22 import javax.inject.Singleton;
23
24 import java.io.File;
25 import java.io.FileInputStream;
26 import java.io.IOException;
27 import java.io.RandomAccessFile;
28 import java.nio.channels.Channels;
29 import java.nio.channels.FileChannel;
30 import java.nio.channels.FileLock;
31 import java.util.Date;
32 import java.util.Properties;
33
34 import org.apache.maven.artifact.Artifact;
35 import org.apache.maven.artifact.repository.ArtifactRepository;
36 import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy;
37 import org.apache.maven.artifact.repository.Authentication;
38 import org.apache.maven.artifact.repository.metadata.RepositoryMetadata;
39 import org.apache.maven.repository.Proxy;
40 import org.codehaus.plexus.logging.AbstractLogEnabled;
41 import org.codehaus.plexus.logging.Logger;
42
43
44
45
46 @Named
47 @Singleton
48 @Deprecated
49 public class DefaultUpdateCheckManager extends AbstractLogEnabled implements UpdateCheckManager {
50
51 private static final String ERROR_KEY_SUFFIX = ".error";
52
53 public DefaultUpdateCheckManager() {}
54
55 public DefaultUpdateCheckManager(Logger logger) {
56 enableLogging(logger);
57 }
58
59 public static final String LAST_UPDATE_TAG = ".lastUpdated";
60
61 private static final String TOUCHFILE_NAME = "resolver-status.properties";
62
63 @Override
64 public boolean isUpdateRequired(Artifact artifact, ArtifactRepository repository) {
65 File file = artifact.getFile();
66
67 ArtifactRepositoryPolicy policy = artifact.isSnapshot() ? repository.getSnapshots() : repository.getReleases();
68
69 if (!policy.isEnabled()) {
70 if (getLogger().isDebugEnabled()) {
71 getLogger()
72 .debug("Skipping update check for " + artifact + " (" + file + ") from " + repository.getId()
73 + " (" + repository.getUrl() + ")");
74 }
75
76 return false;
77 }
78
79 if (getLogger().isDebugEnabled()) {
80 getLogger()
81 .debug("Determining update check for " + artifact + " (" + file + ") from " + repository.getId()
82 + " (" + repository.getUrl() + ")");
83 }
84
85 if (file == null) {
86
87 return true;
88 }
89
90 Date lastCheckDate;
91
92 if (file.exists()) {
93 lastCheckDate = new Date(file.lastModified());
94 } else {
95 File touchfile = getTouchfile(artifact);
96 lastCheckDate = readLastUpdated(touchfile, getRepositoryKey(repository));
97 }
98
99 return (lastCheckDate == null) || policy.checkOutOfDate(lastCheckDate);
100 }
101
102 @Override
103 public boolean isUpdateRequired(RepositoryMetadata metadata, ArtifactRepository repository, File file) {
104
105
106
107
108
109
110 ArtifactRepositoryPolicy policy = metadata.getPolicy(repository);
111
112 if (!policy.isEnabled()) {
113 if (getLogger().isDebugEnabled()) {
114 getLogger()
115 .debug("Skipping update check for " + metadata.getKey() + " (" + file + ") from "
116 + repository.getId() + " (" + repository.getUrl() + ")");
117 }
118
119 return false;
120 }
121
122 if (getLogger().isDebugEnabled()) {
123 getLogger()
124 .debug("Determining update check for " + metadata.getKey() + " (" + file + ") from "
125 + repository.getId() + " (" + repository.getUrl() + ")");
126 }
127
128 if (file == null) {
129
130 return true;
131 }
132
133 Date lastCheckDate = readLastUpdated(metadata, repository, file);
134
135 return (lastCheckDate == null) || policy.checkOutOfDate(lastCheckDate);
136 }
137
138 private Date readLastUpdated(RepositoryMetadata metadata, ArtifactRepository repository, File file) {
139 File touchfile = getTouchfile(metadata, file);
140
141 String key = getMetadataKey(repository, file);
142
143 return readLastUpdated(touchfile, key);
144 }
145
146 @Override
147 public String getError(Artifact artifact, ArtifactRepository repository) {
148 File touchFile = getTouchfile(artifact);
149 return getError(touchFile, getRepositoryKey(repository));
150 }
151
152 @Override
153 public void touch(Artifact artifact, ArtifactRepository repository, String error) {
154 File file = artifact.getFile();
155
156 File touchfile = getTouchfile(artifact);
157
158 if (file.exists()) {
159 touchfile.delete();
160 } else {
161 writeLastUpdated(touchfile, getRepositoryKey(repository), error);
162 }
163 }
164
165 @Override
166 public void touch(RepositoryMetadata metadata, ArtifactRepository repository, File file) {
167 File touchfile = getTouchfile(metadata, file);
168
169 String key = getMetadataKey(repository, file);
170
171 writeLastUpdated(touchfile, key, null);
172 }
173
174 String getMetadataKey(ArtifactRepository repository, File file) {
175 return repository.getId() + '.' + file.getName() + LAST_UPDATE_TAG;
176 }
177
178 String getRepositoryKey(ArtifactRepository repository) {
179 StringBuilder buffer = new StringBuilder(256);
180
181 Proxy proxy = repository.getProxy();
182 if (proxy != null) {
183 if (proxy.getUserName() != null) {
184 int hash = (proxy.getUserName() + proxy.getPassword()).hashCode();
185 buffer.append(hash).append('@');
186 }
187 buffer.append(proxy.getHost()).append(':').append(proxy.getPort()).append('>');
188 }
189
190
191 Authentication auth = repository.getAuthentication();
192 if (auth != null) {
193 int hash = (auth.getUsername() + auth.getPassword()).hashCode();
194 buffer.append(hash).append('@');
195 }
196
197
198 buffer.append(repository.getUrl());
199
200 return buffer.toString();
201 }
202
203 private void writeLastUpdated(File touchfile, String key, String error) {
204 synchronized (touchfile.getAbsolutePath().intern()) {
205 if (!touchfile.getParentFile().exists()
206 && !touchfile.getParentFile().mkdirs()) {
207 getLogger()
208 .debug("Failed to create directory: " + touchfile.getParent()
209 + " for tracking artifact metadata resolution.");
210 return;
211 }
212
213 FileChannel channel = null;
214 FileLock lock = null;
215 try {
216 Properties props = new Properties();
217
218 channel = new RandomAccessFile(touchfile, "rw").getChannel();
219 lock = channel.lock();
220
221 if (touchfile.canRead()) {
222 getLogger().debug("Reading resolution-state from: " + touchfile);
223 props.load(Channels.newInputStream(channel));
224 }
225
226 props.setProperty(key, Long.toString(System.currentTimeMillis()));
227
228 if (error != null) {
229 props.setProperty(key + ERROR_KEY_SUFFIX, error);
230 } else {
231 props.remove(key + ERROR_KEY_SUFFIX);
232 }
233
234 getLogger().debug("Writing resolution-state to: " + touchfile);
235 channel.truncate(0);
236 props.store(Channels.newOutputStream(channel), "Last modified on: " + new Date());
237
238 lock.release();
239 lock = null;
240
241 channel.close();
242 channel = null;
243 } catch (IOException e) {
244 getLogger()
245 .debug(
246 "Failed to record lastUpdated information for resolution.\nFile: "
247 + touchfile.toString() + "; key: " + key,
248 e);
249 } finally {
250 if (lock != null) {
251 try {
252 lock.release();
253 } catch (IOException e) {
254 getLogger()
255 .debug("Error releasing exclusive lock for resolution tracking file: " + touchfile, e);
256 }
257 }
258
259 if (channel != null) {
260 try {
261 channel.close();
262 } catch (IOException e) {
263 getLogger().debug("Error closing FileChannel for resolution tracking file: " + touchfile, e);
264 }
265 }
266 }
267 }
268 }
269
270 Date readLastUpdated(File touchfile, String key) {
271 getLogger().debug("Searching for " + key + " in resolution tracking file.");
272
273 Properties props = read(touchfile);
274 if (props != null) {
275 String rawVal = props.getProperty(key);
276 if (rawVal != null) {
277 try {
278 return new Date(Long.parseLong(rawVal));
279 } catch (NumberFormatException e) {
280 getLogger().debug("Cannot parse lastUpdated date: '" + rawVal + "'. Ignoring.", e);
281 }
282 }
283 }
284 return null;
285 }
286
287 private String getError(File touchFile, String key) {
288 Properties props = read(touchFile);
289 if (props != null) {
290 return props.getProperty(key + ERROR_KEY_SUFFIX);
291 }
292 return null;
293 }
294
295 private Properties read(File touchfile) {
296 if (!touchfile.canRead()) {
297 getLogger().debug("Skipped unreadable resolution tracking file: " + touchfile);
298 return null;
299 }
300
301 synchronized (touchfile.getAbsolutePath().intern()) {
302 try {
303 Properties props = new Properties();
304
305 try (FileInputStream in = new FileInputStream(touchfile)) {
306 try (FileLock lock = in.getChannel().lock(0, Long.MAX_VALUE, true)) {
307 getLogger().debug("Reading resolution-state from: " + touchfile);
308 props.load(in);
309
310 return props;
311 }
312 }
313
314 } catch (IOException e) {
315 getLogger().debug("Failed to read resolution tracking file: " + touchfile, e);
316
317 return null;
318 }
319 }
320 }
321
322 File getTouchfile(Artifact artifact) {
323 StringBuilder sb = new StringBuilder(128);
324 sb.append(artifact.getArtifactId());
325 sb.append('-').append(artifact.getBaseVersion());
326 if (artifact.getClassifier() != null) {
327 sb.append('-').append(artifact.getClassifier());
328 }
329 sb.append('.').append(artifact.getType()).append(LAST_UPDATE_TAG);
330 return new File(artifact.getFile().getParentFile(), sb.toString());
331 }
332
333 File getTouchfile(RepositoryMetadata metadata, File file) {
334 return new File(file.getParent(), TOUCHFILE_NAME);
335 }
336 }