package com.clientsoft.webpack.util;

import java.io.*;
import java.net.*;

import java.util.HashMap;
import java.util.Vector;
import java.util.Iterator;
import java.util.Hashtable;
import java.util.StringTokenizer;

import javax.xml.parsers.*;

import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Document;

import org.apache.xml.serialize.Method;
import org.apache.xml.serialize.SerializerFactory;
import org.apache.xml.serialize.XMLSerializer;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.DOMSerializer;

public class ServletTester {

    public boolean run = true;
    public Statistics stats = new Statistics();

    private static int iterations = 0;
    private static int clients = 0;
    private static Vector threads = new Vector();
    private static URL url = null;
    final static DocumentBuilderFactory parserFactory = DocumentBuilderFactory.newInstance();
    final static SerializerFactory print_factory = SerializerFactory.getSerializerFactory(Method.XML);

    public ServletTester () throws Exception {super();}

    public static void main(String[] args) {
        ServletTester cb = null;
        try {
            for(int i = 0; i < args.length; i++) { // collect command line options

                if( args[i].equalsIgnoreCase("-i") ) {
                    i++;
                    iterations = Integer.parseInt(args[i]);
                } else if( args[i].equalsIgnoreCase("-w") ) {
                    i++;
                    clients = Integer.parseInt(args[i]);
                } else if( args[i].equalsIgnoreCase("-u") ) {
                    i++;
                    url = new URL( args[i] );
                }
            }
            url.getHost(); // test to make sure url is valid
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }

        try {
            cb = new ServletTester();
            cb.doWork();
        } catch(Exception cbe) {
            System.err.println( cbe.getMessage() );
            cbe.printStackTrace();
        } finally {
            cb.stats.print();
            System.exit(-2);
        }
    }

    void doWork() throws Exception {
        for(int i = 0; i < clients; i++ ) {
            Worker worker = new Worker();
            Thread t = new Thread( worker );
            t.start();
            threads.add(worker);
        }
        while(run) {
            Thread.sleep(2000);
            int lastReported = 0;
            int percent_complete = stats.getPercentComplete();
            if(percent_complete > lastReported) {
                lastReported = percent_complete;
                System.out.println( "Percent Complete: " + percent_complete );
                if((percent_complete % 5) == 0) {
                    System.out.println( "Transactions per sec: " + stats.transPerSec() );
                }
            }
            run = !threads.isEmpty();
        }
        stats.print();
    }

    class Worker implements Runnable {
        Document request_doc = null;
        Worker() throws Exception {
            DocumentBuilder parser = ServletTester.getParser();
            String str_xml = new String("<request><echo name=\"value\">test me</echo></request>");
            ByteArrayInputStream in = new ByteArrayInputStream(str_xml.getBytes());
            request_doc = parser.parse(in);
        }

        public void run() {
            System.out.println("Starting");
            for(int i = 0; i < iterations; i++) {
                try {
                  long start = System.currentTimeMillis();
                  sendXmlTransaction(url,request_doc);
                  long end = System.currentTimeMillis();
                  stats.addToRequestCount();
                  stats.totalTime(start,end);
                } catch(Exception e) {
                    e.printStackTrace();
                    stats.addToErrorCount();
                }
            }
            threads.remove(this);
        }

        /** used by the AgentServlet to send xml resonse
         * @param url Url to send xml to
         * @param doc w3c Docuemnt to send
         * @throws IOException Throws when can not locate URL
         * @throws SAXException thrown on parsing exception
         * @return the responce document that is part of the CBProtocol
         */
        public Document sendXmlTransaction (final URL myUrl, Document doc)
        throws Exception {

            URLConnection conn;
            conn = myUrl.openConnection ();
            conn.setAllowUserInteraction(true);
            conn.setDoOutput(true);
            conn.setRequestProperty ("Content-Type", "text/xml");
            ByteArrayOutputStream bout = ServletTester.getByteArrayStream(doc);
            conn.setRequestProperty("Content-Length", String.valueOf( bout.size() ));
            OutputStream out = conn.getOutputStream();
            bout.writeTo(out);
            out.flush ();
            out.close ();	// ESSENTIAL!

            InputStream in = conn.getInputStream();
            doc = ServletTester.getParser().parse(in);
            return doc;
        }
    }

    class Statistics implements Cloneable {
        int total = (int)iterations * clients;
        int request_count = 0;
        int error_count = 0;
        long total_time = 0;

        synchronized void addToRequestCount() {request_count++;}
        synchronized void addToErrorCount() {error_count++;}

        void print() {
            System.err.println("\n\n\nSuccessful = " + request_count);
            System.err.println("Error = " + error_count);
            System.err.println("Trans per sec: "+ transPerSec() );
        }

        synchronized int getPercentComplete() {
            double x,y,z;
            x = request_count;
            y = error_count;
            z = total;
            return new Double( (x + y) / z * ((double)100) ).intValue();
        }

        synchronized void totalTime(long start, long end) {
            total_time = total_time + (end - start);
        }

        synchronized int transPerSec() {
            double x = request_count;
            double t = total_time;
            double th = .001;
            return new Double( ((x*1000)/t) ).intValue();
        }

        public synchronized Object clone() {
            try {
                return this.clone();
            } catch(Exception e) {throw new RuntimeException( e.getMessage() );}
        }
    }

    /** Method added for convience to count content length for responce.
     * @param doc XML docuemnt to buffer
     * @throws IOException thrown if stream is closed
     * @return the serialized document as ByteArrayStream
     */
    final static ByteArrayOutputStream getByteArrayStream(Document doc) throws Exception {
        ByteArrayOutputStream retval = new ByteArrayOutputStream();
         OutputFormat _format = new OutputFormat();
        _format.setPreserveSpace(true); // bug fix (\r\n are converted to HEX20 with out fix)
        _format.setIndenting(true); //pretty print
        DOMSerializer xmlWriter = print_factory.makeSerializer(retval, _format).asDOMSerializer();
        xmlWriter.serialize(doc);
       return retval;
    }

    final static DocumentBuilder getParser() throws Exception {
        DocumentBuilder parser = null;
        parser = parserFactory.newDocumentBuilder();
        return parser;
    }
} 