package org.openslx.filetransfer; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.net.Socket; import javax.net.ssl.SSLContext; import org.apache.log4j.Logger; public class Uploader extends Transfer { private static final Logger log = Logger.getLogger( Uploader.class ); /***********************************************************************/ /** * Actively establish upload connection to given peer. * * @param host Host name or address to connect to * @param port Port to connect to * @param context ssl context for establishing a secure connection * @throws IOException */ public Uploader( String host, int port, int readTimeoutMs, SSLContext context, String token ) throws IOException { super( host, port, readTimeoutMs, context, log ); outStream.writeByte( 'U' ); if ( !sendToken( token ) || !sendEndOfMeta() ) throw new IOException( "Sending token failed" ); } /***********************************************************************/ /** * Constructor for master uploader. * Sends back the socket for datatransfer. * * @throws IOException */ public Uploader( Socket socket ) throws IOException { super( socket, log ); } /***********************************************************************/ /** * Method for sending File with filename. * * @param filename */ public boolean upload( String filename ) { return upload( filename, null ); } @SuppressWarnings( "resource" ) public boolean upload( String filename, UploadStatusCallback callback ) { if ( shouldGetToken() ) { log.error( "You didn't call getToken yet!" ); return false; } RandomAccessFile file = null; try { try { file = new RandomAccessFile( new File( filename ), "r" ); } catch ( FileNotFoundException e ) { this.close( "Could not open given file for reading.", callback, true ); return false; } while ( !Thread.currentThread().isInterrupted() ) { // Loop as long as remote peer is requesting chunks from this file // Read meta data of remote peer - either new range, or it's telling us it's done MetaData meta = readMetaData(); if ( meta == null ) { this.close( "Did not get meta data from remote peer.", callback, true ); return false; } if ( meta.isDone() ) // Download complete? break; // Not complete, so there must be another range request FileRange requestedRange = meta.getRange(); if ( requestedRange == null ) { this.close( "Peer did not include RANGE in meta data.", callback, true ); return false; } // Range inside file? try { if ( requestedRange.endOffset > file.length() ) { this.close( "Requested range is larger than file size, aborting.", callback, true ); return false; } } catch ( IOException e ) { this.close( "Could not get current length of file " + filename, callback, false ); return false; } // Seek to requested chunk try { file.seek( requestedRange.startOffset ); } catch ( IOException e ) { this.close( "Could not seek to start of requested range in given file (" + requestedRange.startOffset + ")", callback, true ); return false; } // Send confirmation of range we're about to send try { long ptr = file.getFilePointer(); if ( !sendRange( ptr, ptr + requestedRange.getLength() ) || !sendEndOfMeta() ) { this.close( "Could not send range confirmation" ); return false; } } catch ( IOException e ) { this.close( "Could not determine current position in file " + filename ); return false; } // Finally send requested chunk byte[] data = new byte[ 500000 ]; // 500kb int hasRead = 0; int length = requestedRange.getLength(); while ( hasRead < length ) { int ret; try { ret = file.read( data, 0, Math.min( length - hasRead, data.length ) ); } catch ( IOException e ) { this.close( "Error reading from file ", callback, true ); return false; } if ( ret == -1 ) { this.close( "Error occured in Uploader.sendFile() while reading from File to send.", callback, true ); return false; } hasRead += ret; try { outStream.write( data, 0, ret ); } catch ( IOException e ) { this.close( "Sending payload failed" ); return false; } if ( callback != null ) callback.uploadProgress( ret ); } } } finally { Transfer.safeClose( file, transferSocket ); } return true; } }