com.jkoolcloud.jesl.simulator.TNT4JSimulator.java Source code

Java tutorial

Introduction

Here is the source code for com.jkoolcloud.jesl.simulator.TNT4JSimulator.java

Source

/*
 * Copyright 2015 JKOOL, LLC.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jkoolcloud.jesl.simulator;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.Map;
import java.util.Random;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

import com.jkoolcloud.jesl.simulator.tnt4j.JKCloudConnection;
import com.jkoolcloud.tnt4j.TrackingLogger;
import com.jkoolcloud.tnt4j.config.DefaultConfigFactory;
import com.jkoolcloud.tnt4j.config.TrackerConfig;
import com.jkoolcloud.tnt4j.core.OpLevel;
import com.jkoolcloud.tnt4j.core.UsecTimestamp;
import com.jkoolcloud.tnt4j.utils.Utils;

/**
 * JESL Activity Simulator which uses TNT4J to simulate application activities and transactions.
 * This will simulate activities by loading activity definitions from an XML file.
 *
 * @version $Revision: $
 */
public class TNT4JSimulator {

    public enum SimulatorRunType {
        RUN_SIM, REPLAY_SIM
    }

    private static SimulatorRunType runType = SimulatorRunType.RUN_SIM;
    private static String jkProtocol = "https";
    private static String jkHost = null;
    private static int jkPort = 443;
    private static String jkAccessToken = null;
    private static String simFileName = "";
    private static boolean uniqueTags = false;
    private static boolean uniqueCorrs = false;
    private static boolean uniqueIds = false;
    private static OpLevel traceLevel = OpLevel.INFO;
    private static String jkFileName = "";
    private static int valuePctChg = 0;
    private static Random ranGen = new Random();
    private static long numIterations = 1;
    private static boolean generateValues = false;
    private static long ttl = 0L;

    private static JKCloudConnection gwConn = null;
    private static TrackingLogger logger = null;
    private static long iteration = 0L;

    public static String readFromConsole(String prompt) throws IOException {
        return System.console().readLine(prompt);
    }

    public static void error(String msg, Throwable e) {
        logger.tnt(OpLevel.ERROR, null, null, msg, e);
    }

    public static void warn(String msg) {
        logger.tnt(OpLevel.WARNING, null, null, msg, (Object[]) null);
    }

    public static void info(String msg) {
        logger.tnt(OpLevel.INFO, null, null, msg, (Object[]) null);
    }

    public static boolean isDebugEnabled() {
        return (traceLevel == OpLevel.DEBUG || traceLevel == OpLevel.TRACE);
    }

    public static void debug(UsecTimestamp simTime, String msg) {
        if (isDebugEnabled()) {
            if (simTime == null)
                simTime = new UsecTimestamp();
            logger.tnt(OpLevel.DEBUG, null, null, "SimTime=" + simTime + ": " + msg);
        }
    }

    public static boolean isTraceEnabled() {
        return (traceLevel == OpLevel.TRACE);
    }

    public static void trace(UsecTimestamp simTime, String msg) {
        if (isTraceEnabled()) {
            if (simTime == null)
                simTime = new UsecTimestamp();
            logger.tnt(OpLevel.TRACE, null, null, "SimTime=" + simTime + ": " + msg);
        }
    }

    public static String getProtocol() {
        return jkProtocol;
    }

    public static String getHost() {
        return jkHost;
    }

    public static int getPort() {
        return jkPort;
    }

    public static String getFileName() {
        return jkFileName;
    }

    public static String getConnectUrl() {
        return jkProtocol.equalsIgnoreCase("file") ? (jkProtocol.toLowerCase() + "://" + jkFileName)
                : (jkProtocol.toLowerCase() + "://" + jkHost + ":" + jkPort);
    }

    public static String getAccessToken() {
        return jkAccessToken;
    }

    public static boolean useUniqueTags() {
        return uniqueTags;
    }

    public static boolean useUniqueCorrs() {
        return uniqueCorrs;
    }

    public static boolean useUniqueIds() {
        return uniqueIds;
    }

    public static boolean isGenerateValues() {
        return generateValues;
    }

    public static boolean isVaryingValuesEnabled() {
        return valuePctChg != 0;
    }

    public static long varyValue(long value) {
        if (value == 0 || valuePctChg == 0)
            return value;

        // nextInt returns value in range [0,n).  We want values in range [-n,n).
        double percentChg = (ranGen.nextInt(valuePctChg * 2) - valuePctChg) / 100.0;

        // let's keep a lid on variations
        if (percentChg > 0.99)
            percentChg = 0.99;
        if (percentChg < -0.99)
            percentChg = -0.99;

        long newValue = (long) (value * (1.0 + percentChg));
        if (newValue < 0.0 && value > 0.0)
            newValue = 0;

        return newValue;
    }

    public static long varyValue(int value) {
        if (value == 0 || valuePctChg == 0)
            return value;

        // nextInt returns value in range [0,n).  We want values in range [-n,n).
        double percentChg = (ranGen.nextInt(valuePctChg * 2) - valuePctChg) / 100.0;

        // let's keep a lid on variations
        if (percentChg > 0.99)
            percentChg = 0.99;
        if (percentChg < -0.99)
            percentChg = -0.99;

        int newValue = (int) (value * (1.0 + percentChg));
        if (newValue < 0 && value > 0)
            newValue = 0;

        return newValue;
    }

    public static double varyValue(double value) {
        if (value == 0 || valuePctChg == 0)
            return value;

        // nextInt returns value in range [0,n).  We want values in range [-n,n).
        double percentChg = (ranGen.nextInt(valuePctChg * 2) - valuePctChg) / 100.0;

        // let's keep a lid on variations
        if (percentChg > 0.99)
            percentChg = 0.99;
        if (percentChg < -0.99)
            percentChg = -0.99;

        double newValue = value * (1.0 + percentChg);
        if (newValue < 0.0 && value > 0.0)
            newValue = 0;

        return newValue;
    }

    public static void incrementValue(Map<String, Long> map, String item, long amount) {
        Long prev = map.put(item, amount);
        if (prev != null) {
            map.put(item, prev.longValue() + amount);
        }
    }

    public static long getIteration() {
        return iteration;
    }

    public static long getTTL() {
        return ttl;
    }

    public static String newUUID() {
        return logger.newUUID();
    }

    private static void printUsage(String error) {
        if (!StringUtils.isEmpty(error))
            System.out.println(error);

        System.out.println("\nValid arguments:\n");
        System.out.println(
                "  to run simulation:      run -A:<access_token> [-T:<jk_host>] [-P:<jk_port>] [-C:tcp|http|https] [-f:<sim_def_file_name>]");
        System.out.println(
                "                              [-p:<percentage>] [-V:name=value] [-G:<jk_file_name>] [-i:<iterations>] [-u] [-t:<ttl_hours>]\n");
        System.out.println(
                "  to replay simulation:   replay -A:<access_token> -T:<jk_host> [-P:<jk_port>] [-C:tcp|http|https] -G:<jk_file_name>\n");
        System.out.println("  for usage information:  help\n");
        System.out.println("where:");
        System.out.println("    -A    -  jKool streaming access token (required with '-T')");
        System.out.println("    -T    -  Host name or IP address of jKool streaming data service");
        System.out.println("             (if not specified, data is not sent to jKool service)");
        System.out.println("    -V    -  Define a global variable (property) name=value pair");
        System.out.println("    -P    -  Port jKool data streaming service is listening on (default: SSL 443)");
        System.out.println("    -C    -  Connection type to use for jKool data streaming service (default: https)");
        System.out.println("    -f    -  Use <sim_def_file_name> as simulation configuration");
        System.out.println("    -p    -  Vary all numeric values by +/- <percentage>");
        System.out.println("    -G    -  Read/Write jKool service messages from/to <jk_file_name>");
        System.out.println("             (if writing, data is appended to an existing file)");
        System.out.println("    -i    -  Number of iterations to make on <sim_file_name>");
        System.out.println("    -u    -  Make tags, correlators, ids unique between iterations");
        System.out.println("    -ut   -  Make tags unique between iterations");
        System.out.println("    -uc   -  Make correlators unique between iterations");
        System.out.println("    -ui   -  Make tracking ids unique between iterations");
        System.out.println("    -t    -  Time-to-live (TTL) for all tracking items, in hours");
        System.out.println("                > 0 : tracking events are deleted after the specified number of hours");
        System.out.println(
                "                = 0 : tracking events are deleted based on Retention quota for repository (default)");
        System.out.println(
                "                < 0 : tracking events are not persisted to data store (only processed by Real-time Grid)");
        System.out.println("\nFor 'run' mode, must specify at least of one: '-T', '-G'");

        System.exit(StringUtils.isEmpty(error) ? 0 : 1);
    }

    private static void printUsedArgs() {
        System.out.format(
                "Arguments: runype=%s, url=%s, generateValues=%s, uniqueTags=%s,  uniqueCorrs=%s, uniqueIds=%s, percent=%d, simFile=%s, jkFile=%s\n",
                runType, getConnectUrl(), generateValues, uniqueTags, uniqueCorrs, uniqueIds, valuePctChg,
                simFileName, jkFileName);
    }

    protected static void processArgs(TNT4JSimulatorParserHandler xmlHandler, String[] args) {
        if (args.length == 0)
            printUsage("Missing simulation type");

        String runTypeArg = args[0];
        if (runTypeArg.equals("run"))
            runType = SimulatorRunType.RUN_SIM;
        else if (runTypeArg.equals("replay"))
            runType = SimulatorRunType.REPLAY_SIM;
        else if (runTypeArg.equals("help"))
            printUsage(null);
        else
            printUsage("Invalid simulation type");

        boolean invalidArg = false;
        for (int i = 1; i < args.length; i++) {
            String arg = args[i];

            if (arg == null)
                continue;

            // Process arguments common to all simulation types first
            if (arg.startsWith("-A:")) {
                jkAccessToken = arg.substring(3);
                if (StringUtils.isEmpty(jkAccessToken))
                    printUsage("Missing <access_token> for '-A' argument");
            } else if (arg.startsWith("-V:")) {
                String[] var = arg.substring(3).split("=");
                if (var.length == 2) {
                    System.out.println("Defining variable: '" + var[0] + "=" + var[1] + "'");
                    xmlHandler.setVar(var[0], var[1]);
                } else {
                    printUsage("Variable must have name=value pair");
                }
            } else if (arg.startsWith("-T:")) {
                jkHost = arg.substring(3);
                if (StringUtils.isEmpty(jkHost))
                    printUsage("Missing <jk_host> for '-T' argument");
            } else if (arg.startsWith("-P:")) {
                try {
                    jkPort = Integer.parseInt(arg.substring(3));
                } catch (NumberFormatException e) {
                    printUsage("Missing or invalid <jk_port> for '-P' argument");
                }
            } else if (arg.startsWith("-C:")) {
                jkProtocol = arg.substring(3).toLowerCase();
                if (!"tcp".equals(jkProtocol) && !"http".equals(jkProtocol) && !"https".equals(jkProtocol))
                    printUsage(
                            "Invalid connection protocol for '-C' argument (must be one of: 'tcp', 'http', 'https')");
            } else if (runType == SimulatorRunType.RUN_SIM || runType == SimulatorRunType.REPLAY_SIM) {
                if (arg.startsWith("-G:")) {
                    jkFileName = arg.substring(3);
                    if (StringUtils.isEmpty(jkFileName))
                        printUsage("Missing <jk_file_name> for '-G' argument");
                    jkProtocol = "file";
                } else if (runType == SimulatorRunType.RUN_SIM) {
                    if (arg.startsWith("-f:")) {
                        simFileName = arg.substring(3);
                        if (StringUtils.isEmpty(simFileName))
                            printUsage("Missing <sim_def_file_name> for '-f' argument");
                    } else if (arg.startsWith("-i:")) {
                        String iterations = arg.substring(3);
                        try {
                            numIterations = Long.parseLong(iterations);
                        } catch (NumberFormatException e) {
                            if (StringUtils.isEmpty(iterations))
                                printUsage("Missing <iterations> for '-i' argument");
                            else
                                printUsage("Invalid <iterations> for '-i' argument (" + arg.substring(3) + ")");
                        }
                        if (numIterations <= 0)
                            printUsage("<iterations> for '-i' argument must be > 0");
                    } else if (arg.startsWith("-p:")) {
                        String percentStr = arg.substring(3);
                        if (StringUtils.isEmpty(percentStr))
                            printUsage("Missing <percentage> for '-p' argument");
                        valuePctChg = Integer.parseInt(percentStr);
                        if (valuePctChg < 0 || valuePctChg > 100)
                            printUsage(
                                    "Percentage for varying values ('-p' argument) must be in the range [0,100)");
                    } else if (arg.startsWith("-t:")) {
                        String ttlStr = arg.substring(3);
                        try {
                            ttl = Long.parseLong(ttlStr);
                            if (ttl > 0L)
                                ttl *= 3600;
                        } catch (NumberFormatException e) {
                            if (StringUtils.isEmpty(ttlStr))
                                printUsage("Missing <ttl_hours> for '-t' argument");
                            else
                                printUsage("Invalid <ttl_hours> for '-t' argument (" + arg.substring(3) + ")");
                        }
                    } else if (arg.equals("-ut")) {
                        uniqueTags = true;
                    } else if (arg.equals("-uc")) {
                        uniqueCorrs = true;
                    } else if (arg.equals("-ui")) {
                        uniqueIds = true;
                    } else if (arg.equals("-u")) {
                        uniqueTags = true;
                        uniqueCorrs = true;
                        uniqueIds = true;
                    } else if (arg.equals("-g")) {
                        generateValues = true;
                    } else {
                        invalidArg = true;
                    }
                } else {
                    invalidArg = true;
                }
            } else {
                invalidArg = true;
            }

            if (invalidArg) {
                if (arg.startsWith("-"))
                    printUsage("Invalid argument: " + arg);
                else
                    printUsage("Invalid argument list");
            }
        }

        if (runType == SimulatorRunType.RUN_SIM) {
            if (StringUtils.isEmpty(jkHost) && StringUtils.isEmpty(jkFileName))
                printUsage("Must specify one of '-T' or '-G'");

            if (StringUtils.isEmpty(jkAccessToken) && !StringUtils.isEmpty(jkHost))
                printUsage("Must specify '-A'");
        }

        if (runType == SimulatorRunType.REPLAY_SIM) {
            if (StringUtils.isEmpty(jkAccessToken))
                printUsage("Must specify '-A'");

            if (StringUtils.isEmpty(jkHost))
                printUsage("Missing host or URL");

            if (StringUtils.isEmpty(jkFileName))
                printUsage("Missing XML file name");
        }
        printUsedArgs();
    }

    public static void main(String[] args) {
        boolean isTTY = (System.console() != null);
        long startTime = System.currentTimeMillis();

        try {
            SAXParserFactory parserFactory = SAXParserFactory.newInstance();
            SAXParser theParser = parserFactory.newSAXParser();
            TNT4JSimulatorParserHandler xmlHandler = new TNT4JSimulatorParserHandler();

            processArgs(xmlHandler, args);

            TrackerConfig simConfig = DefaultConfigFactory.getInstance().getConfig(TNT4JSimulator.class.getName());
            logger = TrackingLogger.getInstance(simConfig.build());
            if (logger.isSet(OpLevel.TRACE))
                traceLevel = OpLevel.TRACE;
            else if (logger.isSet(OpLevel.DEBUG))
                traceLevel = OpLevel.DEBUG;

            if (runType == SimulatorRunType.RUN_SIM) {
                if (StringUtils.isEmpty(simFileName)) {
                    simFileName = "tnt4j-sim.xml";
                    String fileName = readFromConsole("Simulation file [" + simFileName + "]: ");

                    if (!StringUtils.isEmpty(fileName))
                        simFileName = fileName;
                }

                StringBuffer simDef = new StringBuffer();
                BufferedReader simLoader = new BufferedReader(new FileReader(simFileName));
                String line;
                while ((line = simLoader.readLine()) != null)
                    simDef.append(line).append("\n");
                simLoader.close();

                info("jKool Activity Simulator Run starting: file=" + simFileName + ", iterations=" + numIterations
                        + ", ttl.sec=" + ttl);
                startTime = System.currentTimeMillis();

                if (isTTY && numIterations > 1)
                    System.out.print("Iteration: ");
                int itTrcWidth = 0;
                for (iteration = 1; iteration <= numIterations; iteration++) {
                    itTrcWidth = printProgress("Executing Iteration", iteration, itTrcWidth);

                    theParser.parse(new InputSource(new StringReader(simDef.toString())), xmlHandler);

                    if (!Utils.isEmpty(jkFileName)) {
                        PrintWriter gwFile = new PrintWriter(new FileOutputStream(jkFileName, true));
                        gwFile.println("");
                        gwFile.close();
                    }
                }
                if (numIterations > 1)
                    System.out.println("");

                info("jKool Activity Simulator Run finished, elapsed time = "
                        + DurationFormatUtils.formatDurationHMS(System.currentTimeMillis() - startTime));
                printMetrics(xmlHandler.getSinkStats(), "Total Sink Statistics");
            } else if (runType == SimulatorRunType.REPLAY_SIM) {
                info("jKool Activity Simulator Replay starting: file=" + jkFileName + ", iterations="
                        + numIterations);
                connect();
                startTime = System.currentTimeMillis();

                // Determine number of lines in file
                BufferedReader gwFile = new BufferedReader(new java.io.FileReader(jkFileName));
                for (numIterations = 0; gwFile.readLine() != null; numIterations++)
                    ;
                gwFile.close();

                // Reopen the file and
                gwFile = new BufferedReader(new java.io.FileReader(jkFileName));
                if (isTTY && numIterations > 1)
                    System.out.print("Processing Line: ");
                int itTrcWidth = 0;
                String gwMsg;
                iteration = 0;
                while ((gwMsg = gwFile.readLine()) != null) {
                    iteration++;
                    if (isTTY)
                        itTrcWidth = printProgress("Processing Line", iteration, itTrcWidth);
                    gwConn.write(gwMsg);
                }
                if (isTTY && numIterations > 1)
                    System.out.println("");
                long endTime = System.currentTimeMillis();

                info("jKool Activity Simulator Replay finished, elasped.time = "
                        + DurationFormatUtils.formatDurationHMS(endTime - startTime));
            }
        } catch (Exception e) {
            if (e instanceof SAXParseException) {
                SAXParseException spe = (SAXParseException) e;
                error("Error at line: " + spe.getLineNumber() + ", column: " + spe.getColumnNumber(), e);
            } else {
                error("Error running simulator", e);
            }
        } finally {
            try {
                Thread.sleep(1000L);
            } catch (Exception e) {
            }
            TNT4JSimulator.disconnect();
        }

        System.exit(0);
    }

    public static JKCloudConnection connect() throws IOException {
        if (StringUtils.isEmpty(jkHost))
            return null;

        if (gwConn != null)
            return gwConn;

        String gwUrl = TNT4JSimulator.getConnectUrl();
        TNT4JSimulator.debug(new UsecTimestamp(),
                "Connecting to service=" + gwUrl + " with access token=" + jkAccessToken + " ...");
        gwConn = new JKCloudConnection(gwUrl, jkAccessToken, logger);
        gwConn.open();

        return gwConn;
    }

    public static void disconnect() {
        if (gwConn != null) {
            try {
                gwConn.close();
            } catch (Exception e) {
            }
            gwConn = null;
        }
    }

    private static int printProgress(String text, long iteration, int trcWidth) {
        if (numIterations == 1)
            return 0;

        int itPct = (int) ((double) iteration / numIterations * 100.0);
        int maxItWidth = (int) (Math.log10(numIterations) + 1);
        String bkSpStr = StringUtils.leftPad("", trcWidth, '\b');
        String trcMsg = String.format(bkSpStr + "%" + maxItWidth + "d (%02d%%)", iteration, itPct);
        trcWidth = trcMsg.length() - bkSpStr.length();
        System.out.print(trcMsg);
        return trcWidth;
    }

    private static <V extends Object> void printMetrics(Map<String, V> map, String label) {
        if (map == null)
            return;
        info(label + ":");
        for (String key : map.keySet()) {
            V value = map.get(key);
            String valueStr = (value instanceof Number ? String.format("%,15d", value)
                    : String.format("%s", value.toString()));
            info(String.format("    %-50s %s", key + ":", valueStr));
        }
    }
}