net.tjado.jcdbe.jcdbe.java Source code

Java tutorial

Introduction

Here is the source code for net.tjado.jcdbe.jcdbe.java

Source

/*
 * This file is part of JCDBE - Java Connect Database Engine
 * 
 * Copyright (C) 2013  Tjado Mcke
 * 
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 */

package net.tjado.jcdbe;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.ini4j.Ini;
import org.ini4j.InvalidFileFormatException;

/**<pre>
 * JCDBE - Java Central DataBase Engine
 * 
 * Main structure for connecting to a large amount of databases
 * DB connections/query executions will be done threaded.
 * 
 * 
 * Project Overview:
 * 
 *  jcdbe.java                  -> main
 *  jcdbeTest.java              -> simple JDBC test class
 *  DatabaseThreadSlave.java    -> worker object for slave threads
 *  
 *  DatabaseOracle.java         -> Oracle Database Layer
 *  DatabaseList.java           -> synchronized access to HashMap with all DB infos
 *  
 *  Input.java                  -> Interface specification for input
 *      InputCSV.java                   -> CSV input
 *      InputDatabase.java              -> DB input
 *  
 *  Output.java                 -> Interface specification for output
 *      OutputCSV.java                  -> CSV output
 *      OutputDatabase.java             -> DB output
 *      OutputDummy.java                -> Dummy output
 *  
 *  Log.java                    -> Logging Helper Class for log4j (from apache)
 *  
 * </pre>
 * 
 * @author  Tjado Mcke <tjado@maecke.de>
 * @version 0.4
 * 
 */

public class jcdbe {

    // Logger
    private static Log log = Log.getInstance();

    // start time will be saved to this variable for benchmark
    private static long measureTimeStart = 0;

    // queue for (Runnable) jobs
    private static ArrayBlockingQueue<Runnable> workQueue = null;

    // input class of database list (InputDatabase, InputCSV)
    private static String inputClass = null;

    // output class of results (OutputDatabase, OutputCSV)
    private static String outputClass = null;

    // working directory
    private static String workingDir = System.getProperty("user.dir");

    // default max threads in pool (running + non-running)
    private static int threadMax = 50;

    // default maximum parallel running threads
    private static int threadRun = 25;

    // default thread keep alive time
    private static int threadTTL = 10;

    // default log4j path to property file
    private static String log4jPropertyFile = "config/log4j.properties";

    // default log4j path to property file
    private static String jdbcPropertyeFile = "config/jdbc.properties";

    // default jdbc prefix
    private static String jdbcPrefix = "jdbc:oracle:thin:@";

    // Session Data Unit (SDU) size for Oracle Net8 handshake (e.g. setting this for Oracle CMAN bug
    // 13989986)
    private static Integer sduSize = null;

    // advanced debugging
    public static boolean advDebugging = false;

    public static void main(String[] args) throws Exception {
        // print banner
        System.out.println("------------------------------------------");
        System.out.println("| Java Central DataBase Engine v0.4 beta |");
        System.out.println("------------------------------------------");

        // path to the ini configuration
        String configPath = workingDir + "/config/jcdbe.ini";

        // If the first CLI argument doesn't begin with "-" it must be a config file.
        // We need to do this, to have the possibility to specify other ini config files
        // The ini config file is necessary for the input/output classes, which again specify the
        // required CLI arguments
        if (args.length != 0 && !args[0].startsWith("-")) {
            configPath = args[0];
            // remove first argument from CLI arguments array, so it won't be validated by CLI argument
            // parser
            args = Arrays.copyOfRange(args, 1, args.length);
        }

        // initialize the ini configuration to get required parameters
        Ini config = initConfig(configPath);

        // initialize Logger
        log.init(log4jPropertyFile);

        // setting jdbc property file
        DatabaseOracle.setPropertyFile(jdbcPropertyeFile);

        // declare the input/output classes
        Input input = (Input) Class.forName(inputClass).getDeclaredMethod("getInstance").invoke(null,
                (Object[]) null);
        Output output = (Output) Class.forName(outputClass).getDeclaredMethod("getInstance").invoke(null,
                (Object[]) null);

        // declare options and parser for the CLI arguments
        Options options = new Options();
        CommandLineParser parser = new PosixParser();

        // add "help" CLI argument
        options.addOption("h", "help", false, "print this usage information");

        // add further CLI arguments by the input/output classes
        input.setCLI(options);
        output.setCLI(options);

        CommandLine cli = null;
        try {
            // parse the CLI arguments
            cli = parser.parse(options, args);

            if (cli.hasOption("help") || cli.getOptions().length == 0) {
                HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp("java -jar jcdbe.jar [options]", options);

                System.exit(1);
            }
        } catch (ParseException e) {
            System.out.println(e.getMessage());
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp("java -jar jcdbe.jar [options]", options);

            System.exit(1);
        }

        // output engine config after initializing of Logger
        log.debug("[CONFIG] Working Directory: " + workingDir);
        log.debug("[CONFIG] Input class:  " + inputClass);
        log.debug("[CONFIG] Output class: " + outputClass);
        log.debug("[CONFIG] JDBC URL prefix: " + jdbcPrefix);
        log.debug("[CONFIG] Java Library Path: " + System.getProperty("java.library.path"));
        log.debug("[CONFIG] Oracle SDU size: " + sduSize);
        log.debug("[CONFIG] Max. threads: " + threadMax);
        log.debug("[CONFIG] Max. running threads: " + threadRun);
        log.debug("[CONFIG] Thread idle timeout: " + threadTTL);
        log.debug("[CONFIG] Advanced Debugging: " + advDebugging);

        // validate Input arguments
        input.validateParameters(cli, config);
        // validate Output arguments
        output.validateParameters(cli, config);

        // start benchmark time
        measureTimeStart();

        log.info("[INPUT] Initialization");
        // run input init and check if it was successfull....
        if (!input.init()) {
            log.fatal("Error during input init...");
            System.exit(2);
        }
        log.info("[OUTPUT] Initialization");
        // run output init and check if it was successfull....
        if (!output.init()) {
            log.fatal("[OUTPUT] Error during output init...");
            System.exit(3);
        }

        // init thread pool
        workQueue = new ArrayBlockingQueue<Runnable>(99999);
        ThreadPoolExecutor threads = new ThreadPoolExecutor(threadRun, threadMax, threadTTL, TimeUnit.SECONDS,
                workQueue);

        // get DatabaseList object which will manage all database infos (url, username, pw, status...)
        DatabaseList dbList = input.getDatabaseList();

        if (dbList.size() == 0) {
            log.info("[QUEUE] database list is empty... nothing do to.");
            System.exit(1);
        }

        // get all SQL queries to execute
        // Integer = Query ID
        // String = SQL Text
        Map<Integer, String> queries = input.getQueries();

        log.info("[QUEUE] Starting Threads");

        // loop thru dbList to create & execute/queue all threads
        for (Integer id : dbList) {
            try {
                // create new runnable instance
                DatabaseThreadSlave slaveThread = new DatabaseThreadSlave(id, dbList, queries, output, jdbcPrefix,
                        sduSize);
                // insert runnable instance into dbList
                dbList.setThread(id, slaveThread);

                // add runnable instance into thread pool queue
                threads.execute(slaveThread);
            } catch (Exception e) {
                advDebug(e);
                log.warn("Exception in thread-starter loop (DBID: " + id + "): " + e.getMessage());
            }
        }

        //
        // waiting for all threads to complete
        //
        // the timeout handling will be done completely over JDBC
        // see docs for more information
        //

        while (!dbList.isFinished() || threads.getActiveCount() > 0) {
            Thread.sleep(500);
        }

        log.info("[QUEUE] Shutting down all threads");
        threads.shutdown();

        Thread.sleep(2000);

        log.info("[INPUT] close input...");
        input.close();

        log.info("[OUTPUT] close output...");
        output.close();

        // end time-benchmark and output
        measureTimeEnd();

        // rc=0
        System.exit(0);
    }

    private static Ini initConfig(String iniFile) {

        // temporary string for storing/checking parameter values
        String checkParam = null;

        Ini ini = null;
        try {
            ini = new Ini(new File(iniFile));
        } catch (InvalidFileFormatException e) {
            System.out.println("Error: Invalid format of config file!");
            System.exit(1);
        } catch (IOException e) {
            System.out.println(iniFile);
            System.out.println("Error: Config file not existent or readable!");
            System.exit(1);
        }

        // package name
        // because refactoring would be harder with hardcoded names
        String packageName = jcdbe.class.getPackage().getName();

        // input class
        try {
            inputClass = packageName + ".Input" + ini.get("main", "input").trim();
        } catch (NullPointerException e) {
            System.out.println("Error: engine input parameter not found!");
            System.exit(1);
        }
        // check if class exists
        try {
            Class.forName(inputClass);
        } catch (ClassNotFoundException e) {
            System.out.printf("Error: %s class not found!\n", inputClass);
            System.exit(1);
        }

        // output class
        try {
            outputClass = packageName + ".Output" + ini.get("main", "output").trim();
        } catch (NullPointerException e) {
            System.out.println("Error: engine output parameter not found!");
            System.exit(1);
        }
        // check if class exists
        try {
            Class.forName(outputClass);
        } catch (ClassNotFoundException e) {
            System.out.printf("Error: %s class not found!\n", outputClass);
            System.exit(1);
        }

        // log4j property file
        checkParam = ini.get("main", "log4jPropertyFile");
        if (checkParam != null) {
            log4jPropertyFile = checkParam;
        }

        // jdbc property file
        checkParam = ini.get("main", "jdbcPropertyeFile");
        if (checkParam != null) {
            jdbcPropertyeFile = checkParam;
        }

        // threadMax
        checkParam = ini.get("main", "threadMax");
        if (checkParam != null) {
            threadMax = Integer.parseInt(checkParam);
        }

        // threadRun
        checkParam = ini.get("main", "threadRun");
        if (checkParam != null) {
            threadRun = Integer.parseInt(checkParam);
        }

        // threadTTL
        checkParam = ini.get("main", "threadTTL");
        if (checkParam != null) {
            threadTTL = Integer.parseInt(checkParam);
        }

        // oracleHome
        String oracleHome = ini.get("main", "oracleHome");

        // if oracleHome is set then using thick client
        if (oracleHome != null) {
            String oraLib = oracleHome + "/lib/libocijdbc11.so";

            // check if libocijdbc11.so is found in oracle home
            File f = new File(oraLib);
            if (!f.exists()) {
                System.out.printf("Error: Thick client lib not found: %s\n", oraLib);
                System.exit(1);
            }

            // set thick client prefix
            jdbcPrefix = "jdbc:oracle:oci:@";

            // set library path for thick client
            String lib = System.getProperty("java.library.path");
            System.setProperty("java.library.path", lib + ":" + oracleHome + "/lib");
        }

        // Session Data Unit (SDU) size
        // for Oracle Net handshake (e.g. setting this for Oracle CMAN bug 13989986)
        // Will only be set for Oracle Connect Strings!
        checkParam = ini.get("main", "oracleSDU");
        if (checkParam != null) {
            try {
                Integer tempSDU = Integer.parseInt(checkParam);

                // SDU needs to be power of two
                if (tempSDU != 0 && (tempSDU & (tempSDU - 1)) == 0) {
                    sduSize = tempSDU;
                }

            } catch (NumberFormatException e) {
                sduSize = null;
            }
        }

        // threadMax
        checkParam = ini.get("main", "threadMax");
        if (checkParam != null) {
            threadMax = Integer.parseInt(checkParam);
        }

        // printStackTrace
        checkParam = ini.get("main", "printStackTrace");
        if (checkParam != null) {
            advDebugging = Boolean.parseBoolean(checkParam);
        }

        // return config handler for further processing in the Input* and Output* classes
        return ini;
    }

    private static void measureTimeStart() {
        measureTimeStart = System.currentTimeMillis();
        log.info("[BENCH] Start: " + measureTimeStart);
    }

    private static void measureTimeEnd() {
        long dateEnd = System.currentTimeMillis();
        log.info("[BENCH] End: " + dateEnd);
        log.info("[BENCH] Time: " + (dateEnd - measureTimeStart) / 1000 + " seconds");
    }

    public static void advDebug(Exception e) {
        if (advDebugging)
            e.printStackTrace();
    }

}