summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/filetransfer/Transfer.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/org/openslx/filetransfer/Transfer.java')
-rw-r--r--src/main/java/org/openslx/filetransfer/Transfer.java327
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();
+ }
+
+}