package org.openslx.filetransfer;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import org.apache.log4j.Logger;
public class Downloader
{
// Some instance variables.
private SSLSocketFactory sslSocketFactory;
private SSLSocket satelliteSocket;
private DataOutputStream dataToServer;
private DataInputStream dataFromServer;
private String TOKEN = null;
private String RANGE = null;
private String outputFilename = null;
private String ERROR = null;
private static Logger log = Logger.getLogger( Downloader.class );
/***********************************************************************/
/**
* Constructor for satellite downloader.
* Tries to connect to specific ip and port and sending type of action.
*
* @param ip
* @param port
*/
public Downloader( String ip, int port, SSLContext context )
{
try {
// create socket.
sslSocketFactory = context.getSocketFactory();
satelliteSocket = (SSLSocket)sslSocketFactory.createSocket( ip, port );
satelliteSocket.setSoTimeout( 2000 ); // set socket timeout.
dataToServer = new DataOutputStream( satelliteSocket.getOutputStream() );
dataToServer.writeByte( 'D' );
dataFromServer = new DataInputStream( satelliteSocket.getInputStream() );
} catch ( Exception e ) {
e.printStackTrace();
}
}
/***********************************************************************/
/**
* Constructor for master downloader.
* Given parameter is the socket over which the transfer is going.
*
* @param socket
*/
public Downloader( SSLSocket socket )
{
try {
satelliteSocket = socket;
dataToServer = new DataOutputStream( satelliteSocket.getOutputStream() );
dataFromServer = new DataInputStream( satelliteSocket.getInputStream() );
} catch ( IOException e ) {
e.printStackTrace();
}
}
/***********************************************************************/
/**
* Method for setting outputFilename.
*
* @param filename
*/
public void setOutputFilename( String filename )
{
outputFilename = filename;
}
/***********************************************************************/
/**
* Method for getting outputFilename.
*
* @return outputFilename
*/
public String getOutputFilename()
{
return outputFilename;
}
/***********************************************************************/
/**
* Method for sending token for identification from satellite to master.
*
* @param t
*/
public Boolean sendToken( String token )
{
try {
TOKEN = token;
String sendToken = "TOKEN=" + TOKEN;
byte[] data = sendToken.getBytes( StandardCharsets.UTF_8 );
dataToServer.writeByte( data.length );
dataToServer.write( data );
} catch ( SocketTimeoutException ste ) {
ste.printStackTrace();
log.info( "Socket timeout occured ... close connection." );
this.close();
} catch ( IOException e ) {
e.printStackTrace();
readMetaData();
if ( ERROR != null ) {
if ( ERROR == "timeout" ) {
log.info( "Socket timeout occured ... close connection." );
this.close();
}
}
log.info( "Sending TOKEN in Downloader failed..." );
return false;
}
return true;
}
/***********************************************************************/
/**
* Method to send range of the file, which should be uploaded.
* Helpful for knowing how much was already uploaded if
* connection aborts.
*
* @param a
* @param b
*/
public Boolean sendRange( int a, int b )
{
try {
RANGE = a + ":" + b;
String sendRange = "RANGE=" + RANGE;
byte[] data = sendRange.getBytes( StandardCharsets.UTF_8 );
dataToServer.writeByte( data.length );
dataToServer.write( data );
} catch ( SocketTimeoutException ste ) {
ste.printStackTrace();
log.info( "Socket timeout occured ... close connection." );
this.close();
} catch ( IOException e ) {
e.printStackTrace();
readMetaData();
if ( ERROR != null ) {
if ( ERROR == "timeout" ) {
log.info( "Socket timeout occured ... close connection." );
this.close();
}
}
log.info( "Sending RANGE in Uploader failed..." );
return false;
}
return true;
}
/***********************************************************************/
/**
* Method for reading incoming token for identification.
*
*/
public String getToken()
{
return TOKEN;
}
/***********************************************************************/
/**
* Method for reading range of file, which is downloaded.
* Helpful for knowing how much is already downloaded if connection aborts.
*/
public String getRange()
{
return RANGE;
}
/***********************************************************************/
/**
* Getter for beginning of RANGE.
*
* @return
*/
public int getStartOfRange()
{
if ( RANGE != null ) {
String[] splitted = RANGE.split( ":" );
return Integer.parseInt( splitted[0] );
}
return -1;
}
/***********************************************************************/
/**
* Getter for end of RANGE.
*
* @return
*/
public int getEndOfRange()
{
if ( RANGE != null ) {
String[] splitted = RANGE.split( ":" );
return Integer.parseInt( splitted[1] );
}
return -1;
}
/***********************************************************************/
/**
* Method for returning difference of current Range.
*
* @return
*/
public int getDiffOfRange()
{
int diff = Math.abs( getEndOfRange() - getStartOfRange() );
return diff;
}
/***********************************************************************/
/**
* Method for reading MetaData, like TOKEN and FileRange.
* Split incoming bytes after first '=' and store value to specific
* variable.
*
*/
public Boolean readMetaData()
{
try {
while ( true ) {
byte[] incoming = new byte[ 255 ]; // TODO: problematische Größe.
// First get length.
int retLengthByte;
retLengthByte = dataFromServer.read( incoming, 0, 1 );
if (retLengthByte != 1) {
this.close();
return false;
}
int length = incoming[0] & 0xFF;
log.info( "length (downloader): " + length );
if ( length == 0 )
break;
/**
* Read the next available bytes and split by '=' for
* getting TOKEN or RANGE.
*/
int hasRead = 0;
while ( hasRead < length ) {
int ret = dataFromServer.read( incoming, hasRead, length - hasRead );
if ( ret == -1 ) {
log.info( "Error occured while reading Metadata." );
return false;
}
hasRead += ret;
}
String data = new String( incoming, 0, length, "UTF-8" );
// System.out.println(data);
String[] splitted = data.split( "=" );
// System.out.println("splitted[0]: " + splitted[0]);
// System.out.println("splitted[1]: " + splitted[1]);
if ( splitted[0] != null && splitted[0].equals( "TOKEN" ) ) {
if ( splitted[1] != null )
TOKEN = splitted[1];
log.info( "TOKEN: " + TOKEN );
}
else if ( splitted[0].equals( "RANGE" ) ) {
if ( splitted[1] != null )
RANGE = splitted[1];
log.info( "RANGE: '" + RANGE + "'" );
}
else if ( splitted[0].equals( "ERROR" ) ) {
if ( splitted[1] != null )
ERROR = splitted[1];
log.info( "ERROR: " + ERROR );
return false;
}
}
} catch ( SocketTimeoutException ste ) {
ste.printStackTrace();
sendErrorCode( "timeout" );
log.info( "Socket Timeout occured in Downloader." );
this.close();
return false;
} catch ( Exception e ) {
e.printStackTrace();
this.close();
return false;
}
return true;
}
/***********************************************************************/
/**
* Method for reading Binary. Reading the current Range of incoming binary.
*
*/
public Boolean readBinary()
{
RandomAccessFile file = null;
try {
int length = getDiffOfRange();
byte[] incoming = new byte[ 4000 ];
int hasRead = 0;
file = new RandomAccessFile( new File( outputFilename ), "rw" );
file.seek( getStartOfRange() );
while ( hasRead < length ) {
int ret = dataFromServer.read( incoming, 0, Math.min( length - hasRead, incoming.length ) );
// log.info("hasRead: " + hasRead + " length: " + length + " ret: " + ret);
if ( ret == -1 ) {
log.info( "Error occured in Downloader.readBinary(),"
+ " while reading binary." );
return false;
}
hasRead += ret;
file.write( incoming, 0, ret );
}
} catch ( SocketTimeoutException ste ) {
ste.printStackTrace();
sendErrorCode( "timeout" );
log.info( "Socket timeout occured ... close connection." );
this.close();
} catch ( Exception e ) {
e.printStackTrace();
log.info( "Reading RANGE " + getStartOfRange() + ":" + getEndOfRange()
+ " of file failed..." );
this.close();
return false;
} finally {
if ( file != null ) {
try {
file.close();
} catch ( IOException e ) {
e.printStackTrace();
}
}
}
return true;
}
/***********************************************************************/
/**
* Method for sending error Code to server. For example in case of wrong
* token, send code for wrong token.
*
*/
public Boolean sendErrorCode( String errString )
{
try {
String sendError = "ERROR=" + errString;
byte[] data = sendError.getBytes( StandardCharsets.UTF_8 );
dataToServer.writeByte( data.length );
dataToServer.write( data );
} catch ( IOException e ) {
e.printStackTrace();
this.close();
return false;
}
return true;
}
/***********************************************************************/
/**
* Method for closing connection, if download has finished.
*
*/
public void close()
{
try {
if ( satelliteSocket != null ) {
this.satelliteSocket.close();
satelliteSocket = null;
}
if ( dataFromServer != null )
dataFromServer.close();
if ( dataToServer != null )
dataToServer.close();
} catch ( IOException e ) {
e.printStackTrace();
}
}
}