com.nubits.nubot.utils.Utils.java Source code

Java tutorial

Introduction

Here is the source code for com.nubits.nubot.utils.Utils.java

Source

/*
 * Copyright (C) 2015 Nu Development Team
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package com.nubits.nubot.utils;

import com.nubits.nubot.NTP.NTPClient;
import com.nubits.nubot.bot.SessionManager;
import com.nubits.nubot.global.Passwords;
import com.nubits.nubot.global.Settings;
import com.nubits.nubot.models.OrderToPlace;
import org.apache.commons.io.FileUtils;
import org.joda.time.DateTime;
import org.joda.time.Duration;
import org.joda.time.Period;
import org.joda.time.format.PeriodFormatter;
import org.joda.time.format.PeriodFormatterBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import javax.net.ssl.*;
import java.awt.*;
import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;

import static com.nubits.nubot.utils.LiquidityPlot.addPlot;
import static com.nubits.nubot.utils.LiquidityPlot.plot;

public class Utils {

    private static final Logger LOG = LoggerFactory.getLogger(Utils.class.getName());

    private static final int maxThreadsError = 30;

    public static void logActiveThreads() {
        int active = Thread.activeCount();
        LOG.trace("currently active threads: " + active);
        Thread allThreads[] = new Thread[active];
        Thread.enumerate(allThreads);

        for (int i = 0; i < active; i++) {
            Thread t = allThreads[i];
            LOG.trace(i + ": " + t + " id: " + t.getId() + " name: " + t.getName() + " " + t.getContextClassLoader()
                    + " group: " + t.getThreadGroup() + " alive" + t.isAlive());
            LOG.trace("super: " + t.getClass().getSuperclass());
        }

        if (active > maxThreadsError) {
            LOG.error("too many threads started");
        }
    }

    /**
     * @param originalString
     * @param passphrase
     * @param pathToOutput
     * @return
     */
    public static String encodeToFile(String originalString, String passphrase, String pathToOutput) {
        String encodedString = "";
        MessageDigest digest;
        try {

            //System.out.println("Writing " +originalString +" to "+ pathToOutput +" with \npassphrase = "+passphrase);

            //Encapsule the passphrase in a 16bit SecretKeySpec key
            digest = MessageDigest.getInstance("SHA");
            digest.update(passphrase.getBytes());
            SecretKeySpec key = new SecretKeySpec(digest.digest(), 0, 16, "AES");

            //Cypher the message
            Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
            aes.init(Cipher.ENCRYPT_MODE, key);

            byte[] ciphertext = aes.doFinal(originalString.getBytes());
            encodedString = new String(ciphertext);

            FileUtils.writeByteArrayToFile(new File(pathToOutput), ciphertext);

        } catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | IllegalBlockSizeException | BadPaddingException ex) {
            LOG.error(ex.toString());
        }

        return encodedString;
    }

    /**
     * @param pathToFile
     * @param passphrase
     * @return
     */
    public static String decode(String pathToFile, String passphrase) {
        String clearString = null;
        MessageDigest digest;
        try {
            //Encapsule the passphrase in a 16bit SecretKeySpec key
            digest = MessageDigest.getInstance("SHA");
            digest.update(passphrase.getBytes());
            SecretKeySpec key = new SecretKeySpec(digest.digest(), 0, 16, "AES");

            Cipher aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
            aes.init(Cipher.DECRYPT_MODE, key);

            byte[] ciphertextBytes = FileUtils.readFileToByteArray(new File(pathToFile));
            clearString = new String(aes.doFinal(ciphertextBytes));

        } catch (IOException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
                | BadPaddingException | IllegalBlockSizeException ex) {
            LOG.error(ex.toString());
            return "-1";
        }
        return clearString;
    }

    // http://stackoverflow.com/questions/2808535/round-a-double-to-2-decimal-places
    public static double round(double value, int places) {
        if (places < 0) {
            throw new IllegalArgumentException();
        }

        BigDecimal bd = new BigDecimal(value);
        bd = bd.setScale(places, RoundingMode.HALF_DOWN);
        return bd.doubleValue();
    }

    /**
     * @return
     */
    public static boolean isWindowsPlatform() {
        String os = System.getProperty("os.name");
        if (os != null && os.startsWith("Windows")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @return
     */
    public static boolean isMacPlatform() {
        String os = System.getProperty("os.name");
        if (os != null && os.startsWith("Mac")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * @param arg
     * @return
     */
    public static String toHex(String arg) {
        return String.format("%040x", new BigInteger(1, arg.getBytes(/*YOUR_CHARSET?*/)));
    }

    /**
     * @return
     */
    public static String getTimestampString() {
        DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        Date date = new Date();
        return dateFormat.format(date);
    }

    public static long getTimestampLong() {
        Date timeStamp = new Date();
        return timeStamp.getTime();

    }

    public static String getHTML(String url, boolean removeNonLatinChars) throws IOException {
        String line = "", all = "";
        URL myUrl = null;
        BufferedReader br = null;
        try {
            myUrl = new URL(url);

            URLConnection con = myUrl.openConnection();
            con.setConnectTimeout(1000 * 8);
            con.setReadTimeout(1000 * 8);
            InputStream in = con.getInputStream();

            br = new BufferedReader(new InputStreamReader(in));

            while ((line = br.readLine()) != null) {
                all += line;
            }
        } finally {
            if (br != null) {
                br.close();
            }
        }

        if (removeNonLatinChars) {
            all = all.replaceAll("[^\\x00-\\x7F]", "");
        }
        return all;
    }

    public static void printSeparator() {
        LOG.info("\n----------- -----------  -----------\n");
    }

    //When parsing Json with org.json.simple.JSONObject, use this for doubles
    //not needed if using alibaba json parser
    public static double getDouble(Object obj) {
        double toRet = -1;
        if (obj.getClass().equals((new Long((long) 10)).getClass())) //If the price is round (i.e. 100) the type will be parsed as Long
        {
            Long l = new Long((long) obj);
            toRet = l.doubleValue();
        } else {
            try {
                toRet = (Double) obj;
            } catch (ClassCastException e) {
                //probably a String
                try {
                    toRet = Double.parseDouble((String) obj);
                } catch (ClassCastException ex) {
                    LOG.error("cannot parse object : " + obj.toString());
                    return -1;
                }
            }
        }
        return toRet;
    }

    public static long getOneDayInMillis() {
        return 1000 * 60 * 60 * 24;
    }

    //Computes the seconds missing till the next remote minutes clocks
    public static int getSecondsToRemoteMinute() {
        Date remoteDate = new NTPClient().getTime();
        Calendar remoteCalendar = new GregorianCalendar();
        remoteCalendar.setTime(remoteDate);
        int remoteTimeInSeconds = remoteCalendar.get(Calendar.SECOND);
        int delay = (60 - remoteTimeInSeconds);
        return delay;
    }

    /**
     * Returns a pseudo-random number between min and max, inclusive. The
     * difference between min and max can be at most
     * <code>Integer.MAX_VALUE - 1</code>.
     *
     * @param min Minimum value
     * @param max Maximum value. Must be greater than min.
     * @return Integer between min and max, inclusive.
     * @see java.util.Random#nextInt(int)
     */
    public static int randInt(int min, int max) {

        // NOTE: Usually this should be a field rather than a method
        // variable so that it is not re-seeded every call.
        Random rand = new Random();

        // nextInt is normally exclusive of the top value,
        // so add 1 to make it inclusive
        int randomNum = rand.nextInt((max - min) + 1) + min;

        return randomNum;
    }

    /**
     * For use with the multi-custodian option. The walls are removed and
     * re-added every three minutes. The bot needs to wait for the next 3 minute
     * window before placing walls
     *
     * @return delay
     */
    public static int getSecondsToNextwindow(int windowWidthSeconds) {
        Date remoteDate = new NTPClient().getTime();
        Calendar remoteCalendar = new GregorianCalendar();
        remoteCalendar.setTime(remoteDate);
        int remoteTimeInSeconds = remoteCalendar.get(Calendar.SECOND);
        int remoteTimeInMinutes = remoteCalendar.get(Calendar.MINUTE);
        int minutesTillWindow = windowWidthSeconds - (remoteTimeInMinutes % windowWidthSeconds);
        int delay = ((60 * minutesTillWindow) - remoteTimeInSeconds);
        return delay;
    }

    public static int safeLongToInt(long l) {
        if (l < Integer.MIN_VALUE || l > Integer.MAX_VALUE) {
            throw new IllegalArgumentException(l + " cannot be cast to int without changing its value.");
        }
        return (int) l;
    }

    public static String calcDate(long millisecs) {
        String timezone = "UTC";
        SimpleDateFormat date_format = new SimpleDateFormat("MMM dd yyyy HH:mm:ss.SSS");
        date_format.setTimeZone(TimeZone.getTimeZone("timezone"));
        Date resultdate = new Date(millisecs);
        return date_format.format(resultdate) + " " + timezone;

    }

    public static String generateSessionID() {
        String sep = "|";
        //Generate a random alfanumeric id of 6 chars
        String uid = UUID.randomUUID().toString().substring(0, 6);
        //Get nubot version
        String version = VersionInfo.getVersionName();
        //Get timestamp
        String timest = "" + getTimestampLong();
        //conatenate
        return version + sep + timest + sep + uid;
    }

    /**
     * Install a trust manager that does not validate certificate chains for https calls
     *
     * @throws Exception
     */
    private static void installTrustAllManager() throws Exception {

        // Create a trust manager that does not validate certificate chains
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        } };

        // Install the all-trusting trust manager
        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        };
        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
    }

    public static void installKeystore(boolean trustAll) {

        LOG.info("installKeystore. trustall: " + trustAll);
        if (trustAll) {
            try {
                Utils.installTrustAllManager();
            } catch (Exception ex) {
                LOG.error(ex.toString());
            }

        } else {

            //load file depending whether run from inside a Jar or not

            String wdir = FilesystemUtils.getBotAbsolutePath();
            String wdirpath = wdir + "/" + Settings.KEYSTORE_PATH;

            LOG.info("Reading keystorefile from : " + wdirpath);

            System.setProperty("javax.net.ssl.trustStore", wdirpath);
            System.setProperty("javax.net.ssl.trustStorePassword", Passwords.KEYSTORE_ENCRYPTION_PASS);
        }
    }

    public static void drawOrderBooks(ArrayList<OrderToPlace> sellOrders, ArrayList<OrderToPlace> buyOrders,
            double pegPrice) {
        double[] xSell = new double[sellOrders.size()];
        double[] ySell = new double[sellOrders.size()];
        double[] xBuy = new double[buyOrders.size()];
        double[] yBuy = new double[buyOrders.size()];

        for (int i = 0; i < sellOrders.size(); i++) {
            OrderToPlace tempOrder = sellOrders.get(i);
            xSell[i] = tempOrder.getPrice() * pegPrice * 100;
            ySell[i] = tempOrder.getSize();

        }

        for (int i = 0; i < buyOrders.size(); i++) {
            OrderToPlace tempOrder = buyOrders.get(i);
            xBuy[i] = tempOrder.getPrice() * pegPrice * 100;
            yBuy[i] = tempOrder.getSize();

        }

        plot(xSell, ySell); // create a plot using xaxis and yvalues
        addPlot(xBuy, yBuy); // create a second plot on top of first

    }

    public static String getDurationDate(DateTime from, DateTime to) {
        Duration duration = new Duration(from, to);
        Period period = duration.toPeriod();
        Period normalizedPeriod = period.normalizedStandard();
        PeriodFormatter minutesAndSeconds = new PeriodFormatterBuilder().appendDays().appendSuffix(" day", " days")
                .appendSeparator(" ").printZeroIfSupported()
                //.minimumPrintedDigits(2)
                .appendHours().appendSuffix(" hour", " hours").appendSeparator(" ").appendMinutes()
                .appendSuffix(" minute", " minutes").printZeroIfSupported()
                //.minimumPrintedDigits(2)
                .appendSeparator(" ").appendSeconds().appendSuffix(" second", " seconds")
                //.minimumPrintedDigits(2)
                .toFormatter();

        /*
        .printZeroAlways()
        .appendMinutes()
        .appendSeparator(":")
        .appendSeconds()
        .toFormatter();*/
        String result = minutesAndSeconds.print(normalizedPeriod);
        return result;
    }

    public static String getBotUptimeDate() {
        DateTime now = new DateTime();
        return getDurationDate(SessionManager.sessionStartDate, now);
    }

    //Return the uptime of the bot [hours]
    /*public static String getBotUptimeReadable() {
    long upTimeMs = System.currentTimeMillis() - Global.sessionStarted;
    String toReturn = "";
    if (getDaysFromMillis(upTimeMs) > 2) {
        toReturn = getDaysFromMillis(upTimeMs) + " days";
    } else if (getHoursFromMillis(upTimeMs) > 2) {
        toReturn = getHoursFromMillis(upTimeMs) + " hours";
    } else {
        toReturn = getMinutesFromMillis(upTimeMs) + " minutes";
    }
    return toReturn;
    }*/

    //Format numbers in decimal notation with custom digits
    public static String formatNumber(double number, int digits) {
        DecimalFormat df = new DecimalFormat("0");
        df.setMaximumFractionDigits(digits);
        return df.format(number);
    }

    public static void launchBrowser(String url) {
        if (Desktop.isDesktopSupported()) {
            try {
                Desktop.getDesktop().browse(new URI(url));
            } catch (IOException e) {
                LOG.error(e.toString());
            } catch (URISyntaxException e) {
                LOG.error(e.toString());
            }
        } else {
            LOG.warn("Can't launch browser: Desktop not supported.");
        }
    }

    public static double getHoursFromMillis(long millis) {
        return round((getMinutesFromMillis(millis)) / 60, 2);
    }

    public static double getMinutesFromMillis(long millis) {
        return round(((millis / 1000) / 60), 2);
    }

    public static double getDaysFromMillis(long millis) {
        return round((getHoursFromMillis(millis)) / 24, 2);
    }

}