View Javadoc
1   package org.apache.maven.wagon.providers.http;
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 static org.hamcrest.CoreMatchers.instanceOf;
23  import static org.junit.Assert.assertEquals;
24  import static org.junit.Assert.assertFalse;
25  import static org.junit.Assert.assertNotNull;
26  import static org.junit.Assert.assertThat;
27  import static org.junit.Assert.assertTrue;
28  import static org.junit.Assert.fail;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.InterruptedIOException;
33  import java.lang.reflect.Field;
34  import java.net.ConnectException;
35  import java.net.MalformedURLException;
36  import java.net.URL;
37  import java.net.URLClassLoader;
38  import java.net.UnknownHostException;
39  import java.util.ArrayList;
40  import java.util.Collection;
41  import java.util.Set;
42  
43  import javax.net.ssl.SSLException;
44  
45  import org.apache.http.client.HttpRequestRetryHandler;
46  import org.apache.http.impl.client.CloseableHttpClient;
47  import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
48  import org.apache.http.impl.execchain.RedirectExec;
49  import org.apache.http.impl.execchain.RetryExec;
50  import org.apache.maven.wagon.InputData;
51  import org.apache.maven.wagon.repository.Repository;
52  import org.apache.maven.wagon.resource.Resource;
53  import org.apache.maven.wagon.shared.http.AbstractHttpClientWagon;
54  import org.junit.Ignore;
55  import org.junit.Test;
56  
57  public class AbstractHttpClientWagonTest
58  {
59      @Ignore("This test is validating nothing and require internet connection which we should avoid so ignore it")
60      public void test()
61          throws Exception
62      {
63          AbstractHttpClientWagon wagon = new AbstractHttpClientWagon()
64          {
65          };
66  
67          Repository repository = new Repository( "central", "http://repo.maven.apache.org/maven2" );
68  
69          wagon.connect( repository );
70  
71          Resource resource = new Resource();
72  
73          resource.setName( "junit/junit/maven-metadata.xml" );
74  
75          InputData inputData = new InputData();
76  
77          inputData.setResource( resource );
78  
79          wagon.fillInputData( inputData );
80  
81          wagon.disconnect();
82      }
83  
84      @Test
85      public void retryableConfigurationDefaultTest() throws Exception
86      {
87          doTestHttpClient( new Runnable()
88          {
89              @Override
90              public void run()
91              {
92                  final HttpRequestRetryHandler handler = getCurrentHandler();
93                  assertNotNull( handler );
94                  assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
95                  final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
96                  assertEquals( 3, impl.getRetryCount() );
97                  assertFalse( impl.isRequestSentRetryEnabled() );
98              }
99          });
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 }