001package org.apache.maven.wagon.providers.webdav;
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 org.apache.commons.httpclient.Credentials;
023import org.apache.commons.httpclient.Header;
024import org.apache.commons.httpclient.HostConfiguration;
025import org.apache.commons.httpclient.HttpClient;
026import org.apache.commons.httpclient.HttpConnectionManager;
027import org.apache.commons.httpclient.HttpMethod;
028import org.apache.commons.httpclient.HttpStatus;
029import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
030import org.apache.commons.httpclient.NTCredentials;
031import org.apache.commons.httpclient.UsernamePasswordCredentials;
032import org.apache.commons.httpclient.auth.AuthScope;
033import org.apache.commons.httpclient.cookie.CookiePolicy;
034import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
035import org.apache.commons.httpclient.methods.GetMethod;
036import org.apache.commons.httpclient.methods.HeadMethod;
037import org.apache.commons.httpclient.methods.PutMethod;
038import org.apache.commons.httpclient.methods.RequestEntity;
039import org.apache.commons.httpclient.params.HttpMethodParams;
040import org.apache.commons.httpclient.util.DateParseException;
041import org.apache.commons.httpclient.util.DateUtil;
042import org.apache.commons.io.IOUtils;
043import org.apache.maven.wagon.InputData;
044import org.apache.maven.wagon.OutputData;
045import org.apache.maven.wagon.PathUtils;
046import org.apache.maven.wagon.ResourceDoesNotExistException;
047import org.apache.maven.wagon.StreamWagon;
048import org.apache.maven.wagon.TransferFailedException;
049import org.apache.maven.wagon.Wagon;
050import org.apache.maven.wagon.authorization.AuthorizationException;
051import org.apache.maven.wagon.events.TransferEvent;
052import org.apache.maven.wagon.proxy.ProxyInfo;
053import org.apache.maven.wagon.repository.Repository;
054import org.apache.maven.wagon.resource.Resource;
055import org.apache.maven.wagon.shared.http.EncodingUtil;
056import org.codehaus.plexus.util.IOUtil;
057
058import java.io.ByteArrayInputStream;
059import java.io.File;
060import java.io.FileInputStream;
061import java.io.IOException;
062import java.io.InputStream;
063import java.io.OutputStream;
064import java.nio.ByteBuffer;
065import java.text.SimpleDateFormat;
066import java.util.Date;
067import java.util.Locale;
068import java.util.Properties;
069import java.util.TimeZone;
070import java.util.zip.GZIPInputStream;
071
072/**
073 * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
074 * @author <a href="mailto:james@atlassian.com">James William Dumay</a>
075 */
076public abstract class AbstractHttpClientWagon
077    extends StreamWagon
078{
079    private final class RequestEntityImplementation
080        implements RequestEntity
081    {
082        private final Resource resource;
083
084        private final Wagon wagon;
085
086        private File source;
087
088        private ByteBuffer byteBuffer;
089
090        private RequestEntityImplementation( final InputStream stream, final Resource resource, final Wagon wagon,
091                                             final File source )
092            throws TransferFailedException
093        {
094            if ( source != null )
095            {
096                this.source = source;
097            }
098            else
099            {
100                try
101                {
102                    byte[] bytes = IOUtils.toByteArray( stream );
103                    this.byteBuffer = ByteBuffer.allocate( bytes.length );
104                    this.byteBuffer.put( bytes );
105                    stream.close();
106                }
107                catch ( IOException e )
108                {
109                    throw new TransferFailedException( e.getMessage(), e );
110                }
111                finally
112                {
113                    IOUtils.closeQuietly( stream );
114                }
115            }
116
117            this.resource = resource;
118            this.wagon = wagon;
119        }
120
121        public long getContentLength()
122        {
123            return resource.getContentLength();
124        }
125
126        public String getContentType()
127        {
128            return null;
129        }
130
131        public boolean isRepeatable()
132        {
133            return true;
134        }
135
136        public void writeRequest( OutputStream output )
137            throws IOException
138        {
139            byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
140
141            TransferEvent transferEvent =
142                new TransferEvent( wagon, resource, TransferEvent.TRANSFER_PROGRESS, TransferEvent.REQUEST_PUT );
143            transferEvent.setTimestamp( System.currentTimeMillis() );
144
145            InputStream fin = null;
146            try
147            {
148                fin = this.source != null
149                    ? new FileInputStream( source )
150                    : new ByteArrayInputStream( this.byteBuffer.array() );
151                int remaining = Integer.MAX_VALUE;
152                while ( remaining > 0 )
153                {
154                    int n = fin.read( buffer, 0, Math.min( buffer.length, remaining ) );
155
156                    if ( n == -1 )
157                    {
158                        break;
159                    }
160
161                    fireTransferProgress( transferEvent, buffer, n );
162
163                    output.write( buffer, 0, n );
164
165                    remaining -= n;
166                }
167
168                fin.close();
169                fin = null;
170            }
171            finally
172            {
173                IOUtils.closeQuietly( fin );
174            }
175
176            output.flush();
177        }
178    }
179
180    protected static final int SC_NULL = -1;
181
182    protected static final TimeZone GMT_TIME_ZONE = TimeZone.getTimeZone( "GMT" );
183
184    private HttpClient client;
185
186    protected HttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
187
188    /**
189     * @deprecated Use httpConfiguration instead.
190     */
191    private Properties httpHeaders;
192
193    /**
194     * @since 1.0-beta-6
195     */
196    private HttpConfiguration httpConfiguration;
197
198    private HttpMethod getMethod;
199
200    public void openConnectionInternal()
201    {
202        repository.setUrl( getURL( repository ) );
203        client = new HttpClient( connectionManager );
204
205        // WAGON-273: default the cookie-policy to browser compatible
206        client.getParams().setCookiePolicy( CookiePolicy.BROWSER_COMPATIBILITY );
207
208        String username = null;
209        String password = null;
210        String domain = null;
211
212        if ( authenticationInfo != null )
213        {
214            username = authenticationInfo.getUserName();
215
216            if ( username != null && username.contains( "\\" ) )
217            {
218                String[] domainAndUsername = username.split( "\\\\" );
219                domain = domainAndUsername[0];
220                username = domainAndUsername[1];
221            }
222
223            password = authenticationInfo.getPassword();
224
225
226        }
227
228        String host = getRepository().getHost();
229
230        if ( !( username == null || username.length() == 0 )
231            && !( password == null || password.length() == 0 ) )
232        {
233            Credentials creds;
234            if ( domain != null )
235            {
236                creds = new NTCredentials( username, password, host, domain );
237            }
238            else
239            {
240                creds = new UsernamePasswordCredentials( username, password );
241            }
242
243            int port = getRepository().getPort() > -1 ? getRepository().getPort() : AuthScope.ANY_PORT;
244
245            AuthScope scope = new AuthScope( host, port );
246            client.getState().setCredentials( scope, creds );
247        }
248
249        HostConfiguration hc = new HostConfiguration();
250
251        ProxyInfo proxyInfo = getProxyInfo( getRepository().getProtocol(), getRepository().getHost() );
252        if ( proxyInfo != null )
253        {
254            String proxyUsername = proxyInfo.getUserName();
255            String proxyPassword = proxyInfo.getPassword();
256            String proxyHost = proxyInfo.getHost();
257            int proxyPort = proxyInfo.getPort();
258            String proxyNtlmHost = proxyInfo.getNtlmHost();
259            String proxyNtlmDomain = proxyInfo.getNtlmDomain();
260            if ( proxyHost != null )
261            {
262                hc.setProxy( proxyHost, proxyPort );
263
264                if ( proxyUsername != null && proxyPassword != null )
265                {
266                    Credentials creds;
267                    if ( proxyNtlmHost != null || proxyNtlmDomain != null )
268                    {
269                        creds = new NTCredentials( proxyUsername, proxyPassword, proxyNtlmHost, proxyNtlmDomain );
270                    }
271                    else
272                    {
273                        creds = new UsernamePasswordCredentials( proxyUsername, proxyPassword );
274                    }
275
276                    int port = proxyInfo.getPort() > -1 ? proxyInfo.getPort() : AuthScope.ANY_PORT;
277
278                    AuthScope scope = new AuthScope( proxyHost, port );
279                    client.getState().setProxyCredentials( scope, creds );
280                }
281            }
282        }
283
284        hc.setHost( host );
285
286        //start a session with the webserver
287        client.setHostConfiguration( hc );
288    }
289
290    public void closeConnection()
291    {
292        if ( connectionManager instanceof MultiThreadedHttpConnectionManager )
293        {
294            ( (MultiThreadedHttpConnectionManager) connectionManager ).shutdown();
295        }
296    }
297
298    public void put( File source, String resourceName )
299        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
300    {
301        Resource resource = new Resource( resourceName );
302
303        firePutInitiated( resource, source );
304
305        resource.setContentLength( source.length() );
306
307        resource.setLastModified( source.lastModified() );
308
309        put( null, resource, source );
310    }
311
312    public void putFromStream( final InputStream stream, String destination, long contentLength, long lastModified )
313        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
314    {
315        Resource resource = new Resource( destination );
316
317        firePutInitiated( resource, null );
318
319        resource.setContentLength( contentLength );
320
321        resource.setLastModified( lastModified );
322
323        put( stream, resource, null );
324    }
325
326    private void put( final InputStream stream, Resource resource, File source )
327        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
328    {
329        RequestEntityImplementation requestEntityImplementation =
330            new RequestEntityImplementation( stream, resource, this, source );
331
332        put( resource, source, requestEntityImplementation, buildUrl( resource ) );
333
334    }
335
336    /**
337     * Builds a complete URL string from the repository URL and the relative path of the resource passed.
338     *
339     * @param resource the resource to extract the relative path from.
340     * @return the complete URL
341     */
342    private String buildUrl( Resource resource )
343    {
344        return EncodingUtil.encodeURLToString( getRepository().getUrl(), resource.getName() );
345    }
346
347    private void put( Resource resource, File source, RequestEntityImplementation requestEntityImplementation,
348                      String url )
349        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
350    {
351        // preemptive true for put
352        client.getParams().setAuthenticationPreemptive( true );
353
354        //Parent directories need to be created before posting
355        try
356        {
357            mkdirs( PathUtils.dirname( resource.getName() ) );
358        }
359        catch ( IOException e )
360        {
361            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
362        }
363
364        PutMethod putMethod = new PutMethod( url );
365
366        firePutStarted( resource, source );
367
368        try
369        {
370            putMethod.setRequestEntity( requestEntityImplementation );
371
372            int statusCode;
373            try
374            {
375                statusCode = execute( putMethod );
376
377            }
378            catch ( IOException e )
379            {
380                fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
381
382                throw new TransferFailedException( e.getMessage(), e );
383            }
384
385            fireTransferDebug( url + " - Status code: " + statusCode );
386
387            // Check that we didn't run out of retries.
388            // CHECKSTYLE_OFF: AvoidNestedBlocks
389            switch ( statusCode )
390            {
391                // Success Codes
392                case HttpStatus.SC_OK: // 200
393                case HttpStatus.SC_CREATED: // 201
394                case HttpStatus.SC_ACCEPTED: // 202
395                case HttpStatus.SC_NO_CONTENT:  // 204
396                    break;
397
398                // handle all redirect even if http specs says
399                // " the user agent MUST NOT automatically redirect the request unless it can be confirmed by the user"
400                case HttpStatus.SC_MOVED_PERMANENTLY: // 301
401                case HttpStatus.SC_MOVED_TEMPORARILY: // 302
402                case HttpStatus.SC_SEE_OTHER: // 303
403                    String relocatedUrl = calculateRelocatedUrl( putMethod );
404                    fireTransferDebug( "relocate to " + relocatedUrl );
405                    put( resource, source, requestEntityImplementation, relocatedUrl );
406                    return;
407
408                case SC_NULL:
409                {
410                    TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
411                    fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
412                    throw e;
413                }
414
415                case HttpStatus.SC_FORBIDDEN:
416                    fireSessionConnectionRefused();
417                    throw new AuthorizationException( "Access denied to: " + url );
418
419                case HttpStatus.SC_NOT_FOUND:
420                    throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
421
422                    //add more entries here
423                default:
424                {
425                    TransferFailedException e = new TransferFailedException(
426                        "Failed to transfer file: " + url + ". Return code is: " + statusCode );
427                    fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
428                    throw e;
429                }
430            }
431            // CHECKSTYLE_ON: AvoidNestedBlocks
432
433            firePutCompleted( resource, source );
434        }
435        finally
436        {
437            putMethod.releaseConnection();
438        }
439    }
440
441    protected String calculateRelocatedUrl( EntityEnclosingMethod method )
442    {
443        Header locationHeader = method.getResponseHeader( "Location" );
444        String locationField = locationHeader.getValue();
445        // is it a relative Location or a full ?
446        return locationField.startsWith( "http" ) ? locationField : getURL( getRepository() ) + '/' + locationField;
447    }
448
449    protected void mkdirs( String dirname )
450        throws IOException
451    {
452        // do nothing as default.
453    }
454
455    public boolean resourceExists( String resourceName )
456        throws TransferFailedException, AuthorizationException
457    {
458        StringBuilder url = new StringBuilder( getRepository().getUrl() );
459        if ( !url.toString().endsWith( "/" ) )
460        {
461            url.append( '/' );
462        }
463        url.append( resourceName );
464        HeadMethod headMethod = new HeadMethod( url.toString() );
465
466        int statusCode;
467        try
468        {
469            statusCode = execute( headMethod );
470        }
471        catch ( IOException e )
472        {
473            throw new TransferFailedException( e.getMessage(), e );
474        }
475        try
476        {
477            switch ( statusCode )
478            {
479                case HttpStatus.SC_OK:
480                    return true;
481
482                case HttpStatus.SC_NOT_MODIFIED:
483                    return true;
484
485                case SC_NULL:
486                    throw new TransferFailedException( "Failed to transfer file: " + url );
487
488                case HttpStatus.SC_FORBIDDEN:
489                    throw new AuthorizationException( "Access denied to: " + url );
490
491                case HttpStatus.SC_UNAUTHORIZED:
492                    throw new AuthorizationException( "Not authorized." );
493
494                case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
495                    throw new AuthorizationException( "Not authorized by proxy." );
496
497                case HttpStatus.SC_NOT_FOUND:
498                    return false;
499
500                //add more entries here
501                default:
502                    throw new TransferFailedException(
503                        "Failed to transfer file: " + url + ". Return code is: " + statusCode );
504            }
505        }
506        finally
507        {
508            headMethod.releaseConnection();
509        }
510    }
511
512    protected int execute( HttpMethod httpMethod )
513        throws IOException
514    {
515        int statusCode;
516
517        setParameters( httpMethod );
518        setHeaders( httpMethod );
519
520        statusCode = client.executeMethod( httpMethod );
521        return statusCode;
522    }
523
524    protected void setParameters( HttpMethod method )
525    {
526        HttpMethodConfiguration config =
527            httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
528        if ( config != null )
529        {
530            HttpMethodParams params = config.asMethodParams( method.getParams() );
531            if ( params != null )
532            {
533                method.setParams( params );
534            }
535        }
536
537        if ( config == null || config.getConnectionTimeout() == HttpMethodConfiguration.DEFAULT_CONNECTION_TIMEOUT )
538        {
539            method.getParams().setSoTimeout( getTimeout() );
540        }
541    }
542
543    protected void setHeaders( HttpMethod method )
544    {
545        HttpMethodConfiguration config =
546            httpConfiguration == null ? null : httpConfiguration.getMethodConfiguration( method );
547        if ( config == null || config.isUseDefaultHeaders() )
548        {
549            // TODO: merge with the other headers and have some better defaults, unify with lightweight headers
550            method.addRequestHeader( "Cache-control", "no-cache" );
551            method.addRequestHeader( "Cache-store", "no-store" );
552            method.addRequestHeader( "Pragma", "no-cache" );
553            method.addRequestHeader( "Expires", "0" );
554            method.addRequestHeader( "Accept-Encoding", "gzip" );
555            method.addRequestHeader( "User-Agent", DEFAULT_USER_AGENT );
556        }
557
558        if ( httpHeaders != null )
559        {
560            for ( Object header : httpHeaders.keySet() )
561            {
562                if ( "User-Agent".equals( header ) )
563                {
564                    method.setRequestHeader( (String) header, httpHeaders.getProperty( (String) header ) );
565                }
566                else
567                {
568                    method.addRequestHeader( (String) header, httpHeaders.getProperty( (String) header ) );
569                }
570            }
571        }
572
573        Header[] headers = config == null ? null : config.asRequestHeaders();
574        if ( headers != null )
575        {
576            for ( Header header : headers )
577            {
578                method.addRequestHeader( header );
579            }
580        }
581    }
582
583    private static final String DEFAULT_USER_AGENT = getDefaultUserAgent();
584
585    private static String getDefaultUserAgent()
586    {
587        Properties props = new Properties();
588
589        InputStream is = AbstractHttpClientWagon.class.getResourceAsStream(
590            "/META-INF/maven/org.apache.maven.wagon/wagon-webdav-jackrabbit/pom.properties" );
591        if ( is != null )
592        {
593            try
594            {
595                props.load( is );
596                is.close();
597                is = null;
598            }
599            catch ( IOException ignore )
600            {
601                // ignore
602            }
603            finally
604            {
605                IOUtil.close( is );
606            }
607        }
608
609        String ver = props.getProperty( "version", "unknown-version" );
610        return "Apache-Maven-Wagon/" + ver + " (Java " + System.getProperty( "java.version" ) + "; ";
611    }
612
613    /**
614     * getUrl
615     * Implementors can override this to remove unwanted parts of the url such as role-hints
616     *
617     * @param repository
618     * @return
619     */
620    protected String getURL( Repository repository )
621    {
622        return repository.getUrl();
623    }
624
625    protected HttpClient getClient()
626    {
627        return client;
628    }
629
630    public void setConnectionManager( HttpConnectionManager connectionManager )
631    {
632        this.connectionManager = connectionManager;
633    }
634
635    public Properties getHttpHeaders()
636    {
637        return httpHeaders;
638    }
639
640    public void setHttpHeaders( Properties httpHeaders )
641    {
642        this.httpHeaders = httpHeaders;
643    }
644
645    public HttpConfiguration getHttpConfiguration()
646    {
647        return httpConfiguration;
648    }
649
650    public void setHttpConfiguration( HttpConfiguration httpConfiguration )
651    {
652        this.httpConfiguration = httpConfiguration;
653    }
654
655    public void fillInputData( InputData inputData )
656        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
657    {
658        Resource resource = inputData.getResource();
659
660        StringBuilder url = new StringBuilder( getRepository().getUrl() );
661        if ( !url.toString().endsWith( "/" ) )
662        {
663            url.append( '/' );
664        }
665        url.append( resource.getName() );
666
667        getMethod = new GetMethod( url.toString() );
668
669        long timestamp = resource.getLastModified();
670        if ( timestamp > 0 )
671        {
672            SimpleDateFormat fmt = new SimpleDateFormat( "EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US );
673            fmt.setTimeZone( GMT_TIME_ZONE );
674            Header hdr = new Header( "If-Modified-Since", fmt.format( new Date( timestamp ) ) );
675            fireTransferDebug( "sending ==> " + hdr + "(" + timestamp + ")" );
676            getMethod.addRequestHeader( hdr );
677        }
678
679        int statusCode;
680        try
681        {
682            statusCode = execute( getMethod );
683        }
684        catch ( IOException e )
685        {
686            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
687
688            throw new TransferFailedException( e.getMessage(), e );
689        }
690
691        fireTransferDebug( url + " - Status code: " + statusCode );
692
693        // TODO [BP]: according to httpclient docs, really should swallow the output on error. verify if that is
694        // required
695        // CHECKSTYLE_OFF: AvoidNestedBlocks
696        switch ( statusCode )
697        {
698            case HttpStatus.SC_OK:
699                break;
700
701            case HttpStatus.SC_NOT_MODIFIED:
702                // return, leaving last modified set to original value so getIfNewer should return unmodified
703                return;
704
705            case SC_NULL:
706            {
707                TransferFailedException e = new TransferFailedException( "Failed to transfer file: " + url );
708                fireTransferError( resource, e, TransferEvent.REQUEST_GET );
709                throw e;
710            }
711
712            case HttpStatus.SC_FORBIDDEN:
713                fireSessionConnectionRefused();
714                throw new AuthorizationException( "Access denied to: " + url );
715
716            case HttpStatus.SC_UNAUTHORIZED:
717                fireSessionConnectionRefused();
718                throw new AuthorizationException( "Not authorized." );
719
720            case HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED:
721                fireSessionConnectionRefused();
722                throw new AuthorizationException( "Not authorized by proxy." );
723
724            case HttpStatus.SC_NOT_FOUND:
725                throw new ResourceDoesNotExistException( "File: " + url + " does not exist" );
726
727                // add more entries here
728            default:
729            {
730                cleanupGetTransfer( resource );
731                TransferFailedException e = new TransferFailedException(
732                    "Failed to transfer file: " + url + ". Return code is: " + statusCode );
733                fireTransferError( resource, e, TransferEvent.REQUEST_GET );
734                throw e;
735            }
736        }
737        // CHECKSTYLE_ON: AvoidNestedBlocks
738
739        InputStream is = null;
740
741        Header contentLengthHeader = getMethod.getResponseHeader( "Content-Length" );
742
743        if ( contentLengthHeader != null )
744        {
745            try
746            {
747                long contentLength = Integer.valueOf( contentLengthHeader.getValue() ).intValue();
748
749                resource.setContentLength( contentLength );
750            }
751            catch ( NumberFormatException e )
752            {
753                fireTransferDebug(
754                    "error parsing content length header '" + contentLengthHeader.getValue() + "' " + e );
755            }
756        }
757
758        Header lastModifiedHeader = getMethod.getResponseHeader( "Last-Modified" );
759
760        long lastModified = 0;
761
762        if ( lastModifiedHeader != null )
763        {
764            try
765            {
766                lastModified = DateUtil.parseDate( lastModifiedHeader.getValue() ).getTime();
767
768                resource.setLastModified( lastModified );
769            }
770            catch ( DateParseException e )
771            {
772                fireTransferDebug( "Unable to parse last modified header" );
773            }
774
775            fireTransferDebug( "last-modified = " + lastModifiedHeader.getValue() + " (" + lastModified + ")" );
776        }
777
778        Header contentEncoding = getMethod.getResponseHeader( "Content-Encoding" );
779        boolean isGZipped = contentEncoding != null && "gzip".equalsIgnoreCase( contentEncoding.getValue() );
780
781        try
782        {
783            is = getMethod.getResponseBodyAsStream();
784            if ( isGZipped )
785            {
786                is = new GZIPInputStream( is );
787            }
788        }
789        catch ( IOException e )
790        {
791            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
792
793            String msg =
794                "Error occurred while retrieving from remote repository:" + getRepository() + ": " + e.getMessage();
795
796            throw new TransferFailedException( msg, e );
797        }
798
799        inputData.setInputStream( is );
800    }
801
802    protected void cleanupGetTransfer( Resource resource )
803    {
804        if ( getMethod != null )
805        {
806            getMethod.releaseConnection();
807        }
808    }
809
810    @Override
811    public void putFromStream( InputStream stream, String destination )
812        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
813    {
814        putFromStream( stream, destination, -1, -1 );
815    }
816
817    @Override
818    protected void putFromStream( InputStream stream, Resource resource )
819        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
820    {
821        putFromStream( stream, resource.getName(), -1, -1 );
822    }
823
824    @Override
825    public void fillOutputData( OutputData outputData )
826        throws TransferFailedException
827    {
828        // no needed in this implementation but throw an Exception if used
829        throw new IllegalStateException( "this wagon http client must not use fillOutputData" );
830    }
831}