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