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

import org.jboss.logging.Logger;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.test.jca.interfaces.HAConnectionSession;
import org.jboss.test.jca.adapter.MockedXADataSource;
import org.jboss.system.ServiceMBean;

import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.EJBException;
import javax.ejb.CreateException;
import javax.sql.DataSource;
import javax.naming.InitialContext;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.ReflectionException;
import javax.management.AttributeNotFoundException;
import java.rmi.RemoteException;
import java.sql.Connection;

/**
 * @ejb.bean
 *    name="HAConnectionSession"
 *    view-type="remote"
 *    type="Stateless"
 *
 * @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
 * @version <tt>$Revision: 1.1.2.2 $</tt>
 */
public class HAConnectionSessionBean
   implements SessionBean
{
   private static final Logger log = Logger.getLogger(HAConnectionSessionBean.class);

   private SessionContext ctx;

   /**
    * @ejb.interface-method
    * @ejb.transaction type="NotSupported"
    */
   public void testHaLocalConnection() throws Exception
   {
      String url1 = "jdbc:hsqldb:hsql://localhost:1702";
      String url2 = "jdbc:hsqldb:hsql://localhost:1703";
      ObjectName db1 = new ObjectName("jboss:service=Hypersonic,database=haLocalDB1");
      ObjectName db2 = new ObjectName("jboss:service=Hypersonic,database=haLocalDB2");

      MBeanServer server = MBeanServerLocator.locateJBoss();

      waitForState(server, db1, ServiceMBean.STARTED);
      waitForState(server, db2, ServiceMBean.STARTED);

      DataSource ds = (DataSource)new InitialContext().lookup("java:/TestHADefaultDS");

      stopDb(server, db2);
      String expectedUrl = url1;

      for(int i = 0; i < 10; ++i)
      {
         HAConnectionSession me = (HAConnectionSession)ctx.getEJBObject();
         String conUrl = me.getHAConnectionURL(ds);
         log.debug("got connection to: " + conUrl);

         if(!expectedUrl.equals(conUrl))
         {
            throw new Exception("Expected " + expectedUrl + " but got " + conUrl);
         }

         if(conUrl.equals(url1))
         {
            stopDb(server, db1);
            startDb(server, db2);
            expectedUrl = url2;
         }
         else if(conUrl.equals(url2))
         {
            stopDb(server, db2);
            startDb(server, db1);
            expectedUrl = url1;
         }
         else
         {
            throw new Exception("Unexpected connection url: " + conUrl);
         }
      }
   }

   /**
    * @ejb.interface-method
    * @ejb.transaction type="NotSupported"
    */
   public void testHaXaConnection() throws Exception
   {
      String[] urls = MockedXADataSource.getUrls();
      for(int i = 1; i < urls.length; ++i)
      {
         MockedXADataSource.stop(urls[i]);
      }

      DataSource ds = (DataSource)new InitialContext().lookup("java:/MockedHaXaDS");
      HAConnectionSession facade = (HAConnectionSession)ctx.getEJBObject();

      for(int i = 0; i < 3*urls.length; ++i)
      {
         String url = facade.getHAConnectionURL(ds);
         int urlIndex = i % urls.length;

         if(!url.equals(urls[urlIndex]))
         {
            throw new IllegalStateException("Connected to a wrong database: " + url + ", expected " + urls[urlIndex]);
         }

         MockedXADataSource.stop(url);

         urlIndex = (i + 1) % urls.length;
         MockedXADataSource.start(urls[urlIndex]);
      }
   }

   /**
    * @ejb.interface-method
    * @ejb.transaction type="RequiresNew"
    */
   public String getHAConnectionURL(DataSource ds) throws Exception
   {
      Connection con = null;
      try
      {
         con = ds.getConnection();
         return con.getMetaData().getURL();
      }
      finally
      {
         if(con != null)
         {
            con.close();
         }
      }
   }

   private void startDb(MBeanServer server, ObjectName db2)
      throws InstanceNotFoundException, MBeanException, ReflectionException, AttributeNotFoundException
   {
      server.invoke(db2, "start", null, null);
      waitForState(server, db2, ServiceMBean.STARTED);
   }

   private void stopDb(MBeanServer server, ObjectName db2)
      throws InstanceNotFoundException,
      MBeanException,
      ReflectionException,
      AttributeNotFoundException
   {
      server.invoke(db2, "stop", null, null);
      waitForState(server, db2, ServiceMBean.STOPPED);
   }

   private void waitForState(MBeanServer server, ObjectName db2, int state)
      throws MBeanException, AttributeNotFoundException, InstanceNotFoundException, ReflectionException
   {
      Integer stateValue = (Integer)server.getAttribute(db2, "State");
      while(stateValue.intValue() != state)
      {
         try
         {
            Thread.sleep(500);
         }
         catch(InterruptedException e)
         {
         }
         stateValue = (Integer)server.getAttribute(db2, "State");
      }
   }

   /**
    * @throws javax.ejb.CreateException Description of Exception
    * @ejb.create-method
    */
   public void ejbCreate() throws CreateException
   {
   }

   public void setSessionContext(SessionContext ctx) throws EJBException, RemoteException
   {
      this.ctx = ctx;
   }

   public void ejbRemove() throws EJBException, RemoteException
   {
   }

   public void ejbActivate() throws EJBException, RemoteException
   {
   }

   public void ejbPassivate() throws EJBException, RemoteException
   {
   }
}