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 java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.net.Authenticator;
27  import java.net.HttpURLConnection;
28  import java.net.MalformedURLException;
29  import java.net.PasswordAuthentication;
30  import java.net.URL;
31  import java.net.URLConnection;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.Properties;
35  import java.util.zip.GZIPInputStream;
36  
37  import org.apache.maven.wagon.ConnectionException;
38  import org.apache.maven.wagon.InputData;
39  import org.apache.maven.wagon.OutputData;
40  import org.apache.maven.wagon.ResourceDoesNotExistException;
41  import org.apache.maven.wagon.StreamWagon;
42  import org.apache.maven.wagon.TransferFailedException;
43  import org.apache.maven.wagon.authentication.AuthenticationException;
44  import org.apache.maven.wagon.authorization.AuthorizationException;
45  import org.apache.maven.wagon.events.TransferEvent;
46  import org.apache.maven.wagon.proxy.ProxyInfo;
47  import org.apache.maven.wagon.resource.Resource;
48  import org.apache.maven.wagon.shared.http.HtmlFileListParser;
49  
50  /**
51   * LightweightHttpWagon
52   * 
53   * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
54   * @version $Id: LightweightHttpWagon.java 834247 2009-11-09 21:46:14Z bentmann $
55   * @plexus.component role="org.apache.maven.wagon.Wagon" role-hint="http" instantiation-strategy="per-lookup"
56   */
57  public class LightweightHttpWagon
58      extends StreamWagon
59  {
60      private String previousProxyExclusions;
61  
62      private String previousHttpProxyHost;
63  
64      private String previousHttpProxyPort;
65  
66      private HttpURLConnection putConnection;
67  
68      /**
69       * Whether to use any proxy cache or not.
70       * 
71       * @plexus.configuration default="false"
72       */
73      private boolean useCache;
74  
75      /** @plexus.configuration */
76      private Properties httpHeaders;
77  
78      /**
79       * Builds a complete URL string from the repository URL and the relative path passed.
80       * 
81       * @param path the relative path
82       * @return the complete URL
83       */
84      private String buildUrl( String path )
85      {
86          final String repoUrl = getRepository().getUrl();
87  
88          path = path.replace( ' ', '+' );
89  
90          if ( repoUrl.charAt( repoUrl.length() - 1 ) != '/' )
91          {
92              return repoUrl + '/' + path;
93          }
94  
95          return repoUrl + path;
96      }
97  
98      public void fillInputData( InputData inputData )
99          throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
100     {
101         Resource resource = inputData.getResource();
102         try
103         {
104             URL url = new URL( buildUrl( resource.getName() ) );
105             HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
106             urlConnection.setRequestProperty( "Accept-Encoding", "gzip" );
107             if ( !useCache )
108             {
109                 urlConnection.setRequestProperty( "Pragma", "no-cache" );
110             }
111 
112             addHeaders( urlConnection );
113 
114             // TODO: handle all response codes
115             int responseCode = urlConnection.getResponseCode();
116             if ( responseCode == HttpURLConnection.HTTP_FORBIDDEN
117                 || responseCode == HttpURLConnection.HTTP_UNAUTHORIZED )
118             {
119                 throw new AuthorizationException( "Access denied to: " + buildUrl( resource.getName() ) );
120             }
121 
122             InputStream is = urlConnection.getInputStream();
123             String contentEncoding = urlConnection.getHeaderField( "Content-Encoding" );
124             boolean isGZipped = contentEncoding == null ? false : "gzip".equalsIgnoreCase( contentEncoding );
125             if ( isGZipped )
126             {
127                 is = new GZIPInputStream( is );
128             }
129             inputData.setInputStream( is );
130             resource.setLastModified( urlConnection.getLastModified() );
131             resource.setContentLength( urlConnection.getContentLength() );
132         }
133         catch ( MalformedURLException e )
134         {
135             throw new ResourceDoesNotExistException( "Invalid repository URL: " + e.getMessage(), e );
136         }
137         catch ( FileNotFoundException e )
138         {
139             throw new ResourceDoesNotExistException( "Unable to locate resource in repository", e );
140         }
141         catch ( IOException e )
142         {
143             throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
144         }
145     }
146 
147     private void addHeaders( URLConnection urlConnection )
148     {
149         if ( httpHeaders != null )
150         {
151             for ( Iterator i = httpHeaders.keySet().iterator(); i.hasNext(); )
152             {
153                 String header = (String) i.next();
154                 urlConnection.setRequestProperty( header, httpHeaders.getProperty( header ) );
155             }
156         }
157     }
158 
159     public void fillOutputData( OutputData outputData )
160         throws TransferFailedException
161     {
162         Resource resource = outputData.getResource();
163         try
164         {
165             URL url = new URL( buildUrl( resource.getName() ) );
166             putConnection = (HttpURLConnection) url.openConnection();
167 
168             addHeaders( putConnection );
169 
170             putConnection.setRequestMethod( "PUT" );
171             putConnection.setDoOutput( true );
172             outputData.setOutputStream( putConnection.getOutputStream() );
173         }
174         catch ( IOException e )
175         {
176             throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
177         }
178     }
179 
180     protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
181         throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
182     {
183         try
184         {
185             int statusCode = putConnection.getResponseCode();
186 
187             switch ( statusCode )
188             {
189                 // Success Codes
190                 case HttpURLConnection.HTTP_OK: // 200
191                 case HttpURLConnection.HTTP_CREATED: // 201
192                 case HttpURLConnection.HTTP_ACCEPTED: // 202
193                 case HttpURLConnection.HTTP_NO_CONTENT: // 204
194                     break;
195 
196                 case HttpURLConnection.HTTP_FORBIDDEN:
197                     throw new AuthorizationException( "Access denied to: " + buildUrl( resource.getName() ) );
198 
199                 case HttpURLConnection.HTTP_NOT_FOUND:
200                     throw new ResourceDoesNotExistException( "File: " + buildUrl( resource.getName() )
201                         + " does not exist" );
202 
203                     // add more entries here
204                 default:
205                     throw new TransferFailedException( "Failed to transfer file: " + buildUrl( resource.getName() )
206                         + ". Return code is: " + statusCode );
207             }
208         }
209         catch ( IOException e )
210         {
211             fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
212 
213             throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
214         }
215     }
216 
217     protected void openConnectionInternal()
218         throws ConnectionException, AuthenticationException
219     {
220         previousHttpProxyHost = System.getProperty( "http.proxyHost" );
221         previousHttpProxyPort = System.getProperty( "http.proxyPort" );
222         previousProxyExclusions = System.getProperty( "http.nonProxyHosts" );
223 
224         final ProxyInfo proxyInfo = getProxyInfo( "http", getRepository().getHost() );
225         if ( proxyInfo != null )
226         {
227             setSystemProperty( "http.proxyHost", proxyInfo.getHost() );
228             setSystemProperty( "http.proxyPort", String.valueOf( proxyInfo.getPort() ) );
229             setSystemProperty( "http.nonProxyHosts", proxyInfo.getNonProxyHosts() );
230         }
231         else
232         {
233             setSystemProperty( "http.proxyHost", null );
234             setSystemProperty( "http.proxyPort", null );
235         }
236 
237         final boolean hasProxy = ( proxyInfo != null && proxyInfo.getUserName() != null );
238         final boolean hasAuthentication = ( authenticationInfo != null && authenticationInfo.getUserName() != null );
239         if ( hasProxy || hasAuthentication )
240         {
241             Authenticator.setDefault( new Authenticator()
242             {
243                 protected PasswordAuthentication getPasswordAuthentication()
244                 {
245                     // TODO: ideally use getRequestorType() from JDK1.5 here...
246                     if ( hasProxy && getRequestingHost().equals( proxyInfo.getHost() )
247                         && getRequestingPort() == proxyInfo.getPort() )
248                     {
249                         String password = "";
250                         if ( proxyInfo.getPassword() != null )
251                         {
252                             password = proxyInfo.getPassword();
253                         }
254                         return new PasswordAuthentication( proxyInfo.getUserName(), password.toCharArray() );
255                     }
256 
257                     if ( hasAuthentication )
258                     {
259                         String password = "";
260                         if ( authenticationInfo.getPassword() != null )
261                         {
262                             password = authenticationInfo.getPassword();
263                         }
264                         return new PasswordAuthentication( authenticationInfo.getUserName(), password.toCharArray() );
265                     }
266 
267                     return super.getPasswordAuthentication();
268                 }
269             } );
270         }
271         else
272         {
273             Authenticator.setDefault( null );
274         }
275     }
276 
277     public void closeConnection()
278         throws ConnectionException
279     {
280         if ( putConnection != null )
281         {
282             putConnection.disconnect();
283         }
284 
285         setSystemProperty( "http.proxyHost", previousHttpProxyHost );
286         setSystemProperty( "http.proxyPort", previousHttpProxyPort );
287         setSystemProperty( "http.nonProxyHosts", previousProxyExclusions );
288     }
289 
290     public List getFileList( String destinationDirectory )
291         throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
292     {
293         InputData inputData = new InputData();
294 
295         if ( destinationDirectory.length() > 0 && !destinationDirectory.endsWith( "/" ) )
296         {
297             destinationDirectory += "/";
298         }
299 
300         String url = buildUrl( destinationDirectory );
301 
302         Resource resource = new Resource( destinationDirectory );
303 
304         inputData.setResource( resource );
305 
306         fillInputData( inputData );
307 
308         InputStream is = inputData.getInputStream();
309 
310         if ( is == null )
311         {
312             throw new TransferFailedException( url + " - Could not open input stream for resource: '" + resource
313                                                + "'" );
314         }
315 
316         return HtmlFileListParser.parseFileList( url, is );
317     }
318 
319     public boolean resourceExists( String resourceName )
320         throws TransferFailedException, AuthorizationException
321     {
322         HttpURLConnection headConnection;
323 
324         try
325         {
326             URL url = new URL( buildUrl( new Resource( resourceName ).getName() ) );
327             headConnection = (HttpURLConnection) url.openConnection();
328 
329             addHeaders( headConnection );
330 
331             headConnection.setRequestMethod( "HEAD" );
332             headConnection.setDoOutput( true );
333 
334             int statusCode = headConnection.getResponseCode();
335 
336             switch ( statusCode )
337             {
338                 case HttpURLConnection.HTTP_OK:
339                     return true;
340 
341                 case HttpURLConnection.HTTP_FORBIDDEN:
342                     throw new AuthorizationException( "Access denied to: " + url );
343 
344                 case HttpURLConnection.HTTP_NOT_FOUND:
345                     return false;
346 
347                 case HttpURLConnection.HTTP_UNAUTHORIZED:
348                     throw new AuthorizationException( "Access denied to: " + url );
349 
350                 default:
351                     throw new TransferFailedException( "Failed to look for file: " + buildUrl( resourceName )
352                         + ". Return code is: " + statusCode );
353             }
354         }
355         catch ( IOException e )
356         {
357             throw new TransferFailedException( "Error transferring file: " + e.getMessage(), e );
358         }
359     }
360 
361     public boolean isUseCache()
362     {
363         return useCache;
364     }
365 
366     public void setUseCache( boolean useCache )
367     {
368         this.useCache = useCache;
369     }
370 
371     public Properties getHttpHeaders()
372     {
373         return httpHeaders;
374     }
375 
376     public void setHttpHeaders( Properties httpHeaders )
377     {
378         this.httpHeaders = httpHeaders;
379     }
380 
381     void setSystemProperty( String key, String value )
382     {
383         if ( value != null )
384         {
385             System.setProperty( key, value );
386         }
387         else
388         {
389             System.getProperties().remove( key );
390         }
391     }
392 
393 }