// SQLRetrieverThread class
// Programmed by Alan Maxwell
//
// Revision History
// ----------------
// v1.21: 13 Dec 2002
//   Added an option 'doRowTotal' which specifies whether the result page should
//   show the total number of rows the query returned when only a subset is
//   displayed in summary... access via get/set methods...
// v1.20: 28 Nov 2002
//   Renamed the class from SQLRetriever to SQLRetrieverThread!
//   Rewrite the class to implement the 'runnable' interface, allowing this
//   class to run as a separate thread. This will allow the SQL retrieval
//   to proceed in the background while the user/gui gets on with other
//   things.
//   The controlling class can run in a while loop and test for
//   'isQueryFinished()' periodically. The query can ended prematurely by
//   calling 'stopQuery()'.
// v1.05: 30 Oct 2002
//   Worked on a 'formatter' array which takes an array of SQLDataFormatter
//   objects for each of the columns describing how to format them properly.
//   This 'formatting' array is built up ONCE (as the heading row is formed)
//   with a call to getSQLDataFormatterArray(). Then, as we iterate through the
//   returned rows, we can refer to the format 'lookup table' and return the
//   nicely formatted data...
//
// v1.02: 25 Oct 2002
//   Removed the CSVSafeString() function which checks that strings have no
//   commas or quotes in them - Mike will make sure that the SQL database
//   does not have these in the data...
//
// v1.01: 15 Oct 2002
//   Revised the constructor to accept the new SQLServerInfoJDBC class which
//   more tightly and neatly encompasses all the info associated with a JDBC
//   SQLServer
//
// v1.00: 03 Oct 2002
//   Initial Release
//
// Synopsis
// --------
// This class allows the user to specify details of an MS SQL Server database
// and then execute SQL queries on that database (assuming a successful
// connection can be established) to return the results as an neat HTML
// document, with the query results returned as a neatly formatted table.
//
// The user can apply special formatting to specific data columns by
// implementing a subclass of this object, and overriding the formatColumnData()
// method to return the desired (string) representation of the data...

package net.alanmaxwell.sql;

import org.apache.log4j.Logger;

import uk.ac.roe.wfau.StringToRADec;
import uk.ac.roe.wfau.UploadFileToTable;
import uk.ac.roe.wfau.WSAHTMLSchema;

import java.io.*;
import java.util.*;
import java.util.zip.*;
import java.util.StringTokenizer;

//import FormatRS.*;
import java.sql.*;

import javax.servlet.http.*;
import net.mar.FormatRS;
import net.mar.ParseVOT;
import net.mar.FITSWriter;
import net.mar.RetrieverMapper;
import net.mar.UCDManager;
import net.mar.RetrieverManager;
import net.alanmaxwell.html.HTMLStrings;

import net.alanmaxwell.sql.SQLServerInfoJDBC;
import net.alanmaxwell.sql.SQLDataFormatter;

import javax.mail.*;
import javax.mail.internet.*;

/**
 * After providing the correct initialisation, the run() method call be called
 * either specifically or as a separate thread to have the SQL query executed.
 * The value of isQueryFinished() can be repeatedly tested to see if the query
 * has completed before calling getResultsHTML() to return the HTML result of
 * the query (usually a results table with pre/post amble but sometimes an error
 * message).
 * 
 * @author Alan Maxwell
 */
public class GenericSQLRetrieverThread implements Runnable {
    // Some constants...
    public static final int SQLROWS_DEFAULT = 50; // Default returned # table
                                                  // rows

    public static final int SQLROWS_UPPERLIMIT = 100; // Maximum EVER returned
                                                      // rows..

    public static final int ROWSWRITTEN_UPPERLIMIT = 150000000;

    public static final int FACTOR = 2; // total rows cycle through = factor *
                                        // rowswrittenupperlimit
    public static final int MAXRESBUFFERSIZE=1024*1024;
    public static final int OUTPUT_NONE = 0;

    public static final int OUTPUT_CSV = 1;

    public static final int OUTPUT_FITS = 2;

    public static final int OUTPUT_VOT = 3;

    public static final int COMPRESS_NONE = 0;

    public static final int COMPRESS_ZIP = 1;

    public static final int COMPRESS_GZIP = 2;

    public String uploadFile = null;
    public String dataType = null;

    public BufferedReader uploadBufferedReader;

    public static final int OUTPUT_BUFFERSIZE = 524288; // .5Meg file buffer!

    private static UCDManager ucdMgr;
    
    private String fromAddress="";
    private String Subject="";

    private static RetrieverManager retMgr;

    private static RetrieverMapper retMap;

    // Important vars that are accessed by several functions, not synchronized!
    protected Connection databaseConnection = null;

    protected Statement databaseQuery = null;

    protected ResultSet results = null;

    protected ResultSetMetaData resultsMetaData = null;

    protected int row = 0;

    protected int rowsTotalUpperLimit = 0;

    protected int rowsWrittenUpperLimit = 0;

    protected int rowsWrittenToFile = 0;

    protected int resultsNumColumns = 0;

    protected String columnData = "";

    protected String[] columnNames = null;

    protected SQLDataFormatter[] columnFormats = null;

    FormatRS frs = new FormatRS();

    String CSVName = "";

    String CSVFileName = "";

    String FITSName = "";

    String FITSFileName = "";

    String emailAddress = null;

    // 'static' initialiser is called when class is first loaded to initialise
    // and install the database driver(s)!
    static {
        // Load in the Microsoft-provided JDBC SQL Server driver
        try {
            Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDriver");
            Class.forName("net.sourceforge.jtds.jdbc.Driver");

        } catch (ClassNotFoundException e) {
            // A class not found error means the SQL driver class could not be
            // found.
            // Which means that this driver would not be available until this is
            // remedied. This flag variable will signal this to the class!

            System.err.println("SQLRetrieverThread::static(): Could not load SQLServerDriver class!");
        }
        ;


    }; // END of 'static' initialiser

    // The CSVNumber variable is used to generate unique filenames for CSV files
    // when people request them. The idea when CSV is made, the
    // getNextCSVNumber()
    // method will be called to return the next number in sequence and also to
    // increment the counter. Note that thisd is a 'synchronised' method,
    // meaning
    // that it is THREAD SAFE and so can be called by any thread (as long as it
    // is
    // through the getNextCSVNumber() method. DO NOT MODIFY CSVNumber variable
    // DIRECTLY!!
    private static final int CVSNUM_UPPERLIMIT = 60000;

    private static int CSVNumber = 0; // 'Unique' counter for CSV
                                      // requests/filenames

    private static synchronized int getNextCSVNumber() {
        CSVNumber++;

        if (CSVNumber > CVSNUM_UPPERLIMIT) {
            CSVNumber = 0;
        }
        ;

        return CSVNumber;
    };

    
    public String [][] showArray = { {"specObjId"},{"multiframeidh","multifrafffmeid"} };
    boolean [] showBoolean = new boolean [] {false};
    
    
    public StringBuffer getShowTitles () {
        StringBuffer st=new StringBuffer("");
            if (showBoolean[1]) {
                st.append("<th nowrap=\"nowrap\">CAS</th>");
            }
            if (showBoolean[2]) {
                st.append("<td>Link</td>");
            }
            
        
        return st;
    }
    public StringBuffer getShowLinks (ResultSet rs) {
        StringBuffer st=new StringBuffer("");
        try {
            if (showBoolean[1]) {
                st.append("<td><a href=\"http://cas.sdss.org/astro/en/tools/explore/obj.asp?sid="
                        +rs.getString("specObjID") + 
                        "\"  onclick=\"popWin=open('http://cas.sdss.org/astro/en/tools/explore/obj.asp?sid=" +
                        rs.getString("specObjID") + 
                        "','cas','');popWin.focus();return false\">cas</a></td>");
            }
            
 
            if (showBoolean[2]) {
                st.append("");
            }
        }
        catch (Exception e){
            
        }
        
        return st;
    }
    public void showArrayPresent (ResultSetMetaData rsmd) {
        boolean isPresent =false;
        
        try {
        //System.out.println(showArray.length);
        String [] tArray;
        byte [] tBoolean;
        showBoolean = new boolean [showArray.length+1];
        
        for (int i=0;i < showArray.length; i++) {
           // System.out.println("iarray"+i);
            tArray=showArray[i];
            tBoolean = new byte [tArray.length] ;
            for (int k=1; k<= rsmd.getColumnCount();k++) {
                for (int j=0;j < tArray.length; j++)  {
                if (rsmd.getColumnName(k).equalsIgnoreCase(tArray[j])) {
                    tBoolean[j]=1;
                }
                }
            }
           // System.out.println("array"+i);
            int tTot=0;
            for (int j=0;j < tArray.length; j++)  {
                tTot=tTot+tBoolean[j];
            }
            if (tTot==tArray.length) {
                showBoolean[i+1]=true;
                showBoolean[0]=true;
            }
            //System.out.println(":"+showBoolean[i]);
        }
        }
        catch (Exception e) {
            System.out.println(e);
            
        }
    //    return showBoolean;
    }
    

    public GenericSQLRetrieverThread(SQLServerInfoJDBC newServerInfo) {
        setServerInfo(newServerInfo);
    };

    protected SQLServerInfoJDBC serverInfo = null;

    public void setFromAddress(String fa) {
        fromAddress=fa;
    };
    
    public void setSubject(String sbj) {
        Subject=sbj;
    };
    
    protected synchronized void setServerInfo(SQLServerInfoJDBC newServerInfo) {
        serverInfo = newServerInfo;
    };
    
    UploadFileToTable upTable=null;
    
    public void setUploadFileToTable(UploadFileToTable newUpTable) {
        upTable=newUpTable;
    }
 

    public synchronized void setUploadFile(String newUploadFile) {
        uploadFile = newUploadFile;
    }
    public synchronized void setUploadFile(String newUploadFile,String newdataType) {
        uploadFile = newUploadFile;
        dataType=newdataType;
    }

    public synchronized void setUpBufferedReader(BufferedReader newUpBufferedReader) {
        BufferedReader uploadBufferedReader = newUpBufferedReader;
    }

    protected boolean queryTerminated = false;

    public synchronized void setQueryTerminated(boolean status) {
        queryTerminated = status;
    };

    public synchronized boolean isQueryTerminated() {
        return queryTerminated;
    };

    protected boolean queryProcessing = true;

    public synchronized void setDBConnectionClosed() {
        if (databaseConnection != null) {
            System.out.println("im closing");
            try {
                databaseQuery.setQueryTimeout(1);
                //databaseConnection.close();
            } catch (SQLException se) {
                System.out.println(se);
            }
            ;
        }
        ;
    }

    public synchronized void setQueryProcessing(boolean status) {
        queryProcessing = status;
    };

    public synchronized boolean isQueryProcessing() {
        return queryProcessing;
    };

    boolean mightBeBackground = false;

    public synchronized void setMightBeBackground(boolean newMBB) {
        mightBeBackground = newMBB;
    };

    public synchronized boolean isMightBeBackground() {
        return mightBeBackground;
    };

    public synchronized void setEmailAddress(String newEmailAddress) {
        emailAddress = newEmailAddress;
    }

    public synchronized String getEmailAddress() {
        return emailAddress;
    }

    protected String radius = "0";

    public synchronized void setRadius(String newRadius) {
        radius = newRadius;
    }

    protected String pair = "nearest";

    public synchronized void setPair(String newPair) {
        pair = newPair;
    }

    protected int rowsHTML = SQLROWS_DEFAULT;

    public synchronized void setRowsHTML(int newRows) {
        if (newRows > SQLROWS_UPPERLIMIT) {
            rowsHTML = SQLROWS_UPPERLIMIT;
        } else if (newRows < 0) {
            rowsHTML = 0;
        } else {
            rowsHTML = newRows;
        }
        ;
    };

    public synchronized int getRowsHTML() {
        return rowsHTML;
    };

    public synchronized int getRowsReturned() {
        return row;
    };

    protected boolean doRowTotal = true;

    public synchronized boolean getDoRowTotal() {
        return doRowTotal;
    };

    public synchronized void setDoRowTotal(boolean status) {
        doRowTotal = status;
    };

    protected String SQLQuery = null;

    public synchronized void setSQLQuery(String newQuery) {
        SQLQuery = newQuery;

        setQueryProcessing(true); // We haven't got the results for this
                                  // query...
    };

    public synchronized String getSQLQuery() {
        return SQLQuery;
    };

    protected int dataOutputFormat = OUTPUT_NONE;

    public synchronized void setOutputFormat(int newFormat) {
        dataOutputFormat = newFormat;
    };

    public synchronized int getOutputFormat() {
        return dataOutputFormat;
    };

    protected int dataOutputCompression = COMPRESS_NONE;

    public synchronized void setOutputCompression(int newCompression) {
        dataOutputCompression = newCompression;
    };

    public synchronized int getOutputCompression() {
        return dataOutputCompression;
    };

    protected StringBuffer htmlResults = new StringBuffer("");

    protected synchronized void clearResultsHTML() {
        htmlResults = new StringBuffer("");
    };

    protected synchronized void addToResultsHTML(String newLine) {
        htmlResults.append(newLine);
    };

    public synchronized String getResultsHTML() {
        return htmlResults.toString();
    };

    protected String HTMLTableRowColorHeader = WSAHTMLSchema.headRow;

    //protected String HTMLTableRowColorOdd = "#99CCFF";
    //protected String HTMLTableRowColorOdd = "#FFCCCC";
    protected String HTMLTableRowColorOdd = WSAHTMLSchema.oddRow;

    protected String HTMLTableRowColorEven = WSAHTMLSchema.evenRow;

    public synchronized void setHTMLTableColorRowHeader(String newColor) {
        HTMLTableRowColorHeader = newColor;
    };

    public synchronized String getHTMLTableColorRowHeader() {
        return HTMLTableRowColorHeader;
    };

    public synchronized void setHTMLTableColorRowOdd(String newColor) {
        HTMLTableRowColorOdd = newColor;
    };

    public synchronized String getHTMLTableColorRowOdd() {
        return HTMLTableRowColorOdd;
    };

    public synchronized void setHTMLTableColorRowEven(String newColor) {
        HTMLTableRowColorEven = newColor;
    };

    public synchronized String getHTMLTableColorRowEven() {
        return HTMLTableRowColorEven;
    };

    protected String outputBaseDIR = "";

    public synchronized void setOutputBaseDIR(String newDir) {
        outputBaseDIR = newDir;
    };

    public synchronized String getOutputBaseDIR() {
        return outputBaseDIR;
    };

    protected String outputBaseURL = "";

    public synchronized void setOutputBaseURL(String newUrl) {
        outputBaseURL = newUrl;
    };

    public synchronized String getOutputBaseURL() {
        return outputBaseURL;
    };

    protected String outputAppendage = "";

    public synchronized void setOutputAppendage(String newAppendage) {
        outputAppendage = newAppendage;
    };

    public synchronized String getOutputAppendage() {
        return outputAppendage;
    };

    protected void generateErrorHTML(String errorMessage) {
        clearResultsHTML();
        addToResultsHTML(HTMLStrings.getHTMLErrorFragment(errorMessage));
    };

    // The following functions generate 'formatter arrays' which hold an array
    // of SQLDataFormatter (or subclasses) objects that apply formatting to the
    // related data cell...

    protected SQLDataFormatter getSQLDataFormatterForColumn(int column) {
        return new SQLDataFormatter();
    };

    private SQLDataFormatter[] getSQLDataFormatterArray() {
        // Can access MaxColumns, which should represent the num columns in
        // result!
        // Can access 'metadata' object, which should have been set before now!

        // BE AWARE OF THE DIFFERENCE IN ARRAY NUMBERING, SQL columns start at
        // 1,
        // Java arrays start at 0!!!

        SQLDataFormatter[] tempArray = new SQLDataFormatter[resultsNumColumns];

        for (int column = 1; column <= resultsNumColumns; column++) {
            tempArray[column - 1] = getSQLDataFormatterForColumn(column);
        }
        ;

        return tempArray;
    };

    protected String getPreTableHTML() {
        return "";
    };

    protected void verifyQueryAndOptions() throws Exception {
        // Dummy class does nothing... override in subclass if needed...
    };

    protected String getPostTableHTML() {
        String firstPart, secondPart = "";
        String extra = "";
        if (row >= rowsTotalUpperLimit) {
            extra = " <b> at least (i.e. before it was halted) </b> ";
        }

        if (getDoRowTotal()) {
            if (row == 1) {
                firstPart = "      (Query returned 1 result row, \n ";
            } else {
                firstPart = "      (Query returned " + extra + row + " result rows, \n";
            }

            if (rowsHTML == 0) {
                secondPart = "      no rows are shown in the displayed table.) \n";
            } else if (row <= rowsHTML) {
                secondPart = "      all rows are shown in the displayed table.) \n";
            } else if (rowsHTML == 1) {
                secondPart = "      only the first row is shown in the displayed table.) \n";
            } else {
                secondPart = "      only the first " + rowsHTML + " rows are shown in the displayed table.) \n";
            }
        } else {
            if (row <= rowsHTML) {
                if (row == 1) {
                    firstPart = "      (Query returned 1 result row, \n";
                } else {
                    firstPart = "      (Query returned " + row + " result rows, \n";
                }

                secondPart = "      all rows are shown in the displayed table.) \n";
            } else if (rowsHTML == 0) {
                firstPart = "      (Query returned more than 0 result rows, \n";
                secondPart = "      no rows are shown in the displayed table.) \n";
            } else if (rowsHTML == 1) {
                firstPart = "      (Query returned more than 1 result row, \n";
                secondPart = "      only the first row is shown in the displayed table.) \n";
            } else {
                firstPart = "      (Query returned more than " + rowsHTML + " result rows, \n";
                secondPart = "      only the first " + rowsHTML + " rows are shown in the displayed table.) \n";
            }
        }

        return "    <p> \n" + firstPart + secondPart + "    </p> \n\n";
    };

    protected String getInsertedTableCellsHeaderHTML() {
        return "        <th>&nbsp;</th> \n";
    };

    protected String getInsertedTableCellsDataHTML() {
        return "        <td nowrap=\"nowrap\" class=\"RowHeader\" bgcolor=\"" + getHTMLTableColorRowHeader() + "\"><b>"
                + row + "</b></td> \n";
    };

    /**
     * Execute the SQL query on the provided JDBC server and store the results
     * in the htmlResults StringBuffer ready for later retrieval...
     * 
     * @author Alan Maxwell
     */
    public void run() {
 
        setQueryProcessing(true);
        setQueryTerminated(false);

        StringBuffer resultsStringBuffer = new StringBuffer("");

        int runningNumber = getNextCSVNumber();

        String uniqID = String.valueOf(Calendar.getInstance().get(Calendar.DAY_OF_MONTH)) + "_"
                + String.valueOf(Calendar.getInstance().get(Calendar.HOUR_OF_DAY)) + "_"
                + String.valueOf(Calendar.getInstance().get(Calendar.MINUTE)) + "_"
                + String.valueOf(Calendar.getInstance().get(Calendar.SECOND)) + "_" + runningNumber;
       

        PrintWriter CSVWriter = null;
        OutputStream CSVOutputStream = null;
        // WSASQLRetrieverThread wsa=(WSASQLRetrieverThread)smap.get(uniqID);
        //logger.warn(String.valueOf(wsa.getOutputFormat()));
 
        // Cache some variables to avoid repeated thread-locked access (slow)
        int OutputFormatCache = getOutputFormat();

        try {
            //   System.out.println ("1a");
            try {
                // Main execution here - retrieving of query and formatting
                // results...
                if (serverInfo == null) {
                    generateErrorHTML("<b>Error in run():</b> No serverInfo provided.");
                    return;
                }
                ;

                // Open database connection...
                databaseConnection = DriverManager.getConnection(serverInfo.databaseURL, serverInfo.dbUsername,
                        serverInfo.dbPassword);

                databaseQuery = databaseConnection.createStatement();
                databaseQuery.setQueryTimeout(7200);
                databaseQuery.setFetchSize(500);
                // allow query to run on for a factor
                databaseQuery.setMaxRows((ROWSWRITTEN_UPPERLIMIT*FACTOR)+1);
                // Attempt to 'do' the query...

                if (uploadFile == null) {
                    //					databaseQuery.setQueryTimeout(30);
                    results = databaseQuery.executeQuery(SQLQuery);
                    //        System.out.println ("1");

                } else {
                    StringBuffer SQLQueryBuffer = new StringBuffer("");
                    try {
                        
                        if (dataType!=null && dataType.equalsIgnoreCase("table")) {
                            upTable.createDBTable(databaseQuery.getConnection(),"#upload");
                            resultsStringBuffer.append("<p>#upload table created.");
                            resultsStringBuffer.append("<p>#upload columns: "+upTable.getColumnNames()+"<p>");
                            SQLQueryBuffer.append(SQLQuery);
                        }
                        else {
                        if (dataType != null ) {
                            if (dataType.equalsIgnoreCase("float")) {
                                //SQLQueryBuffer = new StringBuffer(
                                //"create table #upload (upID float)");
                                databaseQuery.executeUpdate("create table #upload (upID float)");
                                
                        }
                            else {
                               // SQLQueryBuffer = new StringBuffer(
                               // "create table #upload (upID bigInt)");
                                databaseQuery.executeUpdate("create table #upload (upID bigInt)");
                            }
                        }
                        else {
                        SQLQueryBuffer = new StringBuffer(
                                "create table #upload (up_ra float, up_dec float, up_id int, up_name varchar(32)) ");
                        }
                        //	SQLQuery="create table #upload (up_ra float, up_dec
                        // float, up_id int, up_name varchar(32)) ";

                        File file = new File(uploadFile);
                        FileReader fr = new FileReader(file);
                        BufferedReader in = new BufferedReader(fr);
                        String line;
              
                        int count = 0;
                        databaseConnection.setAutoCommit(false);
                        
                        PreparedStatement ps = databaseConnection.prepareStatement(
                        "INSERT into #upload values (?)");
                        int noParts = 0;
                        long ll=0;
                       double dd=0.0;
                       System.out.println(dataType);
                        while (((line = in.readLine()) != null) && !isQueryTerminated()) {
                            count = count + 1;
                            String lineTrim = line.trim();
                            noParts = 0;
                            StringTokenizer st = new StringTokenizer(lineTrim, " ,");
                            noParts = st.countTokens();
                            //	    System.out.println(count);
                            String[] parts = new String[st.countTokens()];
                            
 

                            for (int i = 0; i < noParts; i++) {
                                parts[i] = st.nextToken();
                                //			    resultsStringBuffer.append(parts[i] + ":" + i
                                // +"<br>");
                            }
                            if (noParts > 2) {
                                SQLQueryBuffer.append(" insert into #upload (up_ra, up_dec, up_id, up_name) values ("
                                        + parts[1] + "," + parts[2] + "," + count + ",'" + parts[0] + "'	)");
                            }
                            else {
                                
                                //SQLQueryBuffer.append(" insert into #upload (upID) values ("
                                //        + parts[0]  + "	)");
                               // databaseQuery.addBatch("insert into #upload  values ("+
                               //         parts[0]  + "	)");
                                if (dataType.equalsIgnoreCase("float")) {
                                    dd=Double.parseDouble(parts[0].trim());
                                    ps.setDouble(1,dd);
                                }
                                else {
                                ll=Long.parseLong(parts[0].trim());
                                ps.setLong(1,ll);
                                }
                            
                                ps.addBatch();
                                
                            }

                        }
                        if (dataType == null) {
 
                        SQLQueryBuffer.append(" create table #x (up_id int,objID bigint,distance float)");
                        }
                      
                        
                        databaseConnection.commit();
                        
  //                      int [] updateCounts=databaseQuery.executeBatch();
                        int [] updateCounts=ps.executeBatch();
                        databaseConnection.setAutoCommit(true);
                        
                        
                        
                        SQLQueryBuffer.append(SQLQuery);
                        resultsStringBuffer.append("<br/>"+updateCounts.length+" rows uploaded<p></p><!-- "+SQLQueryBuffer+" -->");

                        file.delete();
                        }
                    } catch (FileNotFoundException e) {
                    }
                    //		databaseQuery.setQueryTimeout(3);
                    //		resultsStringBuffer.append("timeout set");
                    results = databaseQuery.executeQuery(SQLQueryBuffer.toString());
                } // end of uploadfile
                
                
                
                
                resultsMetaData = results.getMetaData();
                // System.out.println ("2");
                frs.setRSMD(resultsMetaData);
                //resultsStringBuffer.append(getOutputBaseDIR() + " "+
                // getOutputAppendage());
                FITSWriter fw = new FITSWriter();

                //resultsStringBuffer.append("heretada");
                //addToResultsHTML(resultsStringBuffer.toString());
                ParseVOT pVOT = new ParseVOT();
                resultsNumColumns = resultsMetaData.getColumnCount();

                showArrayPresent(resultsMetaData);
                
                rowsWrittenUpperLimit = ((int) Math.round(ROWSWRITTEN_UPPERLIMIT / (resultsNumColumns * 1000.)) * 1000) - 1;
                if (rowsWrittenUpperLimit < 1 || rowsWrittenUpperLimit > ROWSWRITTEN_UPPERLIMIT) {
                    rowsWrittenUpperLimit = 2;
                }

                rowsTotalUpperLimit = FACTOR * (rowsWrittenUpperLimit + 1);
                // System.out.println(rowsWrittenUpperLimit);
                columnNames = new String[resultsNumColumns];

                switch (OutputFormatCache) {
                case OUTPUT_NONE:
                    break; // case OUTPUT_NONE does nothing
                case OUTPUT_FITS:
                    FITSName = "results" + uniqID; //getNextCSVNumber()
                    FITSFileName = FITSName + ".fits";
                    File FITSFile = new File(getOutputBaseDIR() + getOutputAppendage() + FITSFileName);
                    if (FITSFile.exists()) {
                        FITSFile.delete();
                    }

                    fw.setFileName(getOutputBaseDIR() + getOutputAppendage() + FITSFileName);
                    fw.setRSMD(resultsMetaData);

                    break;
                case OUTPUT_CSV:
                    CSVName = "results" + uniqID; //getNextCSVNumber()

                    switch (getOutputCompression()) {
                    case COMPRESS_NONE:
                        CSVFileName = CSVName + ".csv";
                        try {

                            CSVOutputStream = new BufferedOutputStream(new FileOutputStream(getOutputBaseDIR()
                                    + getOutputAppendage() + CSVFileName), OUTPUT_BUFFERSIZE);
                        } catch (Exception e) {
                            CSVOutputStream = null;
                        }
                        break; // case COMPRESS_NONE
                    case COMPRESS_ZIP:
                        CSVFileName = CSVName + ".zip";
                        try {
                            CSVOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(
                                    getOutputBaseDIR() + getOutputAppendage() + CSVFileName), OUTPUT_BUFFERSIZE));

                            // Prepare the zip
                            ((ZipOutputStream) CSVOutputStream).setLevel(9);
                            ((ZipOutputStream) CSVOutputStream).putNextEntry(new ZipEntry(CSVName + ".csv"));
                        } catch (Exception e) {
                            CSVOutputStream = null;
                        }
                        break; // case COMPRESS_ZIP
                    case COMPRESS_GZIP:
                        CSVFileName = CSVName + ".csv.gz";
                        try {
                            CSVOutputStream = new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(
                                    getOutputBaseDIR() + getOutputAppendage() + CSVFileName), OUTPUT_BUFFERSIZE));
                        } catch (Exception e) {
                            CSVOutputStream = null;
                        }
                        break; // case COMPRESS_GZIP
                    } // switch

                    if (CSVOutputStream != null) {
                        try {
                            CSVWriter = new PrintWriter(CSVOutputStream, true);
                        } catch (Exception e) {
                            CSVWriter = null;
                        }
                        ;
                    }
                    ;

                    break; // case OUTPUT_CSV

                case OUTPUT_VOT:
                    //pVOT=new ParseVOT();
                    pVOT.setRSMD(resultsMetaData);
                    CSVName = "results" + uniqID; //getNextCSVNumber()

                    switch (getOutputCompression()) {
                    case COMPRESS_NONE:
                        CSVFileName = CSVName + ".xml";
                        try {

                            CSVOutputStream = new BufferedOutputStream(new FileOutputStream(getOutputBaseDIR()
                                    + getOutputAppendage() + CSVFileName), OUTPUT_BUFFERSIZE);
                        } catch (Exception e) {
                            CSVOutputStream = null;
                        }
                        break; // case COMPRESS_NONE
                    case COMPRESS_ZIP:
                        CSVFileName = CSVName + ".zip";
                        try {
                            CSVOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(
                                    getOutputBaseDIR() + getOutputAppendage() + CSVFileName), OUTPUT_BUFFERSIZE));

                            // Prepare the zip
                            ((ZipOutputStream) CSVOutputStream).setLevel(9);
                            ((ZipOutputStream) CSVOutputStream).putNextEntry(new ZipEntry(CSVName + ".xml"));
                        } catch (Exception e) {
                            CSVOutputStream = null;
                        }
                        break; // case COMPRESS_ZIP
                    case COMPRESS_GZIP:
                        CSVFileName = CSVName + ".xml.gz";
                        try {
                            CSVOutputStream = new GZIPOutputStream(new BufferedOutputStream(new FileOutputStream(
                                    getOutputBaseDIR() + getOutputAppendage() + CSVFileName), OUTPUT_BUFFERSIZE));
                        } catch (Exception e) {
                            CSVOutputStream = null;
                        }
                        break; // case COMPRESS_GZIP
                    } // switch

                    if (CSVOutputStream != null) {
                        try {
                            CSVWriter = new PrintWriter(CSVOutputStream, true);
                        } catch (Exception e) {
                            CSVWriter = null;
                        }
                        ;
                    }
                    ;
                    CSVWriter.println(pVOT.getVOTHeader());
                    CSVWriter.println(pVOT.setRSMD(resultsMetaData));
                    break; // case OUTPUT_VOT

                } // switch (OutputFormatCache)

                // System.out.println ("3");

                resultsStringBuffer.append(getPreTableHTML());

                resultsStringBuffer
                        .append(
                                //"<form action=\"http://www-wfau.roe.ac.uk/~sss/cgi-bin/checkbox.cgi\" method=post>"
                                 "    <table border=\"1\" cellspacing=\"0\" cellpadding=\"3\" " + "bgcolor=\""
                                + getHTMLTableColorRowOdd() + "\"> \n");

                boolean GenerateHTML = true; // Set properly elsewhere...
                boolean doForm = false;
                boolean RAPresent = false;
                boolean DecPresent = false;
                int RACol = 0;
                int DecCol = 0;
                if (resultsNumColumns > 0) // We have at least one column
                                           // returned!!!
                {
                    if (GenerateHTML == true) {
                        resultsStringBuffer.append("      <tr bgcolor=\"" + getHTMLTableColorRowHeader() + "\"> \n");

                        // Insert (if any) extra cells before real data, can be
                        // overridden
                        // in subclasses - defaults to printing the row
                        // number...
                        resultsStringBuffer.append(getInsertedTableCellsHeaderHTML());
                    }
                    ;
                    for (int column = 1; column <= resultsNumColumns; column++) {
                        columnNames[column - 1] = resultsMetaData.getColumnName(column);

                        if (columnNames[column - 1].equalsIgnoreCase("RA")) {
                            RACol = column - 1;
                            RAPresent = true;
                        }

                        if (columnNames[column - 1].equalsIgnoreCase("DEC")) {
                            DecCol = column - 1;
                            DecPresent = true;
                        }
                    }

                    if (GenerateHTML == true && RAPresent && DecPresent) {

                     //   resultsStringBuffer.append("        <th nowrap=\"nowrap\">" + "&nbsp;" + "</th> \n");
                    }
                    ;
                    
                    if (GenerateHTML == true && showBoolean[0]){
                        resultsStringBuffer.append(getShowTitles());
                    }

                    for (int column = 1; column <= resultsNumColumns; column++) {
                        columnNames[column - 1] = resultsMetaData.getColumnName(column);

                        if (GenerateHTML == true) {
                            if (columnNames[column - 1].length() < 1) {
                                resultsStringBuffer.append("        <th nowrap=\"nowrap\">" + "&nbsp;" + "</th> \n");
                            } else {
                                resultsStringBuffer.append("        <th nowrap=\"nowrap\">" + columnNames[column - 1]
                                        + "</th> \n");
                            }
                            ;
                        }
                        ;

                        // Also, if needed, generate the header row for the CSV
                        // file...
                        if ((OutputFormatCache == OUTPUT_CSV) && (CSVWriter != null)) {
                            if (column == resultsNumColumns) {
                                CSVWriter.println(columnNames[column - 1]);
                            } else {
                                CSVWriter.print(columnNames[column - 1] + ",");
                            }
                            ;
                        }
                        ; // if (GenerateCSV == true)

                    }
                    ;

                    if (GenerateHTML == true) {
                        resultsStringBuffer.append("      </tr> \n");
                    }
                }
                ;

                columnFormats = getSQLDataFormatterArray();

                // Verify if the query is OK before continuing...
                verifyQueryAndOptions();

                // Now process the results (if any)...
                row = 0;
                String[] strArray;
                strArray = new String[resultsNumColumns];

                while ((!isQueryTerminated())
                        && ((OutputFormatCache != OUTPUT_NONE) && row <= rowsWrittenUpperLimit || (GenerateHTML == true))
                        && (results.next())) {
                    row++;
                   // Thread.sleep(100);
                    GenerateHTML = ((row <= rowsHTML) && (row <= SQLROWS_UPPERLIMIT) && resultsStringBuffer.length() < MAXRESBUFFERSIZE);

                    // Put in the start of the HTML data row, before doing the
                    // FOR...
                    // and also the first data element (the generated row
                    // number)...
                    if (GenerateHTML == true) {
                        if ((row % 2) == 0) {
                            resultsStringBuffer.append("      <tr bgcolor=\"" + getHTMLTableColorRowEven()
                                    + "\" class=\"RowEven\"> \n");
                        } else {
                            resultsStringBuffer.append("      <tr bgcolor=\"" + getHTMLTableColorRowOdd()
                                    + "\" class=\"RowOdd\"> \n");
                        }
                        ;

               
                        resultsStringBuffer.append(getInsertedTableCellsDataHTML());
                    }
                    ;

                    // Now that the headers are out of the way, we can do what
                    // we like
                    // with the data for each of the columns (of this row) in
                    // turn...

                    if ((GenerateHTML == true) || (OutputFormatCache == OUTPUT_CSV)
                            || (OutputFormatCache == OUTPUT_VOT)) {
                        strArray = frs.setRS(results);
                    }

                    if ((OutputFormatCache == OUTPUT_FITS)) {
                        fw.setRS(results);
                    }

                    if (RAPresent && DecPresent && GenerateHTML) {
                     /*   resultsStringBuffer.append("        <td align=\"right\" nowrap=\"nowrap\">"
                                + "<input type=checkbox name=ch value=\"" + strArray[RACol].trim() + ":"
                                + strArray[DecCol].trim() + "\">" + "</td> \n");
                                */
                    }
                    if (showBoolean[0]&& GenerateHTML){
                        resultsStringBuffer.append(getShowLinks(results));
                    }
                    for (int column = 1; column <= resultsNumColumns; column++) {
                        // Extract the raw data for this column as a string
                        // (safest as string)

                        /*
                         * if (results.getString(column) == null) { columnData =
                         * null; } else { columnData =
                         * results.getString(column).trim(); };
                         */

                        // Firstly, if we are still within our requested display
                        // limits
                        // generate a header row for the HTML table for this
                        // database row...
                        if (GenerateHTML == true) {
                            columnData = strArray[column - 1];
                            resultsStringBuffer.append("        <td align=\"right\" nowrap=\"nowrap\">"
                                    + columnFormats[column - 1].formatDataForHTML(columnData) + "</td> \n");
                        }
                        ; // if (GenerateHTML == true)

                        // Then, with the same returned data, we now write it
                        // out to the CSV...
                        if ((OutputFormatCache == OUTPUT_CSV) && (CSVWriter != null)) {
                            //columnData = strArray[column-1];
                            if (column == resultsNumColumns) {
                                CSVWriter.println(
                                //columnFormats[column -
                                // 1].formatDataForCSV(columnData)
                                        strArray[column - 1]);
                            } else {
                                CSVWriter.print(
                                //columnFormats[column -
                                // 1].formatDataForCSV(columnData) + ","
                                        strArray[column - 1] + ",");
                            }
                            ;
                        }
                        ; // if (GenerateCSV == true)

                        if ((OutputFormatCache == OUTPUT_VOT) && (CSVWriter != null)) {
                            //columnData = strArray[column-1];
                            if (column == 1) {
                                CSVWriter.print("<TR><TD>" + strArray[column - 1] + "</TD>");
                            } else if (column == resultsNumColumns) {
                                CSVWriter.println("<TD>" + strArray[column - 1] + "</TD></TR>");
                            } else {
                                CSVWriter.print(
                                //columnFormats[column -
                                // 1].formatDataForCSV(columnData) + ","
                                        "<TD>" + strArray[column - 1] + "</TD>");
                            }
                            ;
                        }
                        ; // if (GenerateCSV == true)

                    }
                    ; // for

                    // Put in the start of the HTML data row, before doing the
                    // FOR...
                    if (GenerateHTML == true) {
                        resultsStringBuffer.append("      </tr> \n");
                    }
                    ;
                }
                ; // END of while results.next()...

                // Now do an extra count of the total number of rows that would
                // have
                // been returned if the above while loop was stopped
                // prematurely. If
                // the above while cycled through all the rows, this will do
                // nothing.
                rowsWrittenToFile = row;
                while ((!isQueryTerminated()) && (getDoRowTotal()) && (results.next() && row < rowsTotalUpperLimit)) {
                    row++;
                }
                ;
               if (isQueryTerminated()){
                   resultsStringBuffer.insert(0,"WARNING: Stopped by request" );
               }

                resultsStringBuffer.append("    </table>\n\n");

                resultsStringBuffer.append(getPostTableHTML());

                if ((OutputFormatCache == OUTPUT_FITS)) {
                    fw.close();
                    if (dataOutputCompression == 2) {

                        try {
                            // Create the GZIP output stream
                            String outFilename = getOutputBaseDIR() + getOutputAppendage() + FITSFileName + ".gz";
                            GZIPOutputStream out = new GZIPOutputStream(new FileOutputStream(outFilename));

                            // Open the input file
                            String inFilename = getOutputBaseDIR() + getOutputAppendage() + FITSFileName;
                            FileInputStream in = new FileInputStream(inFilename);

                            // Transfer bytes from the input file to the GZIP
                            // output stream
                            byte[] buf = new byte[1024];
                            int len;
                            while ((len = in.read(buf)) > 0) {
                                out.write(buf, 0, len);
                            }
                            in.close();

                            // Complete the GZIP file
                            out.finish();
                            out.close();
                            FITSFileName = FITSName + ".fits.gz";
                        } catch (IOException e) {
                        }
                    }
                }

                if ((OutputFormatCache == OUTPUT_CSV) && (CSVWriter != null)) {
                    CSVWriter.flush(); // Ensure all data is finished

                    if (getOutputCompression() == COMPRESS_ZIP) {
                        ((ZipOutputStream) CSVOutputStream).closeEntry();
                    }
                    ;

                    CSVWriter.close();
                }
                ;

                if ((OutputFormatCache == OUTPUT_VOT) && (CSVWriter != null)) {
                    CSVWriter.print(pVOT.getFooter());

                    CSVWriter.flush(); // Ensure all data is finished

                    if (getOutputCompression() == COMPRESS_ZIP) {
                        ((ZipOutputStream) CSVOutputStream).closeEntry();
                    }
                    ;

                    CSVWriter.close();
                }
                ;

                if (OutputFormatCache != OUTPUT_NONE) {
                    int rowtemp = rowsWrittenUpperLimit + 1;
                    if (row > rowsWrittenToFile) {
                        resultsStringBuffer.append("<p><b> NB The number of rows written to the output file "
                                + "reached the maximum allowed " + rowtemp + "</b>");
                    }
                }

                if (OutputFormatCache == OUTPUT_FITS) {
                    resultsStringBuffer.append("    <p> \n" + "      <img src=\"" + getOutputBaseURL()
                            + "images/icons/save.gif\" \n" + "alt=\"Save icon\" /> \n" + "      <a href=\""
                            + getOutputBaseURL() + getOutputAppendage() + FITSFileName + "\">"
                            + "Download Results File </a>, your results in a FITS file \n" + "      (Contains <b>");

                    if (rowsWrittenToFile == 1) {
                        resultsStringBuffer.append("1 row</b>, ");
                    } else {
                        resultsStringBuffer.append(rowsWrittenToFile + " rows</b>, ");
                    }
                    ;
                    File FITSFile = new File(getOutputBaseDIR() + getOutputAppendage() + FITSFileName);

                    long FITSFileSize = FITSFile.length();

                    if (FITSFileSize > 1000000) {
                        resultsStringBuffer.append(HTMLStrings.numbersTo1DP.format(FITSFileSize / 1024.0 / 1024.0)
                                + " MB) \n");
                    } else if (FITSFileSize > 1000) {
                        resultsStringBuffer.append(HTMLStrings.numbersTo1DP.format(FITSFileSize / 1024.0) + " KB) \n");
                    } else {
                        resultsStringBuffer.append(FITSFileSize + " bytes) \n");
                    }
                    ;

                    resultsStringBuffer.append("</p> \n\n");

                    resultsStringBuffer
                            .append("    <p> \n"
                                    + "<img src=\"http://surveys.roe.ac.uk/ssa/topcat.gif\" />"
                                    + "<a href=\"http://surveys.roe.ac.uk:8080/ssa/launch.jsp?file="
                                    + getOutputBaseURL()
                                    + getOutputAppendage()
                                    + FITSFileName
                                    + "\""
                                    + ">Launch file in Topcat</a> (requires Java 1.5 and Java Web Start, approx 12Mb download for Topcat application)</p>");
                }

                if (OutputFormatCache == OUTPUT_CSV || OutputFormatCache == OUTPUT_VOT) {
                    if (CSVWriter != null) {
                        resultsStringBuffer.append("    <p> \n" + "      <img src=\"" + getOutputBaseURL()
                                + "images/icons/save.gif\" \n" + "alt=\"Save icon\" /> \n" + "      <a href=\""
                                + getOutputBaseURL() + getOutputAppendage() + CSVFileName + "\">"
                                + "Download Results File </a>, your results in a ASCII/VOTable file \n"
                                + "      (Contains <b>");

                        if (rowsWrittenToFile == 1) {
                            resultsStringBuffer.append("1 row</b>, ");
                        } else {
                            resultsStringBuffer.append(rowsWrittenToFile + " rows</b>, ");
                        }
                        ;

                        File CSVFile = new File(getOutputBaseDIR() + getOutputAppendage() + CSVFileName);

                        long CSVFileSize = CSVFile.length();

                        if (CSVFileSize > 1000000) {
                            resultsStringBuffer.append(HTMLStrings.numbersTo1DP.format(CSVFileSize / 1024.0 / 1024.0)
                                    + " MB) \n");
                        } else if (CSVFileSize > 1000) {
                            resultsStringBuffer.append(HTMLStrings.numbersTo1DP.format(CSVFileSize / 1024.0)
                                    + " KB) \n");
                        } else {
                            resultsStringBuffer.append(CSVFileSize + " bytes) \n");
                        }
                        ;

                        resultsStringBuffer.append("</p> \n\n");

                        if (OutputFormatCache == OUTPUT_VOT) {
                            resultsStringBuffer
                                    .append("    <p> \n"
                                            + "<img src=\"http://surveys.roe.ac.uk/ssa/topcat.gif\" />"
                                            + "<a href=\"http://surveys.roe.ac.uk:8080/ssa/launch.jsp?file="
                                            + getOutputBaseURL()
                                            + getOutputAppendage()
                                            + CSVFileName
                                            + "\""
                                            + ">Launch file in Topcat</a> (requires Java 1.5 and Java Web Start, approx 12Mb download for Topcat application)<p>");
                        }

                    } else {
                        resultsStringBuffer.append("    <p> \n"
                                + "      Sorry - the server experienced an unknown problem while creating "
                                + "your ASCII/VOTable file, please try again later.\n" + "    </p> \n\n");
                    }
                    ;
                }
                ;

                if (RAPresent && DecPresent && row > 0) {
                    /*
                    resultsStringBuffer
                            .append("<SCRIPT LANGUAGE=\"javascript\">"
                                    + "function checkAll(){"
                                    + "for (var i=0;i<document.forms[0].ch.length;i++)"
                                    + "document.forms[0].ch[i].checked = true;}"
                                    + "function uncheckAll(){"
                                    + "for (var i=0;i<document.forms[0].ch.length;i++)"
                                    + "document.forms[0].ch[i].checked = false;}"
                                    + "</script>"
                                    + "To extract images centred on the RA and Decs returned in the above table "
                                    + "check the boxes of the objects you wish to view, select the waveband(s) "
                                    + "required and enter a size."
                                    + "<p><input class=\"FontSans\" type=\"button\" name=\"selectall\" value=\"&nbsp;check all&nbsp;\" onclick=\"checkAll();\">"
                                    + "&nbsp&nbsp;<input class=\"FontSans\" type=\"button\" name=\"selectall\" value=\"uncheck all\" onclick=\"uncheckAll();\">"
                                    + "<p>Survey/Waveband (tick at least one box):<br>"
                                    + "UKJ-<INPUT TYPE=\"checkbox\" NAME=\"waveband1\" VALUE=\"1\">"
                                    + "&nbsp&nbsp UKR-<INPUT TYPE=\"checkbox\" NAME=\"waveband2\" VALUE=\"1\">"
                                    + "&nbsp&nbsp UKI-<INPUT TYPE=\"checkbox\" NAME=\"waveband3\" VALUE=\"1\">"
                                    + "&nbsp&nbsp ESO-<INPUT TYPE=\"checkbox\" NAME=\"waveband4\" VALUE=\"1\">"
                                    + "&nbsp&nbsp PAE-<INPUT TYPE=\"checkbox\" NAME=\"waveband5\" VALUE=\"1\"><p>"
                                    + "Size of extracted box: <input type=\"text\" name=\"size\" value=\"1\" size=4 maxlength=4>"
                                    + " arcmin (maximum 3)<p>"
                                    + "<input class=\"FontSans\" type=submit value=\"getThumbnails\">");
                                    */
                }

                //resultsStringBuffer.append("</form>\n");

                // Finally, copy the results into the string...
                clearResultsHTML();
                addToResultsHTML(resultsStringBuffer.toString());
            } catch (SQLException se) {
                System.err.println("SQLRetrieverThread::run(): an SQLException -> " + se.getMessage()
                        + se.getErrorCode());

                generateErrorHTML("<b>SQL Error:</b> " + se.getMessage());
            } catch (Exception e) {
                // General exception catch-all. Any exceptions in run() that
                // fall
                // through are trapped and neatly formatted here...

                System.err.println("SQLRetrieverThread::run(): Exception -> " + e.toString());

                generateErrorHTML("<b>Error in run():</b> " + e.toString());
            }
            ;
        } finally {
           
            setQueryProcessing(false);
            
            /*
             * setDBConnectionClosed(); System.out.println("closing");
             */

            try {
                databaseConnection.close();
                // System.out.println("here1"+Calendar.getInstance().getTime());
            } catch (SQLException se) {
            }
            ;

            // Free up used resources...
            CSVWriter = null;
            CSVOutputStream = null;
            resultsStringBuffer = null;
            resultsMetaData = null;
            if (results != null) {
                try {
                    //databaseQuery.cancel(); // was commented out
                    results.close();
                    //System.out.println("here2"+Calendar.getInstance().getTime());

                } catch (SQLException se) {
                    //System.out.println("here3"+Calendar.getInstance().getTime()
                    // + se.getMessage());
                }
                ;
            }
            ;

            results = null;

            if (databaseQuery != null) {
                try {
                    //databaseQuery.cancel();
                    databaseQuery.close();
                    //        	System.out.println("here3"+Calendar.getInstance().getTime()
                    // );
                } catch (SQLException se) {
                }
                ;
            }
            ;

            databaseQuery = null;

            databaseConnection = null;

            if (isMightBeBackground()) {
                int waitCount = 0;
                while (getEmailAddress() == null && waitCount < 15) {
                    
                    waitCount = waitCount + 1;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ie) {
                        // Do nothing when interrupted...
                    }

                } //end of while

                //  System.out.println("email "+getEmailAddress() +" " +
                // Calendar.getInstance().getTime() );
                if (getEmailAddress() != null) {
                    if (getEmailAddress().length() > 3) {
                        String HTMLFileName = getOutputBaseDIR() + getOutputAppendage() + "results" + uniqID + ".html";
                        File HTMLFile = new File(HTMLFileName);
                        try {
                            HTMLFile.createNewFile();
                            FileWriter HTMLfw = new FileWriter(HTMLFile);
                            PrintWriter HTMLpw = new PrintWriter(HTMLfw);
                            HTMLpw.println(HTMLStrings.getHTMLHeadWithScript("SQL Query Results",
                            //              "http://grendel12.roe.ac.uk/~avo/ssa/" +
                            // "configurestyles.js"));
                                    "http://surveys.roe.ac.uk/wsa/" + "configurestyles.js"));
                            HTMLpw.println("<h3>Database - SQL Query Results</h3>");
                            HTMLpw.println("<p>"+SQLQuery+"<p>");
                            HTMLpw.println(htmlResults.toString());
                            HTMLpw.println("</body> </html>");
                            HTMLfw.close();

                        } catch (IOException e) {
                        }
                        try {
                            java.util.Properties properties = System.getProperties();
                            Session session = Session.getInstance(properties, null);
                            // Construct a message
                            MimeMessage message = new MimeMessage(session);
                            message.setFrom(new InternetAddress(fromAddress));
                            message.addRecipient(Message.RecipientType.TO, new InternetAddress(getEmailAddress()));
                            message.addRecipient(Message.RecipientType.BCC, new InternetAddress("mar@roe.ac.uk"));
                            message.setSubject(Subject);
                            message.setText("The results of your query can be retrieved from \n" + getOutputBaseURL()
                                    + getOutputAppendage() + "results" + uniqID + ".html");

                            //message.setText(getOutputBaseURL() +
                            // getOutputAppendage() +"results"+ uniqID +
                            // ".html");

                            // Connect to the transport
                            Transport transport = session.getTransport("smtp");
                            transport.connect("mail.roe.ac.uk", "", "");

                            // Send the message and close the connection
                            transport.sendMessage(message, message.getAllRecipients());
                            transport.close();
                        } catch (MessagingException me) {
                           // System.out.println(me.getMessage());
                        }

                    }
                }

            } // end of ismightbebackground

            System.gc();

        } // end of finally
        
    } // end of run
}; // END of class SQLRetrieverThread
