uk.ac.ebi.enaega.uploadclient.UploadClientUDT.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.ebi.enaega.uploadclient.UploadClientUDT.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package uk.ac.ebi.enaega.uploadclient;

import it.sauronsoftware.ftp4j.CircularBuffer;
import static it.sauronsoftware.ftp4j.FTPClient.TYPE_BINARY;
import static it.sauronsoftware.ftp4j.FTPClient.TYPE_TEXTUAL;
import it.sauronsoftware.ftp4j.FTPCommunicationChannel;
import it.sauronsoftware.ftp4j.FTPDataTransferException;
import it.sauronsoftware.ftp4j.FTPDataTransferListener;
import it.sauronsoftware.ftp4j.FTPReply;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.net.ConnectException;
import java.net.InetAddress;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.security.Security;
import java.text.DecimalFormat;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import main.java.udt.UDPEndPoint;
import main.java.udt.UDTOutputStream;
import main.java.udt.util.UnifiedSocket;
import net.sourceforge.bromo.CommandException;
import net.sourceforge.bromo.PasvServer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.util.Arrays;

/**
 *
 * @author asenf
 */
public class UploadClientUDT implements Runnable {

    private InputStream inputStream;
    private long streamOffset;
    private OutputStream dataTransferOutputStream;
    private PasvServer pasvServer;
    private boolean modezEnabled;
    private FTPDataTransferListener listener;
    private long offset;
    private MyTableModel mm;
    private int tp;
    private int SEND_AND_RECEIVE_BUFFER_SIZE;
    private PGPPublicKey pgKey;
    private String charSet;
    private String fileName;
    private InetAddress dataHost;
    private int hostPort;
    private FTPCommunicationChannel communication;

    private String result = "", result1 = "";
    private long c = 0;

    public UploadClientUDT(InputStream inputStream, long streamOffset, OutputStream dataTransferOutputStream,
            PasvServer pasvServer, boolean modezEnabled, FTPDataTransferListener listener, long offset,
            MyTableModel mm, int tp, int SEND_AND_RECEIVE_BUFFER_SIZE, PGPPublicKey pgKey, String charSet,
            String fileName, InetAddress dataHost, int hostPort, FTPCommunicationChannel communication) {
        this.inputStream = inputStream;
        this.streamOffset = streamOffset;
        this.dataTransferOutputStream = dataTransferOutputStream;
        this.pasvServer = pasvServer;
        this.modezEnabled = modezEnabled;
        this.listener = listener;
        this.offset = offset;
        this.mm = mm;
        this.tp = tp;
        this.SEND_AND_RECEIVE_BUFFER_SIZE = SEND_AND_RECEIVE_BUFFER_SIZE;
        this.pgKey = pgKey;
        this.charSet = charSet;
        this.fileName = fileName;
        this.dataHost = dataHost;
        this.hostPort = hostPort;
        this.communication = communication;
    }

    @Override
    public void run() {
        System.setProperty("java.net.preferIPv4Stack", "true");
        ByteArrayOutputStream baos = null; // PGP
        MessageDigest crypt_digest = null; // PGP
        OutputStream literalOut = null, encOut = null, compressedOut = null; // PGP
        int DEFAULT_BUFFER_SIZE = 65 * 1024; // PGP
        PGPEncryptedDataGenerator encryptedDataGenerator = null; // PGP
        PGPCompressedDataGenerator compressedDataGenerator = null; // PGP
        PGPLiteralDataGenerator literalDataGenerator = null; // PGP

        if (this.pgKey != null) { // EGA: Encrypt file
            try {
                Security.addProvider(new BouncyCastleProvider());
                baos = new ByteArrayOutputStream(2 * DEFAULT_BUFFER_SIZE); // Write to memory!
                try {
                    crypt_digest = MessageDigest.getInstance("MD5");
                } catch (NoSuchAlgorithmException ex) {
                    Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
                }

                // Encrypted Data Generator -- needs unlimited Security Policy
                encryptedDataGenerator = new PGPEncryptedDataGenerator(PGPEncryptedData.CAST5, true,
                        new SecureRandom(), "BC");
                try {
                    encryptedDataGenerator.addMethod(this.pgKey);
                    encOut = encryptedDataGenerator.open(baos, new byte[DEFAULT_BUFFER_SIZE]);
                } catch (NoSuchProviderException ex) {
                    Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
                } catch (PGPException ex) {
                    Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
                }

                // Compression
                compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedDataGenerator.ZIP);
                compressedOut = compressedOut = new BufferedOutputStream(compressedDataGenerator.open(encOut));

                // Literal Data Generator and Output Stream
                literalDataGenerator = new PGPLiteralDataGenerator();
                literalOut = literalDataGenerator.open(compressedOut, PGPLiteralData.BINARY, fileName, new Date(),
                        new byte[DEFAULT_BUFFER_SIZE]); // 1<<16                                
            } catch (Throwable t) {
                JOptionPane.showMessageDialog(new JFrame(),
                        "Can't instantiate cipher key.\nBe sure to have JCE Unlimited Strength Security Policy installed.",
                        "Cipher Key Problem", JOptionPane.ERROR_MESSAGE);
                String[] failure = { "Cipher Issues" };
                return;
                //return failure;
            }
        }

        // Upload the stream.
        // Transfer! Start by accepting aconnection initiated by the server
        UnifiedSocket dataSocket = null;

        // Upload the stream.
        long c = 0;
        System.out.println("1 getting " + dataHost.toString() + "   " + hostPort);
        try {
            //                    dataSocket = pasvServer.getMatching( dataHost, hostPort );
            dataSocket = getSocket_timeout(); //getSocket();
            //if (dataSocket == null) dataSocket = getSocket_timeout();
            if (dataSocket == null)
                throw new ConnectException();
            System.out.println("2");

            // Skips.
            inputStream.skip(streamOffset);
            // Opens the data transfer connection.
            dataTransferOutputStream = dataSocket.getOutputStream(); // TCP or UDT - automatically

            // Listeners. Initialize to offset!
            if (listener != null) {
                listener.started();
                int large = 0, small = 0;
                large = (int) (offset / Integer.MAX_VALUE); // times the size of largest int
                small = (int) (offset % Integer.MAX_VALUE); // remainder
                for (int iu = 0; iu < large; iu++)
                    listener.transferred(Integer.MAX_VALUE);
                listener.transferred(small);
            }
            long tot = 0;
            int idx = 0; // Update the table model
            if (mm != null) { // For Table Updates
                idx = mm.getCur();
                mm.setValueAt("0%", idx, 5);
                tot = mm.getSize();
            }

            String line = communication.readFTPReply().toString();
            System.out.println("Server Reply (STOR test.txt): " + line);

            // Let's do it!
            if (tp == TYPE_TEXTUAL) { // ------------------------------------- TEXT DATA ------------------
                char[] buffer = new char[SEND_AND_RECEIVE_BUFFER_SIZE];
                byte[] bbuffer = new byte[SEND_AND_RECEIVE_BUFFER_SIZE];
                int l, l_tot = 0;
                long cnt = 0, cnt_ = 0;
                long block_cnt = 0, block_cnt_pre = 0;
                long flush_point = 1048576L * 10L;
                long pre_flush_point = 1048576L * 9L;

                Reader reader = new InputStreamReader(inputStream);
                Writer writer = new OutputStreamWriter(dataTransferOutputStream, charSet);

                MessageDigest digest = null; // calculate checksum of incoming data stream
                try {
                    digest = MessageDigest.getInstance("MD5"); // get the hash algorithm
                } catch (NoSuchAlgorithmException ex) {
                    Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
                }

                long dt_file = System.currentTimeMillis(), dt_upd = System.currentTimeMillis(); // time before upload
                while ((l = reader.read(buffer)) != -1) { // The actual data transfer loop -------------------
                    System.arraycopy(buffer, 0, bbuffer, 0, l);
                    digest.update(bbuffer, 0, l); // Calculate MD5 for TCP stream

                    if (this.pgKey != null) { // Cipher - write to mem buffer first, to enable MD5
                        literalOut.write(bbuffer, 0, l);
                        literalOut.flush();
                        byte[] buffer_ = baos.toByteArray();
                        crypt_digest.update(buffer_);
                        char[] buffer__ = new char[buffer_.length];
                        System.arraycopy(buffer_, l_tot, buffer__, 0, buffer_.length);
                        cnt += buffer_.length;
                        cnt_ += buffer_.length;
                        block_cnt += buffer_.length;
                        block_cnt_pre += buffer_.length;
                        writer.write(buffer__);
                        baos.reset();
                    } else { // Write directly to socket
                        cnt += buffer.length;
                        cnt_ += buffer.length;
                        block_cnt += buffer.length;
                        block_cnt_pre += buffer.length;
                        writer.write(buffer, 0, l);
                    }
                    if ((block_cnt_pre / pre_flush_point) > 0) {
                        block_cnt_pre = 0;
                        writer.flush();
                    }
                    //writer.flush();

                    if (listener != null) {
                        listener.transferred(l);
                        if (l > 0) { // l = bytes, dt_file = milliseconds
                            dt_upd = System.currentTimeMillis() - dt_upd;
                            double rate = (((double) (l)) / (((double) (dt_upd)))) * 1000.0; // bytes/s
                            if (rate > 0) {
                                String sd = size_display((long) (rate));
                                if (sd != null && sd.length() > 0)
                                    listener.setText(size_display((long) (rate)) + "/s");
                            }
                            dt_upd = System.currentTimeMillis();
                        }
                    }

                    if (mm != null && tot != 0) {
                        l_tot += l;
                        double pct = (double) l_tot / (double) tot;
                        int i_pct = (int) Math.floor(pct * 100);
                        String s_pct = String.valueOf(i_pct) + "%";
                        mm.setValueAt(s_pct, idx, 5);
                    }
                }

                // Get a representation of the MD5 digest (checksum)
                byte[] md5sum = digest.digest();
                result = "";
                for (int i = 0; i < md5sum.length; i++)
                    result += Integer.toString((md5sum[i] & 0xff) + 0x100, 16).substring(1);

            } else if (tp == TYPE_BINARY) { // ------------------------------ BINARY DATA ---------------------
                System.out.println("Binary Transfer");
                long cnt = 0, cnt_ = 0;
                long block_cnt = 0, block_cnt_pre = 0;
                long flush_point = 1048576L * 10L;
                long pre_flush_point = 1048576L * 9L;
                int bufsze = 250 * (UDPEndPoint.DATAGRAM_SIZE - 24);
                byte[] buffer = new byte[bufsze];

                int l;
                long l_tot = 0;

                MessageDigest digest = null; // calculate checksum of incoming data stream
                try {
                    digest = MessageDigest.getInstance("MD5"); // get the hash algorithm
                } catch (NoSuchAlgorithmException ex) {
                    Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
                }

                // Actual transfer identical between TCP and UDT
                long dt_file = System.currentTimeMillis(), dt_upd = System.currentTimeMillis(); // time before upload
                CircularBuffer cb = new CircularBuffer(bufsze + (20 * (UDPEndPoint.DATAGRAM_SIZE - 24)));

                System.out.println("Actual Send Loop starts");
                while ((l = inputStream.read(buffer)) != -1) { // The actual data transfer loop -------------------
                    if (this.pgKey != null) { // PGP Key present - encrypted Stream
                        literalOut.write(buffer, 0, l); // Write to mem buffer - this is the encryption
                        literalOut.flush();
                        byte[] buffer_ = baos.toByteArray(); // retrieve that buffer
                        crypt_digest.update(buffer_); // update crypto MD5
                        c += buffer_.length;
                        cb.put(buffer_); // Write into circular buffer                                                
                        baos.reset(); // empty mem buffer
                    } else {
                        c += l; //buffer.length; // only what was read!
                        cb.put(Arrays.copyOf(buffer, l)); // Write into circular buffer
                    }

                    // Send the data in packet-sized portions from circular buffer temp structure
                    while (cb.getSize() > (UDPEndPoint.DATAGRAM_SIZE - 24)) {
                        byte[] sendbuf = new byte[(UDPEndPoint.DATAGRAM_SIZE - 24)];
                        sendbuf = cb.get(sendbuf.length);

                        cnt += sendbuf.length;
                        cnt_ += sendbuf.length;
                        block_cnt += sendbuf.length;
                        block_cnt_pre += sendbuf.length;
                        dataTransferOutputStream.write(sendbuf, 0, sendbuf.length); // buffer, 0, l

                        if ((block_cnt_pre / pre_flush_point) > 0) {
                            block_cnt_pre = 0;
                            ((UDTOutputStream) dataTransferOutputStream).pre_flush();
                        }

                        if ((block_cnt / flush_point) > 0) {
                            block_cnt = 0;
                            dataTransferOutputStream.flush();
                        }
                    }

                    digest.update(buffer, 0, l); // Calculate plain text MD5 for TCP stream

                    if (listener != null) {
                        listener.transferred(l); // Update total-progress bar
                        if (l > 0) { // l = bytes, dt_file = milliseconds
                            dt_upd = System.currentTimeMillis() - dt_upd;
                            double rate = (((double) (l)) / (((double) (dt_upd)))) * 1000.0; // bytes/s
                            if (rate > 0) {
                                String sd = size_display((long) (rate));
                                if (sd != null && sd.length() > 0)
                                    listener.setText(size_display((long) (rate)) + "/s");
                            }
                            dt_upd = System.currentTimeMillis();
                        }
                    }

                    if (mm != null && tot != 0) { // Update in-table progress bar
                        l_tot += l;
                        double pct = (double) l_tot / (double) tot;
                        int i_pct = (int) Math.floor(pct * 100);
                        String s_pct = String.valueOf(i_pct) + "%";
                        mm.setValueAt(s_pct, idx, 5);
                    }
                } // Input stream completely read ---
                if (cb.getSize() > 0) { // Flush the buffer at the end - MD5s already complete at this point
                    byte[] sendbuf = new byte[cb.getSize()];
                    sendbuf = cb.get(sendbuf.length);
                    dataTransferOutputStream.write(sendbuf, 0, sendbuf.length);
                }

                // Get a representation of the MD5 digest (checksum) plain file
                byte[] md5sum = digest.digest();
                result = "";
                for (int i = 0; i < md5sum.length; i++)
                    result += Integer.toString((md5sum[i] & 0xff) + 0x100, 16).substring(1);
            }

            // Upload loop complete. Close PGP streams, if used (produces extra data)
            if (this.pgKey != null) {
                literalOut.close();
                literalDataGenerator.close();
                // Close all other streams
                compressedOut.close();
                compressedDataGenerator.close();
                encOut.close();
                encryptedDataGenerator.close();
                byte[] buffer_ = baos.toByteArray(); // retrieve that buffer
                crypt_digest.update(buffer_); // update crypto MD5
                dataTransferOutputStream.write(buffer_); // Write cipher data to socket
                dataTransferOutputStream.flush();
                baos.close();

                // Get a representation of the MD5 digest (checksum) cipher file
                byte[] md5sum = crypt_digest.digest();
                result1 = "";
                for (int i = 0; i < md5sum.length; i++)
                    result1 += Integer.toString((md5sum[i] & 0xff) + 0x100, 16).substring(1);
            }

            dataTransferOutputStream.flush();
        } catch (Throwable e) {
            if (listener != null) {
                listener.aborted();
            }
            try {
                throw new FTPDataTransferException("I/O error in data transfer", e);
            } catch (FTPDataTransferException ex) {
                Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
            }
        } finally {
            // Closing stream and data connection.
            if (dataTransferOutputStream != null) {
                try {
                    dataTransferOutputStream.flush();
                    dataTransferOutputStream.close();
                } catch (Throwable t) {
                    ;
                }
            }
            try {
                dataSocket.close();
            } catch (Throwable t) {
                ;
            }
            // Set to null the instance-level input stream.
            dataTransferOutputStream = null;
            // Consume the result reply of the transfer.
            FTPReply readFTPReply = null;
            try {
                readFTPReply = communication.readFTPReply();
            } catch (Throwable ex) {
                Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
            }
            System.out.println(readFTPReply.toString());
            // Change the operation status.
        }

    }

    private UnifiedSocket getSocket_timeout() throws IOException, CommandException {
        UnifiedSocket x = null;
        Future y = null;

        Callable callable = new Callable() {
            @Override
            public UnifiedSocket call() throws Exception {
                //if (!dataPasv) {
                //        net.sourceforge.bromo.Logger.log( net.sourceforge.bromo.Logger.LOG_DEBUG, "ServerDTP.getSocket(PORT) = " + dataHost + ":" + dataPort );
                //        return new UnifiedSocket( dataHost, dataPort, serverPI.isTCP() );
                //}
                net.sourceforge.bromo.Logger.log(net.sourceforge.bromo.Logger.LOG_DEBUG,
                        "UploadClientUDT.getSocket(PASV) = " + dataHost + ":" + hostPort);
                return pasvServer.getMatching(dataHost, hostPort);
            }
        };
        ExecutorService exec = Executors.newSingleThreadExecutor();
        Future<UnifiedSocket> res = exec.submit(callable);

        try {
            x = res.get(5, TimeUnit.SECONDS);
        } catch (ExecutionException ex) {
            java.util.logging.Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TimeoutException ex) {
            java.util.logging.Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InterruptedException ex) {
            java.util.logging.Logger.getLogger(UploadClientUDT.class.getName()).log(Level.SEVERE, null, ex);
        }
        if (!res.isDone())
            res.cancel(true);
        exec.shutdown();

        return x;
    }

    public String get_md5() {
        return this.result;
    }

    public String get_crypto_md5() {
        return this.result1;
    }

    public long get_c() {
        return this.c;
    }

    private String size_display(long in) { // expects bytes
        String result = "";
        DecimalFormat df = new DecimalFormat("#,##0.00");

        double in_format = 0;
        if (in < 1024) {
            result = in + " Bytes";
        } else if (in < Math.pow(1024, 2)) {
            in_format = (in / Math.pow(1024, 1));
            //result = new Double(df.format(in_format)).doubleValue() + " KB";
            result = df.format(in_format) + " KB";
        } else if (in < Math.pow(1024, 3)) {
            in_format = (in / Math.pow(1024, 2));
            //result = new Double(df.format(in_format)).doubleValue() + " MB";
            result = df.format(in_format) + " MB";
        } else if (in < Math.pow(1024, 4)) {
            in_format = (in / Math.pow(1024, 3));
            //result = new Double(df.format(in_format)).doubleValue() + " GB";
            result = df.format(in_format) + " GB";
        } else if (in < Math.pow(1024, 5)) {
            in_format = (in / Math.pow(1024, 4));
            //result = new Double(df.format(in_format)).doubleValue() + " TB";
            result = df.format(in_format) + " TB";
        } else if (in < Math.pow(1024, 6)) {
            in_format = (in / Math.pow(1024, 5));
            //result = new Double(df.format(in_format)).doubleValue() + " PB";
            result = df.format(in_format) + " PB";
        }

        return result;
    }
}