This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.proxy;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.archiva.admin.model.RepositoryAdminException;
023import org.apache.archiva.admin.model.beans.NetworkProxy;
024import org.apache.archiva.admin.model.beans.ProxyConnectorRuleType;
025import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin;
026import org.apache.archiva.common.filelock.FileLockException;
027import org.apache.archiva.common.filelock.FileLockManager;
028import org.apache.archiva.common.filelock.FileLockTimeoutException;
029import org.apache.archiva.common.filelock.Lock;
030import org.apache.archiva.configuration.ArchivaConfiguration;
031import org.apache.archiva.configuration.Configuration;
032import org.apache.archiva.configuration.ConfigurationNames;
033import org.apache.archiva.configuration.NetworkProxyConfiguration;
034import org.apache.archiva.configuration.ProxyConnectorConfiguration;
035import org.apache.archiva.configuration.ProxyConnectorRuleConfiguration;
036import org.apache.archiva.model.ArtifactReference;
037import org.apache.archiva.model.Keys;
038import org.apache.archiva.model.RepositoryURL;
039import org.apache.archiva.policies.DownloadErrorPolicy;
040import org.apache.archiva.policies.DownloadPolicy;
041import org.apache.archiva.policies.PolicyConfigurationException;
042import org.apache.archiva.policies.PolicyViolationException;
043import org.apache.archiva.policies.PostDownloadPolicy;
044import org.apache.archiva.policies.PreDownloadPolicy;
045import org.apache.archiva.policies.ProxyDownloadException;
046import org.apache.archiva.policies.urlcache.UrlFailureCache;
047import org.apache.archiva.proxy.common.WagonFactory;
048import org.apache.archiva.proxy.common.WagonFactoryException;
049import org.apache.archiva.proxy.common.WagonFactoryRequest;
050import org.apache.archiva.proxy.model.ProxyConnector;
051import org.apache.archiva.proxy.model.ProxyFetchResult;
052import org.apache.archiva.proxy.model.RepositoryProxyConnectors;
053import org.apache.archiva.redback.components.registry.Registry;
054import org.apache.archiva.redback.components.registry.RegistryListener;
055import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
056import org.apache.archiva.repository.ManagedRepository;
057import org.apache.archiva.repository.ManagedRepositoryContent;
058import org.apache.archiva.repository.PasswordCredentials;
059import org.apache.archiva.repository.RemoteRepository;
060import org.apache.archiva.repository.RemoteRepositoryContent;
061import org.apache.archiva.repository.RepositoryContentFactory;
062import org.apache.archiva.repository.RepositoryCredentials;
063import org.apache.archiva.repository.RepositoryRegistry;
064import org.apache.archiva.repository.metadata.MetadataTools;
065import org.apache.archiva.repository.metadata.RepositoryMetadataException;
066import org.apache.archiva.scheduler.ArchivaTaskScheduler;
067import org.apache.archiva.scheduler.repository.model.RepositoryTask;
068import org.apache.commons.collections4.CollectionUtils;
069import org.apache.commons.io.FilenameUtils;
070import org.apache.commons.lang.StringUtils;
071import org.apache.commons.lang.SystemUtils;
072import org.apache.maven.wagon.ConnectionException;
073import org.apache.maven.wagon.ResourceDoesNotExistException;
074import org.apache.maven.wagon.Wagon;
075import org.apache.maven.wagon.WagonException;
076import org.apache.maven.wagon.authentication.AuthenticationException;
077import org.apache.maven.wagon.authentication.AuthenticationInfo;
078import org.apache.maven.wagon.proxy.ProxyInfo;
079import org.apache.maven.wagon.repository.Repository;
080import org.apache.tools.ant.types.selectors.SelectorUtils;
081import org.slf4j.Logger;
082import org.slf4j.LoggerFactory;
083import org.slf4j.MarkerFactory;
084import org.springframework.stereotype.Service;
085
086import javax.annotation.PostConstruct;
087import javax.inject.Inject;
088import javax.inject.Named;
089import java.io.IOException;
090import java.nio.file.Files;
091import java.nio.file.Path;
092import java.nio.file.Paths;
093import java.util.ArrayList;
094import java.util.Collections;
095import java.util.LinkedHashMap;
096import java.util.List;
097import java.util.Map;
098import java.util.Map.Entry;
099import java.util.Properties;
100import java.util.concurrent.ConcurrentHashMap;
101import java.util.concurrent.ConcurrentMap;
102
103/**
104 * DefaultRepositoryProxyConnectors
105 * TODO exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
106 * your average brown onion
107 */
108@Service("repositoryProxyConnectors#default")
109public class DefaultRepositoryProxyConnectors
110    implements RepositoryProxyConnectors, RegistryListener
111{
112    private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
113
114    @Inject
115    @Named(value = "archivaConfiguration#default")
116    private ArchivaConfiguration archivaConfiguration;
117
118    @Inject
119    @Named(value = "repositoryContentFactory#default")
120    private RepositoryContentFactory repositoryFactory;
121
122    @Inject
123    @Named(value = "metadataTools#default")
124    private MetadataTools metadataTools;
125
126    @Inject
127    private Map<String, PreDownloadPolicy> preDownloadPolicies;
128
129    @Inject
130    private Map<String, PostDownloadPolicy> postDownloadPolicies;
131
132    @Inject
133    private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
134
135    @Inject
136    private UrlFailureCache urlFailureCache;
137
138    private ConcurrentMap<String, List<ProxyConnector>> proxyConnectorMap = new ConcurrentHashMap<>();
139
140    private ConcurrentMap<String, ProxyInfo> networkProxyMap = new ConcurrentHashMap<>();
141
142    @Inject
143    private WagonFactory wagonFactory;
144
145    @Inject
146    @Named(value = "archivaTaskScheduler#repository")
147    private ArchivaTaskScheduler<RepositoryTask> scheduler;
148
149    @Inject
150    private RepositoryRegistry repositoryRegistry;
151
152    @Inject
153    private NetworkProxyAdmin networkProxyAdmin;
154
155    @Inject
156    @Named(value = "fileLockManager#default")
157    private FileLockManager fileLockManager;
158
159    @PostConstruct
160    public void initialize()
161    {
162        initConnectorsAndNetworkProxies();
163        archivaConfiguration.addChangeListener( this );
164
165    }
166
167    @SuppressWarnings("unchecked")
168    private void initConnectorsAndNetworkProxies()
169    {
170
171        ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
172        this.proxyConnectorMap.clear();
173
174        Configuration configuration = archivaConfiguration.getConfiguration();
175
176        List<ProxyConnectorRuleConfiguration> allProxyConnectorRuleConfigurations =
177            configuration.getProxyConnectorRuleConfigurations();
178
179        List<ProxyConnectorConfiguration> proxyConfigs = configuration.getProxyConnectors();
180        for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
181        {
182            String key = proxyConfig.getSourceRepoId();
183
184            // Create connector object.
185            ProxyConnector connector = new ProxyConnector();
186
187            ManagedRepository repo = repositoryRegistry.getManagedRepository( proxyConfig.getSourceRepoId( ) );
188            if (repo==null) {
189                log.error("Cannot find source repository after config change "+proxyConfig.getSourceRepoId());
190                continue;
191            }
192            connector.setSourceRepository(repo.getContent());
193            RemoteRepository rRepo = repositoryRegistry.getRemoteRepository( proxyConfig.getTargetRepoId() );
194            if (rRepo==null) {
195                log.error("Cannot find target repository after config change "+proxyConfig.getSourceRepoId());
196                continue;
197            }
198            connector.setTargetRepository(rRepo.getContent());
199
200            connector.setProxyId( proxyConfig.getProxyId() );
201            connector.setPolicies( proxyConfig.getPolicies() );
202            connector.setOrder( proxyConfig.getOrder() );
203            connector.setDisabled( proxyConfig.isDisabled() );
204
205            // Copy any blacklist patterns.
206            List<String> blacklist = new ArrayList<>( 0 );
207            if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
208            {
209                blacklist.addAll( proxyConfig.getBlackListPatterns() );
210            }
211            connector.setBlacklist( blacklist );
212
213            // Copy any whitelist patterns.
214            List<String> whitelist = new ArrayList<>( 0 );
215            if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
216            {
217                whitelist.addAll( proxyConfig.getWhiteListPatterns() );
218            }
219            connector.setWhitelist( whitelist );
220
221            List<ProxyConnectorRuleConfiguration> proxyConnectorRuleConfigurations =
222                findProxyConnectorRules( connector.getSourceRepository().getId(),
223                                         connector.getTargetRepository().getId(),
224                                         allProxyConnectorRuleConfigurations );
225
226            if ( !proxyConnectorRuleConfigurations.isEmpty() )
227            {
228                for ( ProxyConnectorRuleConfiguration proxyConnectorRuleConfiguration : proxyConnectorRuleConfigurations )
229                {
230                    if ( StringUtils.equals( proxyConnectorRuleConfiguration.getRuleType(),
231                                             ProxyConnectorRuleType.BLACK_LIST.getRuleType() ) )
232                    {
233                        connector.getBlacklist().add( proxyConnectorRuleConfiguration.getPattern() );
234                    }
235
236                    if ( StringUtils.equals( proxyConnectorRuleConfiguration.getRuleType(),
237                                             ProxyConnectorRuleType.WHITE_LIST.getRuleType() ) )
238                    {
239                        connector.getWhitelist().add( proxyConnectorRuleConfiguration.getPattern() );
240                    }
241                }
242            }
243
244            // Get other connectors
245            List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
246            if ( connectors == null )
247            {
248                // Create if we are the first.
249                connectors = new ArrayList<>( 1 );
250            }
251
252            // Add the connector.
253            connectors.add( connector );
254
255            // Ensure the list is sorted.
256            Collections.sort( connectors, proxyOrderSorter );
257
258            // Set the key to the list of connectors.
259            this.proxyConnectorMap.put( key, connectors );
260
261
262        }
263
264        this.networkProxyMap.clear();
265
266        List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
267        for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
268        {
269            String key = networkProxyConfig.getId();
270
271            ProxyInfo proxy = new ProxyInfo();
272
273            proxy.setType( networkProxyConfig.getProtocol() );
274            proxy.setHost( networkProxyConfig.getHost() );
275            proxy.setPort( networkProxyConfig.getPort() );
276            proxy.setUserName( networkProxyConfig.getUsername() );
277            proxy.setPassword( networkProxyConfig.getPassword() );
278
279            this.networkProxyMap.put( key, proxy );
280        }
281
282    }
283
284    private List<ProxyConnectorRuleConfiguration> findProxyConnectorRules( String sourceRepository,
285                                                                           String targetRepository,
286                                                                           List<ProxyConnectorRuleConfiguration> all )
287    {
288        List<ProxyConnectorRuleConfiguration> proxyConnectorRuleConfigurations = new ArrayList<>();
289
290        for ( ProxyConnectorRuleConfiguration proxyConnectorRuleConfiguration : all )
291        {
292            for ( ProxyConnectorConfiguration proxyConnector : proxyConnectorRuleConfiguration.getProxyConnectors() )
293            {
294                if ( StringUtils.equals( sourceRepository, proxyConnector.getSourceRepoId() ) && StringUtils.equals(
295                    targetRepository, proxyConnector.getTargetRepoId() ) )
296                {
297                    proxyConnectorRuleConfigurations.add( proxyConnectorRuleConfiguration );
298                }
299            }
300        }
301
302        return proxyConnectorRuleConfigurations;
303    }
304
305    @Override
306    public Path fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
307        throws ProxyDownloadException
308    {
309        Path localFile = toLocalFile( repository, artifact );
310
311        Properties requestProperties = new Properties();
312        requestProperties.setProperty( "filetype", "artifact" );
313        requestProperties.setProperty( "version", artifact.getVersion() );
314        requestProperties.setProperty( "managedRepositoryId", repository.getId() );
315
316        List<ProxyConnector> connectors = getProxyConnectors( repository );
317        Map<String, Exception> previousExceptions = new LinkedHashMap<>();
318        for ( ProxyConnector connector : connectors )
319        {
320            if ( connector.isDisabled() )
321            {
322                continue;
323            }
324
325            RemoteRepositoryContent targetRepository = connector.getTargetRepository();
326            requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
327
328            String targetPath = targetRepository.toPath( artifact );
329
330            if ( SystemUtils.IS_OS_WINDOWS )
331            {
332                // toPath use system PATH_SEPARATOR so on windows url are \ which doesn't work very well :-)
333                targetPath = FilenameUtils.separatorsToUnix( targetPath );
334            }
335
336            try
337            {
338                Path downloadedFile =
339                    transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
340                                  true );
341
342                if ( fileExists( downloadedFile ) )
343                {
344                    log.debug( "Successfully transferred: {}", downloadedFile.toAbsolutePath() );
345                    return downloadedFile;
346                }
347            }
348            catch ( NotFoundException e )
349            {
350                log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
351                           targetRepository.getRepository().getId() );
352            }
353            catch ( NotModifiedException e )
354            {
355                log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
356                           targetRepository.getRepository().getId() );
357            }
358            catch ( ProxyException | RepositoryAdminException e )
359            {
360                validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
361                                  targetRepository, localFile, e, previousExceptions );
362            }
363        }
364
365        if ( !previousExceptions.isEmpty() )
366        {
367            throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
368                                              previousExceptions );
369        }
370
371        log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
372
373        return null;
374    }
375
376    @Override
377    public Path fetchFromProxies( ManagedRepositoryContent repository, String path )
378    {
379        Path localFile = Paths.get( repository.getRepoRoot(), path );
380
381        // no update policies for these paths
382        if ( Files.exists(localFile) )
383        {
384            return null;
385        }
386
387        Properties requestProperties = new Properties();
388        requestProperties.setProperty( "filetype", "resource" );
389        requestProperties.setProperty( "managedRepositoryId", repository.getId() );
390
391        List<ProxyConnector> connectors = getProxyConnectors( repository );
392        for ( ProxyConnector connector : connectors )
393        {
394            if ( connector.isDisabled() )
395            {
396                continue;
397            }
398
399            RemoteRepositoryContent targetRepository = connector.getTargetRepository();
400            requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
401
402            String targetPath = path;
403
404            try
405            {
406                Path downloadedFile =
407                    transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
408                                  false );
409
410                if ( fileExists( downloadedFile ) )
411                {
412                    log.debug( "Successfully transferred: {}", downloadedFile.toAbsolutePath() );
413                    return downloadedFile;
414                }
415            }
416            catch ( NotFoundException e )
417            {
418                log.debug( "Resource {} not found on repository \"{}\".", path,
419                           targetRepository.getRepository().getId() );
420            }
421            catch ( NotModifiedException e )
422            {
423                log.debug( "Resource {} not updated on repository \"{}\".", path,
424                           targetRepository.getRepository().getId() );
425            }
426            catch ( ProxyException e )
427            {
428                log.warn(
429                    "Transfer error from repository {} for resource {}, continuing to next repository. Error message: {}",
430                    targetRepository.getRepository().getId(), path, e.getMessage() );
431                log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ),
432                           "Transfer error from repository \"{}"
433                               + "\" for resource {}, continuing to next repository. Error message: {}",
434                           targetRepository.getRepository().getId(), path, e.getMessage(), e );
435            }
436            catch ( RepositoryAdminException e )
437            {
438                log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ),
439                           "Transfer error from repository {} for resource {}, continuing to next repository. Error message: {}",
440                           targetRepository.getRepository().getId(), path, e.getMessage(), e );
441                log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ), "Full stack trace", e );
442            }
443        }
444
445        log.debug( "Exhausted all target repositories, resource {} not found.", path );
446
447        return null;
448    }
449
450    @Override
451    public ProxyFetchResult fetchMetadataFromProxies( ManagedRepositoryContent repository, String logicalPath )
452    {
453        Path localFile = Paths.get( repository.getRepoRoot(), logicalPath );
454
455        Properties requestProperties = new Properties();
456        requestProperties.setProperty( "filetype", "metadata" );
457        boolean metadataNeedsUpdating = false;
458        long originalTimestamp = getLastModified( localFile );
459
460        List<ProxyConnector> connectors = new ArrayList<>( getProxyConnectors( repository ) );
461        for ( ProxyConnector connector : connectors )
462        {
463            if ( connector.isDisabled() )
464            {
465                continue;
466            }
467
468            RemoteRepositoryContent targetRepository = connector.getTargetRepository();
469
470            Path localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
471            long originalMetadataTimestamp = getLastModified( localRepoFile );
472
473            try
474            {
475                transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
476                              true );
477
478                if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
479                {
480                    metadataNeedsUpdating = true;
481                }
482            }
483            catch ( NotFoundException e )
484            {
485
486                log.debug( "Metadata {} not found on remote repository '{}'.", logicalPath,
487                           targetRepository.getRepository().getId(), e );
488
489            }
490            catch ( NotModifiedException e )
491            {
492
493                log.debug( "Metadata {} not updated on remote repository '{}'.", logicalPath,
494                           targetRepository.getRepository().getId(), e );
495
496            }
497            catch ( ProxyException | RepositoryAdminException e )
498            {
499                log.warn(
500                    "Transfer error from repository {} for versioned Metadata {}, continuing to next repository. Error message: {}",
501                    targetRepository.getRepository().getId(), logicalPath, e.getMessage() );
502                log.debug( "Full stack trace", e );
503            }
504        }
505
506        if ( hasBeenUpdated( localFile, originalTimestamp ) )
507        {
508            metadataNeedsUpdating = true;
509        }
510
511        if ( metadataNeedsUpdating || !Files.exists(localFile))
512        {
513            try
514            {
515                metadataTools.updateMetadata( repository, logicalPath );
516            }
517            catch ( RepositoryMetadataException e )
518            {
519                log.warn( "Unable to update metadata {}:{}", localFile.toAbsolutePath(), e.getMessage(), e );
520            }
521
522        }
523
524        if ( fileExists( localFile ) )
525        {
526            return new ProxyFetchResult( localFile, metadataNeedsUpdating );
527        }
528
529        return new ProxyFetchResult( null, false );
530    }
531
532    /**
533     * @param connector
534     * @param remoteRepository
535     * @param tmpMd5
536     * @param tmpSha1
537     * @param tmpResource
538     * @param url
539     * @param remotePath
540     * @param resource
541     * @param workingDirectory
542     * @param repository
543     * @throws ProxyException
544     * @throws NotModifiedException
545     * @throws org.apache.archiva.admin.model.RepositoryAdminException
546     */
547    protected void transferResources( ProxyConnector connector, RemoteRepositoryContent remoteRepository, Path tmpMd5,
548                                      Path tmpSha1, Path tmpResource, String url, String remotePath, Path resource,
549                                      Path workingDirectory, ManagedRepositoryContent repository )
550        throws ProxyException, NotModifiedException, RepositoryAdminException
551    {
552        Wagon wagon = null;
553        try
554        {
555            RepositoryURL repoUrl = remoteRepository.getURL();
556            String protocol = repoUrl.getProtocol();
557            NetworkProxy networkProxy = null;
558            if ( StringUtils.isNotBlank( connector.getProxyId() ) )
559            {
560                networkProxy = networkProxyAdmin.getNetworkProxy( connector.getProxyId() );
561            }
562            WagonFactoryRequest wagonFactoryRequest = new WagonFactoryRequest( "wagon#" + protocol,
563                                                                               remoteRepository.getRepository().getExtraHeaders() ).networkProxy(
564                networkProxy );
565            wagon = wagonFactory.getWagon( wagonFactoryRequest );
566            if ( wagon == null )
567            {
568                throw new ProxyException( "Unsupported target repository protocol: " + protocol );
569            }
570
571            if ( wagon == null )
572            {
573                throw new ProxyException( "Unsupported target repository protocol: " + protocol );
574            }
575
576            boolean connected = connectToRepository( connector, wagon, remoteRepository );
577            if ( connected )
578            {
579                transferArtifact( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
580                                  tmpResource );
581
582                // TODO: these should be used to validate the download based on the policies, not always downloaded
583                // to
584                // save on connections since md5 is rarely used
585                transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, ".sha1",
586                                  tmpSha1 );
587                transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, ".md5",
588                                  tmpMd5 );
589            }
590        }
591        catch ( NotFoundException e )
592        {
593            urlFailureCache.cacheFailure( url );
594            throw e;
595        }
596        catch ( NotModifiedException e )
597        {
598            // Do not cache url here.
599            throw e;
600        }
601        catch ( ProxyException e )
602        {
603            urlFailureCache.cacheFailure( url );
604            throw e;
605        }
606        catch ( WagonFactoryException e )
607        {
608            throw new ProxyException( e.getMessage(), e );
609        }
610        finally
611        {
612            if ( wagon != null )
613            {
614                try
615                {
616                    wagon.disconnect();
617                }
618                catch ( ConnectionException e )
619                {
620                    log.warn( "Unable to disconnect wagon.", e );
621                }
622            }
623        }
624    }
625
626    private void transferArtifact( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
627                                   ManagedRepositoryContent repository, Path resource, Path tmpDirectory,
628                                   Path destFile )
629        throws ProxyException
630    {
631        transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, destFile );
632    }
633
634    private long getLastModified( Path file )
635    {
636        if ( !Files.exists(file) || !Files.isRegularFile(file) )
637        {
638            return 0;
639        }
640
641        try
642        {
643            return Files.getLastModifiedTime(file).toMillis();
644        }
645        catch ( IOException e )
646        {
647            log.error("Could get the modified time of file {}", file.toAbsolutePath());
648            return 0;
649        }
650    }
651
652    private boolean hasBeenUpdated( Path file, long originalLastModified )
653    {
654        if ( !Files.exists(file) || !Files.isRegularFile(file) )
655        {
656            return false;
657        }
658
659        long currentLastModified = getLastModified( file );
660        return ( currentLastModified > originalLastModified );
661    }
662
663    private Path toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
664                                  String targetPath )
665    {
666        String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
667        return Paths.get( repository.getRepoRoot(), repoPath );
668    }
669
670    /**
671     * Test if the provided ManagedRepositoryContent has any proxies configured for it.
672     */
673    @Override
674    public boolean hasProxies( ManagedRepositoryContent repository )
675    {
676        synchronized ( this.proxyConnectorMap )
677        {
678            return this.proxyConnectorMap.containsKey( repository.getId() );
679        }
680    }
681
682    private Path toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
683    {
684        return repository.toFile( artifact );
685    }
686
687    /**
688     * Simple method to test if the file exists on the local disk.
689     *
690     * @param file the file to test. (may be null)
691     * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
692     */
693    private boolean fileExists( Path file )
694    {
695        if ( file == null )
696        {
697            return false;
698        }
699
700        if ( !Files.exists(file))
701        {
702            return false;
703        }
704
705        return Files.isRegularFile(file);
706    }
707
708    /**
709     * Perform the transfer of the file.
710     *
711     * @param connector         the connector configuration to use.
712     * @param remoteRepository  the remote repository get the resource from.
713     * @param remotePath        the path in the remote repository to the resource to get.
714     * @param repository        the managed repository that will hold the file
715     * @param resource          the local file to place the downloaded resource into
716     * @param requestProperties the request properties to utilize for policy handling.
717     * @param executeConsumers  whether to execute the consumers after proxying
718     * @return the local file that was downloaded, or null if not downloaded.
719     * @throws NotFoundException    if the file was not found on the remote repository.
720     * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
721     *                              the remote resource is not newer than the local File.
722     * @throws ProxyException       if transfer was unsuccessful.
723     */
724    private Path transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
725                               ManagedRepositoryContent repository, Path resource, Properties requestProperties,
726                               boolean executeConsumers )
727        throws ProxyException, NotModifiedException, RepositoryAdminException
728    {
729        String url = remoteRepository.getURL().getUrl();
730        if ( !url.endsWith( "/" ) )
731        {
732            url = url + "/";
733        }
734        url = url + remotePath;
735        requestProperties.setProperty( "url", url );
736
737        // Is a whitelist defined?
738        if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
739        {
740            // Path must belong to whitelist.
741            if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
742            {
743                log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
744                           remotePath, remoteRepository.getRepository().getName() );
745                return null;
746            }
747        }
748
749        // Is target path part of blacklist?
750        if ( matchesPattern( remotePath, connector.getBlacklist() ) )
751        {
752            log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
753                       remoteRepository.getRepository().getName() );
754            return null;
755        }
756
757        // Handle pre-download policy
758        try
759        {
760            validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
761        }
762        catch ( PolicyViolationException e )
763        {
764            String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
765            if ( fileExists( resource ) )
766            {
767                log.debug( "{} : using already present local file.", emsg );
768                return resource;
769            }
770
771            log.debug( emsg );
772            return null;
773        }
774
775        Path workingDirectory = createWorkingDirectory( repository );
776        Path tmpResource = workingDirectory.resolve(resource.getFileName());
777        Path tmpMd5 = workingDirectory.resolve(resource.getFileName().toString() + ".md5" );
778        Path tmpSha1 = workingDirectory.resolve( resource.getFileName().toString() + ".sha1" );
779
780        try
781        {
782
783            transferResources( connector, remoteRepository, tmpMd5, tmpSha1, tmpResource, url, remotePath, resource,
784                               workingDirectory, repository );
785
786            // Handle post-download policies.
787            try
788            {
789                validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
790            }
791            catch ( PolicyViolationException e )
792            {
793                log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
794                executeConsumers = false;
795                if ( !fileExists( tmpResource ) )
796                {
797                    resource = null;
798                }
799            }
800
801            if ( resource != null )
802            {
803                synchronized ( resource.toAbsolutePath().toString().intern() )
804                {
805                    Path directory = resource.getParent();
806                    moveFileIfExists( tmpMd5, directory );
807                    moveFileIfExists( tmpSha1, directory );
808                    moveFileIfExists( tmpResource, directory );
809                }
810            }
811        }
812        finally
813        {
814            org.apache.archiva.common.utils.FileUtils.deleteQuietly( workingDirectory );
815        }
816
817        if ( executeConsumers )
818        {
819            // Just-in-time update of the index and database by executing the consumers for this artifact
820            //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
821            queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
822        }
823
824        return resource;
825    }
826
827    private void queueRepositoryTask( String repositoryId, Path localFile )
828    {
829        RepositoryTask task = new RepositoryTask();
830        task.setRepositoryId( repositoryId );
831        task.setResourceFile( localFile );
832        task.setUpdateRelatedArtifacts( true );
833        task.setScanAll( true );
834
835        try
836        {
837            scheduler.queueTask( task );
838        }
839        catch ( TaskQueueException e )
840        {
841            log.error( "Unable to queue repository task to execute consumers on resource file ['{}"
842                           + "'].", localFile.getFileName() );
843        }
844    }
845
846    /**
847     * Moves the file into repository location if it exists
848     *
849     * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
850     * @param directory  directory to write files to
851     */
852    private void moveFileIfExists( Path fileToMove, Path directory )
853        throws ProxyException
854    {
855        if ( fileToMove != null && Files.exists(fileToMove) )
856        {
857            Path newLocation = directory.resolve(fileToMove.getFileName());
858            moveTempToTarget( fileToMove, newLocation );
859        }
860    }
861
862    /**
863     * <p>
864     * Quietly transfer the checksum file from the remote repository to the local file.
865     * </p>
866     *
867     * @param wagon            the wagon instance (should already be connected) to use.
868     * @param remoteRepository the remote repository to transfer from.
869     * @param remotePath       the remote path to the resource to get.
870     * @param repository       the managed repository that will hold the file
871     * @param resource         the local file that should contain the downloaded contents
872     * @param tmpDirectory     the temporary directory to download to
873     * @param ext              the type of checksum to transfer (example: ".md5" or ".sha1")
874     * @throws ProxyException if copying the downloaded file into place did not succeed.
875     */
876    private void transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
877                                   ManagedRepositoryContent repository, Path resource, Path tmpDirectory, String ext,
878                                   Path destFile )
879        throws ProxyException
880    {
881        String url = remoteRepository.getURL().getUrl() + remotePath + ext;
882
883        // Transfer checksum does not use the policy.
884        if ( urlFailureCache.hasFailedBefore( url ) )
885        {
886            return;
887        }
888
889        try
890        {
891            transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
892            log.debug( "Checksum {} Downloaded: {} to move to {}", url, destFile, resource );
893        }
894        catch ( NotFoundException e )
895        {
896            urlFailureCache.cacheFailure( url );
897            log.debug( "Transfer failed, checksum not found: {}", url );
898            // Consume it, do not pass this on.
899        }
900        catch ( NotModifiedException e )
901        {
902            log.debug( "Transfer skipped, checksum not modified: {}", url );
903            // Consume it, do not pass this on.
904        }
905        catch ( ProxyException e )
906        {
907            urlFailureCache.cacheFailure( url );
908            log.warn( "Transfer failed on checksum: {} : {}", url, e.getMessage(), e );
909            // Critical issue, pass it on.
910            throw e;
911        }
912    }
913
914    /**
915     * Perform the transfer of the remote file to the local file specified.
916     *
917     * @param wagon            the wagon instance to use.
918     * @param remoteRepository the remote repository to use
919     * @param remotePath       the remote path to attempt to get
920     * @param repository       the managed repository that will hold the file
921     * @param origFile         the local file to save to
922     * @throws ProxyException if there was a problem moving the downloaded file into place.
923     */
924    private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
925                                     ManagedRepositoryContent repository, Path origFile, Path destFile )
926        throws ProxyException
927    {
928        assert ( remotePath != null );
929
930        // Transfer the file.
931        try
932        {
933            boolean success = false;
934
935            if ( !Files.exists(origFile))
936            {
937                log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
938                wagon.get( addParameters( remotePath, remoteRepository.getRepository() ), destFile.toFile() );
939                success = true;
940
941                // You wouldn't get here on failure, a WagonException would have been thrown.
942                log.debug( "Downloaded successfully." );
943            }
944            else
945            {
946                log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
947                try
948                {
949                    success = wagon.getIfNewer( addParameters( remotePath, remoteRepository.getRepository() ), destFile.toFile(),
950                                                Files.getLastModifiedTime(origFile).toMillis());
951                }
952                catch ( IOException e )
953                {
954                    throw new ProxyException( "Failed to the modification time of "+origFile.toAbsolutePath() );
955                }
956                if ( !success )
957                {
958                    throw new NotModifiedException(
959                        "Not downloaded, as local file is newer than remote side: " + origFile.toAbsolutePath() );
960                }
961
962                if ( Files.exists(destFile))
963                {
964                    log.debug( "Downloaded successfully." );
965                }
966            }
967        }
968        catch ( ResourceDoesNotExistException e )
969        {
970            throw new NotFoundException(
971                "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
972                e );
973        }
974        catch ( WagonException e )
975        {
976            // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
977
978            String msg =
979                "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
980            if ( e.getCause() != null )
981            {
982                msg += " (cause: " + e.getCause() + ")";
983            }
984            throw new ProxyException( msg, e );
985        }
986    }
987
988    /**
989     * Apply the policies.
990     *
991     * @param policies  the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
992     * @param settings  the map of settings for the policies to execute. (Map of String policy keys, to String policy
993     *                  setting)
994     * @param request   the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, Path)}
995     *                  )
996     * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, Path)})
997     * @throws PolicyViolationException
998     */
999    private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
1000                                   Properties request, Path localFile )
1001        throws PolicyViolationException
1002    {
1003        for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
1004        {
1005            // olamy with spring rolehint is now downloadPolicy#hint
1006            // so substring after last # to get the hint as with plexus
1007            String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
1008            DownloadPolicy policy = entry.getValue();
1009            String defaultSetting = policy.getDefaultOption();
1010
1011            String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
1012
1013            log.debug( "Applying [{}] policy with [{}]", key, setting );
1014            try
1015            {
1016                policy.applyPolicy( setting, request, localFile );
1017            }
1018            catch ( PolicyConfigurationException e )
1019            {
1020                log.error( e.getMessage(), e );
1021            }
1022        }
1023    }
1024
1025    private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
1026                                   Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
1027                                   Path localFile, Exception exception, Map<String, Exception> previousExceptions )
1028        throws ProxyDownloadException
1029    {
1030        boolean process = true;
1031        for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
1032        {
1033
1034            // olamy with spring rolehint is now downloadPolicy#hint
1035            // so substring after last # to get the hint as with plexus
1036            String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
1037            DownloadErrorPolicy policy = entry.getValue();
1038            String defaultSetting = policy.getDefaultOption();
1039            String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
1040
1041            log.debug( "Applying [{}] policy with [{}]", key, setting );
1042            try
1043            {
1044                // all policies must approve the exception, any can cancel
1045                process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
1046                if ( !process )
1047                {
1048                    break;
1049                }
1050            }
1051            catch ( PolicyConfigurationException e )
1052            {
1053                log.error( e.getMessage(), e );
1054            }
1055        }
1056
1057        if ( process )
1058        {
1059            // if the exception was queued, don't throw it
1060            if ( !previousExceptions.containsKey( content.getId() ) )
1061            {
1062                throw new ProxyDownloadException(
1063                    "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
1064                    content.getId(), exception );
1065            }
1066        }
1067        else
1068        {
1069            // if the exception was queued, but cancelled, remove it
1070            previousExceptions.remove( content.getId() );
1071        }
1072
1073        log.warn(
1074            "Transfer error from repository {} for artifact {} , continuing to next repository. Error message: {}",
1075            content.getRepository().getId(), Keys.toKey( artifact ), exception.getMessage() );
1076        log.debug( "Full stack trace", exception );
1077    }
1078
1079    /**
1080     * Creates a working directory
1081     *
1082     * @param repository
1083     * @return file location of working directory
1084     */
1085    private Path createWorkingDirectory( ManagedRepositoryContent repository )
1086    {
1087        try
1088        {
1089            return Files.createTempDirectory( "temp" );
1090        }
1091        catch ( IOException e )
1092        {
1093            throw new RuntimeException( e.getMessage(), e );
1094        }
1095
1096    }
1097
1098    /**
1099     * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
1100     * downloaded files.
1101     *
1102     * @param temp   The completed download file
1103     * @param target The final location of the downloaded file
1104     * @throws ProxyException when the temp file cannot replace the target file
1105     */
1106    private void moveTempToTarget( Path temp, Path target )
1107        throws ProxyException
1108    {
1109
1110        Lock lock;
1111        try
1112        {
1113            lock = fileLockManager.writeFileLock( target );
1114            try {
1115                Files.deleteIfExists(lock.getFile());
1116            } catch (IOException e) {
1117                throw new ProxyException( "Unable to overwrite existing target file: " + target.toAbsolutePath() );
1118            }
1119
1120            try {
1121                Files.createDirectories(lock.getFile().getParent());
1122            } catch (IOException e) {
1123                throw new ProxyException("Unable to create parent directory "+lock.getFile().getParent());
1124            }
1125
1126            try
1127            {
1128                Files.move(temp, lock.getFile() );
1129            }
1130            catch ( IOException e )
1131            {
1132                log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1133
1134                try
1135                {
1136                    Files.copy( temp, lock.getFile());
1137                }
1138                catch ( IOException e2 )
1139                {
1140                    if ( Files.exists(lock.getFile()) )
1141                    {
1142                        log.debug( "Tried to copy file {} to {} but file with this name already exists.",
1143                            temp.getFileName(), lock.getFile().toAbsolutePath() );
1144                    }
1145                    else
1146                    {
1147                        throw new ProxyException(
1148                            "Cannot copy tmp file " + temp.toAbsolutePath() + " to its final location", e2 );
1149                    }
1150                }
1151                finally
1152                {
1153                    org.apache.archiva.common.utils.FileUtils.deleteQuietly( temp );
1154                }
1155            }
1156
1157        }
1158        catch ( FileLockException | FileLockTimeoutException e )
1159        {
1160            throw new ProxyException( e.getMessage(), e );
1161        }
1162    }
1163
1164    /**
1165     * Using wagon, connect to the remote repository.
1166     *
1167     * @param connector        the connector configuration to utilize (for obtaining network proxy configuration from)
1168     * @param wagon            the wagon instance to establish the connection on.
1169     * @param remoteRepository the remote repository to connect to.
1170     * @return true if the connection was successful. false if not connected.
1171     */
1172    private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1173                                         RemoteRepositoryContent remoteRepository )
1174    {
1175        boolean connected = false;
1176
1177        final ProxyInfo networkProxy =
1178            connector.getProxyId() == null ? null : this.networkProxyMap.get( connector.getProxyId() );
1179
1180        if ( log.isDebugEnabled() )
1181        {
1182            if ( networkProxy != null )
1183            {
1184                // TODO: move to proxyInfo.toString()
1185                String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1186                    + " to connect to remote repository " + remoteRepository.getURL();
1187                if ( networkProxy.getNonProxyHosts() != null )
1188                {
1189                    msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1190                }
1191                if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1192                {
1193                    msg += "; as user: " + networkProxy.getUserName();
1194                }
1195                log.debug( msg );
1196            }
1197        }
1198
1199        AuthenticationInfo authInfo = null;
1200        String username = "";
1201        String password = "";
1202        RepositoryCredentials repCred = remoteRepository.getRepository().getLoginCredentials();
1203        if (repCred!=null && repCred instanceof PasswordCredentials) {
1204            PasswordCredentials pwdCred = (PasswordCredentials) repCred;
1205            username = pwdCred.getUsername();
1206            password = pwdCred.getPassword()==null ? "" : new String(pwdCred.getPassword());
1207        }
1208
1209        if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1210        {
1211            log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1212            authInfo = new AuthenticationInfo();
1213            authInfo.setUserName( username );
1214            authInfo.setPassword( password );
1215        }
1216
1217        // Convert seconds to milliseconds
1218
1219        long timeoutInMilliseconds = remoteRepository.getRepository().getTimeout().toMillis();
1220
1221        // Set timeout  read and connect
1222        // FIXME olamy having 2 config values
1223        wagon.setReadTimeout( (int) timeoutInMilliseconds );
1224        wagon.setTimeout( (int)  timeoutInMilliseconds );
1225
1226        try
1227        {
1228            Repository wagonRepository =
1229                new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1230            wagon.connect( wagonRepository, authInfo, networkProxy );
1231            connected = true;
1232        }
1233        catch ( ConnectionException | AuthenticationException e )
1234        {
1235            log.warn( "Could not connect to {}: {}", remoteRepository.getRepository().getName(), e.getMessage() );
1236            connected = false;
1237        }
1238
1239        return connected;
1240    }
1241
1242    /**
1243     * Tests whitelist and blacklist patterns against path.
1244     *
1245     * @param path     the path to test.
1246     * @param patterns the list of patterns to check.
1247     * @return true if the path matches at least 1 pattern in the provided patterns list.
1248     */
1249    private boolean matchesPattern( String path, List<String> patterns )
1250    {
1251        if ( CollectionUtils.isEmpty( patterns ) )
1252        {
1253            return false;
1254        }
1255
1256        if ( !path.startsWith( "/" ) )
1257        {
1258            path = "/" + path;
1259        }
1260
1261        for ( String pattern : patterns )
1262        {
1263            if ( !pattern.startsWith( "/" ) )
1264            {
1265                pattern = "/" + pattern;
1266            }
1267
1268            if ( SelectorUtils.matchPath( pattern, path, false ) )
1269            {
1270                return true;
1271            }
1272        }
1273
1274        return false;
1275    }
1276
1277    /**
1278     * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1279     */
1280    @Override
1281    public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1282    {
1283
1284        if ( !this.proxyConnectorMap.containsKey( repository.getId() ) )
1285        {
1286            return Collections.emptyList();
1287        }
1288        List<ProxyConnector> ret = new ArrayList<>( this.proxyConnectorMap.get( repository.getId() ) );
1289
1290        Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1291        return ret;
1292
1293    }
1294
1295    @Override
1296    public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1297    {
1298        if ( ConfigurationNames.isNetworkProxy( propertyName ) //
1299            || ConfigurationNames.isManagedRepositories( propertyName ) //
1300            || ConfigurationNames.isRemoteRepositories( propertyName ) //
1301            || ConfigurationNames.isProxyConnector( propertyName ) ) //
1302        {
1303            initConnectorsAndNetworkProxies();
1304        }
1305    }
1306
1307    protected String addParameters( String path, RemoteRepository remoteRepository )
1308    {
1309        if ( remoteRepository.getExtraParameters().isEmpty() )
1310        {
1311            return path;
1312        }
1313
1314        boolean question = false;
1315
1316        StringBuilder res = new StringBuilder( path == null ? "" : path );
1317
1318        for ( Entry<String, String> entry : remoteRepository.getExtraParameters().entrySet() )
1319        {
1320            if ( !question )
1321            {
1322                res.append( '?' ).append( entry.getKey() ).append( '=' ).append( entry.getValue() );
1323            }
1324        }
1325
1326        return res.toString();
1327    }
1328
1329
1330    @Override
1331    public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1332    {
1333        /* do nothing */
1334    }
1335
1336    public ArchivaConfiguration getArchivaConfiguration()
1337    {
1338        return archivaConfiguration;
1339    }
1340
1341    public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1342    {
1343        this.archivaConfiguration = archivaConfiguration;
1344    }
1345
1346    public RepositoryContentFactory getRepositoryFactory()
1347    {
1348        return repositoryFactory;
1349    }
1350
1351    public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1352    {
1353        this.repositoryFactory = repositoryFactory;
1354    }
1355
1356    public MetadataTools getMetadataTools()
1357    {
1358        return metadataTools;
1359    }
1360
1361    public void setMetadataTools( MetadataTools metadataTools )
1362    {
1363        this.metadataTools = metadataTools;
1364    }
1365
1366    public UrlFailureCache getUrlFailureCache()
1367    {
1368        return urlFailureCache;
1369    }
1370
1371    public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1372    {
1373        this.urlFailureCache = urlFailureCache;
1374    }
1375
1376    public WagonFactory getWagonFactory()
1377    {
1378        return wagonFactory;
1379    }
1380
1381    public void setWagonFactory( WagonFactory wagonFactory )
1382    {
1383        this.wagonFactory = wagonFactory;
1384    }
1385
1386    public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1387    {
1388        return preDownloadPolicies;
1389    }
1390
1391    public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1392    {
1393        this.preDownloadPolicies = preDownloadPolicies;
1394    }
1395
1396    public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1397    {
1398        return postDownloadPolicies;
1399    }
1400
1401    public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1402    {
1403        this.postDownloadPolicies = postDownloadPolicies;
1404    }
1405
1406    public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1407    {
1408        return downloadErrorPolicies;
1409    }
1410
1411    public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1412    {
1413        this.downloadErrorPolicies = downloadErrorPolicies;
1414    }
1415}