001package org.apache.maven.wagon;
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.maven.wagon.authentication.AuthenticationException;
023import org.apache.maven.wagon.authentication.AuthenticationInfo;
024import org.apache.maven.wagon.authorization.AuthorizationException;
025import org.apache.maven.wagon.events.SessionEvent;
026import org.apache.maven.wagon.events.SessionEventSupport;
027import org.apache.maven.wagon.events.SessionListener;
028import org.apache.maven.wagon.events.TransferEvent;
029import org.apache.maven.wagon.events.TransferEventSupport;
030import org.apache.maven.wagon.events.TransferListener;
031import org.apache.maven.wagon.proxy.ProxyInfo;
032import org.apache.maven.wagon.proxy.ProxyInfoProvider;
033import org.apache.maven.wagon.proxy.ProxyUtils;
034import org.apache.maven.wagon.repository.Repository;
035import org.apache.maven.wagon.repository.RepositoryPermissions;
036import org.apache.maven.wagon.resource.Resource;
037import org.codehaus.plexus.util.IOUtil;
038
039import java.io.File;
040import java.io.FileInputStream;
041import java.io.FileNotFoundException;
042import java.io.IOException;
043import java.io.InputStream;
044import java.io.OutputStream;
045import java.nio.Buffer;
046import java.nio.ByteBuffer;
047import java.nio.channels.Channels;
048import java.nio.channels.ReadableByteChannel;
049import java.util.List;
050
051import static java.lang.Math.max;
052import static java.lang.Math.min;
053
054/**
055 * Implementation of common facilities for Wagon providers.
056 *
057 * @author <a href="michal.maczka@dimatics.com">Michal Maczka</a>
058 */
059public abstract class AbstractWagon
060    implements Wagon
061{
062    protected static final int DEFAULT_BUFFER_SIZE = 4 * 1024;
063    protected static final int MAXIMUM_BUFFER_SIZE = 512 * 1024;
064
065    /**
066     * To efficiently buffer data, use a multiple of 4 KiB as this is likely to match the hardware
067     * buffer size of certain storage devices.
068     */
069    protected static final int BUFFER_SEGMENT_SIZE = 4 * 1024;
070
071    /**
072     * The desired minimum amount of chunks in which a {@link Resource} shall be
073     * {@link #transfer(Resource, InputStream, OutputStream, int, long) transferred}.
074     * This corresponds to the minimum times {@link #fireTransferProgress(TransferEvent, byte[], int)}
075     * is executed. 100 notifications is a conservative value that will lead to small chunks for
076     * any artifact less that {@link #BUFFER_SEGMENT_SIZE} * {@link #MINIMUM_AMOUNT_OF_TRANSFER_CHUNKS}
077     * in size.
078     */
079    protected static final int MINIMUM_AMOUNT_OF_TRANSFER_CHUNKS = 100;
080
081    protected Repository repository;
082
083    protected SessionEventSupport sessionEventSupport = new SessionEventSupport();
084
085    protected TransferEventSupport transferEventSupport = new TransferEventSupport();
086
087    protected AuthenticationInfo authenticationInfo;
088
089    protected boolean interactive = true;
090
091
092    private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
093
094    /**
095     * read timeout value
096     *
097     * @since 2.2
098     */
099    private int readTimeout =
100        Integer.parseInt( System.getProperty( "maven.wagon.rto", Integer.toString( Wagon.DEFAULT_READ_TIMEOUT ) ) );
101
102    private ProxyInfoProvider proxyInfoProvider;
103
104    /**
105     * @deprecated
106     */
107    protected ProxyInfo proxyInfo;
108
109    private RepositoryPermissions permissionsOverride;
110
111    // ----------------------------------------------------------------------
112    // Accessors
113    // ----------------------------------------------------------------------
114
115    public Repository getRepository()
116    {
117        return repository;
118    }
119
120    public ProxyInfo getProxyInfo()
121    {
122        return proxyInfoProvider != null ? proxyInfoProvider.getProxyInfo( null ) : null;
123    }
124
125    public AuthenticationInfo getAuthenticationInfo()
126    {
127        return authenticationInfo;
128    }
129
130    // ----------------------------------------------------------------------
131    // Connection
132    // ----------------------------------------------------------------------
133
134    public void openConnection()
135        throws ConnectionException, AuthenticationException
136    {
137        try
138        {
139            openConnectionInternal();
140        }
141        catch ( ConnectionException e )
142        {
143            fireSessionConnectionRefused();
144
145            throw e;
146        }
147        catch ( AuthenticationException e )
148        {
149            fireSessionConnectionRefused();
150
151            throw e;
152        }
153    }
154
155    public void connect( Repository repository )
156        throws ConnectionException, AuthenticationException
157    {
158        connect( repository, null, (ProxyInfoProvider) null );
159    }
160
161    public void connect( Repository repository, ProxyInfo proxyInfo )
162        throws ConnectionException, AuthenticationException
163    {
164        connect( repository, null, proxyInfo );
165    }
166
167    public void connect( Repository repository, ProxyInfoProvider proxyInfoProvider )
168        throws ConnectionException, AuthenticationException
169    {
170        connect( repository, null, proxyInfoProvider );
171    }
172
173    public void connect( Repository repository, AuthenticationInfo authenticationInfo )
174        throws ConnectionException, AuthenticationException
175    {
176        connect( repository, authenticationInfo, (ProxyInfoProvider) null );
177    }
178
179    public void connect( Repository repository, AuthenticationInfo authenticationInfo, ProxyInfo proxyInfo )
180        throws ConnectionException, AuthenticationException
181    {
182        final ProxyInfo proxy = proxyInfo;
183        connect( repository, authenticationInfo, new ProxyInfoProvider()
184        {
185            public ProxyInfo getProxyInfo( String protocol )
186            {
187                if ( protocol == null || proxy == null || protocol.equalsIgnoreCase( proxy.getType() ) )
188                {
189                    return proxy;
190                }
191                else
192                {
193                    return null;
194                }
195            }
196        } );
197    }
198
199    public void connect( Repository repository, AuthenticationInfo authenticationInfo,
200                         ProxyInfoProvider proxyInfoProvider )
201        throws ConnectionException, AuthenticationException
202    {
203        if ( repository == null )
204        {
205            throw new NullPointerException( "repository cannot be null" );
206        }
207
208        if ( permissionsOverride != null )
209        {
210            repository.setPermissions( permissionsOverride );
211        }
212
213        this.repository = repository;
214
215        if ( authenticationInfo == null )
216        {
217            authenticationInfo = new AuthenticationInfo();
218        }
219
220        if ( authenticationInfo.getUserName() == null )
221        {
222            // Get user/pass that were encoded in the URL.
223            if ( repository.getUsername() != null )
224            {
225                authenticationInfo.setUserName( repository.getUsername() );
226                if ( repository.getPassword() != null && authenticationInfo.getPassword() == null )
227                {
228                    authenticationInfo.setPassword( repository.getPassword() );
229                }
230            }
231        }
232
233        this.authenticationInfo = authenticationInfo;
234
235        this.proxyInfoProvider = proxyInfoProvider;
236
237        fireSessionOpening();
238
239        openConnection();
240
241        fireSessionOpened();
242    }
243
244    protected abstract void openConnectionInternal()
245        throws ConnectionException, AuthenticationException;
246
247    public void disconnect()
248        throws ConnectionException
249    {
250        fireSessionDisconnecting();
251
252        try
253        {
254            closeConnection();
255        }
256        catch ( ConnectionException e )
257        {
258            fireSessionError( e );
259            throw e;
260        }
261
262        fireSessionDisconnected();
263    }
264
265    protected abstract void closeConnection()
266        throws ConnectionException;
267
268    protected void createParentDirectories( File destination )
269        throws TransferFailedException
270    {
271        File destinationDirectory = destination.getParentFile();
272        try
273        {
274            destinationDirectory = destinationDirectory.getCanonicalFile();
275        }
276        catch ( IOException e )
277        {
278            // not essential to have a canonical file
279        }
280        if ( destinationDirectory != null && !destinationDirectory.exists() )
281        {
282            destinationDirectory.mkdirs();
283            if ( !destinationDirectory.exists() )
284            {
285                throw new TransferFailedException(
286                    "Specified destination directory cannot be created: " + destinationDirectory );
287            }
288        }
289    }
290
291    public void setTimeout( int timeoutValue )
292    {
293        connectionTimeout = timeoutValue;
294    }
295
296    public int getTimeout()
297    {
298        return connectionTimeout;
299    }
300
301    // ----------------------------------------------------------------------
302    // Stream i/o
303    // ----------------------------------------------------------------------
304
305    protected void getTransfer( Resource resource, File destination, InputStream input )
306        throws TransferFailedException
307    {
308        getTransfer( resource, destination, input, true, Long.MAX_VALUE );
309    }
310
311    protected void getTransfer( Resource resource, OutputStream output, InputStream input )
312        throws TransferFailedException
313    {
314        getTransfer( resource, output, input, true, Long.MAX_VALUE );
315    }
316
317    @Deprecated
318    protected void getTransfer( Resource resource, File destination, InputStream input, boolean closeInput,
319                                int maxSize )
320        throws TransferFailedException
321    {
322        getTransfer( resource, destination, input, closeInput, (long) maxSize );
323    }
324
325    protected void getTransfer( Resource resource, File destination, InputStream input, boolean closeInput,
326                                long maxSize )
327        throws TransferFailedException
328    {
329        // ensure that the destination is created only when we are ready to transfer
330        fireTransferDebug( "attempting to create parent directories for destination: " + destination.getName() );
331        createParentDirectories( destination );
332
333        fireGetStarted( resource, destination );
334
335        OutputStream output = null;
336        try
337        {
338            output = new LazyFileOutputStream( destination );
339            getTransfer( resource, output, input, closeInput, maxSize );
340            output.close();
341            output = null;
342        }
343        catch ( final IOException e )
344        {
345            if ( destination.exists() )
346            {
347                boolean deleted = destination.delete();
348
349                if ( !deleted )
350                {
351                    destination.deleteOnExit();
352                }
353            }
354
355            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
356
357            String msg = "GET request of: " + resource.getName() + " from " + repository.getName() + " failed";
358
359            throw new TransferFailedException( msg, e );
360        }
361        catch ( TransferFailedException e )
362        {
363            if ( destination.exists() )
364            {
365                boolean deleted = destination.delete();
366
367                if ( !deleted )
368                {
369                    destination.deleteOnExit();
370                }
371            }
372            throw e;
373        }
374        finally
375        {
376            IOUtil.close( output );
377        }
378
379        fireGetCompleted( resource, destination );
380    }
381
382    @Deprecated
383    protected void getTransfer( Resource resource, OutputStream output, InputStream input, boolean closeInput,
384                                int maxSize )
385        throws TransferFailedException
386    {
387        getTransfer( resource, output, input, closeInput, (long) maxSize );
388    }
389
390    protected void getTransfer( Resource resource, OutputStream output, InputStream input, boolean closeInput,
391                                long maxSize )
392        throws TransferFailedException
393    {
394        try
395        {
396            transfer( resource, input, output, TransferEvent.REQUEST_GET, maxSize );
397
398            finishGetTransfer( resource, input, output );
399
400            if ( closeInput )
401            {
402                input.close();
403                input = null;
404            }
405
406        }
407        catch ( IOException e )
408        {
409            fireTransferError( resource, e, TransferEvent.REQUEST_GET );
410
411            String msg = "GET request of: " + resource.getName() + " from " + repository.getName() + " failed";
412
413            throw new TransferFailedException( msg, e );
414        }
415        finally
416        {
417            if ( closeInput )
418            {
419                IOUtil.close( input );
420            }
421
422            cleanupGetTransfer( resource );
423        }
424    }
425
426    protected void finishGetTransfer( Resource resource, InputStream input, OutputStream output )
427        throws TransferFailedException
428    {
429    }
430
431    protected void cleanupGetTransfer( Resource resource )
432    {
433    }
434
435    protected void putTransfer( Resource resource, File source, OutputStream output, boolean closeOutput )
436        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
437    {
438        firePutStarted( resource, source );
439
440        transfer( resource, source, output, closeOutput );
441
442        firePutCompleted( resource, source );
443    }
444
445    /**
446     * Write from {@link File} to {@link OutputStream}
447     *
448     * @param resource    resource to transfer
449     * @param source      file to read from
450     * @param output      output stream
451     * @param closeOutput whether the output stream should be closed or not
452     * @throws TransferFailedException
453     * @throws ResourceDoesNotExistException
454     * @throws AuthorizationException
455     * @since 1.0-beta-1
456     */
457    protected void transfer( Resource resource, File source, OutputStream output, boolean closeOutput )
458        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
459    {
460        InputStream input = null;
461
462        try
463        {
464            input = new FileInputStream( source );
465
466            putTransfer( resource, input, output, closeOutput );
467
468            input.close();
469            input = null;
470        }
471        catch ( FileNotFoundException e )
472        {
473            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
474
475            throw new TransferFailedException( "Specified source file does not exist: " + source, e );
476        }
477        catch ( final IOException e )
478        {
479            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
480
481            throw new TransferFailedException( "Failure transferring " + source, e );
482        }
483        finally
484        {
485            IOUtil.close( input );
486        }
487    }
488
489    protected void putTransfer( Resource resource, InputStream input, OutputStream output, boolean closeOutput )
490        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
491    {
492        try
493        {
494            transfer( resource, input, output, TransferEvent.REQUEST_PUT,
495                      resource.getContentLength() == WagonConstants.UNKNOWN_LENGTH
496                          ? Long.MAX_VALUE
497                          : resource.getContentLength() );
498
499            finishPutTransfer( resource, input, output );
500
501            if ( closeOutput )
502            {
503                output.close();
504                output = null;
505            }
506        }
507        catch ( IOException e )
508        {
509            fireTransferError( resource, e, TransferEvent.REQUEST_PUT );
510
511            String msg = "PUT request to: " + resource.getName() + " in " + repository.getName() + " failed";
512
513            throw new TransferFailedException( msg, e );
514        }
515        finally
516        {
517            if ( closeOutput )
518            {
519                IOUtil.close( output );
520            }
521
522            cleanupPutTransfer( resource );
523        }
524    }
525
526    protected void cleanupPutTransfer( Resource resource )
527    {
528    }
529
530    protected void finishPutTransfer( Resource resource, InputStream input, OutputStream output )
531        throws TransferFailedException, AuthorizationException, ResourceDoesNotExistException
532    {
533    }
534
535    /**
536     * Write from {@link InputStream} to {@link OutputStream}.
537     * Equivalent to {@link #transfer(Resource, InputStream, OutputStream, int, int)} with a maxSize equals to
538     * {@link Integer#MAX_VALUE}
539     *
540     * @param resource    resource to transfer
541     * @param input       input stream
542     * @param output      output stream
543     * @param requestType one of {@link TransferEvent#REQUEST_GET} or {@link TransferEvent#REQUEST_PUT}
544     * @throws IOException
545     */
546    protected void transfer( Resource resource, InputStream input, OutputStream output, int requestType )
547        throws IOException
548    {
549        transfer( resource, input, output, requestType, Long.MAX_VALUE );
550    }
551
552    /**
553     * Write from {@link InputStream} to {@link OutputStream}.
554     * Equivalent to {@link #transfer(Resource, InputStream, OutputStream, int, int)} with a maxSize equals to
555     * {@link Integer#MAX_VALUE}
556     *
557     * @param resource    resource to transfer
558     * @param input       input stream
559     * @param output      output stream
560     * @param requestType one of {@link TransferEvent#REQUEST_GET} or {@link TransferEvent#REQUEST_PUT}
561     * @param maxSize     size of the buffer
562     * @throws IOException
563     * @deprecated Please use the transfer using long as type of maxSize
564     */
565    @Deprecated
566    protected void transfer( Resource resource, InputStream input, OutputStream output, int requestType, int maxSize )
567        throws IOException
568    {
569        transfer( resource, input, output, requestType, (long) maxSize );
570    }
571
572    /**
573     * Write from {@link InputStream} to {@link OutputStream}.
574     * Equivalent to {@link #transfer(Resource, InputStream, OutputStream, int, long)} with a maxSize equals to
575     * {@link Integer#MAX_VALUE}
576     *
577     * @param resource    resource to transfer
578     * @param input       input stream
579     * @param output      output stream
580     * @param requestType one of {@link TransferEvent#REQUEST_GET} or {@link TransferEvent#REQUEST_PUT}
581     * @param maxSize     size of the buffer
582     * @throws IOException
583     */
584    protected void transfer( Resource resource, InputStream input, OutputStream output, int requestType, long maxSize )
585        throws IOException
586    {
587        ByteBuffer buffer = ByteBuffer.allocate( getBufferCapacityForTransfer( resource.getContentLength() ) );
588        int halfBufferCapacity = buffer.capacity() / 2;
589
590        TransferEvent transferEvent = new TransferEvent( this, resource, TransferEvent.TRANSFER_PROGRESS, requestType );
591        transferEvent.setTimestamp( System.currentTimeMillis() );
592
593        ReadableByteChannel in = Channels.newChannel( input );
594
595        long remaining = maxSize;
596        while ( remaining > 0L )
597        {
598            int read = in.read( buffer );
599
600            if ( read == -1 )
601            {
602                // EOF, but some data has not been written yet.
603                if ( ( (Buffer) buffer ).position() != 0 )
604                {
605                    ( (Buffer) buffer ).flip();
606                    fireTransferProgress( transferEvent, buffer.array(), ( (Buffer) buffer ).limit() );
607                    output.write( buffer.array(), 0, ( (Buffer) buffer ).limit() );
608                    ( (Buffer) buffer ).clear();
609                }
610
611                break;
612            }
613
614            // Prevent minichunking/fragmentation: when less than half the buffer is utilized,
615            // read some more bytes before writing and firing progress.
616            if ( ( (Buffer) buffer ).position() < halfBufferCapacity )
617            {
618                continue;
619            }
620
621            ( (Buffer) buffer ).flip();
622            fireTransferProgress( transferEvent, buffer.array(), ( (Buffer) buffer ).limit() );
623            output.write( buffer.array(), 0, ( (Buffer) buffer ).limit() );
624            remaining -= ( (Buffer) buffer ).limit();
625            ( (Buffer) buffer ).clear();
626        }
627        output.flush();
628    }
629
630    /**
631     * Provides a buffer size for efficiently transferring the given amount of bytes such that
632     * it is not fragmented into too many chunks. For larger files larger buffers are provided such that downstream
633     * {@link #fireTransferProgress(TransferEvent, byte[], int) listeners} are not notified too frequently.
634     * For instance, transferring gigabyte-sized resources would result in millions of notifications when using
635     * only a few kibibytes of buffer, drastically slowing down transfer since transfer progress listeners and
636     * notifications are synchronous and may block, e.g., when writing download progress status to console.
637     *
638     * @param numberOfBytes can be 0 or less, in which case a default buffer size is used.
639     * @return a byte buffer suitable for transferring the given amount of bytes without too many chunks.
640     */
641    protected int getBufferCapacityForTransfer( long numberOfBytes )
642    {
643        if ( numberOfBytes <= 0L )
644        {
645            return DEFAULT_BUFFER_SIZE;
646        }
647
648        final long numberOfBufferSegments =  numberOfBytes
649                / ( BUFFER_SEGMENT_SIZE * MINIMUM_AMOUNT_OF_TRANSFER_CHUNKS );
650        final long potentialBufferSize = numberOfBufferSegments * BUFFER_SEGMENT_SIZE;
651        if ( potentialBufferSize > Integer.MAX_VALUE )
652        {
653            return MAXIMUM_BUFFER_SIZE;
654        }
655        return min( MAXIMUM_BUFFER_SIZE, max( DEFAULT_BUFFER_SIZE, (int) potentialBufferSize ) );
656    }
657
658    // ----------------------------------------------------------------------
659    //
660    // ----------------------------------------------------------------------
661
662    protected void fireTransferProgress( TransferEvent transferEvent, byte[] buffer, int n )
663    {
664        transferEventSupport.fireTransferProgress( transferEvent, buffer, n );
665    }
666
667    protected void fireGetCompleted( Resource resource, File localFile )
668    {
669        long timestamp = System.currentTimeMillis();
670
671        TransferEvent transferEvent =
672            new TransferEvent( this, resource, TransferEvent.TRANSFER_COMPLETED, TransferEvent.REQUEST_GET );
673
674        transferEvent.setTimestamp( timestamp );
675
676        transferEvent.setLocalFile( localFile );
677
678        transferEventSupport.fireTransferCompleted( transferEvent );
679    }
680
681    protected void fireGetStarted( Resource resource, File localFile )
682    {
683        long timestamp = System.currentTimeMillis();
684
685        TransferEvent transferEvent =
686            new TransferEvent( this, resource, TransferEvent.TRANSFER_STARTED, TransferEvent.REQUEST_GET );
687
688        transferEvent.setTimestamp( timestamp );
689
690        transferEvent.setLocalFile( localFile );
691
692        transferEventSupport.fireTransferStarted( transferEvent );
693    }
694
695    protected void fireGetInitiated( Resource resource, File localFile )
696    {
697        long timestamp = System.currentTimeMillis();
698
699        TransferEvent transferEvent =
700            new TransferEvent( this, resource, TransferEvent.TRANSFER_INITIATED, TransferEvent.REQUEST_GET );
701
702        transferEvent.setTimestamp( timestamp );
703
704        transferEvent.setLocalFile( localFile );
705
706        transferEventSupport.fireTransferInitiated( transferEvent );
707    }
708
709    protected void firePutInitiated( Resource resource, File localFile )
710    {
711        long timestamp = System.currentTimeMillis();
712
713        TransferEvent transferEvent =
714            new TransferEvent( this, resource, TransferEvent.TRANSFER_INITIATED, TransferEvent.REQUEST_PUT );
715
716        transferEvent.setTimestamp( timestamp );
717
718        transferEvent.setLocalFile( localFile );
719
720        transferEventSupport.fireTransferInitiated( transferEvent );
721    }
722
723    protected void firePutCompleted( Resource resource, File localFile )
724    {
725        long timestamp = System.currentTimeMillis();
726
727        TransferEvent transferEvent =
728            new TransferEvent( this, resource, TransferEvent.TRANSFER_COMPLETED, TransferEvent.REQUEST_PUT );
729
730        transferEvent.setTimestamp( timestamp );
731
732        transferEvent.setLocalFile( localFile );
733
734        transferEventSupport.fireTransferCompleted( transferEvent );
735    }
736
737    protected void firePutStarted( Resource resource, File localFile )
738    {
739        long timestamp = System.currentTimeMillis();
740
741        TransferEvent transferEvent =
742            new TransferEvent( this, resource, TransferEvent.TRANSFER_STARTED, TransferEvent.REQUEST_PUT );
743
744        transferEvent.setTimestamp( timestamp );
745
746        transferEvent.setLocalFile( localFile );
747
748        transferEventSupport.fireTransferStarted( transferEvent );
749    }
750
751    protected void fireSessionDisconnected()
752    {
753        long timestamp = System.currentTimeMillis();
754
755        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_DISCONNECTED );
756
757        sessionEvent.setTimestamp( timestamp );
758
759        sessionEventSupport.fireSessionDisconnected( sessionEvent );
760    }
761
762    protected void fireSessionDisconnecting()
763    {
764        long timestamp = System.currentTimeMillis();
765
766        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_DISCONNECTING );
767
768        sessionEvent.setTimestamp( timestamp );
769
770        sessionEventSupport.fireSessionDisconnecting( sessionEvent );
771    }
772
773    protected void fireSessionLoggedIn()
774    {
775        long timestamp = System.currentTimeMillis();
776
777        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_LOGGED_IN );
778
779        sessionEvent.setTimestamp( timestamp );
780
781        sessionEventSupport.fireSessionLoggedIn( sessionEvent );
782    }
783
784    protected void fireSessionLoggedOff()
785    {
786        long timestamp = System.currentTimeMillis();
787
788        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_LOGGED_OFF );
789
790        sessionEvent.setTimestamp( timestamp );
791
792        sessionEventSupport.fireSessionLoggedOff( sessionEvent );
793    }
794
795    protected void fireSessionOpened()
796    {
797        long timestamp = System.currentTimeMillis();
798
799        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_OPENED );
800
801        sessionEvent.setTimestamp( timestamp );
802
803        sessionEventSupport.fireSessionOpened( sessionEvent );
804    }
805
806    protected void fireSessionOpening()
807    {
808        long timestamp = System.currentTimeMillis();
809
810        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_OPENING );
811
812        sessionEvent.setTimestamp( timestamp );
813
814        sessionEventSupport.fireSessionOpening( sessionEvent );
815    }
816
817    protected void fireSessionConnectionRefused()
818    {
819        long timestamp = System.currentTimeMillis();
820
821        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_CONNECTION_REFUSED );
822
823        sessionEvent.setTimestamp( timestamp );
824
825        sessionEventSupport.fireSessionConnectionRefused( sessionEvent );
826    }
827
828    protected void fireSessionError( Exception exception )
829    {
830        long timestamp = System.currentTimeMillis();
831
832        SessionEvent sessionEvent = new SessionEvent( this, exception );
833
834        sessionEvent.setTimestamp( timestamp );
835
836        sessionEventSupport.fireSessionError( sessionEvent );
837
838    }
839
840    protected void fireTransferDebug( String message )
841    {
842        transferEventSupport.fireDebug( message );
843    }
844
845    protected void fireSessionDebug( String message )
846    {
847        sessionEventSupport.fireDebug( message );
848    }
849
850    public boolean hasTransferListener( TransferListener listener )
851    {
852        return transferEventSupport.hasTransferListener( listener );
853    }
854
855    public void addTransferListener( TransferListener listener )
856    {
857        transferEventSupport.addTransferListener( listener );
858    }
859
860    public void removeTransferListener( TransferListener listener )
861    {
862        transferEventSupport.removeTransferListener( listener );
863    }
864
865    public void addSessionListener( SessionListener listener )
866    {
867        sessionEventSupport.addSessionListener( listener );
868    }
869
870    public boolean hasSessionListener( SessionListener listener )
871    {
872        return sessionEventSupport.hasSessionListener( listener );
873    }
874
875    public void removeSessionListener( SessionListener listener )
876    {
877        sessionEventSupport.removeSessionListener( listener );
878    }
879
880    protected void fireTransferError( Resource resource, Exception e, int requestType )
881    {
882        TransferEvent transferEvent = new TransferEvent( this, resource, e, requestType );
883        transferEventSupport.fireTransferError( transferEvent );
884    }
885
886
887    public SessionEventSupport getSessionEventSupport()
888    {
889        return sessionEventSupport;
890    }
891
892    public void setSessionEventSupport( SessionEventSupport sessionEventSupport )
893    {
894        this.sessionEventSupport = sessionEventSupport;
895    }
896
897    public TransferEventSupport getTransferEventSupport()
898    {
899        return transferEventSupport;
900    }
901
902    public void setTransferEventSupport( TransferEventSupport transferEventSupport )
903    {
904        this.transferEventSupport = transferEventSupport;
905    }
906
907    /**
908     * This method is used if you are not streaming the transfer, to make sure any listeners dependent on state
909     * (eg checksum observers) succeed.
910     */
911    protected void postProcessListeners( Resource resource, File source, int requestType )
912        throws TransferFailedException
913    {
914        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
915
916        TransferEvent transferEvent = new TransferEvent( this, resource, TransferEvent.TRANSFER_PROGRESS, requestType );
917        transferEvent.setTimestamp( System.currentTimeMillis() );
918        transferEvent.setLocalFile( source );
919
920        InputStream input = null;
921        try
922        {
923            input = new FileInputStream( source );
924
925            while ( true )
926            {
927                int n = input.read( buffer );
928
929                if ( n == -1 )
930                {
931                    break;
932                }
933
934                fireTransferProgress( transferEvent, buffer, n );
935            }
936
937            input.close();
938            input = null;
939        }
940        catch ( IOException e )
941        {
942            fireTransferError( resource, e, requestType );
943
944            throw new TransferFailedException( "Failed to post-process the source file", e );
945        }
946        finally
947        {
948            IOUtil.close( input );
949        }
950    }
951
952    public void putDirectory( File sourceDirectory, String destinationDirectory )
953        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
954    {
955        throw new UnsupportedOperationException( "The wagon you are using has not implemented putDirectory()" );
956    }
957
958    public boolean supportsDirectoryCopy()
959    {
960        return false;
961    }
962
963    protected static String getPath( String basedir, String dir )
964    {
965        String path;
966        path = basedir;
967        if ( !basedir.endsWith( "/" ) && !dir.startsWith( "/" ) )
968        {
969            path += "/";
970        }
971        path += dir;
972        return path;
973    }
974
975    public boolean isInteractive()
976    {
977        return interactive;
978    }
979
980    public void setInteractive( boolean interactive )
981    {
982        this.interactive = interactive;
983    }
984
985    public List<String> getFileList( String destinationDirectory )
986        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
987    {
988        throw new UnsupportedOperationException( "The wagon you are using has not implemented getFileList()" );
989    }
990
991    public boolean resourceExists( String resourceName )
992        throws TransferFailedException, AuthorizationException
993    {
994        throw new UnsupportedOperationException( "The wagon you are using has not implemented resourceExists()" );
995    }
996
997    protected ProxyInfo getProxyInfo( String protocol, String host )
998    {
999        if ( proxyInfoProvider != null )
1000        {
1001            ProxyInfo proxyInfo = proxyInfoProvider.getProxyInfo( protocol );
1002            if ( !ProxyUtils.validateNonProxyHosts( proxyInfo, host ) )
1003            {
1004                return proxyInfo;
1005            }
1006        }
1007        return null;
1008    }
1009
1010    public RepositoryPermissions getPermissionsOverride()
1011    {
1012        return permissionsOverride;
1013    }
1014
1015    public void setPermissionsOverride( RepositoryPermissions permissionsOverride )
1016    {
1017        this.permissionsOverride = permissionsOverride;
1018    }
1019
1020    public void setReadTimeout( int readTimeout )
1021    {
1022        this.readTimeout = readTimeout;
1023    }
1024
1025    public int getReadTimeout()
1026    {
1027        return this.readTimeout;
1028    }
1029}