/*****************************************************************************************************
* Create a TCP/IP server (Using threads to create multiple connections)
* TCP server communication uses both a TCP server socket object and a TCP client socket object.
* A TCP server socket object is used to maintain an open socket connection to all clients.
* Using the accept() method, a TCP server socket will establish a new client socket.
* When a TCP client socket is created you are assigned a port number by the O/S (next port#).
* A new thread is created, and passed the client socket, and the database type. 
* The new thread will handle the communication with the client 
*
* PS. This server takes an SQL command from client,
*     Instantiates a SQL pgm to handle the incoming SQL commands 
*     Sends SQL commands and receives the output data 
*     Returns the output data back to the client
*
* Usage: TCPserver <serverPort> <'oracle'|'mysql'>
* 
* @version 3.0
* @author Sam Sultan
******************************************************************************************************/
import java.io.*;
import java.net.*;

public class TCPserver3
{
    public static void main(String[] args)
    {
        if (args.length == 0 ) {
            System.out.println("Usage: TCPserver3 <serverPort> <dbType>");
            System.exit(0);
        }
        if (args.length == 2 && !(args[1].equals("oracle") || args[1].equals("mysql")) ) 
        {
            System.out.println("<dbType> must be oracle or mysql only");
            System.exit(0);
        }

        String argPort = args[0];                                   //server port number
        String argDB   = (args.length > 1) ? args[1] : "oracle";    //database requested, or oracle if none 

        InetAddress  serverIP;          //IP address object for server    
        InetAddress  clientIP;          //IP address object for client

        int          serverPort;        //server port for listening
        int          clientPort;        //port number for client

        ServerSocket listenTCPsocket;   //TCP socket for listening
        Socket       TCPsocket;         //TCP socket for communication

        int i=0;

        try 
        {
            serverIP   = InetAddress.getLocalHost();            //get local server address
            serverPort = Integer.parseInt(argPort);             //get server port number        

            System.out.println("Server Host Name.....: " + serverIP.getHostName());
            System.out.println("Server IP Address....: " + serverIP.getHostAddress());
            System.out.println("Server Port Requested: " + serverPort);

            listenTCPsocket = new ServerSocket(serverPort);     //socket for listening 

            System.out.println("Server Port Listen on: " + listenTCPsocket.getLocalPort());
            System.out.println();

            while(true)
            {
                System.out.println("Waiting for client connection(s)...... \n");

                TCPsocket = listenTCPsocket.accept();            //creating a Socket
                                                                 //from the ServerSocket
                i++;                
                System.out.println("Client " + i + " connection opened to:");
                
                clientIP    = TCPsocket.getInetAddress();       //get client IP address
                clientPort  = TCPsocket.getPort();              //get port assigned to client 

                System.out.println("Client Host Name.....: " + clientIP.getHostName());
                System.out.println("Client IP Address....: " + clientIP.getHostAddress());
                System.out.println("Client Port Assigned.: " + clientPort);
                System.out.println();

                Socket s = TCPsocket;                                  //assign to variable s  

                TCPserver3Thread t = new TCPserver3Thread(s,argDB);    //instantiate a thread, and
                                                                       //pass it the client socket and dbtype
                t.start();                                             //start the thread 
            }
        }                
        catch(IOException e)
        {
            System.out.println("I/O error: " + e);
            System.exit(-1);
        }
    }
}          

/**************************************************************************************************
* A threaded class that will be created for each client
* When the main thread detects a new client communication, it start a new thread
* Each thread will be responsible for a single client 
***************************************************************************************************/
class TCPserver3Thread extends Thread
{
    InetAddress clientIP;                       //IP address object for client
    int         clientPort;                     //port number for client
    Socket      TCPsocket;                      //TCP socket for communication
    String      DBtype;                         //The database type requested

    TCPserver3Thread(Socket s, String dbType)   //constructor 
    {
        TCPsocket = s;                          //save socket obj.
        DBtype    = dbType;                     //save database type.
    }   

//=======================================================================================
    public void run()
    {
        String recvString;                  //string received from client
        String sendString;                  //string to send client

        String threadName = this.getName();

        try 
        {
            clientIP    = TCPsocket.getInetAddress();       //get client IP address
            clientPort  = TCPsocket.getPort();              //get port assigned to client 

            InputStream        socketIn1 = TCPsocket.getInputStream();          //ref to socket input 
            InputStreamReader  socketIn2 = new InputStreamReader(socketIn1);    //convert to 16 bit
            BufferedReader     socketIn  = new BufferedReader(socketIn2);       //to use readLine()

            OutputStream       socketOut1 = TCPsocket.getOutputStream();        //ref to socket output 
            OutputStreamWriter socketOut2 = new OutputStreamWriter(socketOut1); //convert to 16 bit
            BufferedWriter     socketOut3 = new BufferedWriter(socketOut2);     //buffer output
            PrintWriter        socketOut  = new PrintWriter(socketOut3);        //to use println()

            while(true)
            {
                /*------------------ receiving data ---------------------*/
    
                System.out.println(threadName + " waiting for client input from.... "
                                              + clientIP + ":" + clientPort);
                System.out.println();

                recvString = socketIn.readLine();                           //receive data from the socket

                if((recvString==null) || recvString.trim().length()==0)     //if empty 
                    break;      

                System.out.println("Received..: " + recvString);

                /*------------------ processing data ---------------------*/    
                
                String user = "demo";                                       //set default values
                String pswd = "demo";                                       //if nothing is received
                String db   = DBtype.equals("oracle") ? "app12c" : user;

                String lines[] = recvString.split("\t");                    //split on '\t' 
                                                                            //'\n' is NG. it is used to terminate stream                                                                            

                if (lines.length > 0 && lines[0].indexOf("USER:") >= 0)     //if user is received, use it instead
                {
                    user = lines[0].substring(lines[0].indexOf(":")+1);     
                    lines[0] = "";
                }
                if (lines.length > 1 && lines[1].indexOf("PSWD:") >= 0)     //if pswd is received, use it instead       
                {
                    pswd = lines[1].substring(lines[1].indexOf(":")+1);
                    lines[1] = "";
                }
                if (lines.length > 2 && lines[2].indexOf("DBTYPE:") >= 0)   //if dbtype is received, use it instead
                {
                    DBtype = lines[2].substring(lines[2].indexOf(":")+1);
                    db     = DBtype.equals("oracle") ? "app12c" : user; 
                    lines[2] = "";
                }

                String sql = "";                                            //re-concatenate all the lines
                for(int i=0; i<lines.length; i++)
                    sql += lines[i] + "\n";
                    
                SQLaccess pgm1 = new SQLaccess(user, pswd, db, DBtype);     //instantiate a SQL processing pgm
                
                sendString = pgm1.execSQL(sql); 

                /*------------------ sending data ---------------------*/    

//              System.out.println("Sending...: " + sendString);            //no need    

                socketOut.println(sendString);                              //send data thru the socket     
                socketOut.flush();                                          //flush buffer
            }

            System.out.println("Closing connection to: "+ clientIP +":"+ clientPort);
            socketIn.close();
            socketOut.close();
            TCPsocket.close();
        }
        catch(IOException e)
        {
            System.out.println("I/O error: " + e);
            System.exit(-1);
        }
    }       
}
/**************************************************************************************************/