blob: 9704da69136f2e850b7e9650828456ebd9ecb02e [file] [log] [blame]
package org.apache.maven.wagon.providers.http;
/*
* Copyright 2001-2005 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.
*/
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpRecoverableException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.util.DateParseException;
import org.apache.commons.httpclient.util.DateParser;
import org.apache.commons.lang.StringUtils;
import org.apache.maven.wagon.AbstractWagon;
import org.apache.maven.wagon.ResourceDoesNotExistException;
import org.apache.maven.wagon.TransferFailedException;
import org.apache.maven.wagon.authentication.AuthenticationInfo;
import org.apache.maven.wagon.authorization.AuthorizationException;
import org.apache.maven.wagon.resource.Resource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.Date;
import java.util.TimeZone;
import java.text.SimpleDateFormat;
/**
* @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
* @version $Id$
*/
public class HttpWagon
extends AbstractWagon
{
private final static int DEFAULT_NUMBER_OF_ATTEMPTS = 3;
private final static int SC_NULL = -1;
private HttpClient client = null;
private int numberOfAttempts = DEFAULT_NUMBER_OF_ATTEMPTS;
private static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" );
public void openConnection()
{
client = new HttpClient( new MultiThreadedHttpConnectionManager() );
final AuthenticationInfo authInfo = getRepository().getAuthenticationInfo();
String username = null;
String password = null;
if ( authInfo != null )
{
username = authInfo.getUserName();
password = authInfo.getPassword();
}
String host = getRepository().getHost();
if ( StringUtils.isNotEmpty( username ) && StringUtils.isNotEmpty( password ) )
{
Credentials creds = new UsernamePasswordCredentials( username, password );
client.getState().setCredentials( null, host, creds );
client.getState().setAuthenticationPreemptive( true );
}
HostConfiguration hc = new HostConfiguration();
if ( proxyInfo != null )
{
String proxyUsername = proxyInfo.getUserName();
String proxyPassword = proxyInfo.getPassword();
String proxyHost = proxyInfo.getHost();
int proxyPort = proxyInfo.getPort();
String proxyNtlmHost = proxyInfo.getNtlmHost();
String proxyNtlmDomain = proxyInfo.getNtlmDomain();
if ( proxyHost != null )
{
hc.setProxy( proxyHost, proxyPort );
if ( proxyUsername != null && proxyPassword != null )
{
Credentials creds;
if ( proxyNtlmHost != null || proxyNtlmDomain != null )
{
creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
}
else
{
creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
}
client.getState().setProxyCredentials( null, proxyHost, creds );
client.getState().setAuthenticationPreemptive( true );
}
}
}
hc.setHost( host );
//start a session with the webserver
client.setHostConfiguration( hc );
}
// put
public void put( File source, String resourceName )
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
{
String url = getRepository().getUrl() + "/" + resourceName;
PutMethod putMethod = new PutMethod( url );
Resource resource = new Resource( resourceName );
try
{
InputStream is = new PutInputStream( source, resource, this, getTransferEventSupport() );
putMethod.setRequestBody( is );
}
catch ( FileNotFoundException e )
{
fireTransferError( resource, e );
throw new ResourceDoesNotExistException( "Source file does not exist: " + source, e );
}
int statusCode = SC_NULL;
int attempt = 0;
fireTransferDebug( "about to execute client for put" );
// We will retry up to NumberOfAttempts times.
while ( ( statusCode == SC_NULL ) && ( attempt < getNumberOfAttempts() ) )
{
try
{
firePutStarted( resource, source );
statusCode = client.executeMethod( putMethod );
firePutCompleted( resource, source );
}
catch ( HttpRecoverableException e )
{
attempt++;
continue;
}
catch ( IOException e )
{
throw new TransferFailedException( e.getMessage(), e );
}
}
fireTransferDebug( url + " - Status code: " + statusCode );
// Check that we didn't run out of retries.
switch ( statusCode )
{
case HttpStatus.SC_OK:
break;
case HttpStatus.SC_CREATED:
break;
case SC_NULL:
throw new TransferFailedException( "Failed to transfer file: " + url + " after " + attempt +
" attempts" );
case HttpStatus.SC_FORBIDDEN:
throw new AuthorizationException( "Access denided to: " + url );
case HttpStatus.SC_NOT_FOUND:
throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
//add more entries here
default :
throw new TransferFailedException( "Failed to transfer file: " + url + ". Return code is: " +
statusCode );
}
putMethod.releaseConnection();
firePutCompleted( resource, source );
}
public void closeConnection()
{
}
public void get( String resourceName, File destination )
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
{
get( resourceName, destination, 0 );
}
public boolean getIfNewer( String resourceName, File destination, long timestamp ) throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
{
return get( resourceName, destination, timestamp );
}
/**
*
* @param resourceName
* @param destination
* @param timestamp the timestamp to check against, only downloading if newer. If <code>0</code>, always download
* @return
* @throws TransferFailedException
* @throws ResourceDoesNotExistException
* @throws AuthorizationException
*
* @return <code>true</code> if newer version was downloaded, <code>false</code> otherwise.
*/
public boolean get( String resourceName, File destination, long timestamp )
throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
{
boolean retValue = false;
String url = getRepository().getUrl() + "/" + resourceName;
GetMethod getMethod = new GetMethod( url );
try
{
getMethod.addRequestHeader( "Cache-control", "no-cache" );
getMethod.addRequestHeader( "Cache-store", "no-store" );
getMethod.addRequestHeader( "Pragma", "no-cache" );
getMethod.addRequestHeader( "Expires", "0" );
if ( timestamp > 0 )
{
SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
fmt.setTimeZone( GMT_TIME_ZONE );
Header hdr = new Header( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
getMethod.addRequestHeader( hdr );
}
int statusCode = SC_NULL;
int attempt = 0;
// We will retry up to NumberOfAttempts times.
while ( ( statusCode == SC_NULL ) && ( attempt < getNumberOfAttempts() ) )
{
try
{
// execute the getMethod.
statusCode = client.executeMethod( getMethod );
}
catch ( HttpRecoverableException e )
{
attempt++;
continue;
}
catch ( IOException e )
{
throw new TransferFailedException( e.getMessage(), e );
}
}
fireTransferDebug( url + " - Status code: " + statusCode );
// TODO [BP]: according to httpclient docs, really should swallow the output on error. verify if that is required
switch ( statusCode )
{
case HttpStatus.SC_OK:
break;
case HttpStatus.SC_NOT_MODIFIED:
return false;
case SC_NULL:
throw new TransferFailedException( "Failed to transfer file: " + url + " after " + attempt +
" attempts" );
case HttpStatus.SC_FORBIDDEN:
throw new AuthorizationException( "Access denided to: " + url );
case HttpStatus.SC_UNAUTHORIZED:
throw new AuthorizationException( "Not authorized." );
case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
throw new AuthorizationException( "Not authorized by proxy." );
case HttpStatus.SC_NOT_FOUND:
throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
//add more entries here
default :
throw new TransferFailedException( "Failed to trasfer file: " + url + ". Return code is: " +
statusCode );
}
Resource resource = new Resource( resourceName );
InputStream is = null;
Header contentLengthHeader = getMethod.getResponseHeader( "Content-Length" );
if ( contentLengthHeader != null )
{
try
{
long contentLength = Integer.valueOf( contentLengthHeader.getValue() ).intValue();
resource.setContentLength( contentLength );
}
catch ( NumberFormatException e )
{
fireTransferDebug( "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e );
}
}
Header lastModifiedHeader = getMethod.getResponseHeader( "Last-Modified" );
long lastModified = 0;
if ( lastModifiedHeader != null )
{
try
{
lastModified = DateParser.parseDate( lastModifiedHeader.getValue() ).getTime();
}
catch ( DateParseException e )
{
fireTransferDebug( "Unable to parse last modified header" );
}
fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" );
}
if ( timestamp < lastModified )
{
retValue = true;
try
{
is = getMethod.getResponseBodyAsStream();
getTransfer( resource, destination, is );
}
catch ( Exception e )
{
fireTransferError( resource, e );
if ( destination.exists() )
{
boolean deleted = destination.delete();
if ( ! deleted )
{
destination.deleteOnExit();
}
}
String msg = "Error occured while deploying to remote repository:" + getRepository();
throw new TransferFailedException( msg, e );
}
finally
{
shutdownStream( is );
}
if ( lastModified > 0 )
{
resource.setLastModified( lastModified );
}
}
else
{
fireTransferDebug( "Local file is newer: not downloaded" );
}
return retValue;
}
finally
{
getMethod.releaseConnection();
}
}
public int getNumberOfAttempts()
{
return numberOfAttempts;
}
public void setNumberOfAttempts( int numberOfAttempts )
{
this.numberOfAttempts = numberOfAttempts;
}
}