diff options
Diffstat (limited to 'src/main/java/org/openslx/filetransfer/Transfer.java')
-rw-r--r-- | src/main/java/org/openslx/filetransfer/Transfer.java | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/src/main/java/org/openslx/filetransfer/Transfer.java b/src/main/java/org/openslx/filetransfer/Transfer.java new file mode 100644 index 0000000..34868e3 --- /dev/null +++ b/src/main/java/org/openslx/filetransfer/Transfer.java @@ -0,0 +1,327 @@ +package org.openslx.filetransfer; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +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 abstract class Transfer +{ + protected final SSLSocketFactory sslSocketFactory; + protected final SSLSocket satelliteSocket; + protected final DataOutputStream dataToServer; + protected final DataInputStream dataFromServer; + protected String TOKEN = null; + protected int[] RANGE = null; + protected String ERROR = null; + + protected final Logger log; + + protected Transfer( String ip, int port, SSLContext context, Logger log ) throws IOException + { + this.log = log; + // create socket. + sslSocketFactory = context.getSocketFactory(); + + satelliteSocket = (SSLSocket)sslSocketFactory.createSocket( ip, port ); + satelliteSocket.setSoTimeout( 2000 ); // set socket timeout. + + dataToServer = new DataOutputStream( satelliteSocket.getOutputStream() ); + dataFromServer = new DataInputStream( satelliteSocket.getInputStream() ); + } + + protected Transfer( SSLSocket socket, Logger log ) throws IOException + { + this.log = log; + satelliteSocket = socket; + dataToServer = new DataOutputStream( satelliteSocket.getOutputStream() ); + dataFromServer = new DataInputStream( satelliteSocket.getInputStream() ); + sslSocketFactory = null; + } + + protected boolean sendRange( int startOffset, int endOffset ) + { + if ( RANGE != null ) { + log.warn( "Range already set!" ); + return false; + } + try { + sendKeyValuePair( "RANGE", startOffset + ":" + endOffset ); + } 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 sending token for identification from satellite to master. + * + * @param token The token to send + */ + public boolean sendToken( String token ) + { + if ( TOKEN != null ) { + log.warn( "Trying to send token while a token is already set! Ignoring..." ); + return false; + } + TOKEN = token; + try { + sendKeyValuePair( "TOKEN", TOKEN ); + } 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 for reading incoming token for identification. + * + */ + public String getToken() + { + return TOKEN; + } + + private boolean parseRange( String range ) + { + if ( range == null ) + return true; + if ( RANGE != null ) { + log.warn( "Warning: RANGE already set when trying to parse from " + range ); + return false; + } + String parts[] = range.split( ":", 2 ); + int ret[] = new int[ 2 ]; + try { + ret[0] = Integer.parseInt( parts[0] ); + ret[1] = Integer.parseInt( parts[1] ); + } catch ( Throwable t ) { + log.warn( "Not parsable range: '" + range + "'" ); + return false; + } + if ( ret[1] <= ret[0] ) { + log.warn( "Invalid range. Start >= end" ); + return false; + } + RANGE = ret; + return true; + } + + /***********************************************************************/ + /** + * Getter for beginning of RANGE. + * + * @return + */ + public int getStartOfRange() + { + if ( RANGE != null ) { + return RANGE[0]; + } + return -1; + } + + /***********************************************************************/ + /** + * Getter for end of RANGE. + * + * @return + */ + public int getEndOfRange() + { + if ( RANGE != null ) { + return RANGE[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. + * + * @return true on success, false if reading failed + */ + public boolean readMetaData() + { + try { + while ( true ) { + byte[] incoming = new byte[ 255 ]; + + // First get length. + int retLengthByte; + retLengthByte = dataFromServer.read( incoming, 0, 1 ); + if ( retLengthByte != 1 ) { + this.close(); + return false; + } + + int length = incoming[0] & 0xFF; + log.debug( "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.warn( "Error occured while reading Metadata." ); + this.close(); + return false; + } + hasRead += ret; + } + + String data = new String( incoming, 0, length, StandardCharsets.UTF_8 ); + + String[] splitted = data.split( "=", 2 ); + if ( splitted.length != 2 ) { + log.warn( "Invalid key value pair received (" + data + ")" ); + continue; + } + if ( splitted[0].equals( "TOKEN" ) ) { + if ( TOKEN != null ) { + log.warn( "Received a token when a token is already set!" ); + this.close(); + return false; + } + TOKEN = splitted[1]; + log.debug( "TOKEN: " + TOKEN ); + } + else if ( splitted[0].equals( "RANGE" ) ) { + if ( !parseRange( splitted[1] ) ) { + this.close(); + return false; + } + log.debug( "RANGE: '" + splitted[1] + "'" ); + } + else if ( splitted[0].equals( "ERROR" ) ) { + ERROR = splitted[1]; + log.debug( "ERROR: " + ERROR ); + } + } + } 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; + } + + private void sendKeyValuePair( String key, String value ) throws IOException + { + byte[] data = ( key + "=" + value ).getBytes( StandardCharsets.UTF_8 ); + dataToServer.writeByte( data.length ); + dataToServer.write( data ); + } + + /***********************************************************************/ + /** + * 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 { + sendKeyValuePair( "ERROR", errString ); + } 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(); + } + if ( dataFromServer != null ) + dataFromServer.close(); + if ( dataToServer != null ) + dataToServer.close(); + } catch ( IOException e ) { + e.printStackTrace(); + } + } + + /** + * Returns whether this transfer/connection is considered valid or usable, + * which means the socket is still properly connected to the remote peer. + * + * @return true or false + */ + public boolean isValid() + { + return satelliteSocket.isConnected() && !satelliteSocket.isClosed() + && !satelliteSocket.isInputShutdown() && !satelliteSocket.isOutputShutdown(); + } + +} |