/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */

package org.jboss.cache.pojo.notification;

import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertSame;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.NotSupportedException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheFactory;
import org.jboss.cache.pojo.notification.event.AttachedEvent;
import org.jboss.cache.pojo.notification.event.DetachedEvent;
import org.jboss.cache.pojo.notification.event.Event;
import org.jboss.cache.pojo.notification.event.FieldModifiedEvent;
import org.jboss.cache.pojo.test.Address;
import org.jboss.cache.pojo.test.Person;
import org.jboss.cache.transaction.DummyTransactionManager;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

// $Id: TxObjectTest.java 4449 2007-09-08 03:26:15Z jason.greene@jboss.com $

/**
 * Tests attach, detach, field modify and tx notifications
 *
 * @author Jason T. Greene
 */
@Test(groups = {"functional"})
public class TxObjectTest 
{
   private PojoCache cache;
   private TxGauranteedListener listener;


   private UserTransaction getTransaction() throws SystemException, NotSupportedException, NamingException
   {
      Properties prop = new Properties();
      prop.put(Context.INITIAL_CONTEXT_FACTORY,
              "org.jboss.cache.transaction.DummyContextFactory");
      return (UserTransaction) new InitialContext(prop).lookup("UserTransaction");
   }


   @BeforeMethod(alwaysRun = true)
   protected void setUp() throws Exception
   {
      String configFile = "META-INF/local-service.xml";
      boolean toStart = false;
      cache = PojoCacheFactory.createCache(configFile, toStart);
      cache.start();

      listener = new TxGauranteedListener();
      cache.addListener(listener);

      DummyTransactionManager.getInstance();
   }

   private <T extends Event> T takeNotification(Class<T> clazz)
   {
      T notification = listener.take(clazz);
      verifyNotification(notification);

      return notification;
   }

   protected void verifyNotification(Event notification)
   {
      assertSame(cache, notification.getContext().getPojoCache());
      assertEquals(true, notification.isLocal());
   }



   @AfterMethod(alwaysRun = true)
   protected void tearDown() throws Exception
   {
      cache.stop();
   }

   public void testAttachNotification() throws Exception
   {
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache.attach("/a", test);
      AttachedEvent attach = takeNotification(AttachedEvent.class);
      assertEquals(test, attach.getSource());
   }

   public void testAttachNotification2() throws Exception
   {
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      Address addr = new Address();
      test.setAddress(addr);
      cache.attach("/a", test);

      // Address Attach
      AttachedEvent attach = takeNotification(AttachedEvent.class);
      assertEquals(addr, attach.getSource());

      // Person Attach
      attach = takeNotification(AttachedEvent.class);
      assertEquals(test, attach.getSource());
   }

   public void testDetachNotification() throws Exception
   {
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache.attach("/a", test);

      // Person Attach
      AttachedEvent attach = takeNotification(AttachedEvent.class);
      assertEquals(test, attach.getSource());

      cache.detach("/a");

      // Person Detach
      DetachedEvent detach = takeNotification(DetachedEvent.class);
      assertEquals(test, detach.getSource());
   }

   public void testFieldNotification() throws Exception
   {
      UserTransaction tx = getTransaction();
      tx.begin();
      Person test = new Person();
      test.setName("Ben");
      test.setAge(10);
      cache.attach("/a", test);
      test.setAge(20);
      Address addr = new Address();
      addr.setCity("Madison");
      addr.setStreet("State St.");
      addr.setZip(53703);
      test.setAddress(addr);
      tx.commit();

      // Person Attach
      AttachedEvent attach = takeNotification(AttachedEvent.class);
      assertEquals(test, attach.getSource());

      // Field modification
      FieldModifiedEvent modify = takeNotification(FieldModifiedEvent.class);
      assertEquals(test, modify.getSource());
      assertEquals(test.getClass().getDeclaredField("age"), modify.getField());
      assertEquals(20, modify.getValue());

      // First Attach
      attach = takeNotification(AttachedEvent.class);
      assertEquals(addr, attach.getSource());

      // Then Modify
      modify = takeNotification(FieldModifiedEvent.class);
      assertEquals(test, modify.getSource());
      assertEquals(test.getClass().getDeclaredField("address"), modify.getField());
      assertEquals(addr, modify.getValue());
   }
}