Coverage Report - org.apache.maven.archiva.proxy.DefaultRepositoryProxyConnectors
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultRepositoryProxyConnectors
0%
0/391
0%
0/146
0
 
 1  
 package org.apache.maven.archiva.proxy;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *  http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 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  
  * DefaultRepositoryProxyConnectors
 80  
  * 
 81  
  * @version $Id: DefaultRepositoryProxyConnectors.java 825082 2009-10-14 10:27:48Z brett $
 82  
  * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
 83  
  *       your average brown onion
 84  
  * @plexus.component role-hint="default"
 85  
  */
 86  0
 public class DefaultRepositoryProxyConnectors
 87  
     implements RepositoryProxyConnectors, RegistryListener, Initializable
 88  
 {
 89  0
     private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
 90  
 
 91  
     /**
 92  
      * @plexus.requirement
 93  
      */
 94  
     private ArchivaConfiguration archivaConfiguration;
 95  
 
 96  
     /**
 97  
      * @plexus.requirement
 98  
      */
 99  
     private RepositoryContentFactory repositoryFactory;
 100  
 
 101  
     /**
 102  
      * @plexus.requirement
 103  
      */
 104  
     private MetadataTools metadataTools;
 105  
 
 106  
     /**
 107  
      * @plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
 108  
      */
 109  
     private Map<String, PreDownloadPolicy> preDownloadPolicies;
 110  
 
 111  
     /**
 112  
      * @plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
 113  
      */
 114  
     private Map<String, PostDownloadPolicy> postDownloadPolicies;
 115  
 
 116  
     /**
 117  
      * @plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
 118  
      */
 119  
     private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
 120  
 
 121  
     /**
 122  
      * @plexus.requirement role-hint="default"
 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  
      * @plexus.requirement
 132  
      */
 133  
     private WagonFactory wagonFactory;
 134  
     
 135  
     /**
 136  
      * @plexus.requirement
 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  
         // no update policies for these paths
 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  
      * Test if the provided ManagedRepositoryContent has any proxies configured for it.
 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  
      * Simple method to test if the file exists on the local disk.
 388  
      * 
 389  
      * @param file the file to test. (may be null)
 390  
      * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
 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  
      * Perform the transfer of the file.
 414  
      * 
 415  
      * @param connector the connector configuration to use.
 416  
      * @param remoteRepository the remote repository get the resource from.
 417  
      * @param remotePath the path in the remote repository to the resource to get.
 418  
      * @param repository the managed repository that will hold the file
 419  
      * @param resource the local file to place the downloaded resource into
 420  
      * @param requestProperties the request properties to utilize for policy handling.
 421  
      * @param executeConsumers whether to execute the consumers after proxying
 422  
      * @return the local file that was downloaded, or null if not downloaded.
 423  
      * @throws NotFoundException if the file was not found on the remote repository.
 424  
      * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
 425  
      *             the remote resource is not newer than the local File.
 426  
      * @throws ProxyException if transfer was unsuccessful.
 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  
         // Is a whitelist defined?
 442  0
         if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
 443  
         {
 444  
             // Path must belong to whitelist.
 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  
         // Is target path part of blacklist?
 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  
         // Handle pre-download policy
 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  
                     // TODO: these should be used to validate the download based on the policies, not always downloaded
 505  
                     // to
 506  
                     // save on connections since md5 is rarely used
 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  
                 // Do not cache url here.
 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  
             // Handle post-download policies.
 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  
             // Just-in-time update of the index and database by executing the consumers for this artifact
 579  
             //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
 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  
      * Moves the file into repository location if it exists
 603  
      * 
 604  
      * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
 605  
      * @param directory directory to write files to
 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  
      * <p>
 619  
      * Quietly transfer the checksum file from the remote repository to the local file.
 620  
      * </p>
 621  
      * 
 622  
      * @param wagon the wagon instance (should already be connected) to use.
 623  
      * @param remoteRepository the remote repository to transfer from.
 624  
      * @param remotePath the remote path to the resource to get.
 625  
      * @param repository the managed repository that will hold the file
 626  
      * @param localFile the local file that should contain the downloaded contents
 627  
      * @param type the type of checksum to transfer (example: ".md5" or ".sha1")
 628  
      * @throws ProxyException if copying the downloaded file into place did not succeed.
 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  
         // Transfer checksum does not use the policy.
 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  
             // Consume it, do not pass this on.
 654  
         }
 655  0
         catch ( NotModifiedException e )
 656  
         {
 657  0
             log.debug( "Transfer skipped, checksum not modified: " + url );
 658  
             // Consume it, do not pass this on.
 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  
             // Critical issue, pass it on.
 665  0
             throw e;
 666  0
         }
 667  0
         return destFile;
 668  
     }
 669  
 
 670  
     /**
 671  
      * Perform the transfer of the remote file to the local file specified.
 672  
      * 
 673  
      * @param wagon the wagon instance to use.
 674  
      * @param remoteRepository the remote repository to use
 675  
      * @param remotePath the remote path to attempt to get
 676  
      * @param repository the managed repository that will hold the file
 677  
      * @param origFile the local file to save to
 678  
      * @return The local file that was transfered.
 679  
      * @throws ProxyException if there was a problem moving the downloaded file into place.
 680  
      * @throws WagonException if there was a problem tranfering the file.
 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  
         // Transfer the file.
 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  
                 // You wouldn't get here on failure, a WagonException would have been thrown.
 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  
             // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
 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  
      * Apply the policies.
 740  
      * 
 741  
      * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
 742  
      * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
 743  
      *            setting)
 744  
      * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)}
 745  
      *            )
 746  
      * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
 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  
                 // all policies must approve the exception, any can cancel
 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  
             // if the exception was queued, don't throw it
 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  
             // if the exception was queued, but cancelled, remove it
 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  
      * Creates a working directory in the repository root for this request
 823  
      * 
 824  
      * @param repository
 825  
      * @return file location of working directory
 826  
      * @throws IOException
 827  
      */
 828  
     private File createWorkingDirectory( ManagedRepositoryContent repository )
 829  
     {
 830  
         // TODO: This is ugly - lets actually clean this up when we get the new repository api
 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  
      * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
 846  
      * downloaded files.
 847  
      * 
 848  
      * @param temp The completed download file
 849  
      * @param target The final location of the downloaded file
 850  
      * @throws ProxyException when the temp file cannot replace the target file
 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  
      * Using wagon, connect to the remote repository.
 891  
      * 
 892  
      * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
 893  
      * @param wagon the wagon instance to establish the connection on.
 894  
      * @param remoteRepository the remote repository to connect to.
 895  
      * @return true if the connection was successful. false if not connected.
 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  
                 // TODO: move to proxyInfo.toString()
 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  
         // Convert seconds to milliseconds
 940  0
         int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
 941  
 
 942  
         // Set timeout
 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  
      * Tests whitelist and blacklist patterns against path.
 968  
      * 
 969  
      * @param path the path to test.
 970  
      * @param patterns the list of patterns to check.
 971  
      * @return true if the path matches at least 1 pattern in the provided patterns list.
 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  
      * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
 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  
         /* do nothing */
 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  
                     // Create connector object.
 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  
                     // Copy any blacklist patterns.
 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  
                     // Copy any whitelist patterns.
 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  
                     // Get other connectors
 1079  0
                     List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
 1080  0
                     if ( connectors == null )
 1081  
                     {
 1082  
                         // Create if we are the first.
 1083  0
                         connectors = new ArrayList<ProxyConnector>();
 1084  
                     }
 1085  
 
 1086  
                     // Add the connector.
 1087  0
                     connectors.add( connector );
 1088  
 
 1089  
                     // Ensure the list is sorted.
 1090  0
                     Collections.sort( connectors, proxyOrderSorter );
 1091  
 
 1092  
                     // Set the key to the list of connectors.
 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  
 }