/*
 * The contents of this file are subject to the Mozilla Public License Version 1.1
 * (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.mozilla.org/MPL/>.
 * 
 * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
 * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
 * language governing rights and limitations under the License.
 * 
 * The Original Code is the Venice Web Communities System.
 * 
 * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
 * for Silverwrist Design Studios.  Portions created by Eric J. Bowersox are
 * Copyright (C) 2002 Eric J. Bowersox/Silverwrist Design Studios.  All Rights Reserved.
 * 
 * Contributor(s): 
 */
package com.silverwrist.dynamo.security;

import java.sql.*;
import java.util.*;
import org.apache.commons.collections.*;
import com.silverwrist.util.*;
import com.silverwrist.dynamo.except.*;
import com.silverwrist.dynamo.iface.*;
import com.silverwrist.dynamo.util.*;

public class AuditWriteOps_mysql extends AuditWriteOps
{
  /*--------------------------------------------------------------------------------
   * Attributes
   *--------------------------------------------------------------------------------
   */

  private DBUtilities m_utils;                // reference to utilities object
  private PropertySerializer m_psz;           // property serializer object
  private ReferenceMap m_eventid_cache;       // private event ID cache

  /*--------------------------------------------------------------------------------
   * Constructor
   *--------------------------------------------------------------------------------
   */

  public AuditWriteOps_mysql(DBConnectionPool pool)
  {
    super(pool);
    m_utils = (DBUtilities)(pool.queryService(DBUtilities.class));
    m_psz = (PropertySerializer)(pool.queryService(PropertySerializer.class));
    m_eventid_cache = new ReferenceMap(ReferenceMap.SOFT,ReferenceMap.SOFT);

  } // end constructor

  /*--------------------------------------------------------------------------------
   * Abstract implementations from class AuditWriteOps
   *--------------------------------------------------------------------------------
   */

  void writeAuditRecord(PropertyKey event, java.util.Date timestamp, int uid, int subid, String ip,
			Object[] props) throws DatabaseException
  {
    Connection conn = null;
    PreparedStatement stmt = null;
    Statement stmt2 = null;
    try
    { // get a connection
      conn = getConnection();
      stmt2 = conn.createStatement();

      // First subphase is to get the event ID corresponding to this event; if it doesn't exist,
      // we need to create it.
      int event_id = -1;
      Integer id_val = (Integer)(m_eventid_cache.get(event));
      if (id_val!=null)  // found it in our own cache
	event_id = id_val.intValue();
      if (event_id<0)
      { // OK, we didn't find it locally, go out to the database and look.
	ResultSet rs = null;
	try
	{ // lock the "auditevent" table until we can do what we have to
	  stmt2.executeUpdate("LOCK TABLES auditevent WRITE;");

	  // look up the event ID
	  stmt = conn.prepareStatement("SELECT eventid FROM auditevent WHERE event_nsid = ? "
				       + "AND event_name = ?;");
	  stmt.setInt(1,event.getNamespaceID());
	  stmt.setString(2,event.getName());
	  rs = stmt.executeQuery();
	  if (rs.next())
	    event_id = rs.getInt(1);
	  else
	  { // insert a new record and get a new, dynamically-generated event ID
	    SQLUtils.shutdown(stmt);
	    stmt = conn.prepareStatement("INSERT INTO auditevent (event_nsid, event_name) VALUES (?, ?);");
	    stmt.setInt(1,event.getNamespaceID());
	    stmt.setString(2,event.getName());
	    stmt.executeUpdate();
	    event_id = MySQLUtils.getLastInsertInt(conn);
	  
	  } // end else

	} // end try
	finally
	{ // unlock the table once we're done here
	  MySQLUtils.unlockTables(conn);
	  SQLUtils.shutdown(rs);

	} // end finally

	m_eventid_cache.put(event,new Integer(event_id));  // and cache it for next time

      } // end if

      try
      { // lock the "audit" table for writing
	stmt2.executeUpdate("LOCK TABLES audit WRITE;");

	// prepare the big statement
	stmt = conn.prepareStatement("INSERT INTO audit (on_date, event, uid, subid, ip, prop0, prop1, "
				     + "prop2, prop3, prop4, prop5, prop6, prop7) VALUES "
				     + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);");
	m_utils.setDateTime(stmt,1,timestamp);
	stmt.setInt(2,event_id);
	stmt.setInt(3,uid);
	stmt.setInt(4,subid);
	stmt.setString(5,ip);
	for (int i=0; i<props.length; i++)
	{ // set all the property values
	  if (props[i]==null)
	    stmt.setNull(6+i,Types.VARCHAR);
	  else
	  { // serialize the property
	    String s = m_psz.serializeProperty(props[i]);
	    if (s==null)
	      throw new DatabaseException(AuditWriteOps_mysql.class,"SecurityMessages","audit.serialize");
	    stmt.setString(6+i,s);

	  } // end else

	} // end for

	stmt.executeUpdate();  // chunk it out!

      } // end try
      finally
      { // and unlock this table once we're done here
	MySQLUtils.unlockTables(conn);

      } // end finally

    } // end try
    catch (SQLException e)
    { // translate to a general DatabaseException
      throw generalException(e);

    } // end catch
    finally
    { // shut everything down
      SQLUtils.shutdown(stmt);
      SQLUtils.shutdown(stmt2);
      SQLUtils.shutdown(conn);

    } // end finally

  } // end writeAuditRecord

} // end class AuditWriteOps_mysql
