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.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; import org.apache.log4j.Logger; public class Uploader { // Some member variables. private SSLSocketFactory sslSocketFactory; private SSLSocket satelliteSocket; private DataOutputStream dataToServer; private DataInputStream dataFromServer; private String TOKEN = null; private String RANGE = null; private String ERROR = null; private static Logger log = Logger.getLogger( Uploader.class ); /***********************************************************************/ /** * Constructor for satellite uploader. * Tries to connect to specific ip and port and sending type of action. * * @param ip * @param port * @throws IOException * @throws KeyStoreException * @throws CertificateException * @throws NoSuchAlgorithmException * @throws KeyManagementException * @throws UnknownHostException */ public Uploader( String ip, int port, SSLContext context ) { try { sslSocketFactory = context.getSocketFactory(); satelliteSocket = (SSLSocket)sslSocketFactory.createSocket( ip, port ); satelliteSocket.setSoTimeout( 2000 ); // set socket timeout. dataToServer = new DataOutputStream( satelliteSocket.getOutputStream() ); dataToServer.writeByte( 'U' ); dataFromServer = new DataInputStream( satelliteSocket.getInputStream() ); } catch ( Exception e ) { e.printStackTrace(); } } /***********************************************************************/ /** * Constructor for master uploader. * Sends back the socket for datatransfer. * * @throws IOException */ public Uploader( SSLSocket socket ) { try { satelliteSocket = socket; dataToServer = new DataOutputStream( satelliteSocket.getOutputStream() ); dataFromServer = new DataInputStream( satelliteSocket.getInputStream() ); } catch ( IOException e ) { e.printStackTrace(); } } /***********************************************************************/ /** * Method for sending token from satellite to master. * Needfull for getting to know what should happens over connection. * * @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(); return false; } 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 Uploader failed..." ); return false; } return true; } /***********************************************************************/ /** * Getter for TOKEN. */ public String getToken() { return TOKEN; } /***********************************************************************/ /** * Method to send range of the file, which should be uploaded. * Helpful for knowing how much was already uploaded if * connection aborts. * * @param actual * @param l */ public Boolean sendRange( long actual, long l ) { try { RANGE = actual + ":" + l; String sendRange = "RANGE=" + RANGE; byte[] data = sendRange.getBytes( StandardCharsets.UTF_8 ); dataToServer.writeByte( data.length ); dataToServer.write( data ); dataToServer.writeByte( 0 ); } 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; } /***********************************************************************/ /** * Getter for RANGE. * * @return */ 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() { if ( getStartOfRange() == -1 || getEndOfRange() == -1 ) { return -1; } 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 ]; // First get length. dataFromServer.read( incoming, 0, 1 ); int length = incoming[0] & 0xFF; if ( length == 0 ) // Stop if 0 was read. 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 in reading Metadata occured!" ); return false; } hasRead += ret; } String data = new String( incoming, "UTF-8" ); String[] splitted = data.split( "=" ); 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 ... close connection" ); this.close(); } catch ( Exception e ) { e.printStackTrace(); return false; } return true; } /***********************************************************************/ /** * Method for sending File with filename. * * @param filename */ public Boolean sendFile( String filename ) { RandomAccessFile file = null; try { file = new RandomAccessFile( new File( filename ), "r" ); if ( getStartOfRange() == -1 ) { return false; } file.seek( getStartOfRange() ); byte[] data = new byte[ 4000 ]; int hasRead = 0; int length = getDiffOfRange(); // System.out.println( "diff of Range: " + length ); while ( hasRead < length ) { int ret = file.read( data, 0, Math.min( length - hasRead, data.length ) ); if ( ret == -1 ) { log.info( "Error occured in Uploader.sendFile()," + " while reading from File to send." ); return false; } hasRead += ret; dataToServer.write( data, 0, ret ); } } catch ( SocketTimeoutException ste ) { ste.printStackTrace(); sendErrorCode( "timeout" ); log.info( "Socket timeout occured ... close connection." ); this.close(); return false; } catch ( IOException ioe ) { ioe.printStackTrace(); readMetaData(); if ( ERROR != null ) { if ( ERROR == "timeout" ) { log.info( "Socket timeout occured ... close connection." ); this.close(); } } log.info( "Sending RANGE " + getStartOfRange() + ":" + getEndOfRange() + " of File " + filename + " failed..." ); return false; } catch ( Exception e ) { e.printStackTrace(); return false; } finally { try { file.close(); } catch ( IOException e ) { } } 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 upload 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(); } } }