summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/filetransfer/Downloader.java
blob: 298bcfc53bcc7850e61c011f250f716fa1d93378 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package org.openslx.filetransfer;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Map;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;

import org.apache.log4j.Logger;

public class Downloader extends Transfer
{

	private static final Logger log = Logger.getLogger( Downloader.class );

	/***********************************************************************/
	/**
	 * Actively initiate a connection to a remote peer for downloading.
	 * 
	 * @param host Host name or address to connect to
	 * @param port Port to connect to
	 * @throws IOException
	 */
	public Downloader( String host, int port, SSLContext context, String token ) throws IOException
	{
		super( host, port, context, log );
		outStream.writeByte( 'D' );
		if ( !sendToken( token ) || !sendEndOfMeta() )
			throw new IOException( "Sending token failed" );
	}

	/***********************************************************************/
	/**
	 * Constructor used by Listener to create an incoming download connection.
	 * 
	 * @param socket established connection to peer which requested an upload.
	 * @throws IOException
	 */
	protected Downloader( SSLSocket socket ) throws IOException
	{
		super( socket, log );
	}

	public boolean download( String destinationFile, WantRangeCallback callback )
	{
		if ( shouldGetToken() ) {
			log.error( "You didn't call getToken yet!" );
			return false;
		}
		FileRange requestedRange;
		RandomAccessFile file = null;
		try {
			try {
				file = new RandomAccessFile( new File( destinationFile ), "rw" );
			} catch ( FileNotFoundException e2 ) {
				log.error( "Cannot open " + destinationFile + " for writing." );
				return false;
			}
			while ( ( requestedRange = callback.get() ) != null ) {
				if ( requestedRange.startOffset < 0 || requestedRange.startOffset >= requestedRange.endOffset ) {
					log.error( "Callback supplied bad range (" + requestedRange.startOffset + " to " + requestedRange.endOffset + ")" );
					return false;
				}
				// Send range request
				if ( !sendRange( requestedRange.startOffset, requestedRange.endOffset ) || !sendEndOfMeta() ) {
					log.error( "Could not send next range request, download failed." );
					return false;
				}
				// See if remote peer acknowledges range request
				MetaData meta = readMetaData();
				if ( meta == null ) {
					log.error( "Did not receive meta data from uploading remote peer after requesting range, aborting." );
					return false;
				}
				FileRange remoteRange = meta.getRange();
				if ( remoteRange == null || !remoteRange.equals( requestedRange ) ) {
					log.error( "Confirmed range by remote peer does not match requested range, aborting download." );
					return false;
				}
				// Receive requested range
				int chunkLength = requestedRange.getLength();
				byte[] incoming = new byte[ 500000 ]; // 500kb
				int hasRead = 0;
				try {
					file.seek( requestedRange.startOffset );
				} catch ( IOException e1 ) {
					log.error( "Could not seek to " + requestedRange.startOffset + " in " + destinationFile + ". Disk full?" );
					return false;
				}
				while ( hasRead < chunkLength ) {
					int ret;
					try {
						ret = dataFromServer.read( incoming, 0, Math.min( chunkLength - hasRead, incoming.length ) );
					} catch ( IOException e ) {
						log.error( "Could not read payload from socket" );
						sendErrorCode( "payload read error" );
						return false;
					}
					if ( ret == -1 ) {
						log.info( "Remote peer unexpectedly closed the connection." );
						return false;
					}
					hasRead += ret;
					try {
						file.write( incoming, 0, ret );
					} catch ( IOException e ) {
						log.error( "Could not write to " + destinationFile + ". Disk full?" );
						return false;
					}
				}
			}
			sendDone();
			sendEndOfMeta();
		} finally {
			if ( file != null )
				try {
					file.close();
				} catch ( IOException e ) {
				}
			this.close( null );
		}
		return true;
	}

}