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