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