git-svn-id: https://svn.apache.org/repos/asf/maven/repository-manager/trunk@373670 13f79535-47bb-0310-9956-ffa450edef68tags/archiva-0.9-alpha-1
<!-- | |||||
~ Copyright 2005-2006 The Apache Software Foundation. | |||||
~ | |||||
~ Licensed under the Apache License, Version 2.0 (the "License"); | |||||
~ you may not use this file except in compliance with the License. | |||||
~ You may obtain a copy of the License at | |||||
~ | |||||
~ http://www.apache.org/licenses/LICENSE-2.0 | |||||
~ | |||||
~ Unless required by applicable law or agreed to in writing, software | |||||
~ distributed under the License is distributed on an "AS IS" BASIS, | |||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
~ See the License for the specific language governing permissions and | |||||
~ limitations under the License. | |||||
--> | |||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||||
<parent> | |||||
<groupId>org.apache.maven.repository</groupId> | |||||
<artifactId>maven-repository-manager</artifactId> | |||||
<version>1.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>maven-repository-proxy</artifactId> | |||||
<name>Maven Repository Proxy</name> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.apache.maven</groupId> | |||||
<artifactId>maven-artifact</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.apache.maven</groupId> | |||||
<artifactId>maven-artifact-manager</artifactId> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
package org.apache.maven.repository.proxy; | |||||
import org.apache.maven.artifact.Artifact; | |||||
import org.apache.maven.artifact.manager.ChecksumFailedException; | |||||
import org.apache.maven.artifact.manager.WagonManager; | |||||
import org.apache.maven.artifact.repository.ArtifactRepository; | |||||
import org.apache.maven.repository.proxy.configuration.ProxyConfiguration; | |||||
import org.apache.maven.repository.proxy.files.DefaultRepositoryFileManager; | |||||
import org.apache.maven.repository.proxy.files.Checksum; | |||||
import org.apache.maven.repository.proxy.repository.ProxyRepository; | |||||
import org.apache.maven.wagon.ConnectionException; | |||||
import org.apache.maven.wagon.ResourceDoesNotExistException; | |||||
import org.apache.maven.wagon.TransferFailedException; | |||||
import org.apache.maven.wagon.UnsupportedProtocolException; | |||||
import org.apache.maven.wagon.Wagon; | |||||
import org.apache.maven.wagon.authentication.AuthenticationException; | |||||
import org.apache.maven.wagon.authorization.AuthorizationException; | |||||
import org.apache.maven.wagon.observers.ChecksumObserver; | |||||
import org.codehaus.plexus.util.FileUtils; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.security.NoSuchAlgorithmException; | |||||
import java.util.Iterator; | |||||
/** | |||||
* @author Edwin Punzalan | |||||
*/ | |||||
public class DefaultProxyManager | |||||
//implements ProxyManager | |||||
{ | |||||
/* @plexus.requirement */ | |||||
private WagonManager wagon; | |||||
private ProxyConfiguration config; | |||||
public DefaultProxyManager( ProxyConfiguration configuration ) | |||||
{ | |||||
config = configuration; | |||||
} | |||||
public File get( String path ) | |||||
throws ProxyException | |||||
{ | |||||
String cachePath = config.getRepositoryCachePath(); | |||||
File cachedFile = new File( cachePath, path ); | |||||
if ( !cachedFile.exists() ) | |||||
{ | |||||
getRemoteFile( path ); | |||||
} | |||||
return cachedFile; | |||||
} | |||||
public File getRemoteFile( String path ) | |||||
throws ProxyException | |||||
{ | |||||
try | |||||
{ | |||||
if ( path.indexOf( "/jars/" ) >= 0 ) | |||||
{ | |||||
//@todo maven 1 repo request | |||||
throw new ProxyException( "Maven 1 repository requests not yet supported." ); | |||||
} | |||||
else if ( path.indexOf( "/poms/" ) >= 0 ) | |||||
{ | |||||
//@todo maven 1 repo request | |||||
throw new ProxyException( "Maven 1 repository requests not yet supported." ); | |||||
} | |||||
else | |||||
{ | |||||
//maven 2 repo request | |||||
Object obj = new DefaultRepositoryFileManager().getRequestedObjectFromPath( path ); | |||||
if ( obj == null ) | |||||
{ | |||||
//right now, only metadata is known to fit here | |||||
return getRepositoryFile( path ); | |||||
} | |||||
else if ( obj instanceof Checksum ) | |||||
{ | |||||
return getRepositoryFile( path, false ); | |||||
} | |||||
else if ( obj instanceof Artifact ) | |||||
{ | |||||
Artifact artifact = (Artifact) obj; | |||||
return getArtifactFile( artifact ); | |||||
} | |||||
else | |||||
{ | |||||
throw new ProxyException( "Could not hande repository object: " + obj.getClass() ); | |||||
} | |||||
} | |||||
} | |||||
catch( TransferFailedException e ) | |||||
{ | |||||
throw new ProxyException( e.getMessage(), e ); | |||||
} | |||||
catch( ResourceDoesNotExistException e) | |||||
{ | |||||
throw new ProxyException( e.getMessage(), e ); | |||||
} | |||||
} | |||||
private File getArtifactFile( Artifact artifact ) | |||||
throws TransferFailedException, ResourceDoesNotExistException | |||||
{ | |||||
ArtifactRepository repoCache = config.getRepositoryCache(); | |||||
File artifactFile = new File( repoCache.getBasedir(), repoCache.pathOf( artifact ) ); | |||||
if ( !artifactFile.exists() ) | |||||
{ | |||||
wagon.getArtifact( artifact, config.getRepositories() ); | |||||
artifactFile = artifact.getFile(); | |||||
} | |||||
return artifactFile; | |||||
} | |||||
private File getRepositoryFile( String path ) | |||||
throws ProxyException | |||||
{ | |||||
return getRepositoryFile( path, true ); | |||||
} | |||||
private File getRepositoryFile( String path, boolean useChecksum ) | |||||
throws ProxyException | |||||
{ | |||||
ArtifactRepository cache = config.getRepositoryCache(); | |||||
File target = new File( cache.getBasedir(), path ); | |||||
for( Iterator repositories = config.getRepositories().iterator(); repositories.hasNext(); ) | |||||
{ | |||||
ProxyRepository repository = (ProxyRepository) repositories.next(); | |||||
try | |||||
{ | |||||
Wagon wagon = this.wagon.getWagon( repository.getProtocol() ); | |||||
//@todo configure wagon | |||||
ChecksumObserver listener = null; | |||||
try | |||||
{ | |||||
listener = repository.getChecksumObserver(); | |||||
if ( listener != null ) | |||||
{ | |||||
wagon.addTransferListener( listener ); | |||||
} | |||||
} | |||||
catch ( NoSuchAlgorithmException e ) | |||||
{ | |||||
System.out.println( "Skipping checksum validation for unsupported algorithm: " | |||||
+ repository.getChecksum() ); | |||||
} | |||||
if ( connectToRepository( wagon, repository ) ) | |||||
{ | |||||
File temp = new File( target.getAbsolutePath() + ".tmp" ); | |||||
temp.deleteOnExit(); | |||||
int tries = 0; | |||||
boolean success = false; | |||||
while( !success ) | |||||
{ | |||||
tries++; | |||||
wagon.get( path, temp ); | |||||
if ( useChecksum ) | |||||
{ | |||||
success = doChecksumCheck( listener, repository, path, wagon ); | |||||
} | |||||
else | |||||
{ | |||||
success = true; | |||||
} | |||||
if ( tries > 1 && !success ) | |||||
{ | |||||
throw new ProxyException( "Checksum failures occurred while downloading " + path ); | |||||
} | |||||
} | |||||
disconnectWagon( wagon ); | |||||
return target; | |||||
} | |||||
//try next repository | |||||
} | |||||
catch ( TransferFailedException e ) | |||||
{ | |||||
System.out.println( "Skipping repository " + repository.getUrl() + | |||||
": " + e.getMessage() ); | |||||
} | |||||
catch ( ResourceDoesNotExistException e ) | |||||
{ | |||||
//do nothing, file not found in this repository | |||||
} | |||||
catch ( AuthorizationException e ) | |||||
{ | |||||
System.out.println( "Skipping repository " + repository.getUrl() + | |||||
": " + e.getMessage() ); | |||||
} | |||||
catch ( UnsupportedProtocolException e ) | |||||
{ | |||||
System.out.println( "Skipping repository " + repository.getUrl() + | |||||
": no wagon configured for protocol " + repository.getProtocol() ); | |||||
} | |||||
} | |||||
throw new ProxyException( "Could not find " + path + " in any of the repositories." ); | |||||
} | |||||
private boolean connectToRepository( Wagon wagon, ProxyRepository repository ) | |||||
{ | |||||
boolean connected = false; | |||||
try | |||||
{ | |||||
wagon.connect( repository ); | |||||
connected = true; | |||||
} | |||||
catch ( ConnectionException e ) | |||||
{ | |||||
System.out.println( "Could not connect to " + repository.getId() + ": " + e.getMessage() ); | |||||
} | |||||
catch ( AuthenticationException e ) | |||||
{ | |||||
System.out.println( "Could not connect to " + repository.getId() + ": " + e.getMessage() ); | |||||
} | |||||
return connected; | |||||
} | |||||
private boolean doChecksumCheck( ChecksumObserver listener, ProxyRepository repository, String path, Wagon wagon ) | |||||
//throws ChecksumFailedException | |||||
{ | |||||
boolean success = false; | |||||
try | |||||
{ | |||||
String checksumExt = repository.getChecksum().getFileExtension(); | |||||
String remotePath = path + "." + checksumExt; | |||||
File checksumFile = new File( config.getRepositoryCache().getBasedir(), remotePath ); | |||||
verifyChecksum( listener.getActualChecksum(), checksumFile, remotePath, checksumExt, wagon ); | |||||
wagon.removeTransferListener( listener ); | |||||
success = true; | |||||
} | |||||
catch ( ChecksumFailedException e ) | |||||
{ | |||||
System.out.println( "*** CHECKSUM FAILED - " + e.getMessage() + " - RETRYING" ); | |||||
} | |||||
return success; | |||||
} | |||||
private void verifyChecksum( String actualChecksum, File destination, String remotePath, | |||||
String checksumFileExtension, Wagon wagon ) | |||||
throws ChecksumFailedException | |||||
{ | |||||
try | |||||
{ | |||||
File tempDestination = new File( destination.getAbsolutePath() + ".tmp" ); | |||||
tempDestination.deleteOnExit(); | |||||
File tempChecksumFile = new File( tempDestination + checksumFileExtension + ".tmp" ); | |||||
tempChecksumFile.deleteOnExit(); | |||||
wagon.get( remotePath + checksumFileExtension, tempChecksumFile ); | |||||
String expectedChecksum = FileUtils.fileRead( tempChecksumFile ); | |||||
// remove whitespaces at the end | |||||
expectedChecksum = expectedChecksum.trim(); | |||||
// check for 'MD5 (name) = CHECKSUM' | |||||
if ( expectedChecksum.startsWith( "MD5" ) ) | |||||
{ | |||||
int lastSpacePos = expectedChecksum.lastIndexOf( ' ' ); | |||||
expectedChecksum = expectedChecksum.substring( lastSpacePos + 1 ); | |||||
} | |||||
else | |||||
{ | |||||
// remove everything after the first space (if available) | |||||
int spacePos = expectedChecksum.indexOf( ' ' ); | |||||
if ( spacePos != -1 ) | |||||
{ | |||||
expectedChecksum = expectedChecksum.substring( 0, spacePos ); | |||||
} | |||||
} | |||||
if ( expectedChecksum.equals( actualChecksum ) ) | |||||
{ | |||||
File checksumFile = new File( destination + checksumFileExtension ); | |||||
if ( checksumFile.exists() ) checksumFile.delete(); | |||||
FileUtils.copyFile( tempChecksumFile, checksumFile ); | |||||
} | |||||
else | |||||
{ | |||||
throw new ChecksumFailedException( "Checksum failed on download: local = '" + actualChecksum + | |||||
"'; remote = '" + expectedChecksum + "'" ); | |||||
} | |||||
} | |||||
catch ( TransferFailedException e ) | |||||
{ | |||||
System.out.println( "Skipping checksum validation for " + remotePath + ": " + e.getMessage() ); | |||||
} | |||||
catch ( ResourceDoesNotExistException e ) | |||||
{ | |||||
System.out.println( "Skipping checksum validation for " + remotePath + ": " + e.getMessage() ); | |||||
} | |||||
catch ( AuthorizationException e ) | |||||
{ | |||||
System.out.println( "Skipping checksum validation for " + remotePath + ": " + e.getMessage() ); | |||||
} | |||||
catch ( IOException e ) | |||||
{ | |||||
throw new ChecksumFailedException( "Invalid checksum file", e ); | |||||
} | |||||
} | |||||
private void disconnectWagon( Wagon wagon ) | |||||
{ | |||||
try | |||||
{ | |||||
wagon.disconnect(); | |||||
} | |||||
catch ( ConnectionException e ) | |||||
{ | |||||
System.err.println( "Problem disconnecting from wagon - ignoring: " + e.getMessage() ); | |||||
} | |||||
} | |||||
} |
package org.apache.maven.repository.proxy; | |||||
/** | |||||
* @author Edwin Punzalan | |||||
*/ | |||||
public class ProxyException | |||||
extends Exception | |||||
{ | |||||
public ProxyException( String message ) | |||||
{ | |||||
super( message ); | |||||
} | |||||
public ProxyException( String message, Throwable t ) | |||||
{ | |||||
super( message, t ); | |||||
} | |||||
} |
package org.apache.maven.repository.proxy; | |||||
import org.apache.maven.artifact.Artifact; | |||||
import org.apache.maven.wagon.ResourceDoesNotExistException; | |||||
import org.apache.maven.wagon.TransferFailedException; | |||||
import java.io.IOException; | |||||
import java.io.InputStream; | |||||
import java.io.File; | |||||
/** | |||||
* @author Edwin Punzalan | |||||
*/ | |||||
public interface ProxyManager | |||||
{ | |||||
File getArtifactFile( Artifact artifact ) | |||||
throws TransferFailedException, ResourceDoesNotExistException, IOException; | |||||
InputStream getArtifactAsStream( Artifact artifact ) | |||||
throws TransferFailedException, ResourceDoesNotExistException, IOException; | |||||
} |
package org.apache.maven.repository.proxy.configuration; | |||||
import org.apache.maven.artifact.repository.ArtifactRepository; | |||||
import org.apache.maven.artifact.repository.ArtifactRepositoryPolicy; | |||||
import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; | |||||
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; | |||||
import org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout; | |||||
import org.apache.maven.repository.proxy.repository.ProxyRepository; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Collections; | |||||
/** | |||||
* @plexus.component role="org.apache.maven.repository.proxy.configuration.ProxyConfiguration" | |||||
* | |||||
* @author Edwin Punzalan | |||||
*/ | |||||
public class ProxyConfiguration | |||||
{ | |||||
public static final String ROLE = ProxyConfiguration.class.getName(); | |||||
/** @plexus.requirement */ | |||||
private ArtifactRepositoryFactory artifactRepositoryFactory; | |||||
private boolean browsable; | |||||
private ArtifactRepository repoCache; | |||||
private ArrayList repositories = new ArrayList(); | |||||
public void setBrowsable( boolean browsable ) | |||||
{ | |||||
this.browsable = browsable; | |||||
} | |||||
public boolean isBrowsable() | |||||
{ | |||||
return browsable; | |||||
} | |||||
public void setRepositoryCachePath( String repoCachePath ) | |||||
{ | |||||
ArtifactRepositoryPolicy standardPolicy; | |||||
standardPolicy = new ArtifactRepositoryPolicy( true, ArtifactRepositoryPolicy.UPDATE_POLICY_ALWAYS, | |||||
ArtifactRepositoryPolicy.CHECKSUM_POLICY_IGNORE ); | |||||
ArtifactRepositoryLayout layout = new DefaultRepositoryLayout(); | |||||
repoCache = artifactRepositoryFactory.createArtifactRepository( "localCache", repoCachePath, layout, | |||||
standardPolicy, standardPolicy ); | |||||
} | |||||
public ArtifactRepository getRepositoryCache( ) | |||||
{ | |||||
return repoCache; | |||||
} | |||||
public String getRepositoryCachePath() | |||||
{ | |||||
return repoCache.getBasedir(); | |||||
} | |||||
public void addRepository( ProxyRepository repository ) | |||||
{ | |||||
repositories.add( repository ); | |||||
} | |||||
public List getRepositories() | |||||
{ | |||||
return Collections.unmodifiableList( repositories ); | |||||
} | |||||
public void setRepositories( ArrayList repositories ) | |||||
{ | |||||
this.repositories = repositories; | |||||
} | |||||
} |
package org.apache.maven.repository.proxy.files; | |||||
/** | |||||
* @author Edwin Punzalan | |||||
*/ | |||||
public class Checksum | |||||
{ | |||||
private String algorithm; | |||||
public Checksum( String algorithm ) | |||||
{ | |||||
this.setAlgorithm( algorithm ); | |||||
} | |||||
public String getFileExtension() | |||||
{ | |||||
if ( "MD5".equals( algorithm ) ) | |||||
{ | |||||
return "md5"; | |||||
} | |||||
else | |||||
{ | |||||
return "sha1"; | |||||
} | |||||
} | |||||
public String getAlgorithm() | |||||
{ | |||||
return algorithm; | |||||
} | |||||
public void setAlgorithm( String algorithm ) | |||||
{ | |||||
this.algorithm = algorithm; | |||||
} | |||||
} |
package org.apache.maven.repository.proxy.files; | |||||
import org.apache.maven.artifact.Artifact; | |||||
import org.apache.maven.artifact.factory.ArtifactFactory; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.StringTokenizer; | |||||
/** | |||||
* @author Edwin Punzalan | |||||
*/ | |||||
public class DefaultRepositoryFileManager | |||||
{ | |||||
/* @plexus.requirement */ | |||||
private ArtifactFactory factory; | |||||
public Object getRequestedObjectFromPath( String path ) | |||||
{ | |||||
if ( path.endsWith( ".pom" ) ) | |||||
{ | |||||
return getArtifactFromPath( path ); | |||||
} | |||||
else if ( path.endsWith( "ar" ) ) | |||||
{ | |||||
return getArtifactFromPath( path ); | |||||
} | |||||
else if ( path.endsWith( ".md5" ) ) | |||||
{ | |||||
return new Checksum( "MD5" ); | |||||
} | |||||
else if ( path.endsWith( ".sha1" ) ) | |||||
{ | |||||
return new Checksum( "SHA-1" ); | |||||
} | |||||
else | |||||
{ | |||||
//@todo handle metadata file requests | |||||
return null; | |||||
} | |||||
} | |||||
private Artifact getArtifactFromPath( String path ) | |||||
{ | |||||
List pathInfo = getReversedPathInfo( path ); | |||||
String filename = getPathToken( pathInfo ); | |||||
String version = getPathToken( pathInfo ); | |||||
String artifactId = getPathToken( pathInfo ); | |||||
String groupId = ""; | |||||
while ( pathInfo.size() > 0 ) | |||||
{ | |||||
if ( groupId.length() == 0 ) | |||||
{ | |||||
groupId = "." + groupId; | |||||
} | |||||
else | |||||
{ | |||||
groupId = getPathToken( pathInfo ) + "." + groupId; | |||||
} | |||||
} | |||||
return factory.createBuildArtifact( groupId, artifactId, version, getFileExtension( filename ) ); | |||||
} | |||||
private List getReversedPathInfo( String path ) | |||||
{ | |||||
List reversedPath = new ArrayList(); | |||||
StringTokenizer tokenizer = new StringTokenizer( path, "/" ); | |||||
while( tokenizer.hasMoreTokens() ) | |||||
{ | |||||
reversedPath.add( 0, tokenizer.nextToken() ); | |||||
} | |||||
return reversedPath; | |||||
} | |||||
private String getPathToken( List path ) | |||||
{ | |||||
if ( path.size() > 0 ) | |||||
{ | |||||
return (String) path.remove( 0 ); | |||||
} | |||||
else | |||||
{ | |||||
return null; | |||||
} | |||||
} | |||||
private String getFileExtension( String filename ) | |||||
{ | |||||
int idx = filename.lastIndexOf( '.' ); | |||||
return filename.substring( idx + 1 ); | |||||
} | |||||
} |
package org.apache.maven.repository.proxy.repository; | |||||
import org.apache.maven.artifact.repository.DefaultArtifactRepository; | |||||
import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout; | |||||
import org.apache.maven.wagon.observers.ChecksumObserver; | |||||
import org.apache.maven.repository.proxy.files.Checksum; | |||||
import java.security.NoSuchAlgorithmException; | |||||
/** | |||||
* @author Edwin Punzalan | |||||
*/ | |||||
public class ProxyRepository | |||||
extends DefaultArtifactRepository | |||||
{ | |||||
private Checksum checksum; | |||||
public ProxyRepository( String id, String url, ArtifactRepositoryLayout layout ) | |||||
{ | |||||
super( id, url, layout ); | |||||
} | |||||
public void setChecksum( String algorithm ) | |||||
{ | |||||
this.checksum = new Checksum( algorithm ); | |||||
} | |||||
public Checksum getChecksum() | |||||
{ | |||||
return checksum; | |||||
} | |||||
public ChecksumObserver getChecksumObserver() | |||||
throws NoSuchAlgorithmException | |||||
{ | |||||
return new ChecksumObserver( checksum.getAlgorithm() ); | |||||
} | |||||
} |
<module>maven-repository-indexer</module> | <module>maven-repository-indexer</module> | ||||
<module>maven-repository-utils</module> | <module>maven-repository-utils</module> | ||||
<module>maven-repository-webapp</module> | <module>maven-repository-webapp</module> | ||||
<module>maven-repository-proxy</module> | |||||
</modules> | </modules> | ||||
<repositories> | <repositories> | ||||
<repository> | <repository> |