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.Test;
55  
56  public class AbstractHttpClientWagonTest
57  {
58      @Test
59      public void test()
60          throws Exception
61      {
62          AbstractHttpClientWagon wagon = new AbstractHttpClientWagon()
63          {
64          };
65  
66          Repository repository = new Repository( "central", "http://repo.maven.apache.org/maven2" );
67  
68          wagon.connect( repository );
69  
70          Resource resource = new Resource();
71  
72          resource.setName( "junit/junit/maven-metadata.xml" );
73  
74          InputData inputData = new InputData();
75  
76          inputData.setResource( resource );
77  
78          wagon.fillInputData( inputData );
79  
80          wagon.disconnect();
81      }
82  
83      @Test
84      public void retryableConfigurationDefaultTest() throws Exception
85      {
86          doTestHttpClient( new Runnable()
87          {
88              @Override
89              public void run()
90              {
91                  final HttpRequestRetryHandler handler = getCurrentHandler();
92                  assertNotNull( handler );
93                  assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
94                  final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
95                  assertEquals( 3, impl.getRetryCount() );
96                  assertFalse( impl.isRequestSentRetryEnabled() );
97              }
98          });
99      }
100 
101     @Test
102     public void retryableConfigurationCountTest() throws Exception
103     {
104         doTestHttpClient( new Runnable()
105         {
106             @Override
107             public void run()
108             {
109                 System.setProperty( "maven.wagon.http.retryHandler.class", "default" );
110                 System.setProperty( "maven.wagon.http.retryHandler.count", "5" );
111 
112                 final HttpRequestRetryHandler handler = getCurrentHandler();
113                 assertNotNull( handler );
114                 assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
115                 final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
116                 assertEquals( 5, impl.getRetryCount() );
117                 assertFalse( impl.isRequestSentRetryEnabled() );
118             }
119         });
120     }
121 
122     @Test
123     public void retryableConfigurationSentTest() throws Exception
124     {
125         doTestHttpClient( new Runnable()
126         {
127             @Override
128             public void run()
129             {
130                 System.setProperty( "maven.wagon.http.retryHandler.class", "default" );
131                 System.setProperty( "maven.wagon.http.retryHandler.requestSentEnabled", "true" );
132 
133                 final HttpRequestRetryHandler handler = getCurrentHandler();
134                 assertNotNull( handler );
135                 assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
136                 final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
137                 assertEquals( 3, impl.getRetryCount() );
138                 assertTrue( impl.isRequestSentRetryEnabled() );
139             }
140         });
141     }
142 
143     @Test
144     public void retryableConfigurationExceptionsTest() throws Exception
145     {
146         doTestHttpClient( new Runnable()
147         {
148             @Override
149             public void run()
150             {
151                 System.setProperty( "maven.wagon.http.retryHandler.class", "default" );
152                 System.setProperty( "maven.wagon.http.retryHandler.nonRetryableClasses", IOException.class.getName() );
153 
154                 final HttpRequestRetryHandler handler = getCurrentHandler();
155                 assertNotNull( handler );
156                 assertThat( handler, instanceOf( DefaultHttpRequestRetryHandler.class ) );
157                 final DefaultHttpRequestRetryHandler impl = DefaultHttpRequestRetryHandler.class.cast(handler);
158                 assertEquals( 3, impl.getRetryCount() );
159                 assertFalse( impl.isRequestSentRetryEnabled() );
160 
161                 try
162                 {
163                     final Field nonRetriableClasses = handler.getClass().getSuperclass()
164                             .getDeclaredField( "nonRetriableClasses" );
165                     if ( !nonRetriableClasses.isAccessible() )
166                     {
167                         nonRetriableClasses.setAccessible(true);
168                     }
169                     final Set<?> exceptions = Set.class.cast( nonRetriableClasses.get(handler) );
170                     assertEquals( 1, exceptions.size() );
171                     assertTrue( exceptions.contains( IOException.class ) );
172                 }
173                 catch ( final Exception e )
174                 {
175                     fail( e.getMessage() );
176                 }
177             }
178         });
179     }
180 
181     private HttpRequestRetryHandler getCurrentHandler()
182     {
183         try
184         {
185             final Class<?> impl = Thread.currentThread().getContextClassLoader().loadClass(
186                         "org.apache.maven.wagon.shared.http.AbstractHttpClientWagon" );
187 
188             final CloseableHttpClient httpClient = CloseableHttpClient.class.cast(
189                     impl.getMethod("getHttpClient").invoke(null) );
190 
191             final Field redirectExec = httpClient.getClass().getDeclaredField( "execChain" );
192             if ( !redirectExec.isAccessible() )
193             {
194                 redirectExec.setAccessible( true );
195             }
196             final RedirectExec redirectExecInstance = RedirectExec.class.cast(
197                     redirectExec.get( httpClient ) );
198 
199             final Field requestExecutor = redirectExecInstance.getClass().getDeclaredField( "requestExecutor" );
200             if ( !requestExecutor.isAccessible() )
201             {
202                 requestExecutor.setAccessible( true );
203             }
204             final RetryExec requestExecutorInstance = RetryExec.class.cast(
205                     requestExecutor.get( redirectExecInstance ) );
206 
207             final Field retryHandler = requestExecutorInstance.getClass().getDeclaredField( "retryHandler" );
208             if ( !retryHandler.isAccessible() )
209             {
210                 retryHandler.setAccessible( true );
211             }
212             return HttpRequestRetryHandler.class.cast( retryHandler.get( requestExecutorInstance ) );
213         }
214         catch ( final Exception e )
215         {
216             throw new IllegalStateException(e);
217         }
218     }
219 
220     private void doTestHttpClient( final Runnable test ) throws Exception
221     {
222         final String classpath = System.getProperty( "java.class.path" );
223         final String[] paths = classpath.split( File.pathSeparator );
224         final Collection<URL> urls = new ArrayList<>( paths.length );
225         for ( final String path : paths )
226         {
227             try
228             {
229                 urls.add( new File( path ).toURI().toURL() );
230             }
231             catch ( final MalformedURLException e )
232             {
233                 fail( e.getMessage() );
234             }
235         }
236         final URLClassLoader loader = new URLClassLoader( urls.toArray( new URL[ paths.length ] ) , new ClassLoader()
237         {
238             @Override
239             protected Class<?> loadClass( final String name, final boolean resolve ) throws ClassNotFoundException
240             {
241                 if ( name.startsWith( "org.apache.maven.wagon.shared.http" ) )
242                 {
243                     throw new ClassNotFoundException( name );
244                 }
245                 return super.loadClass( name, resolve );
246             }
247         });
248         final Thread thread = Thread.currentThread();
249         final ClassLoader contextClassLoader = thread.getContextClassLoader();
250         thread.setContextClassLoader( loader );
251 
252         final String originalClass = System.getProperty( "maven.wagon.http.retryHandler.class", "default" );
253         final String originalSentEnabled = System.getProperty(
254                 "maven.wagon.http.retryHandler.requestSentEnabled", "false" );
255         final String originalCount = System.getProperty( "maven.wagon.http.retryHandler.count", "3" );
256         final String originalExceptions = System.getProperty( "maven.wagon.http.retryHandler.nonRetryableClasses",
257                 InterruptedIOException.class.getName() + ","
258                     + UnknownHostException.class.getName() + ","
259                     + ConnectException.class.getName() + ","
260                     + SSLException.class.getName());
261         try
262         {
263             test.run();
264         }
265         finally
266         {
267             loader.close();
268             thread.setContextClassLoader( contextClassLoader );
269             System.setProperty(  "maven.wagon.http.retryHandler.class", originalClass );
270             System.setProperty(  "maven.wagon.http.retryHandler.requestSentEnabled", originalSentEnabled );
271             System.setProperty(  "maven.wagon.http.retryHandler.count", originalCount );
272             System.setProperty(  "maven.wagon.http.retryHandler.nonRetryableClasses", originalExceptions );
273         }
274     }
275 }