Skip to content
Commits on Source (6)
wagon (3.2.0-1) unstable; urgency=medium
* Team upload.
* New upstream release
- Refreshed the patches
* Generate Java 7 compatible bytecode
* Standards-Version updated to 4.2.1
-- Emmanuel Bourg <ebourg@apache.org> Mon, 19 Nov 2018 10:32:18 +0100
wagon (3.1.0-1) unstable; urgency=medium
* Team upload.
......
......@@ -26,7 +26,7 @@ Build-Depends:
libplexus-utils2-java,
libservlet3.1-java,
maven-debian-helper
Standards-Version: 4.1.5
Standards-Version: 4.2.1
Vcs-Git: https://salsa.debian.org/java-team/wagon.git
Vcs-Browser: https://salsa.debian.org/java-team/wagon
Homepage: http://maven.apache.org/wagon/
......
maven.compiler.release=8
maven.compiler.release=7
maven.test.skip=true
......@@ -5,7 +5,7 @@ Last-Update: 2014-10-09
--- a/wagon-providers/wagon-http/src/test/java/org/apache/maven/wagon/providers/http/AbstractHttpClientWagonTest.java
+++ b/wagon-providers/wagon-http/src/test/java/org/apache/maven/wagon/providers/http/AbstractHttpClientWagonTest.java
@@ -27,7 +27,7 @@
@@ -55,7 +55,7 @@
public class AbstractHttpClientWagonTest
{
......
......@@ -23,13 +23,13 @@ under the License.
<parent>
<groupId>org.apache.maven</groupId>
<artifactId>maven-parent</artifactId>
<version>31</version>
<version>33</version>
<relativePath>../pom/maven/pom.xml</relativePath>
</parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<packaging>pom</packaging>
<name>Apache Maven Wagon</name>
......@@ -200,7 +200,7 @@ under the License.
<connection>scm:git:https://gitbox.apache.org/repos/asf/maven-wagon.git</connection>
<developerConnection>scm:git:https://gitbox.apache.org/repos/asf/maven-wagon.git</developerConnection>
<url>https://github.com/apache/maven-wagon/tree/${project.scm.tag}</url>
<tag>wagon-3.1.0</tag>
<tag>wagon-3.2.0</tag>
</scm>
<issueManagement>
......@@ -215,7 +215,7 @@ under the License.
<distributionManagement>
<site>
<id>apache.website</id>
<url>scm:svn:https://svn.apache.org/repos/infra/websites/production/maven/components/${maven.site.path}</url>
<url>scm:svn:https://svn.apache.org/repos/asf/maven/website/components/${maven.site.path}</url>
</site>
</distributionManagement>
......@@ -310,7 +310,7 @@ under the License.
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.5.1</version>
<version>3.6</version>
</dependency>
<dependency>
......
......@@ -34,7 +34,7 @@ under the License.
<p>In order to guard against corrupted downloads/installations, it is highly recommended to
<a href="http://www.apache.org/dev/release-signing#verifying-signature">verify the signature</a>
of the release bundles against the public <a href="http://www.apache.org/dist/maven/KEYS">KEYS</a> used by the Apache Maven
of the release bundles against the public <a href="https://www.apache.org/dist/maven/KEYS">KEYS</a> used by the Apache Maven
developers.</p>
<p>${project.name} is distributed under the <a href="http://www.apache.org/licenses/">Apache License, version 2.0</a>.</p>
......@@ -108,8 +108,8 @@ under the License.
<tr>
<td>${project.name} ${project.version} (Source zip)</td>
<td><a href="[preferred]maven/wagon/${project.artifactId}-${project.version}-source-release.zip">maven/wagon/${project.artifactId}-${project.version}-source-release.zip</a></td>
<td><a href="http://www.apache.org/dist/maven/wagon/${project.artifactId}-${project.version}-source-release.zip.sha1">maven/wagon/${project.artifactId}-${project.version}-source-release.zip.sha1</a></td>
<td><a href="http://www.apache.org/dist/maven/wagon/${project.artifactId}-${project.version}-source-release.zip.asc">maven/wagon/${project.artifactId}-${project.version}-source-release.zip.asc</a></td>
<td><a href="https://www.apache.org/dist/maven/wagon/${project.artifactId}-${project.version}-source-release.zip.sha512">maven/wagon/${project.artifactId}-${project.version}-source-release.zip.sha512</a></td>
<td><a href="https://www.apache.org/dist/maven/wagon/${project.artifactId}-${project.version}-source-release.zip.asc">maven/wagon/${project.artifactId}-${project.version}-source-release.zip.asc</a></td>
</tr>
</tbody>
</table>
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -61,7 +61,7 @@ public abstract class WagonTestCase
static final class ProgressAnswer implements IAnswer
{
private int size;
public Object answer() throws Throwable
{
int length = (Integer) getCurrentArguments()[2];
......@@ -93,6 +93,8 @@ public int getSize()
protected String resource;
protected boolean testSkipped;
protected File artifactSourceFile;
protected File artifactDestFile;
......@@ -226,6 +228,24 @@ protected Wagon getWagon()
return wagon;
}
/**
* @param cmd the executable to run, not null.
* @return <code>true</code>
*/
public static boolean isSystemCmd( String cmd )
{
try
{
Runtime.getRuntime().exec( cmd );
return true;
}
catch ( IOException e )
{
return false;
}
}
protected void message( String message )
{
logger.info( message );
......@@ -262,6 +282,16 @@ public void testWagonGetIfNewerIsNewer()
}
}
@Override
protected void runTest()
throws Throwable
{
if ( !testSkipped )
{
super.runTest();
}
}
protected boolean supportsGetIfNewer()
{
return true;
......@@ -382,7 +412,7 @@ private void replaceMockForSkippedGetIfNewer( Wagon wagon, int expectedSize )
mockTransferListener.debug( anyString() );
expectLastCall().anyTimes();
replay( mockTransferListener );
}
......
......@@ -74,6 +74,7 @@
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPOutputStream;
/**
......@@ -583,7 +584,7 @@ public void testGzipGet()
sourceFile.deleteOnExit();
String resName = "gzip-res.txt";
String sourceContent = writeTestFileGzip( sourceFile, resName );
String sourceContent = writeTestFile( sourceFile, resName, "gzip" );
wagon.connect( testRepository );
......@@ -605,6 +606,56 @@ public void testGzipGet()
}
}
/* This test cannot be enabled because we cannot tell GzipFilter to compress with deflate only
public void testDeflateGet()
throws Exception
{
Server server = new Server( );
String localRepositoryPath = FileTestUtils.getTestOutputDir().toString();
ServletContextHandler root = new ServletContextHandler( ServletContextHandler.SESSIONS );
root.setResourceBase( localRepositoryPath );
ServletHolder servletHolder = new ServletHolder( new DefaultServlet() );
root.addServlet( servletHolder, "/*" );
FilterHolder filterHolder = new FilterHolder( new GzipFilter() );
root.addFilter( filterHolder, "/deflate/*", EnumSet.of( DispatcherType.REQUEST ) );
addConnector( server );
server.setHandler( root );
server.start();
try
{
Wagon wagon = getWagon();
Repository testRepository = new Repository( "id", getRepositoryUrl( server ) );
File sourceFile = new File( localRepositoryPath + "/deflate" );
sourceFile.deleteOnExit();
String resName = "deflate-res.txt";
String sourceContent = writeTestFile( sourceFile, resName, null );
wagon.connect( testRepository );
File destFile = FileTestUtils.createUniqueFile( getName(), getName() );
destFile.deleteOnExit();
wagon.get( "deflate/" + resName, destFile );
wagon.disconnect();
String destContent = FileUtils.fileRead( destFile );
assertEquals( sourceContent, destContent );
}
finally
{
server.stop();
}
}*/
public void testProxiedRequest()
throws Exception
{
......@@ -1546,7 +1597,7 @@ private Server createSecurityServer( String localRepositoryPath )
}
private String writeTestFileGzip( File parent, String child )
private String writeTestFile( File parent, String child, String compressionType )
throws IOException
{
File file = new File( parent, child );
......@@ -1562,15 +1613,32 @@ private String writeTestFileGzip( File parent, String child )
out.close();
}
file = new File( parent, child + ".gz" );
String ext = "";
if ( "gzip".equals( compressionType ) )
{
ext = ".gz";
}
if ( "deflate".equals( compressionType ) )
{
ext = ".deflate";
}
file = new File( parent, child + ext );
file.deleteOnExit();
String content;
out = new FileOutputStream( file );
out = new GZIPOutputStream( out );
if ( "gzip".equals( compressionType ) )
{
out = new GZIPOutputStream( out );
}
if ( "deflate".equals( compressionType ) )
{
out = new DeflaterOutputStream( out );
}
try
{
// write out different data than non-gz file, so we can
// assert the gz version was returned
// write out different data than non-compressed file, so we can
// assert the compressed version was returned
content = file.getAbsolutePath();
out.write( content.getBytes() );
}
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......@@ -50,12 +50,12 @@ under the License.
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.9</version>
<version>4.4.10</version>
</dependency>
<dependency>
<groupId>org.apache.sshd</groupId>
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-providers</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-providers</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-providers</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -50,6 +50,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.zip.DeflaterInputStream;
import java.util.zip.GZIPInputStream;
/**
......@@ -119,7 +120,7 @@ public void fillInputData( InputData inputData )
URL url = new URL( visitingUrl );
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection( this.proxy );
urlConnection.setRequestProperty( "Accept-Encoding", "gzip" );
urlConnection.setRequestProperty( "Accept-Encoding", "gzip,deflate" );
if ( !useCache )
{
urlConnection.setRequestProperty( "Pragma", "no-cache" );
......@@ -148,6 +149,11 @@ public void fillInputData( InputData inputData )
{
is = new GZIPInputStream( is );
}
boolean isDeflated = contentEncoding != null && "deflate".equalsIgnoreCase( contentEncoding );
if ( isDeflated )
{
is = new DeflaterInputStream( is );
}
inputData.setInputStream( is );
resource.setLastModified( urlConnection.getLastModified() );
resource.setContentLength( urlConnection.getContentLength() );
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-providers</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -32,6 +32,7 @@
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
......@@ -54,7 +55,9 @@
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
......@@ -82,7 +85,10 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
......@@ -288,6 +294,15 @@ public boolean isStreaming()
private static final int MAX_BACKOFF_WAIT_SECONDS =
Integer.parseInt( System.getProperty( "maven.wagon.httpconnectionManager.maxBackoffSeconds", "180" ) );
/**
* Time to live in seconds for an HTTP connection. After that time, the connection will be dropped.
* Intermediates tend to drop connections after some idle period. Set to -1 to maintain connections
* indefinitely. This value defaults to 300 seconds.
*
* @since 3.2
*/
private static final long CONN_TTL =
Long.getLong( "maven.wagon.httpconnectionManager.ttlSeconds", 300L );
protected int backoff( int wait, String url )
throws InterruptedException, TransferFailedException
......@@ -341,7 +356,8 @@ private static PoolingHttpClientConnectionManager createConnManager()
PlainConnectionSocketFactory.INSTANCE ).register(
"https", sslConnectionSocketFactory ).build();
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager( registry );
PoolingHttpClientConnectionManager connManager =
new PoolingHttpClientConnectionManager( registry, null, null, null, CONN_TTL, TimeUnit.SECONDS );
if ( persistentPool )
{
connManager.setDefaultMaxPerRoute( MAX_CONN_PER_ROUTE );
......@@ -354,6 +370,93 @@ private static PoolingHttpClientConnectionManager createConnManager()
return connManager;
}
/**
* The type of the retry handler, defaults to {@code standard}.
* Values can be {@link default DefaultHttpRequestRetryHandler},
* or {@link standard StandardHttpRequestRetryHandler},
* or a fully qualified name class with a no-arg.
*
* @since 3.2
*/
private static final String RETRY_HANDLER_CLASS =
System.getProperty( "maven.wagon.http.retryHandler.class", "standard" );
/**
* Whether or not methods that have successfully sent their request will be retried,
* defaults to {@code false}.
* Note: only used for default and standard retry handlers.
*
* @since 3.2
*/
private static final boolean RETRY_HANDLER_REQUEST_SENT_ENABLED =
Boolean.getBoolean( "maven.wagon.http.retryHandler.requestSentEnabled" );
/**
* Number of retries for the retry handler, defaults to 3.
* Note: only used for default and standard retry handlers.
*
* @since 3.2
*/
private static final int RETRY_HANDLER_COUNT =
Integer.getInteger( "maven.wagon.http.retryHandler.count", 3 );
/**
* Comma-separated list of non-retryable exception classes.
* Note: only used for default retry handler.
*
* @since 3.2
*/
private static final String RETRY_HANDLER_EXCEPTIONS =
System.getProperty( "maven.wagon.http.retryHandler.nonRetryableClasses" );
private static HttpRequestRetryHandler createRetryHandler()
{
switch ( RETRY_HANDLER_CLASS )
{
case "default":
if ( StringUtils.isEmpty( RETRY_HANDLER_EXCEPTIONS ) )
{
return new DefaultHttpRequestRetryHandler(
RETRY_HANDLER_COUNT, RETRY_HANDLER_REQUEST_SENT_ENABLED );
}
return new DefaultHttpRequestRetryHandler(
RETRY_HANDLER_COUNT, RETRY_HANDLER_REQUEST_SENT_ENABLED, getNonRetryableExceptions() )
{
};
case "standard":
return new StandardHttpRequestRetryHandler( RETRY_HANDLER_COUNT, RETRY_HANDLER_REQUEST_SENT_ENABLED );
default:
try
{
final ClassLoader classLoader = AbstractHttpClientWagon.class.getClassLoader();
return HttpRequestRetryHandler.class.cast( classLoader.loadClass( RETRY_HANDLER_CLASS )
.getConstructor().newInstance() );
}
catch ( final Exception e )
{
throw new IllegalArgumentException( e );
}
}
}
private static Collection<Class<? extends IOException>> getNonRetryableExceptions()
{
final List<Class<? extends IOException>> exceptions = new ArrayList<>();
final ClassLoader loader = AbstractHttpClientWagon.class.getClassLoader();
for ( final String ex : RETRY_HANDLER_EXCEPTIONS.split( "," ) )
{
try
{
exceptions.add( ( Class<? extends IOException> ) loader.loadClass( ex ) );
}
catch ( final ClassNotFoundException e )
{
throw new IllegalArgumentException( e );
}
}
return exceptions;
}
private static CloseableHttpClient httpClient = createClient();
private static CloseableHttpClient createClient()
......@@ -362,6 +465,7 @@ private static CloseableHttpClient createClient()
.useSystemProperties() //
.disableConnectionState() //
.setConnectionManager( httpClientConnectionManager ) //
.setRetryHandler( createRetryHandler() )
.build();
}
......@@ -588,8 +692,16 @@ private void put( int wait, Resource resource, File source, HttpEntity httpEntit
try
{
int statusCode = response.getStatusLine().getStatusCode();
String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + ".";
fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
String reasonPhrase = response.getStatusLine().getReasonPhrase();
StringBuilder debugMessage = new StringBuilder();
debugMessage.append( url );
debugMessage.append( " -- " );
debugMessage.append( "status code: " ).append( statusCode );
if ( StringUtils.isNotEmpty( reasonPhrase ) )
{
debugMessage.append( ", reason phrase: " ).append( reasonPhrase );
}
fireTransferDebug( debugMessage.toString() );
// Check that we didn't run out of retries.
switch ( statusCode )
......@@ -609,10 +721,10 @@ private void put( int wait, Resource resource, File source, HttpEntity httpEntit
return;
case HttpStatus.SC_FORBIDDEN:
fireSessionConnectionRefused();
throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
throw new AuthorizationException( "Access denied to: " + url );
case HttpStatus.SC_NOT_FOUND:
throw new ResourceDoesNotExistException( "File: " + url + " does not exist" + reasonPhrase );
throw new ResourceDoesNotExistException( "File " + url + " does not exist" );
case SC_TOO_MANY_REQUESTS:
put( backoff( wait, url ), resource, source, httpEntity, url );
......@@ -620,7 +732,7 @@ private void put( int wait, Resource resource, File source, HttpEntity httpEntit
//add more entries here
default:
TransferFailedException e = new TransferFailedException(
"Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
"Failed to transfer file " + url + " with status code " + statusCode );
fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
throw e;
}
......@@ -688,7 +800,6 @@ private boolean resourceExists( int wait, String resourceName )
try
{
int statusCode = response.getStatusLine().getStatusCode();
String reasonPhrase = ", ReasonPhrase: " + response.getStatusLine().getReasonPhrase() + ".";
boolean result;
switch ( statusCode )
{
......@@ -699,13 +810,13 @@ private boolean resourceExists( int wait, String resourceName )
result = true;
break;
case HttpStatus.SC_FORBIDDEN:
throw new AuthorizationException( "Access denied to: " + url + reasonPhrase );
throw new AuthorizationException( "Access denied to: " + url );
case HttpStatus.SC_UNAUTHORIZED:
throw new AuthorizationException( "Not authorized " + reasonPhrase );
throw new AuthorizationException( "Not authorized" );
case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase );
throw new AuthorizationException( "Not authorized by proxy" );
case HttpStatus.SC_NOT_FOUND:
result = false;
......@@ -717,7 +828,7 @@ private boolean resourceExists( int wait, String resourceName )
//add more entries here
default:
throw new TransferFailedException(
"Failed to transfer file: " + url + ". Return code is: " + statusCode + reasonPhrase );
"Failed to transfer file " + url + " with status code " + statusCode );
}
EntityUtils.consume( response.getEntity() );
......@@ -835,8 +946,6 @@ public void setHeaders( HttpUriRequest method )
method.addHeader( "Cache-control", "no-cache" );
method.addHeader( "Cache-store", "no-store" );
method.addHeader( "Pragma", "no-cache" );
method.addHeader( "Expires", "0" );
method.addHeader( "Accept-Encoding", "gzip" );
}
if ( httpHeaders != null )
......@@ -871,7 +980,7 @@ protected String getUserAgent( HttpUriRequest method )
{
if ( httpHeaders != null )
{
String value = (String) httpHeaders.get( "User-Agent" );
String value = (String) httpHeaders.get( HTTP.USER_AGENT );
if ( value != null )
{
return value;
......@@ -882,7 +991,7 @@ protected String getUserAgent( HttpUriRequest method )
if ( config != null )
{
return (String) config.getHeaders().get( "User-Agent" );
return (String) config.getHeaders().get( HTTP.USER_AGENT );
}
return null;
}
......@@ -986,10 +1095,16 @@ private void fillInputData( int wait, InputData inputData )
CloseableHttpResponse response = execute( getMethod );
closeable = response;
int statusCode = response.getStatusLine().getStatusCode();
String reasonPhrase = ", ReasonPhrase:" + response.getStatusLine().getReasonPhrase() + ".";
fireTransferDebug( url + " - Status code: " + statusCode + reasonPhrase );
String reasonPhrase = response.getStatusLine().getReasonPhrase();
StringBuilder debugMessage = new StringBuilder();
debugMessage.append( url );
debugMessage.append( " -- " );
debugMessage.append( "status code: " ).append( statusCode );
if ( StringUtils.isNotEmpty( reasonPhrase ) )
{
debugMessage.append( ", reason phrase: " ).append( reasonPhrase );
}
fireTransferDebug( debugMessage.toString() );
switch ( statusCode )
{
......@@ -1001,18 +1116,18 @@ private void fillInputData( int wait, InputData inputData )
return;
case HttpStatus.SC_FORBIDDEN:
fireSessionConnectionRefused();
throw new AuthorizationException( "Access denied to: " + url + " " + reasonPhrase );
throw new AuthorizationException( "Access denied to: " + url );
case HttpStatus.SC_UNAUTHORIZED:
fireSessionConnectionRefused();
throw new AuthorizationException( "Not authorized " + reasonPhrase );
throw new AuthorizationException( "Not authorized" );
case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
fireSessionConnectionRefused();
throw new AuthorizationException( "Not authorized by proxy " + reasonPhrase );
throw new AuthorizationException( "Not authorized by proxy" );
case HttpStatus.SC_NOT_FOUND:
throw new ResourceDoesNotExistException( "File: " + url + " " + reasonPhrase );
throw new ResourceDoesNotExistException( "File " + url + " does not exist" );
case SC_TOO_MANY_REQUESTS:
fillInputData( backoff( wait, url ), inputData );
......@@ -1022,7 +1137,7 @@ private void fillInputData( int wait, InputData inputData )
default:
cleanupGetTransfer( resource );
TransferFailedException e = new TransferFailedException(
"Failed to transfer file: " + url + ". Return code is: " + statusCode + " " + reasonPhrase );
"Failed to transfer file " + url + " with status code " + statusCode );
fireTransferError( resource, e, TransferEvent.REQUEST_GET );
throw e;
}
......
......@@ -23,7 +23,7 @@ under the License.
<parent>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-providers</artifactId>
<version>3.1.0</version>
<version>3.2.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
......
......@@ -31,7 +31,7 @@ Maven Wagon HTTP
This component is an implementation of Wagon provider for HTTP access.
It uses {{{http://hc.apache.org/httpcomponents-client-ga/}Apache HttpComponents client}} as lower level layer.
It enables Maven to use remote repositories stored in HTTP servers.
......@@ -57,3 +57,22 @@ Features
* <<<maven.wagon.http.ssl.ignore.validity.dates>>> = true/false (default false), ignore issues with certificate dates.
* <<<maven.wagon.rto>>> = time in ms (default 1800000), read time out.
[]
Since version 3.2, the retry handler can be configured with system properties:
* <<<maven.wagon.http.retryHandler.class>>> supports this set of values:
* <<<default>>> will use an instance of {{{http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.html}<<<DefaultHttpRequestRetryHandler>>>}} respecting <<<requestSentEnabled>>>, <<<count>>> and <<<nonRetryableClasses>>>.
*<< <standard>>> will use an instance of {{{http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/StandardHttpRequestRetryHandler.html}<<<StandardHttpRequestRetryHandler>>>}} respecting <<<requestSentEnabled>>> and <<<count>>>.
* Any fully qualified name of a {{{https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/client/HttpRequestRetryHandler.html}<<<HttpRequestRetryHandler>>>}} implementation will be instantiated with its default constructor.
* <<<maven.wagon.http.retryHandler.requestSentEnabled>>> = <<<requestSentEnabled>>> for <<<default>>> or <<<standard>>> implementations.
* <<<maven.wagon.http.retryHandler.count>>> = number of retries for <<<default>>> or <<<standard>>> implementations.
* <<<maven.wagon.http.retryHandler.nonRetryableClasses>>> = a comma-separated list of fully qualified class names bypassing the retries (only the <<<default>>> implementation).
If not set, the default value from {{{http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/DefaultHttpRequestRetryHandler.html}<<<DefaultHttpRequestRetryHandler>>>}} will be used.
......@@ -19,6 +19,34 @@
* under the License.
*/
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.lang.reflect.Field;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import javax.net.ssl.SSLException;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.execchain.RedirectExec;
import org.apache.http.impl.execchain.RetryExec;
import org.apache.maven.wagon.InputData;
import org.apache.maven.wagon.repository.Repository;
import org.apache.maven.wagon.resource.Resource;
......@@ -51,4 +79,197 @@ public void test()
wagon.disconnect();
}
@Test
public void retryableConfigurationDefaultTest() throws Exception
{
doTestHttpClient( new Runnable()
{
@Override
public void run()
{
final HttpRequestRetryHandler handler = getCurrentHandler();
assertNotNull( handler );
assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
assertEquals( 3, impl.getRetryCount() );
assertFalse( impl.isRequestSentRetryEnabled() );
}
});
}
@Test
public void retryableConfigurationCountTest() throws Exception
{
doTestHttpClient( new Runnable()
{
@Override
public void run()
{
System.setProperty( "maven.wagon.http.retryHandler.class", "default" );
System.setProperty( "maven.wagon.http.retryHandler.count", "5" );
final HttpRequestRetryHandler handler = getCurrentHandler();
assertNotNull( handler );
assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
assertEquals( 5, impl.getRetryCount() );
assertFalse( impl.isRequestSentRetryEnabled() );
}
});
}
@Test
public void retryableConfigurationSentTest() throws Exception
{
doTestHttpClient( new Runnable()
{
@Override
public void run()
{
System.setProperty( "maven.wagon.http.retryHandler.class", "default" );
System.setProperty( "maven.wagon.http.retryHandler.requestSentEnabled", "true" );
final HttpRequestRetryHandler handler = getCurrentHandler();
assertNotNull( handler );
assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
assertEquals( 3, impl.getRetryCount() );
assertTrue( impl.isRequestSentRetryEnabled() );
}
});
}
@Test
public void retryableConfigurationExceptionsTest() throws Exception
{
doTestHttpClient( new Runnable()
{
@Override
public void run()
{
System.setProperty( "maven.wagon.http.retryHandler.class", "default" );
System.setProperty( "maven.wagon.http.retryHandler.nonRetryableClasses", IOException.class.getName() );
final HttpRequestRetryHandler handler = getCurrentHandler();
assertNotNull( handler );
assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
assertEquals( 3, impl.getRetryCount() );
assertFalse( impl.isRequestSentRetryEnabled() );
try
{
final Field nonRetriableClasses = handler.getClass().getSuperclass()
.getDeclaredField( "nonRetriableClasses" );
if ( !nonRetriableClasses.isAccessible() )
{
nonRetriableClasses.setAccessible(true);
}
final Set<?> exceptions = Set.class.cast( nonRetriableClasses.get(handler) );
assertEquals( 1, exceptions.size() );
assertTrue( exceptions.contains( IOException.class ) );
}
catch ( final Exception e )
{
fail( e.getMessage() );
}
}
});
}
private HttpRequestRetryHandler getCurrentHandler()
{
try
{
final Class<?> impl = Thread.currentThread().getContextClassLoader().loadClass(
"org.apache.maven.wagon.shared.http.AbstractHttpClientWagon" );
final CloseableHttpClient httpClient = CloseableHttpClient.class.cast(
impl.getMethod("getHttpClient").invoke(null) );
final Field redirectExec = httpClient.getClass().getDeclaredField( "execChain" );
if ( !redirectExec.isAccessible() )
{
redirectExec.setAccessible( true );
}
final RedirectExec redirectExecInstance = RedirectExec.class.cast(
redirectExec.get( httpClient ) );
final Field requestExecutor = redirectExecInstance.getClass().getDeclaredField( "requestExecutor" );
if ( !requestExecutor.isAccessible() )
{
requestExecutor.setAccessible( true );
}
final RetryExec requestExecutorInstance = RetryExec.class.cast(
requestExecutor.get( redirectExecInstance ) );
final Field retryHandler = requestExecutorInstance.getClass().getDeclaredField( "retryHandler" );
if ( !retryHandler.isAccessible() )
{
retryHandler.setAccessible( true );
}
return HttpRequestRetryHandler.class.cast( retryHandler.get( requestExecutorInstance ) );
}
catch ( final Exception e )
{
throw new IllegalStateException(e);
}
}
private void doTestHttpClient( final Runnable test ) throws Exception
{
final String classpath = System.getProperty( "java.class.path" );
final String[] paths = classpath.split( File.pathSeparator );
final Collection<URL> urls = new ArrayList<>( paths.length );
for ( final String path : paths )
{
try
{
urls.add( new File( path ).toURI().toURL() );
}
catch ( final MalformedURLException e )
{
fail( e.getMessage() );
}
}
final URLClassLoader loader = new URLClassLoader( urls.toArray( new URL[ paths.length ] ) , new ClassLoader()
{
@Override
protected Class<?> loadClass( final String name, final boolean resolve ) throws ClassNotFoundException
{
if ( name.startsWith( "org.apache.maven.wagon.shared.http" ) )
{
throw new ClassNotFoundException( name );
}
return super.loadClass( name, resolve );
}
});
final Thread thread = Thread.currentThread();
final ClassLoader contextClassLoader = thread.getContextClassLoader();
thread.setContextClassLoader( loader );
final String originalClass = System.getProperty( "maven.wagon.http.retryHandler.class", "default" );
final String originalSentEnabled = System.getProperty(
"maven.wagon.http.retryHandler.requestSentEnabled", "false" );
final String originalCount = System.getProperty( "maven.wagon.http.retryHandler.count", "3" );
final String originalExceptions = System.getProperty( "maven.wagon.http.retryHandler.nonRetryableClasses",
InterruptedIOException.class.getName() + ","
+ UnknownHostException.class.getName() + ","
+ ConnectException.class.getName() + ","
+ SSLException.class.getName());
try
{
test.run();
}
finally
{
loader.close();
thread.setContextClassLoader( contextClassLoader );
System.setProperty( "maven.wagon.http.retryHandler.class", originalClass );
System.setProperty( "maven.wagon.http.retryHandler.requestSentEnabled", originalSentEnabled );
System.setProperty( "maven.wagon.http.retryHandler.count", originalCount );
System.setProperty( "maven.wagon.http.retryHandler.nonRetryableClasses", originalExceptions );
}
}
}