/*                          
 *      mBench: The Open Source Micro Benchmark Tool 
 *                                             
 *      Distributable under GPL license. 
 *      See terms of license at gnu.org.                 
 *
 *      Copyright (C) 2005 Sumisho Computer Systems Corp.
 */
package jp.co.scs.mbench.component.quadruplenode;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import jp.co.scs.mbench.Benchmark;
import jp.co.scs.mbench.BenchmarkTerminateException;
import jp.co.scs.mbench.jdbc.DataSourceManager;

/**
 *      Benchmark which insert one row sequentially from 1 to 60000 rows
 *      on table t1 through quadruple SQL nodes
 * 
 *      @author Masato Koga
 */
public class InsertT1Benchmark implements Benchmark {
    /**
     *  Count of connection
     */
    private static int conCount = 0;
    
    /**
     *  The number of delete data
     */
    private static final int insertNumber = 60000;
    
    /**
     *  Value of primary key
     */
    private int key = 0;

    /**
     *  ID of Connection
     */
    private int conId = 0;
    
    /**
     *  Name of the component
     */
    private String benchmarkName = null;
    
    /**
     *  Labels which benchmark component mesures
     */
    private String[] optionLabels = {"status", "primary-key", "retry"};
    
    /**
     *  Connection to database
     */
    private Connection con = null;
    
    /**
     *  Statement of SQL
     */
    private Statement stmt = null;
    
    /** 
     *  Get the component name.
     * 
     *  @return Name of component
     */
    public String getBenchmarkName() {
        return this.benchmarkName;
    }

    /**
     *  Set the component name.
     * 
     *  @param benchmarkName Name of component
     */
    public void setBenchmarkName(String benchmarkName) {
        this.benchmarkName = benchmarkName;
    }
    
    /**
     *  Get labels mesured by benchmark component.
     * 
     *  @return Label of items mesured by benchmark component
     */
    public String[] getOptionLabels() {
        return this.optionLabels;
    }
    
    /**
     *  Set labels mesured by benchmark component.
     * 
     *  @param optionLabels Labels which benchmark component mesures
     */
    public void setOptionLabels(String[] optionLabels){
        this.optionLabels = optionLabels;
    }
    
    /**
     *  Initialize. Get datasource and establish connection.
     *  Target SQL node is decided by round-robin algorithm.
     * 
     *  @throws BenchmarkTerminateException
     */
    public synchronized void init() throws BenchmarkTerminateException {
        try {
            DataSource dataSource = null;
            if (conCount % 4 == 0) {
                dataSource = DataSourceManager.getDataSource("mysql_con1");
            } else if (conCount % 4 == 1) {
                dataSource = DataSourceManager.getDataSource("mysql_con2");
            } else if (conCount % 4 == 2) {
                dataSource = DataSourceManager.getDataSource("mysql_con3");
            } else {
                dataSource = DataSourceManager.getDataSource("mysql_con4");
            }
            conCount++;
            this.conId = conCount;
            
            this.con = dataSource.getConnection();
            this.stmt = con.createStatement();
        } catch (Exception ex) {
            throw new BenchmarkTerminateException(ex);
        }
    }

    /**
     *  Execute benchmark transaction.
     *  <br>
     *  A transaction perform;<br>
     *  <br>
     *  insert into t1 values(${key}, 0);<br>
     *  <br>
     *  ${key} is sequentially decided from 1 to 60000.<br>
     *  Retry in case exception occures before commit.<br>
     *  Return Primary Key and Value of retry number as logging.<br>
     */
    public String[] execute() {
        if (this.key == 0) {
            this.key = insertNumber / conCount * (this.conId - 1) + 1;
        } else {
            key++;
        }
        return doExecute(key, 0);
    }

    /**
     *  Disconnect connection.
     */
    public void clean() {
        try {
            if (this.stmt != null) {
                this.stmt.close();
            }
            if (this.con != null) {
                this.con.close();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
    
    private String[] doExecute(int key, int retry) {
        try {
            // AutoCommit ON
            this.con.setAutoCommit(true);
            this.con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            
            String q1 = "insert into t1 values(" + key + ", 0)";
            this.stmt.executeUpdate(q1);
            return new String[]{"commit", String.valueOf(key),
                    String.valueOf(retry)};
        } catch (SQLException ex) {
            ex.printStackTrace();
            if (this.con != null) {
                key++;
                return doExecute(key, ++retry);
            } else {
                throw new RuntimeException("connection lost");
            }
        }
    }
}
