/*
 * JBoss, the OpenSource WebOS
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 *
 */
package org.jboss.test.jca.ejb;

import java.rmi.RemoteException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.ejb.CreateException;
import javax.ejb.DuplicateKeyException;
import javax.ejb.EJBException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;
import javax.ejb.FinderException;
import javax.ejb.ObjectNotFoundException;
import javax.ejb.NoSuchEntityException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import org.jboss.logging.Logger;

/** A BMP bean which exercises the prepared statement cache.
 * 
 * @author Scott.Stark@jboss.org
 * @version $Revision: 1.1.2.1 $
 */
public class PreparedStatementBean
   implements EntityBean
{
   /** The serialVersionUID */
   private static final long serialVersionUID = 1L;
   static Logger log = Logger.getLogger(PreparedStatementBean.class);
   private EntityContext ctx = null;
   private DataSource ds;
   private String key;
   private String name;

   public void ejbActivate()
   {
   }
   public void ejbPassivate()
   {
   }

   public void ejbLoad()
   {
      key = (String) ctx.getPrimaryKey();
      log.debug("ejbLoad("+key+")");
      try
      {
         Connection con = ds.getConnection();
         try
         {
            PreparedStatement ps = con.prepareStatement("SELECT name FROM BMPTABLE WHERE pk=?");
            try
            {
               ps.setString(1, key);
               ResultSet rs = ps.executeQuery();

               if (rs.next() == false)
                  throw new NoSuchEntityException("Instance " +key +" not found in database.");
               name = rs.getString(1);
               rs.close();
            }
            finally
            {
               ps.close();
            }
         }
         finally
         {
            con.close();
         }
      }
      catch (SQLException e)
      {
         throw new EJBException(e);
      }
   }
   public void ejbStore()
   {
      log.debug("ejbStore(" + key + ")");
      try
      {
         Connection con = ds.getConnection();
         try
         {
            PreparedStatement ps = con.prepareStatement("UPDATE BMPTABLE SET name=? WHERE pk=?");
            try
            {
               ps.setString(1, key);
               ps.setString(2, name);
               ps.executeUpdate();
            }
            finally
            {
               ps.close();
            }
         }
         finally
         {
            con.close();
         }
      }
      catch (SQLException e)
      {
         throw new EJBException(e);
      }
   }
   public void ejbRemove()
   {
      log.debug("ejbRemove(" + key + ") called");
      try
      {
         Connection con = ds.getConnection();
         try
         {
            PreparedStatement ps = con.prepareStatement("DELETE FROM BMPTABLE WHERE pk=?");
            try
            {
               ps.setString(1, key);
               ps.executeUpdate();
            }
            finally
            {
               ps.close();
            }
         }
         finally
         {
            con.close();
         }
      }
      catch (SQLException e)
      {
         throw new EJBException(e);
      }

      log.debug("Removed "+key);
   }

   public void setEntityContext(EntityContext ctx)
   {
      log.debug("setEntityContext() called");
      this.ctx = ctx;
      try
      {
         InitialContext ic = new InitialContext();
         ds = (DataSource) ic.lookup("java:comp/env/jdbc/DataSource");
      }
      catch (NamingException e)
      {
         throw new EJBException("DataSource init failed", e);
      }
   }
   public void unsetEntityContext() throws EJBException, RemoteException
   {
      ds = null;
      
   }

   public String ejbCreate(String pk, String name)
      throws CreateException, DuplicateKeyException
   {
      log.debug("entry ejbCreate("+ pk + ", " + name + ")");
      ensureTableExists();
      try
      {
         Connection con = ds.getConnection();
         try
         {
            PreparedStatement ps = con.prepareStatement("INSERT INTO BMPTABLE VALUES (?,?)");
            try
            {
               ps.setString(1, pk);
               ps.setString(2, name);
               ps.execute();
            }
            finally
            {
               ps.close();
            }
         }
         finally
         {
            con.close();
         }
      }
      catch (SQLException e)
      {
         log.debug("failed", e);

         throw new CreateException("Entity bean creation failure: " +
            e.getMessage());
      }

      this.key = pk;
      this.name = name;
      log.debug("Created BMP: "+pk);
      return pk;
   }

   public void ejbPostCreate(String pk, String name)
   {
      log.debug("entry ejbCreate("+ pk + ", " + name + ")");
   }

  public String ejbFindByPrimaryKey(String pk)
     throws FinderException
  {
     log.debug("ejbFindByPrimaryKey, pk="+pk);
     ensureTableExists();
     try
     {
        Connection con = ds.getConnection();
        try
        {
           PreparedStatement ps;

           ps = con.prepareStatement("SELECT pk FROM BMPTABLE WHERE pk=?");
           try
           {
              ps.setString(1, pk);
              ResultSet rs = ps.executeQuery();
              if (!rs.next())
                 throw new ObjectNotFoundException("No bean with " +"pk=" +pk+ " found.");
              rs.close();
              return pk;
           }
           finally
           {
              ps.close();
           }
        }
        finally
        {
           con.close();
        }
     }
     catch (SQLException e)
     {
        throw new FinderException("Could not find pk="+pk+", msg=" + e.getMessage());
     }
  }

   public long hashEntityTable()
      throws RemoteException
   {
      long hash = 0;
      try
      {
         Connection con = ds.getConnection();
         try
         {
            PreparedStatement ps = con.prepareStatement("SELECT pk FROM BMPTABLE");
            try
            {
               ResultSet rs = ps.executeQuery();
               while( rs.next() )
               {
                  String pk = rs.getString(1);
                  PreparedStatement ps2 = con.prepareStatement("SELECT name FROM BMPTABLE WHERE pk=?");
                  ps2.setString(1, pk);
                  ResultSet rs2 = ps2.executeQuery();
                  if( rs2.next() )
                  {
                     String pkName = rs2.getString(1);
                     hash += pkName.hashCode();
                  }
                  rs2.close();
                  ps2.close();
               }
               rs.close();
            }
            finally
            {
               ps.close();
            }
         }
         finally
         {
            con.close();
         }
      }
      catch (SQLException e)
      {
         throw new RemoteException("Failed to calculate hash", e);
      }
      return hash;
   }

   private void ensureTableExists()
   {
      boolean exists = true;

      try
      {
         Connection con = ds.getConnection();
         try
         {
            Statement s = con.createStatement();
            try
            {
               ResultSet rs = s.executeQuery("SELECT * FROM BMPTABLE");
               rs.close();
            }
            finally
            {
               s.close();
            }
         }
         finally
         {
            con.close();
         }
      }
      catch (SQLException e)
      {
         exists = false;
      }

      if (!exists)
      {
         try
         {
            Connection con = ds.getConnection();
            try
            {
               Statement s = con.createStatement();
               try
               {
                  s.executeUpdate("CREATE TABLE BMPTABLE (pk VARCHAR(16), name VARCHAR(32))");
               }
               finally
               {
                  s.close();
               }
            }
            finally
            {
               con.close();
            }
         }
         catch (SQLException e)
         {
            throw new EJBException(e);
         }
      }
   }

}
