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 int numberOfBufferSegments = (int)
647            numberOfBytes / ( BUFFER_SEGMENT_SIZE * MINIMUM_AMOUNT_OF_TRANSFER_CHUNKS );
648        final int potentialBufferSize = numberOfBufferSegments * BUFFER_SEGMENT_SIZE;
649        return min( MAXIMUM_BUFFER_SIZE, max( DEFAULT_BUFFER_SIZE, potentialBufferSize ) );
650    }
651
652    // ----------------------------------------------------------------------
653    //
654    // ----------------------------------------------------------------------
655
656    protected void fireTransferProgress( TransferEvent transferEvent, byte[] buffer, int n )
657    {
658        transferEventSupport.fireTransferProgress( transferEvent, buffer, n );
659    }
660
661    protected void fireGetCompleted( Resource resource, File localFile )
662    {
663        long timestamp = System.currentTimeMillis();
664
665        TransferEvent transferEvent =
666            new TransferEvent( this, resource, TransferEvent.TRANSFER_COMPLETED, TransferEvent.REQUEST_GET );
667
668        transferEvent.setTimestamp( timestamp );
669
670        transferEvent.setLocalFile( localFile );
671
672        transferEventSupport.fireTransferCompleted( transferEvent );
673    }
674
675    protected void fireGetStarted( Resource resource, File localFile )
676    {
677        long timestamp = System.currentTimeMillis();
678
679        TransferEvent transferEvent =
680            new TransferEvent( this, resource, TransferEvent.TRANSFER_STARTED, TransferEvent.REQUEST_GET );
681
682        transferEvent.setTimestamp( timestamp );
683
684        transferEvent.setLocalFile( localFile );
685
686        transferEventSupport.fireTransferStarted( transferEvent );
687    }
688
689    protected void fireGetInitiated( Resource resource, File localFile )
690    {
691        long timestamp = System.currentTimeMillis();
692
693        TransferEvent transferEvent =
694            new TransferEvent( this, resource, TransferEvent.TRANSFER_INITIATED, TransferEvent.REQUEST_GET );
695
696        transferEvent.setTimestamp( timestamp );
697
698        transferEvent.setLocalFile( localFile );
699
700        transferEventSupport.fireTransferInitiated( transferEvent );
701    }
702
703    protected void firePutInitiated( Resource resource, File localFile )
704    {
705        long timestamp = System.currentTimeMillis();
706
707        TransferEvent transferEvent =
708            new TransferEvent( this, resource, TransferEvent.TRANSFER_INITIATED, TransferEvent.REQUEST_PUT );
709
710        transferEvent.setTimestamp( timestamp );
711
712        transferEvent.setLocalFile( localFile );
713
714        transferEventSupport.fireTransferInitiated( transferEvent );
715    }
716
717    protected void firePutCompleted( Resource resource, File localFile )
718    {
719        long timestamp = System.currentTimeMillis();
720
721        TransferEvent transferEvent =
722            new TransferEvent( this, resource, TransferEvent.TRANSFER_COMPLETED, TransferEvent.REQUEST_PUT );
723
724        transferEvent.setTimestamp( timestamp );
725
726        transferEvent.setLocalFile( localFile );
727
728        transferEventSupport.fireTransferCompleted( transferEvent );
729    }
730
731    protected void firePutStarted( Resource resource, File localFile )
732    {
733        long timestamp = System.currentTimeMillis();
734
735        TransferEvent transferEvent =
736            new TransferEvent( this, resource, TransferEvent.TRANSFER_STARTED, TransferEvent.REQUEST_PUT );
737
738        transferEvent.setTimestamp( timestamp );
739
740        transferEvent.setLocalFile( localFile );
741
742        transferEventSupport.fireTransferStarted( transferEvent );
743    }
744
745    protected void fireSessionDisconnected()
746    {
747        long timestamp = System.currentTimeMillis();
748
749        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_DISCONNECTED );
750
751        sessionEvent.setTimestamp( timestamp );
752
753        sessionEventSupport.fireSessionDisconnected( sessionEvent );
754    }
755
756    protected void fireSessionDisconnecting()
757    {
758        long timestamp = System.currentTimeMillis();
759
760        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_DISCONNECTING );
761
762        sessionEvent.setTimestamp( timestamp );
763
764        sessionEventSupport.fireSessionDisconnecting( sessionEvent );
765    }
766
767    protected void fireSessionLoggedIn()
768    {
769        long timestamp = System.currentTimeMillis();
770
771        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_LOGGED_IN );
772
773        sessionEvent.setTimestamp( timestamp );
774
775        sessionEventSupport.fireSessionLoggedIn( sessionEvent );
776    }
777
778    protected void fireSessionLoggedOff()
779    {
780        long timestamp = System.currentTimeMillis();
781
782        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_LOGGED_OFF );
783
784        sessionEvent.setTimestamp( timestamp );
785
786        sessionEventSupport.fireSessionLoggedOff( sessionEvent );
787    }
788
789    protected void fireSessionOpened()
790    {
791        long timestamp = System.currentTimeMillis();
792
793        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_OPENED );
794
795        sessionEvent.setTimestamp( timestamp );
796
797        sessionEventSupport.fireSessionOpened( sessionEvent );
798    }
799
800    protected void fireSessionOpening()
801    {
802        long timestamp = System.currentTimeMillis();
803
804        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_OPENING );
805
806        sessionEvent.setTimestamp( timestamp );
807
808        sessionEventSupport.fireSessionOpening( sessionEvent );
809    }
810
811    protected void fireSessionConnectionRefused()
812    {
813        long timestamp = System.currentTimeMillis();
814
815        SessionEvent sessionEvent = new SessionEvent( this, SessionEvent.SESSION_CONNECTION_REFUSED );
816
817        sessionEvent.setTimestamp( timestamp );
818
819        sessionEventSupport.fireSessionConnectionRefused( sessionEvent );
820    }
821
822    protected void fireSessionError( Exception exception )
823    {
824        long timestamp = System.currentTimeMillis();
825
826        SessionEvent sessionEvent = new SessionEvent( this, exception );
827
828        sessionEvent.setTimestamp( timestamp );
829
830        sessionEventSupport.fireSessionError( sessionEvent );
831
832    }
833
834    protected void fireTransferDebug( String message )
835    {
836        transferEventSupport.fireDebug( message );
837    }
838
839    protected void fireSessionDebug( String message )
840    {
841        sessionEventSupport.fireDebug( message );
842    }
843
844    public boolean hasTransferListener( TransferListener listener )
845    {
846        return transferEventSupport.hasTransferListener( listener );
847    }
848
849    public void addTransferListener( TransferListener listener )
850    {
851        transferEventSupport.addTransferListener( listener );
852    }
853
854    public void removeTransferListener( TransferListener listener )
855    {
856        transferEventSupport.removeTransferListener( listener );
857    }
858
859    public void addSessionListener( SessionListener listener )
860    {
861        sessionEventSupport.addSessionListener( listener );
862    }
863
864    public boolean hasSessionListener( SessionListener listener )
865    {
866        return sessionEventSupport.hasSessionListener( listener );
867    }
868
869    public void removeSessionListener( SessionListener listener )
870    {
871        sessionEventSupport.removeSessionListener( listener );
872    }
873
874    protected void fireTransferError( Resource resource, Exception e, int requestType )
875    {
876        TransferEvent transferEvent = new TransferEvent( this, resource, e, requestType );
877        transferEventSupport.fireTransferError( transferEvent );
878    }
879
880
881    public SessionEventSupport getSessionEventSupport()
882    {
883        return sessionEventSupport;
884    }
885
886    public void setSessionEventSupport( SessionEventSupport sessionEventSupport )
887    {
888        this.sessionEventSupport = sessionEventSupport;
889    }
890
891    public TransferEventSupport getTransferEventSupport()
892    {
893        return transferEventSupport;
894    }
895
896    public void setTransferEventSupport( TransferEventSupport transferEventSupport )
897    {
898        this.transferEventSupport = transferEventSupport;
899    }
900
901    /**
902     * This method is used if you are not streaming the transfer, to make sure any listeners dependent on state
903     * (eg checksum observers) succeed.
904     */
905    protected void postProcessListeners( Resource resource, File source, int requestType )
906        throws TransferFailedException
907    {
908        byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
909
910        TransferEvent transferEvent = new TransferEvent( this, resource, TransferEvent.TRANSFER_PROGRESS, requestType );
911        transferEvent.setTimestamp( System.currentTimeMillis() );
912        transferEvent.setLocalFile( source );
913
914        InputStream input = null;
915        try
916        {
917            input = new FileInputStream( source );
918
919            while ( true )
920            {
921                int n = input.read( buffer );
922
923                if ( n == -1 )
924                {
925                    break;
926                }
927
928                fireTransferProgress( transferEvent, buffer, n );
929            }
930
931            input.close();
932            input = null;
933        }
934        catch ( IOException e )
935        {
936            fireTransferError( resource, e, requestType );
937
938            throw new TransferFailedException( "Failed to post-process the source file", e );
939        }
940        finally
941        {
942            IOUtil.close( input );
943        }
944    }
945
946    public void putDirectory( File sourceDirectory, String destinationDirectory )
947        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
948    {
949        throw new UnsupportedOperationException( "The wagon you are using has not implemented putDirectory()" );
950    }
951
952    public boolean supportsDirectoryCopy()
953    {
954        return false;
955    }
956
957    protected static String getPath( String basedir, String dir )
958    {
959        String path;
960        path = basedir;
961        if ( !basedir.endsWith( "/" ) && !dir.startsWith( "/" ) )
962        {
963            path += "/";
964        }
965        path += dir;
966        return path;
967    }
968
969    public boolean isInteractive()
970    {
971        return interactive;
972    }
973
974    public void setInteractive( boolean interactive )
975    {
976        this.interactive = interactive;
977    }
978
979    public List<String> getFileList( String destinationDirectory )
980        throws TransferFailedException, ResourceDoesNotExistException, AuthorizationException
981    {
982        throw new UnsupportedOperationException( "The wagon you are using has not implemented getFileList()" );
983    }
984
985    public boolean resourceExists( String resourceName )
986        throws TransferFailedException, AuthorizationException
987    {
988        throw new UnsupportedOperationException( "The wagon you are using has not implemented resourceExists()" );
989    }
990
991    protected ProxyInfo getProxyInfo( String protocol, String host )
992    {
993        if ( proxyInfoProvider != null )
994        {
995            ProxyInfo proxyInfo = proxyInfoProvider.getProxyInfo( protocol );
996            if ( !ProxyUtils.validateNonProxyHosts( proxyInfo, host ) )
997            {
998                return proxyInfo;
999            }
1000        }
1001        return null;
1002    }
1003
1004    public RepositoryPermissions getPermissionsOverride()
1005    {
1006        return permissionsOverride;
1007    }
1008
1009    public void setPermissionsOverride( RepositoryPermissions permissionsOverride )
1010    {
1011        this.permissionsOverride = permissionsOverride;
1012    }
1013
1014    public void setReadTimeout( int readTimeout )
1015    {
1016        this.readTimeout = readTimeout;
1017    }
1018
1019    public int getReadTimeout()
1020    {
1021        return this.readTimeout;
1022    }
1023}