1 | |
package org.apache.maven.archiva.proxy; |
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
import java.io.File; |
23 | |
import java.io.IOException; |
24 | |
import java.util.ArrayList; |
25 | |
import java.util.Collections; |
26 | |
import java.util.HashMap; |
27 | |
import java.util.LinkedHashMap; |
28 | |
import java.util.List; |
29 | |
import java.util.Map; |
30 | |
import java.util.Properties; |
31 | |
import java.util.Map.Entry; |
32 | |
|
33 | |
import org.apache.commons.collections.CollectionUtils; |
34 | |
import org.apache.commons.io.FileUtils; |
35 | |
import org.apache.commons.lang.StringUtils; |
36 | |
import org.apache.maven.archiva.configuration.ArchivaConfiguration; |
37 | |
import org.apache.maven.archiva.configuration.ConfigurationNames; |
38 | |
import org.apache.maven.archiva.configuration.NetworkProxyConfiguration; |
39 | |
import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration; |
40 | |
import org.apache.maven.archiva.model.ArtifactReference; |
41 | |
import org.apache.maven.archiva.model.Keys; |
42 | |
import org.apache.maven.archiva.model.RepositoryURL; |
43 | |
import org.apache.maven.archiva.policies.DownloadErrorPolicy; |
44 | |
import org.apache.maven.archiva.policies.DownloadPolicy; |
45 | |
import org.apache.maven.archiva.policies.PolicyConfigurationException; |
46 | |
import org.apache.maven.archiva.policies.PolicyViolationException; |
47 | |
import org.apache.maven.archiva.policies.PostDownloadPolicy; |
48 | |
import org.apache.maven.archiva.policies.PreDownloadPolicy; |
49 | |
import org.apache.maven.archiva.policies.ProxyDownloadException; |
50 | |
import org.apache.maven.archiva.policies.urlcache.UrlFailureCache; |
51 | |
import org.apache.maven.archiva.repository.ManagedRepositoryContent; |
52 | |
import org.apache.maven.archiva.repository.RemoteRepositoryContent; |
53 | |
import org.apache.maven.archiva.repository.RepositoryContentFactory; |
54 | |
import org.apache.maven.archiva.repository.RepositoryException; |
55 | |
import org.apache.maven.archiva.repository.RepositoryNotFoundException; |
56 | |
import org.apache.maven.archiva.repository.metadata.MetadataTools; |
57 | |
import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException; |
58 | |
import org.apache.maven.archiva.scheduled.ArchivaTaskScheduler; |
59 | |
import org.apache.maven.archiva.scheduled.tasks.RepositoryTask; |
60 | |
import org.apache.maven.archiva.scheduled.tasks.TaskCreator; |
61 | |
import org.apache.maven.wagon.ConnectionException; |
62 | |
import org.apache.maven.wagon.ResourceDoesNotExistException; |
63 | |
import org.apache.maven.wagon.Wagon; |
64 | |
import org.apache.maven.wagon.WagonException; |
65 | |
import org.apache.maven.wagon.authentication.AuthenticationException; |
66 | |
import org.apache.maven.wagon.authentication.AuthenticationInfo; |
67 | |
import org.apache.maven.wagon.proxy.ProxyInfo; |
68 | |
import org.apache.maven.wagon.repository.Repository; |
69 | |
import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; |
70 | |
import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException; |
71 | |
import org.codehaus.plexus.registry.Registry; |
72 | |
import org.codehaus.plexus.registry.RegistryListener; |
73 | |
import org.codehaus.plexus.taskqueue.TaskQueueException; |
74 | |
import org.codehaus.plexus.util.SelectorUtils; |
75 | |
import org.slf4j.Logger; |
76 | |
import org.slf4j.LoggerFactory; |
77 | |
|
78 | |
|
79 | |
|
80 | |
|
81 | |
|
82 | |
|
83 | |
|
84 | |
|
85 | |
|
86 | 0 | public class DefaultRepositoryProxyConnectors |
87 | |
implements RepositoryProxyConnectors, RegistryListener, Initializable |
88 | |
{ |
89 | 0 | private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class ); |
90 | |
|
91 | |
|
92 | |
|
93 | |
|
94 | |
private ArchivaConfiguration archivaConfiguration; |
95 | |
|
96 | |
|
97 | |
|
98 | |
|
99 | |
private RepositoryContentFactory repositoryFactory; |
100 | |
|
101 | |
|
102 | |
|
103 | |
|
104 | |
private MetadataTools metadataTools; |
105 | |
|
106 | |
|
107 | |
|
108 | |
|
109 | |
private Map<String, PreDownloadPolicy> preDownloadPolicies; |
110 | |
|
111 | |
|
112 | |
|
113 | |
|
114 | |
private Map<String, PostDownloadPolicy> postDownloadPolicies; |
115 | |
|
116 | |
|
117 | |
|
118 | |
|
119 | |
private Map<String, DownloadErrorPolicy> downloadErrorPolicies; |
120 | |
|
121 | |
|
122 | |
|
123 | |
|
124 | |
private UrlFailureCache urlFailureCache; |
125 | |
|
126 | 0 | private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>(); |
127 | |
|
128 | 0 | private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>(); |
129 | |
|
130 | |
|
131 | |
|
132 | |
|
133 | |
private WagonFactory wagonFactory; |
134 | |
|
135 | |
|
136 | |
|
137 | |
|
138 | |
private ArchivaTaskScheduler scheduler; |
139 | |
|
140 | |
public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact ) |
141 | |
throws ProxyDownloadException |
142 | |
{ |
143 | 0 | File localFile = toLocalFile( repository, artifact ); |
144 | |
|
145 | 0 | Properties requestProperties = new Properties(); |
146 | 0 | requestProperties.setProperty( "filetype", "artifact" ); |
147 | 0 | requestProperties.setProperty( "version", artifact.getVersion() ); |
148 | 0 | requestProperties.setProperty( "managedRepositoryId", repository.getId() ); |
149 | |
|
150 | 0 | List<ProxyConnector> connectors = getProxyConnectors( repository ); |
151 | 0 | Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>(); |
152 | 0 | for ( ProxyConnector connector : connectors ) |
153 | |
{ |
154 | 0 | if ( connector.isDisabled() ) |
155 | |
{ |
156 | 0 | continue; |
157 | |
} |
158 | |
|
159 | 0 | RemoteRepositoryContent targetRepository = connector.getTargetRepository(); |
160 | 0 | requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() ); |
161 | |
|
162 | 0 | String targetPath = targetRepository.toPath( artifact ); |
163 | |
|
164 | |
try |
165 | |
{ |
166 | 0 | File downloadedFile = |
167 | |
transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties, |
168 | |
true ); |
169 | |
|
170 | 0 | if ( fileExists( downloadedFile ) ) |
171 | |
{ |
172 | 0 | log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() ); |
173 | 0 | return downloadedFile; |
174 | |
} |
175 | |
} |
176 | 0 | catch ( NotFoundException e ) |
177 | |
{ |
178 | 0 | log.debug( "Artifact " + Keys.toKey( artifact ) + " not found on repository \"" |
179 | |
+ targetRepository.getRepository().getId() + "\"." ); |
180 | |
} |
181 | 0 | catch ( NotModifiedException e ) |
182 | |
{ |
183 | 0 | log.debug( "Artifact " + Keys.toKey( artifact ) + " not updated on repository \"" |
184 | |
+ targetRepository.getRepository().getId() + "\"." ); |
185 | |
} |
186 | 0 | catch ( ProxyException e ) |
187 | |
{ |
188 | 0 | validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact, |
189 | |
targetRepository, localFile, e, previousExceptions ); |
190 | 0 | } |
191 | 0 | } |
192 | |
|
193 | 0 | if ( !previousExceptions.isEmpty() ) |
194 | |
{ |
195 | 0 | throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories", |
196 | |
previousExceptions ); |
197 | |
} |
198 | |
|
199 | 0 | log.debug( "Exhausted all target repositories, artifact " + Keys.toKey( artifact ) + " not found." ); |
200 | |
|
201 | 0 | return null; |
202 | |
} |
203 | |
|
204 | |
public File fetchFromProxies( ManagedRepositoryContent repository, String path ) |
205 | |
{ |
206 | 0 | File localFile = new File( repository.getRepoRoot(), path ); |
207 | |
|
208 | |
|
209 | 0 | if ( localFile.exists() ) |
210 | |
{ |
211 | 0 | return null; |
212 | |
} |
213 | |
|
214 | 0 | Properties requestProperties = new Properties(); |
215 | 0 | requestProperties.setProperty( "filetype", "resource" ); |
216 | 0 | requestProperties.setProperty( "managedRepositoryId", repository.getId() ); |
217 | |
|
218 | 0 | List<ProxyConnector> connectors = getProxyConnectors( repository ); |
219 | 0 | for ( ProxyConnector connector : connectors ) |
220 | |
{ |
221 | 0 | if ( connector.isDisabled() ) |
222 | |
{ |
223 | 0 | continue; |
224 | |
} |
225 | |
|
226 | 0 | RemoteRepositoryContent targetRepository = connector.getTargetRepository(); |
227 | 0 | requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() ); |
228 | |
|
229 | 0 | String targetPath = path; |
230 | |
|
231 | |
try |
232 | |
{ |
233 | 0 | File downloadedFile = |
234 | |
transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties, |
235 | |
false ); |
236 | |
|
237 | 0 | if ( fileExists( downloadedFile ) ) |
238 | |
{ |
239 | 0 | log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() ); |
240 | 0 | return downloadedFile; |
241 | |
} |
242 | |
} |
243 | 0 | catch ( NotFoundException e ) |
244 | |
{ |
245 | 0 | log.debug( "Resource " + path + " not found on repository \"" |
246 | |
+ targetRepository.getRepository().getId() + "\"." ); |
247 | |
} |
248 | 0 | catch ( NotModifiedException e ) |
249 | |
{ |
250 | 0 | log.debug( "Resource " + path + " not updated on repository \"" |
251 | |
+ targetRepository.getRepository().getId() + "\"." ); |
252 | |
} |
253 | 0 | catch ( ProxyException e ) |
254 | |
{ |
255 | 0 | log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId() |
256 | |
+ "\" for resource " + path + ", continuing to next repository. Error message: " + e.getMessage() ); |
257 | 0 | log.debug( "Full stack trace", e ); |
258 | 0 | } |
259 | 0 | } |
260 | |
|
261 | 0 | log.debug( "Exhausted all target repositories, resource " + path + " not found." ); |
262 | |
|
263 | 0 | return null; |
264 | |
} |
265 | |
|
266 | |
public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath ) |
267 | |
{ |
268 | 0 | File localFile = new File( repository.getRepoRoot(), logicalPath ); |
269 | |
|
270 | 0 | Properties requestProperties = new Properties(); |
271 | 0 | requestProperties.setProperty( "filetype", "metadata" ); |
272 | 0 | boolean metadataNeedsUpdating = false; |
273 | 0 | long originalTimestamp = getLastModified( localFile ); |
274 | |
|
275 | 0 | List<ProxyConnector> connectors = getProxyConnectors( repository ); |
276 | 0 | for ( ProxyConnector connector : connectors ) |
277 | |
{ |
278 | 0 | if ( connector.isDisabled() ) |
279 | |
{ |
280 | 0 | continue; |
281 | |
} |
282 | |
|
283 | 0 | RemoteRepositoryContent targetRepository = connector.getTargetRepository(); |
284 | |
|
285 | 0 | File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath ); |
286 | 0 | long originalMetadataTimestamp = getLastModified( localRepoFile ); |
287 | |
|
288 | |
try |
289 | |
{ |
290 | 0 | transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties, |
291 | |
true ); |
292 | |
|
293 | 0 | if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) ) |
294 | |
{ |
295 | 0 | metadataNeedsUpdating = true; |
296 | |
} |
297 | |
} |
298 | 0 | catch ( NotFoundException e ) |
299 | |
{ |
300 | 0 | log.debug( "Metadata " + logicalPath + " not found on remote repository \"" |
301 | |
+ targetRepository.getRepository().getId() + "\".", e ); |
302 | |
} |
303 | 0 | catch ( NotModifiedException e ) |
304 | |
{ |
305 | 0 | log.debug( "Metadata " + logicalPath + " not updated on remote repository \"" |
306 | |
+ targetRepository.getRepository().getId() + "\".", e ); |
307 | |
} |
308 | 0 | catch ( ProxyException e ) |
309 | |
{ |
310 | 0 | log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId() |
311 | |
+ "\" for versioned Metadata " + logicalPath + ", continuing to next repository. Error message: " |
312 | |
+ e.getMessage() ); |
313 | 0 | log.debug( "Full stack trace", e ); |
314 | 0 | } |
315 | 0 | } |
316 | |
|
317 | 0 | if ( hasBeenUpdated( localFile, originalTimestamp ) ) |
318 | |
{ |
319 | 0 | metadataNeedsUpdating = true; |
320 | |
} |
321 | |
|
322 | 0 | if ( metadataNeedsUpdating || !localFile.exists() ) |
323 | |
{ |
324 | |
try |
325 | |
{ |
326 | 0 | metadataTools.updateMetadata( repository, logicalPath ); |
327 | |
} |
328 | 0 | catch ( RepositoryMetadataException e ) |
329 | |
{ |
330 | 0 | log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e ); |
331 | 0 | } |
332 | |
} |
333 | |
|
334 | 0 | if ( fileExists( localFile ) ) |
335 | |
{ |
336 | 0 | return localFile; |
337 | |
} |
338 | |
|
339 | 0 | return null; |
340 | |
} |
341 | |
|
342 | |
private long getLastModified( File file ) |
343 | |
{ |
344 | 0 | if ( !file.exists() || !file.isFile() ) |
345 | |
{ |
346 | 0 | return 0; |
347 | |
} |
348 | |
|
349 | 0 | return file.lastModified(); |
350 | |
} |
351 | |
|
352 | |
private boolean hasBeenUpdated( File file, long originalLastModified ) |
353 | |
{ |
354 | 0 | if ( !file.exists() || !file.isFile() ) |
355 | |
{ |
356 | 0 | return false; |
357 | |
} |
358 | |
|
359 | 0 | long currentLastModified = getLastModified( file ); |
360 | 0 | return ( currentLastModified > originalLastModified ); |
361 | |
} |
362 | |
|
363 | |
private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository, |
364 | |
String targetPath ) |
365 | |
{ |
366 | 0 | String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath ); |
367 | 0 | return new File( repository.getRepoRoot(), repoPath ); |
368 | |
} |
369 | |
|
370 | |
|
371 | |
|
372 | |
|
373 | |
public boolean hasProxies( ManagedRepositoryContent repository ) |
374 | |
{ |
375 | 0 | synchronized ( this.proxyConnectorMap ) |
376 | |
{ |
377 | 0 | return this.proxyConnectorMap.containsKey( repository.getId() ); |
378 | 0 | } |
379 | |
} |
380 | |
|
381 | |
private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact ) |
382 | |
{ |
383 | 0 | return repository.toFile( artifact ); |
384 | |
} |
385 | |
|
386 | |
|
387 | |
|
388 | |
|
389 | |
|
390 | |
|
391 | |
|
392 | |
private boolean fileExists( File file ) |
393 | |
{ |
394 | 0 | if ( file == null ) |
395 | |
{ |
396 | 0 | return false; |
397 | |
} |
398 | |
|
399 | 0 | if ( !file.exists() ) |
400 | |
{ |
401 | 0 | return false; |
402 | |
} |
403 | |
|
404 | 0 | if ( !file.isFile() ) |
405 | |
{ |
406 | 0 | return false; |
407 | |
} |
408 | |
|
409 | 0 | return true; |
410 | |
} |
411 | |
|
412 | |
|
413 | |
|
414 | |
|
415 | |
|
416 | |
|
417 | |
|
418 | |
|
419 | |
|
420 | |
|
421 | |
|
422 | |
|
423 | |
|
424 | |
|
425 | |
|
426 | |
|
427 | |
|
428 | |
private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath, |
429 | |
ManagedRepositoryContent repository, File resource, Properties requestProperties, |
430 | |
boolean executeConsumers ) |
431 | |
throws ProxyException, NotModifiedException |
432 | |
{ |
433 | 0 | String url = remoteRepository.getURL().getUrl(); |
434 | 0 | if ( !url.endsWith( "/" ) ) |
435 | |
{ |
436 | 0 | url = url + "/"; |
437 | |
} |
438 | 0 | url = url + remotePath; |
439 | 0 | requestProperties.setProperty( "url", url ); |
440 | |
|
441 | |
|
442 | 0 | if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) ) |
443 | |
{ |
444 | |
|
445 | 0 | if ( !matchesPattern( remotePath, connector.getWhitelist() ) ) |
446 | |
{ |
447 | 0 | log.debug( "Path [" + remotePath |
448 | |
+ "] is not part of defined whitelist (skipping transfer from repository [" |
449 | |
+ remoteRepository.getRepository().getName() + "])." ); |
450 | 0 | return null; |
451 | |
} |
452 | |
} |
453 | |
|
454 | |
|
455 | 0 | if ( matchesPattern( remotePath, connector.getBlacklist() ) ) |
456 | |
{ |
457 | 0 | log.debug( "Path [" + remotePath + "] is part of blacklist (skipping transfer from repository [" |
458 | |
+ remoteRepository.getRepository().getName() + "])." ); |
459 | 0 | return null; |
460 | |
} |
461 | |
|
462 | |
|
463 | |
try |
464 | |
{ |
465 | 0 | validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource ); |
466 | |
} |
467 | 0 | catch ( PolicyViolationException e ) |
468 | |
{ |
469 | 0 | String emsg = "Transfer not attempted on " + url + " : " + e.getMessage(); |
470 | 0 | if ( fileExists( resource ) ) |
471 | |
{ |
472 | 0 | log.debug( emsg + ": using already present local file." ); |
473 | 0 | return resource; |
474 | |
} |
475 | |
|
476 | 0 | log.debug( emsg ); |
477 | 0 | return null; |
478 | 0 | } |
479 | |
|
480 | 0 | File tmpMd5 = null; |
481 | 0 | File tmpSha1 = null; |
482 | 0 | File tmpResource = null; |
483 | |
|
484 | 0 | File workingDirectory = createWorkingDirectory( repository ); |
485 | |
try |
486 | |
{ |
487 | 0 | Wagon wagon = null; |
488 | |
try |
489 | |
{ |
490 | 0 | RepositoryURL repoUrl = remoteRepository.getURL(); |
491 | 0 | String protocol = repoUrl.getProtocol(); |
492 | 0 | wagon = (Wagon) wagonFactory.getWagon( "wagon#" + protocol ); |
493 | 0 | if ( wagon == null ) |
494 | |
{ |
495 | 0 | throw new ProxyException( "Unsupported target repository protocol: " + protocol ); |
496 | |
} |
497 | |
|
498 | 0 | boolean connected = connectToRepository( connector, wagon, remoteRepository ); |
499 | 0 | if ( connected ) |
500 | |
{ |
501 | 0 | tmpResource = new File( workingDirectory, resource.getName() ); |
502 | 0 | transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource ); |
503 | |
|
504 | |
|
505 | |
|
506 | |
|
507 | 0 | tmpSha1 = |
508 | |
transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, |
509 | |
".sha1" ); |
510 | 0 | tmpMd5 = |
511 | |
transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, |
512 | |
".md5" ); |
513 | |
} |
514 | |
} |
515 | 0 | catch ( NotFoundException e ) |
516 | |
{ |
517 | 0 | urlFailureCache.cacheFailure( url ); |
518 | 0 | throw e; |
519 | |
} |
520 | 0 | catch ( NotModifiedException e ) |
521 | |
{ |
522 | |
|
523 | 0 | throw e; |
524 | |
} |
525 | 0 | catch ( ProxyException e ) |
526 | |
{ |
527 | 0 | urlFailureCache.cacheFailure( url ); |
528 | 0 | throw e; |
529 | |
} |
530 | |
finally |
531 | |
{ |
532 | 0 | if ( wagon != null ) |
533 | |
{ |
534 | |
try |
535 | |
{ |
536 | 0 | wagon.disconnect(); |
537 | |
} |
538 | 0 | catch ( ConnectionException e ) |
539 | |
{ |
540 | 0 | log.warn( "Unable to disconnect wagon.", e ); |
541 | 0 | } |
542 | |
} |
543 | |
} |
544 | |
|
545 | |
|
546 | |
try |
547 | |
{ |
548 | 0 | validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource ); |
549 | |
} |
550 | 0 | catch ( PolicyViolationException e ) |
551 | |
{ |
552 | 0 | log.warn( "Transfer invalidated from " + url + " : " + e.getMessage() ); |
553 | 0 | executeConsumers = false; |
554 | 0 | if ( !fileExists( tmpResource ) ) |
555 | |
{ |
556 | 0 | resource = null; |
557 | |
} |
558 | 0 | } |
559 | |
|
560 | 0 | if ( resource != null ) |
561 | |
{ |
562 | 0 | synchronized ( resource.getAbsolutePath().intern() ) |
563 | |
{ |
564 | 0 | File directory = resource.getParentFile(); |
565 | 0 | moveFileIfExists( tmpMd5, directory ); |
566 | 0 | moveFileIfExists( tmpSha1, directory ); |
567 | 0 | moveFileIfExists( tmpResource, directory ); |
568 | 0 | } |
569 | |
} |
570 | |
} |
571 | |
finally |
572 | |
{ |
573 | 0 | FileUtils.deleteQuietly( workingDirectory ); |
574 | 0 | } |
575 | |
|
576 | 0 | if ( executeConsumers ) |
577 | |
{ |
578 | |
|
579 | |
|
580 | 0 | queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource ); |
581 | |
} |
582 | |
|
583 | 0 | return resource; |
584 | |
} |
585 | |
|
586 | |
private void queueRepositoryTask( String repositoryId, File localFile ) |
587 | |
{ |
588 | 0 | RepositoryTask task = TaskCreator.createRepositoryTask( repositoryId, localFile, true, true ); |
589 | |
|
590 | |
try |
591 | |
{ |
592 | 0 | scheduler.queueRepositoryTask( task ); |
593 | |
} |
594 | 0 | catch ( TaskQueueException e ) |
595 | |
{ |
596 | 0 | log.error( "Unable to queue repository task to execute consumers on resource file ['" + |
597 | |
localFile.getName() + "']." ); |
598 | 0 | } |
599 | 0 | } |
600 | |
|
601 | |
|
602 | |
|
603 | |
|
604 | |
|
605 | |
|
606 | |
|
607 | |
private void moveFileIfExists( File fileToMove, File directory ) |
608 | |
throws ProxyException |
609 | |
{ |
610 | 0 | if ( fileToMove != null && fileToMove.exists() ) |
611 | |
{ |
612 | 0 | File newLocation = new File( directory, fileToMove.getName() ); |
613 | 0 | moveTempToTarget( fileToMove, newLocation ); |
614 | |
} |
615 | 0 | } |
616 | |
|
617 | |
|
618 | |
|
619 | |
|
620 | |
|
621 | |
|
622 | |
|
623 | |
|
624 | |
|
625 | |
|
626 | |
|
627 | |
|
628 | |
|
629 | |
|
630 | |
private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath, |
631 | |
ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext ) |
632 | |
throws ProxyException |
633 | |
{ |
634 | 0 | String url = remoteRepository.getURL().getUrl() + remotePath + ext; |
635 | |
|
636 | |
|
637 | 0 | if ( urlFailureCache.hasFailedBefore( url ) ) |
638 | |
{ |
639 | 0 | return null; |
640 | |
} |
641 | |
|
642 | 0 | File destFile = new File( tmpDirectory, resource.getName() + ext ); |
643 | |
|
644 | |
try |
645 | |
{ |
646 | 0 | transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile ); |
647 | 0 | log.debug( "Checksum " + url + " Downloaded: " + destFile + " to move to " + resource ); |
648 | |
} |
649 | 0 | catch ( NotFoundException e ) |
650 | |
{ |
651 | 0 | urlFailureCache.cacheFailure( url ); |
652 | 0 | log.debug( "Transfer failed, checksum not found: " + url ); |
653 | |
|
654 | |
} |
655 | 0 | catch ( NotModifiedException e ) |
656 | |
{ |
657 | 0 | log.debug( "Transfer skipped, checksum not modified: " + url ); |
658 | |
|
659 | |
} |
660 | 0 | catch ( ProxyException e ) |
661 | |
{ |
662 | 0 | urlFailureCache.cacheFailure( url ); |
663 | 0 | log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e ); |
664 | |
|
665 | 0 | throw e; |
666 | 0 | } |
667 | 0 | return destFile; |
668 | |
} |
669 | |
|
670 | |
|
671 | |
|
672 | |
|
673 | |
|
674 | |
|
675 | |
|
676 | |
|
677 | |
|
678 | |
|
679 | |
|
680 | |
|
681 | |
|
682 | |
private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath, |
683 | |
ManagedRepositoryContent repository, File origFile, File destFile ) |
684 | |
throws ProxyException |
685 | |
{ |
686 | 0 | assert ( remotePath != null ); |
687 | |
|
688 | |
|
689 | |
try |
690 | |
{ |
691 | 0 | boolean success = false; |
692 | |
|
693 | 0 | if ( !origFile.exists() ) |
694 | |
{ |
695 | 0 | log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() ); |
696 | 0 | wagon.get( remotePath, destFile ); |
697 | 0 | success = true; |
698 | |
|
699 | |
|
700 | 0 | log.debug( "Downloaded successfully." ); |
701 | |
} |
702 | |
else |
703 | |
{ |
704 | 0 | log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() |
705 | |
+ " if updated" ); |
706 | 0 | success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() ); |
707 | 0 | if ( !success ) |
708 | |
{ |
709 | 0 | throw new NotModifiedException( "Not downloaded, as local file is newer than remote side: " |
710 | |
+ origFile.getAbsolutePath() ); |
711 | |
} |
712 | |
|
713 | 0 | if ( destFile.exists() ) |
714 | |
{ |
715 | 0 | log.debug( "Downloaded successfully." ); |
716 | |
} |
717 | |
} |
718 | |
} |
719 | 0 | catch ( ResourceDoesNotExistException e ) |
720 | |
{ |
721 | 0 | throw new NotFoundException( "Resource [" + remoteRepository.getURL() + "/" + remotePath |
722 | |
+ "] does not exist: " + e.getMessage(), e ); |
723 | |
} |
724 | 0 | catch ( WagonException e ) |
725 | |
{ |
726 | |
|
727 | |
|
728 | 0 | String msg = |
729 | |
"Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage(); |
730 | 0 | if ( e.getCause() != null ) |
731 | |
{ |
732 | 0 | msg += " (cause: " + e.getCause() + ")"; |
733 | |
} |
734 | 0 | throw new ProxyException( msg, e ); |
735 | 0 | } |
736 | 0 | } |
737 | |
|
738 | |
|
739 | |
|
740 | |
|
741 | |
|
742 | |
|
743 | |
|
744 | |
|
745 | |
|
746 | |
|
747 | |
|
748 | |
private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings, |
749 | |
Properties request, File localFile ) |
750 | |
throws PolicyViolationException |
751 | |
{ |
752 | 0 | for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() ) |
753 | |
{ |
754 | 0 | String key = entry.getKey(); |
755 | 0 | DownloadPolicy policy = entry.getValue(); |
756 | 0 | String defaultSetting = policy.getDefaultOption(); |
757 | 0 | String setting = StringUtils.defaultString( settings.get( key ), defaultSetting ); |
758 | |
|
759 | 0 | log.debug( "Applying [" + key + "] policy with [" + setting + "]" ); |
760 | |
try |
761 | |
{ |
762 | 0 | policy.applyPolicy( setting, request, localFile ); |
763 | |
} |
764 | 0 | catch ( PolicyConfigurationException e ) |
765 | |
{ |
766 | 0 | log.error( e.getMessage(), e ); |
767 | 0 | } |
768 | 0 | } |
769 | 0 | } |
770 | |
|
771 | |
private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings, |
772 | |
Properties request, ArtifactReference artifact, RemoteRepositoryContent content, |
773 | |
File localFile, ProxyException exception, Map<String, Exception> previousExceptions ) |
774 | |
throws ProxyDownloadException |
775 | |
{ |
776 | 0 | boolean process = true; |
777 | 0 | for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() ) |
778 | |
{ |
779 | 0 | String key = entry.getKey(); |
780 | 0 | DownloadErrorPolicy policy = entry.getValue(); |
781 | 0 | String defaultSetting = policy.getDefaultOption(); |
782 | 0 | String setting = StringUtils.defaultString( settings.get( key ), defaultSetting ); |
783 | |
|
784 | 0 | log.debug( "Applying [" + key + "] policy with [" + setting + "]" ); |
785 | |
try |
786 | |
{ |
787 | |
|
788 | 0 | process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions ); |
789 | 0 | if ( !process ) |
790 | |
{ |
791 | 0 | break; |
792 | |
} |
793 | |
} |
794 | 0 | catch ( PolicyConfigurationException e ) |
795 | |
{ |
796 | 0 | log.error( e.getMessage(), e ); |
797 | 0 | } |
798 | 0 | } |
799 | |
|
800 | 0 | if ( process ) |
801 | |
{ |
802 | |
|
803 | 0 | if ( !previousExceptions.containsKey( content.getId() ) ) |
804 | |
{ |
805 | 0 | throw new ProxyDownloadException( |
806 | |
"An error occurred in downloading from the remote repository, and the policy is to fail immediately", |
807 | |
content.getId(), exception ); |
808 | |
} |
809 | |
} |
810 | |
else |
811 | |
{ |
812 | |
|
813 | 0 | previousExceptions.remove( content.getId() ); |
814 | |
} |
815 | |
|
816 | 0 | log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " |
817 | |
+ Keys.toKey( artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() ); |
818 | 0 | log.debug( "Full stack trace", exception ); |
819 | 0 | } |
820 | |
|
821 | |
|
822 | |
|
823 | |
|
824 | |
|
825 | |
|
826 | |
|
827 | |
|
828 | |
private File createWorkingDirectory( ManagedRepositoryContent repository ) |
829 | |
{ |
830 | |
|
831 | |
try |
832 | |
{ |
833 | 0 | File tmpDir = File.createTempFile( ".workingdirectory", null, new File( repository.getRepoRoot() ) ); |
834 | 0 | tmpDir.delete(); |
835 | 0 | tmpDir.mkdirs(); |
836 | 0 | return tmpDir; |
837 | |
} |
838 | 0 | catch ( IOException e ) |
839 | |
{ |
840 | 0 | throw new RuntimeException( "Could not create working directory for this request", e ); |
841 | |
} |
842 | |
} |
843 | |
|
844 | |
|
845 | |
|
846 | |
|
847 | |
|
848 | |
|
849 | |
|
850 | |
|
851 | |
|
852 | |
private void moveTempToTarget( File temp, File target ) |
853 | |
throws ProxyException |
854 | |
{ |
855 | 0 | if ( target.exists() && !target.delete() ) |
856 | |
{ |
857 | 0 | throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() ); |
858 | |
} |
859 | |
|
860 | 0 | target.getParentFile().mkdirs(); |
861 | 0 | if ( !temp.renameTo( target ) ) |
862 | |
{ |
863 | 0 | log.warn( "Unable to rename tmp file to its final name... resorting to copy command." ); |
864 | |
|
865 | |
try |
866 | |
{ |
867 | 0 | FileUtils.copyFile( temp, target ); |
868 | |
} |
869 | 0 | catch ( IOException e ) |
870 | |
{ |
871 | 0 | if ( target.exists() ) |
872 | |
{ |
873 | 0 | log.debug( "Tried to copy file " + temp.getName() + " to " + target.getAbsolutePath() |
874 | |
+ " but file with this name already exists." ); |
875 | |
} |
876 | |
else |
877 | |
{ |
878 | 0 | throw new ProxyException( "Cannot copy tmp file " + temp.getAbsolutePath() |
879 | |
+ " to its final location", e ); |
880 | |
} |
881 | |
} |
882 | |
finally |
883 | |
{ |
884 | 0 | FileUtils.deleteQuietly( temp ); |
885 | 0 | } |
886 | |
} |
887 | 0 | } |
888 | |
|
889 | |
|
890 | |
|
891 | |
|
892 | |
|
893 | |
|
894 | |
|
895 | |
|
896 | |
|
897 | |
private boolean connectToRepository( ProxyConnector connector, Wagon wagon, RemoteRepositoryContent remoteRepository ) |
898 | |
{ |
899 | 0 | boolean connected = false; |
900 | |
|
901 | |
final ProxyInfo networkProxy; |
902 | 0 | synchronized ( this.networkProxyMap ) |
903 | |
{ |
904 | 0 | networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() ); |
905 | 0 | } |
906 | |
|
907 | 0 | if ( log.isDebugEnabled() ) |
908 | |
{ |
909 | 0 | if ( networkProxy != null ) |
910 | |
{ |
911 | |
|
912 | 0 | String msg = |
913 | |
"Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort() |
914 | |
+ " to connect to remote repository " + remoteRepository.getURL(); |
915 | 0 | if ( networkProxy.getNonProxyHosts() != null ) |
916 | |
{ |
917 | 0 | msg += "; excluding hosts: " + networkProxy.getNonProxyHosts(); |
918 | |
} |
919 | 0 | if ( StringUtils.isNotBlank( networkProxy.getUserName() ) ) |
920 | |
{ |
921 | 0 | msg += "; as user: " + networkProxy.getUserName(); |
922 | |
} |
923 | 0 | log.debug( msg ); |
924 | |
} |
925 | |
} |
926 | |
|
927 | 0 | AuthenticationInfo authInfo = null; |
928 | 0 | String username = remoteRepository.getRepository().getUsername(); |
929 | 0 | String password = remoteRepository.getRepository().getPassword(); |
930 | |
|
931 | 0 | if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) ) |
932 | |
{ |
933 | 0 | log.debug( "Using username " + username + " to connect to remote repository " + remoteRepository.getURL() ); |
934 | 0 | authInfo = new AuthenticationInfo(); |
935 | 0 | authInfo.setUserName( username ); |
936 | 0 | authInfo.setPassword( password ); |
937 | |
} |
938 | |
|
939 | |
|
940 | 0 | int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000; |
941 | |
|
942 | |
|
943 | 0 | wagon.setTimeout( timeoutInMilliseconds ); |
944 | |
|
945 | |
try |
946 | |
{ |
947 | 0 | Repository wagonRepository = |
948 | |
new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() ); |
949 | 0 | wagon.connect( wagonRepository, authInfo, networkProxy ); |
950 | 0 | connected = true; |
951 | |
} |
952 | 0 | catch ( ConnectionException e ) |
953 | |
{ |
954 | 0 | log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() ); |
955 | 0 | connected = false; |
956 | |
} |
957 | 0 | catch ( AuthenticationException e ) |
958 | |
{ |
959 | 0 | log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() ); |
960 | 0 | connected = false; |
961 | 0 | } |
962 | |
|
963 | 0 | return connected; |
964 | |
} |
965 | |
|
966 | |
|
967 | |
|
968 | |
|
969 | |
|
970 | |
|
971 | |
|
972 | |
|
973 | |
private boolean matchesPattern( String path, List<String> patterns ) |
974 | |
{ |
975 | 0 | if ( CollectionUtils.isEmpty( patterns ) ) |
976 | |
{ |
977 | 0 | return false; |
978 | |
} |
979 | |
|
980 | 0 | if ( !path.startsWith( "/" ) ) |
981 | |
{ |
982 | 0 | path = "/" + path; |
983 | |
} |
984 | |
|
985 | 0 | for ( String pattern : patterns ) |
986 | |
{ |
987 | 0 | if ( !pattern.startsWith( "/" ) ) |
988 | |
{ |
989 | 0 | pattern = "/" + pattern; |
990 | |
} |
991 | |
|
992 | 0 | if ( SelectorUtils.matchPath( pattern, path, false ) ) |
993 | |
{ |
994 | 0 | return true; |
995 | |
} |
996 | |
} |
997 | |
|
998 | 0 | return false; |
999 | |
} |
1000 | |
|
1001 | |
|
1002 | |
|
1003 | |
|
1004 | |
public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository ) |
1005 | |
{ |
1006 | 0 | synchronized ( this.proxyConnectorMap ) |
1007 | |
{ |
1008 | 0 | List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() ); |
1009 | 0 | if ( ret == null ) |
1010 | |
{ |
1011 | 0 | return Collections.emptyList(); |
1012 | |
} |
1013 | |
|
1014 | 0 | Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() ); |
1015 | 0 | return ret; |
1016 | 0 | } |
1017 | |
} |
1018 | |
|
1019 | |
public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue ) |
1020 | |
{ |
1021 | 0 | if ( ConfigurationNames.isNetworkProxy( propertyName ) |
1022 | |
|| ConfigurationNames.isManagedRepositories( propertyName ) |
1023 | |
|| ConfigurationNames.isRemoteRepositories( propertyName ) |
1024 | |
|| ConfigurationNames.isProxyConnector( propertyName ) ) |
1025 | |
{ |
1026 | 0 | initConnectorsAndNetworkProxies(); |
1027 | |
} |
1028 | 0 | } |
1029 | |
|
1030 | |
public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue ) |
1031 | |
{ |
1032 | |
|
1033 | 0 | } |
1034 | |
|
1035 | |
@SuppressWarnings( "unchecked" ) |
1036 | |
private void initConnectorsAndNetworkProxies() |
1037 | |
{ |
1038 | 0 | synchronized ( this.proxyConnectorMap ) |
1039 | |
{ |
1040 | 0 | ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator(); |
1041 | 0 | this.proxyConnectorMap.clear(); |
1042 | |
|
1043 | 0 | List<ProxyConnectorConfiguration> proxyConfigs = |
1044 | |
archivaConfiguration.getConfiguration().getProxyConnectors(); |
1045 | 0 | for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs ) |
1046 | |
{ |
1047 | 0 | String key = proxyConfig.getSourceRepoId(); |
1048 | |
|
1049 | |
try |
1050 | |
{ |
1051 | |
|
1052 | 0 | ProxyConnector connector = new ProxyConnector(); |
1053 | |
|
1054 | 0 | connector.setSourceRepository( repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) ); |
1055 | 0 | connector.setTargetRepository( repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) ); |
1056 | |
|
1057 | 0 | connector.setProxyId( proxyConfig.getProxyId() ); |
1058 | 0 | connector.setPolicies( proxyConfig.getPolicies() ); |
1059 | 0 | connector.setOrder( proxyConfig.getOrder() ); |
1060 | 0 | connector.setDisabled( proxyConfig.isDisabled() ); |
1061 | |
|
1062 | |
|
1063 | 0 | List<String> blacklist = new ArrayList<String>(); |
1064 | 0 | if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) ) |
1065 | |
{ |
1066 | 0 | blacklist.addAll( proxyConfig.getBlackListPatterns() ); |
1067 | |
} |
1068 | 0 | connector.setBlacklist( blacklist ); |
1069 | |
|
1070 | |
|
1071 | 0 | List<String> whitelist = new ArrayList<String>(); |
1072 | 0 | if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) ) |
1073 | |
{ |
1074 | 0 | whitelist.addAll( proxyConfig.getWhiteListPatterns() ); |
1075 | |
} |
1076 | 0 | connector.setWhitelist( whitelist ); |
1077 | |
|
1078 | |
|
1079 | 0 | List<ProxyConnector> connectors = this.proxyConnectorMap.get( key ); |
1080 | 0 | if ( connectors == null ) |
1081 | |
{ |
1082 | |
|
1083 | 0 | connectors = new ArrayList<ProxyConnector>(); |
1084 | |
} |
1085 | |
|
1086 | |
|
1087 | 0 | connectors.add( connector ); |
1088 | |
|
1089 | |
|
1090 | 0 | Collections.sort( connectors, proxyOrderSorter ); |
1091 | |
|
1092 | |
|
1093 | 0 | this.proxyConnectorMap.put( key, connectors ); |
1094 | |
} |
1095 | 0 | catch ( RepositoryNotFoundException e ) |
1096 | |
{ |
1097 | 0 | log.warn( "Unable to use proxy connector: " + e.getMessage(), e ); |
1098 | |
} |
1099 | 0 | catch ( RepositoryException e ) |
1100 | |
{ |
1101 | 0 | log.warn( "Unable to use proxy connector: " + e.getMessage(), e ); |
1102 | 0 | } |
1103 | 0 | } |
1104 | |
|
1105 | 0 | } |
1106 | |
|
1107 | 0 | synchronized ( this.networkProxyMap ) |
1108 | |
{ |
1109 | 0 | this.networkProxyMap.clear(); |
1110 | |
|
1111 | 0 | List<NetworkProxyConfiguration> networkProxies = |
1112 | |
archivaConfiguration.getConfiguration().getNetworkProxies(); |
1113 | 0 | for ( NetworkProxyConfiguration networkProxyConfig : networkProxies ) |
1114 | |
{ |
1115 | 0 | String key = networkProxyConfig.getId(); |
1116 | |
|
1117 | 0 | ProxyInfo proxy = new ProxyInfo(); |
1118 | |
|
1119 | 0 | proxy.setType( networkProxyConfig.getProtocol() ); |
1120 | 0 | proxy.setHost( networkProxyConfig.getHost() ); |
1121 | 0 | proxy.setPort( networkProxyConfig.getPort() ); |
1122 | 0 | proxy.setUserName( networkProxyConfig.getUsername() ); |
1123 | 0 | proxy.setPassword( networkProxyConfig.getPassword() ); |
1124 | |
|
1125 | 0 | this.networkProxyMap.put( key, proxy ); |
1126 | 0 | } |
1127 | 0 | } |
1128 | 0 | } |
1129 | |
|
1130 | |
public void initialize() |
1131 | |
throws InitializationException |
1132 | |
{ |
1133 | 0 | initConnectorsAndNetworkProxies(); |
1134 | 0 | archivaConfiguration.addChangeListener( this ); |
1135 | 0 | } |
1136 | |
} |