001package org.apache.maven.wagon.providers.http; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import static org.hamcrest.CoreMatchers.instanceOf; 023import static org.junit.Assert.assertEquals; 024import static org.junit.Assert.assertFalse; 025import static org.junit.Assert.assertNotNull; 026import static org.junit.Assert.assertThat; 027import static org.junit.Assert.assertTrue; 028import static org.junit.Assert.fail; 029 030import java.io.File; 031import java.io.IOException; 032import java.io.InterruptedIOException; 033import java.lang.reflect.Field; 034import java.net.ConnectException; 035import java.net.MalformedURLException; 036import java.net.URL; 037import java.net.URLClassLoader; 038import java.net.UnknownHostException; 039import java.util.ArrayList; 040import java.util.Collection; 041import java.util.Set; 042 043import javax.net.ssl.SSLException; 044 045import org.apache.http.client.HttpRequestRetryHandler; 046import org.apache.http.impl.client.CloseableHttpClient; 047import org.apache.http.impl.client.DefaultHttpRequestRetryHandler; 048import org.apache.http.impl.execchain.RedirectExec; 049import org.apache.http.impl.execchain.RetryExec; 050import org.apache.maven.wagon.InputData; 051import org.apache.maven.wagon.repository.Repository; 052import org.apache.maven.wagon.resource.Resource; 053import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon; 054import org.junit.Ignore; 055import org.junit.Test; 056 057public class AbstractHttpClientWagonTest 058{ 059 @Ignore("This test is validating nothing and require internet connection which we should avoid so ignore it") 060 public void test() 061 throws Exception 062 { 063 AbstractHttpClientWagon wagon = new AbstractHttpClientWagon() 064 { 065 }; 066 067 Repository repository = new Repository( "central", "http://repo.maven.apache.org/maven2" ); 068 069 wagon.connect( repository ); 070 071 Resource resource = new Resource(); 072 073 resource.setName( "junit/junit/maven-metadata.xml" ); 074 075 InputData inputData = new InputData(); 076 077 inputData.setResource( resource ); 078 079 wagon.fillInputData( inputData ); 080 081 wagon.disconnect(); 082 } 083 084 @Test 085 public void retryableConfigurationDefaultTest() throws Exception 086 { 087 doTestHttpClient( new Runnable() 088 { 089 @Override 090 public void run() 091 { 092 final HttpRequestRetryHandler handler = getCurrentHandler(); 093 assertNotNull( handler ); 094 assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) ); 095 final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler); 096 assertEquals( 3, impl.getRetryCount() ); 097 assertFalse( impl.isRequestSentRetryEnabled() ); 098 } 099 }); 100 } 101 102 @Test 103 public void retryableConfigurationCountTest() throws Exception 104 { 105 doTestHttpClient( new Runnable() 106 { 107 @Override 108 public void run() 109 { 110 System.setProperty( "maven.wagon.http.retryHandler.class", "default" ); 111 System.setProperty( "maven.wagon.http.retryHandler.count", "5" ); 112 113 final HttpRequestRetryHandler handler = getCurrentHandler(); 114 assertNotNull( handler ); 115 assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) ); 116 final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler); 117 assertEquals( 5, impl.getRetryCount() ); 118 assertFalse( impl.isRequestSentRetryEnabled() ); 119 } 120 }); 121 } 122 123 @Test 124 public void retryableConfigurationSentTest() throws Exception 125 { 126 doTestHttpClient( new Runnable() 127 { 128 @Override 129 public void run() 130 { 131 System.setProperty( "maven.wagon.http.retryHandler.class", "default" ); 132 System.setProperty( "maven.wagon.http.retryHandler.requestSentEnabled", "true" ); 133 134 final HttpRequestRetryHandler handler = getCurrentHandler(); 135 assertNotNull( handler ); 136 assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) ); 137 final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler); 138 assertEquals( 3, impl.getRetryCount() ); 139 assertTrue( impl.isRequestSentRetryEnabled() ); 140 } 141 }); 142 } 143 144 @Test 145 public void retryableConfigurationExceptionsTest() throws Exception 146 { 147 doTestHttpClient( new Runnable() 148 { 149 @Override 150 public void run() 151 { 152 System.setProperty( "maven.wagon.http.retryHandler.class", "default" ); 153 System.setProperty( "maven.wagon.http.retryHandler.nonRetryableClasses", IOException.class.getName() ); 154 155 final HttpRequestRetryHandler handler = getCurrentHandler(); 156 assertNotNull( handler ); 157 assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) ); 158 final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler); 159 assertEquals( 3, impl.getRetryCount() ); 160 assertFalse( impl.isRequestSentRetryEnabled() ); 161 162 try 163 { 164 final Field nonRetriableClasses = handler.getClass().getSuperclass() 165 .getDeclaredField( "nonRetriableClasses" ); 166 if ( !nonRetriableClasses.isAccessible() ) 167 { 168 nonRetriableClasses.setAccessible(true); 169 } 170 final Set<?> exceptions = Set.class.cast( nonRetriableClasses.get(handler) ); 171 assertEquals( 1, exceptions.size() ); 172 assertTrue( exceptions.contains( IOException.class ) ); 173 } 174 catch ( final Exception e ) 175 { 176 fail( e.getMessage() ); 177 } 178 } 179 }); 180 } 181 182 private HttpRequestRetryHandler getCurrentHandler() 183 { 184 try 185 { 186 final Class<?> impl = Thread.currentThread().getContextClassLoader().loadClass( 187 "org.apache.maven.wagon.shared.http.AbstractHttpClientWagon" ); 188 189 final CloseableHttpClient httpClient = CloseableHttpClient.class.cast( 190 impl.getMethod("getHttpClient").invoke(null) ); 191 192 final Field redirectExec = httpClient.getClass().getDeclaredField( "execChain" ); 193 if ( !redirectExec.isAccessible() ) 194 { 195 redirectExec.setAccessible( true ); 196 } 197 final RedirectExec redirectExecInstance = RedirectExec.class.cast( 198 redirectExec.get( httpClient ) ); 199 200 final Field requestExecutor = redirectExecInstance.getClass().getDeclaredField( "requestExecutor" ); 201 if ( !requestExecutor.isAccessible() ) 202 { 203 requestExecutor.setAccessible( true ); 204 } 205 final RetryExec requestExecutorInstance = RetryExec.class.cast( 206 requestExecutor.get( redirectExecInstance ) ); 207 208 final Field retryHandler = requestExecutorInstance.getClass().getDeclaredField( "retryHandler" ); 209 if ( !retryHandler.isAccessible() ) 210 { 211 retryHandler.setAccessible( true ); 212 } 213 return HttpRequestRetryHandler.class.cast( retryHandler.get( requestExecutorInstance ) ); 214 } 215 catch ( final Exception e ) 216 { 217 throw new IllegalStateException(e); 218 } 219 } 220 221 private void doTestHttpClient( final Runnable test ) throws Exception 222 { 223 final String classpath = System.getProperty( "java.class.path" ); 224 final String[] paths = classpath.split( File.pathSeparator ); 225 final Collection<URL> urls = new ArrayList<>( paths.length ); 226 for ( final String path : paths ) 227 { 228 try 229 { 230 urls.add( new File( path ).toURI().toURL() ); 231 } 232 catch ( final MalformedURLException e ) 233 { 234 fail( e.getMessage() ); 235 } 236 } 237 final URLClassLoader loader = new URLClassLoader( urls.toArray( new URL[ paths.length ] ) , new ClassLoader() 238 { 239 @Override 240 protected Class<?> loadClass( final String name, final boolean resolve ) throws ClassNotFoundException 241 { 242 if ( name.startsWith( "org.apache.maven.wagon.shared.http" ) ) 243 { 244 throw new ClassNotFoundException( name ); 245 } 246 return super.loadClass( name, resolve ); 247 } 248 }); 249 final Thread thread = Thread.currentThread(); 250 final ClassLoader contextClassLoader = thread.getContextClassLoader(); 251 thread.setContextClassLoader( loader ); 252 253 final String originalClass = System.getProperty( "maven.wagon.http.retryHandler.class", "default" ); 254 final String originalSentEnabled = System.getProperty( 255 "maven.wagon.http.retryHandler.requestSentEnabled", "false" ); 256 final String originalCount = System.getProperty( "maven.wagon.http.retryHandler.count", "3" ); 257 final String originalExceptions = System.getProperty( "maven.wagon.http.retryHandler.nonRetryableClasses", 258 InterruptedIOException.class.getName() + "," 259 + UnknownHostException.class.getName() + "," 260 + ConnectException.class.getName() + "," 261 + SSLException.class.getName()); 262 try 263 { 264 test.run(); 265 } 266 finally 267 { 268 loader.close(); 269 thread.setContextClassLoader( contextClassLoader ); 270 System.setProperty( "maven.wagon.http.retryHandler.class", originalClass ); 271 System.setProperty( "maven.wagon.http.retryHandler.requestSentEnabled", originalSentEnabled ); 272 System.setProperty( "maven.wagon.http.retryHandler.count", originalCount ); 273 System.setProperty( "maven.wagon.http.retryHandler.nonRetryableClasses", originalExceptions ); 274 } 275 } 276}