content: /**
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   */
  package org.apache.aries.samples.ariestrader.web;
  
  import java.io.BufferedReader;
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.math.BigDecimal;
  import java.net.URL;
  import java.util.ArrayList;
  
  import org.apache.aries.samples.ariestrader.api.TradeDBManager;
  import org.apache.aries.samples.ariestrader.api.TradeServiceUtilities;
  import org.apache.aries.samples.ariestrader.api.TradeServices;
  import org.apache.aries.samples.ariestrader.api.TradeServicesManager;
  import org.apache.aries.samples.ariestrader.api.persistence.AccountDataBean;
  import org.apache.aries.samples.ariestrader.util.Log;
  import org.apache.aries.samples.ariestrader.util.TradeConfig;
  
  /**
   * TradeBuildDB uses operations provided by the TradeApplication to 
   *   (a) create the Database tables 
   *   (b) populate a AriesTrader database without creating the tables. 
   * Specifically, a new AriesTrader User population is created using
   * UserIDs of the form "uid:xxx" where xxx is a sequential number 
   * (e.g. uid:0, uid:1, etc.). New stocks are also created of the form "s:xxx",
   * again where xxx represents sequential numbers (e.g. s:1, s:2, etc.)
   */
  public class TradeBuildDB {
  
      private static TradeServicesManager tradeServicesManager = null;
      private static TradeDBManager tradeDBManager = null;
  
      /**
       * Populate a Trade DB using standard out as a log
       */
      public TradeBuildDB() throws Exception {
          this(new java.io.PrintWriter(System.out), false);
      }
  
      /**
       * Re-create the AriesTrader db tables and populate them OR just populate a 
       * AriesTrader DB, logging to the provided output stream
       */
      public TradeBuildDB(java.io.PrintWriter out, boolean createTables)
          throws Exception {
          String symbol, companyName;
          int errorCount = 0; // Give up gracefully after 10 errors
          if (tradeServicesManager == null) {
              tradeServicesManager = TradeServiceUtilities.getTradeServicesManager();
          }
          TradeServices tradeServices = tradeServicesManager.getTradeServices();
  
          if (tradeDBManager == null) {
              tradeDBManager = TradeServiceUtilities.getTradeDBManager();
          }
  
          // TradeStatistics.statisticsEnabled=false; // disable statistics
          out.println("<HEAD><BR><EM> TradeBuildDB: Building AriesTrader Database...</EM><BR>"
              + "This operation will take several minutes. Please wait...</HEAD>");
          out.println("<BODY>");
  
          if (createTables) {
              boolean success = false;
              String dbProductName = null;
              String fileLocation = null;
              URL ddlFile = null;
              Object[] sqlBuffer = null;
  
              // Find out the Database being used
              try {
                  dbProductName = tradeDBManager.checkDBProductName();
              } catch (Exception e) {
                  Log.error(e, "TradeBuildDB: Unable to check DB Product name");
              }
              if (dbProductName == null) {
                  out.println("<BR>TradeBuildDB: **** Unable to check DB Product name,"
                      + "please check Database/AppServer configuration and retry ****</BR></BODY>");
                  return;
              }
  
              // Locate DDL file for the specified database
              try {
                  out.println("<BR>TradeBuildDB: **** Database Product detected: "
                      + dbProductName + " ****</BR>");
                  if (dbProductName.startsWith("DB2/")) { // if db is DB2
                      fileLocation = File.separatorChar + "dbscripts" + File.separatorChar + "db2" + File.separatorChar + "Table.ddl";
                  } else if (dbProductName.startsWith("Apache Derby")) { // if db is Derby
                      fileLocation = File.separatorChar + "dbscripts" + File.separatorChar + "derby" + File.separatorChar + "Table.ddl";
                  } else if (dbProductName.startsWith("Oracle")) { // if the Db is Oracle
                      fileLocation = File.separatorChar + "dbscripts" + File.separatorChar + "oracle" + File.separatorChar + "Table.ddl";
                  } else { // Unsupported "Other" Database
                      fileLocation = File.separatorChar + "dbscripts" + File.separatorChar + "other" + File.separatorChar + "Table.ddl";
                      out.println("<BR>TradeBuildDB: **** This Database is "
                          + "unsupported/untested use at your own risk ****</BR>");
                  }
  
                  ddlFile = this.getClass().getResource(fileLocation);                
  
              } catch (Exception e) {
                  Log.error(e,
                      "TradeBuildDB: Unable to locate DDL file for the specified database");
                  out.println("<BR>TradeBuildDB: **** Unable to locate DDL file for "
                      + "the specified database ****</BR></BODY>");
                  return;
              }
  
              // parse the DDL file and fill the SQL commands into a buffer
              try {
                  sqlBuffer = parseDDLToBuffer(ddlFile);
              } catch (Exception e) {
                  Log.error(e, "TradeBuildDB: Unable to parse DDL file");
                  out.println("<BR>TradeBuildDB: **** Unable to parse DDL file for the specified "+
                      "database ****</BR></BODY>");
                  return;
              }
              if ((sqlBuffer == null) || (sqlBuffer.length == 0)) {
                  out.println("<BR>TradeBuildDB: **** Parsing DDL file returned empty buffer, please check "+
                      "that a valid DB specific DDL file is available and retry ****</BR></BODY>");
                  return;
              }
  
              // send the sql commands buffer to drop and recreate the AriesTrader tables
              out.println("<BR>TradeBuildDB: **** Dropping and Recreating the AriesTrader tables... ****</BR>");
              try {
                  success = tradeDBManager.recreateDBTables(sqlBuffer, out);
              } catch (Exception e) {
                  Log.error(e,
                      "TradeBuildDB: Unable to drop and recreate AriesTrader Db Tables, "+
                      "please check for database consistency before continuing");
              }
              if (!success) {
                  out.println("<BR>TradeBuildDB: **** Unable to drop and recreate AriesTrader Db Tables, "+
                      "please check for database consistency before continuing ****</BR></BODY>");
                  return;
              }
              out.println("<BR>TradeBuildDB: **** AriesTrader tables successfully created! ****</BR><BR><b> "+
                  "Please Stop and Re-start your AriesTrader application (or your application server) and then use "+
                  "the \"Repopulate AriesTrader Database\" link to populate your database.</b></BR><BR><BR></BODY>");
              return;
          } // end of createDBTables
  
          out.println("<BR>TradeBuildDB: **** Creating "
              + TradeConfig.getMAX_QUOTES() + " Quotes ****</BR>");
          // Attempt to delete all of the Trade users and Trade Quotes first
          try {
              tradeDBManager.resetTrade(true);
          } catch (Exception e) {
              Log.error(e, "TradeBuildDB: Unable to delete Trade users "+
                  "(uid:0, uid:1, ...) and Trade Quotes (s:0, s:1, ...)");
          }
          for (int i = 0; i < TradeConfig.getMAX_QUOTES(); i++) {
              symbol = "s:" + i;
              companyName = "S" + i + " Incorporated";
              try {
                  tradeServices.createQuote(symbol, companyName,
  				    new java.math.BigDecimal(TradeConfig.rndPrice()));
                  if (i % 10 == 0) {
                      out.print("....." + symbol);
                      if (i % 100 == 0) {
                          out.println(" -<BR>");
                          out.flush();
                      }
                  }
              } catch (Exception e) {
                  if (errorCount++ >= 10) {
                      String error = "Populate Trade DB aborting after 10 create quote errors. Check "+
                          "the EJB datasource configuration. Check the log for details <BR><BR> Exception is: <BR> "
                          + e.toString();
                      Log.error(e, error);
                      throw e;
                  }
              }
          }
          out.println("<BR>");
          out.println("<BR>**** Registering " + TradeConfig.getMAX_USERS()
              + " Users **** ");
          errorCount = 0; // reset for user registrations
  
          // Registration is a formal operation in Trade 2.
          for (int i = 0; i < TradeConfig.getMAX_USERS(); i++) {
              String userID = "uid:" + i;
              String fullname = TradeConfig.rndFullName();
              String email = TradeConfig.rndEmail(userID);
              String address = TradeConfig.rndAddress();
              String creditcard = TradeConfig.rndCreditCard();
              double initialBalance =
                  (double) (TradeConfig.rndInt(100000)) + 200000;
              if (i == 0) {
                  initialBalance = 1000000; // uid:0 starts with a cool million.
              }
              try {
                  AccountDataBean accountData =
                      tradeServices.register(userID, "xxx", fullname, address,
                          email, creditcard, new BigDecimal(initialBalance));
                  if (accountData != null) {
                      if (i % 50 == 0) {
                          out.print("<BR>Account# " + accountData.getAccountID()
                              + " userID=" + userID);
                      }
  
                      // 0-MAX_HOLDING (inclusive), avg holdings per user = (MAX-0)/2
                      int holdings = TradeConfig.rndInt(TradeConfig.getMAX_HOLDINGS() + 1); 
                      double quantity = 0;
                      for (int j = 0; j < holdings; j++) {
                          symbol = TradeConfig.rndSymbol();
                          quantity = TradeConfig.rndQuantity();
                          tradeServices.buy(userID, symbol, quantity,
  						    TradeConfig.orderProcessingMode);
                      }
                      if (i % 50 == 0) {
                          out.println(" has " + holdings + " holdings.");
                          out.flush();
                      }
                  } else {
                      out.println("<BR>UID " + userID
                          + " already registered.</BR>");
                      out.flush();
                  }
  
              } catch (Exception e) {
                  if (errorCount++ >= 10) {
                      String error = "Populate Trade DB aborting after 10 user registration errors. "+
                          "Check the log for details. <BR><BR> Exception is: <BR>" + e.toString();
                      Log.error(e, error);
                      throw e;
                  }
              }
          } // end-for
          out.println("</BODY>");
      }
  
      public Object[] parseDDLToBuffer(URL ddlFile) throws Exception {
          BufferedReader br = null;
          InputStreamReader ir = null;
          ArrayList sqlBuffer = new ArrayList(30); // initial capacity 30 assuming we have 30 ddl-sql statements to read
  
          try {
              if (Log.doTrace())
                  Log.traceEnter("TradeBuildDB:parseDDLToBuffer - " + ddlFile);
  
              ir = new InputStreamReader(ddlFile.openStream());
              br = new BufferedReader(ir);
              String s;
              String sql = new String();
              while ((s = br.readLine()) != null) {
                  s = s.trim();
                  if ((s.length() != 0) && (s.charAt(0) != '#')) // Empty lines or lines starting with "#" are ignored
                  {
                      sql = sql + " " + s;
                      if (s.endsWith(";")) // reached end of sql statement
                      {
                          sql = sql.replace(';', ' '); // remove the semicolon
                          sqlBuffer.add(sql);
                          sql = "";
                      }
                  }
              }
          } catch (IOException ex) {
              Log.error("TradeBuildDB:parseDDLToBuffer Exeception during open/read of File: "
                  + ddlFile, ex);
              throw ex;
          } finally {
              if (br != null) {
                  try {
                      br.close();
                  } catch (IOException ex) {
                      Log.error("TradeBuildDB:parseDDLToBuffer Failed to close BufferedReader",
                          ex);
                  }
              }
          }
          return sqlBuffer.toArray();
      }
  
      public static void main(String args[]) throws Exception {
          new TradeBuildDB();
  
      }
  }
  