/*
 * JBoss, Home of Professional Open Source
 * Copyright 2008, Red Hat Middleware LLC, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.test.managed.factory.test;

import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import junit.framework.Test;
import junit.framework.TestSuite;

import org.jboss.annotation.factory.AnnotationCreator;
import org.jboss.managed.api.ManagedObject;
import org.jboss.managed.api.ManagedProperty;
import org.jboss.managed.api.annotation.ManagementComponent;
import org.jboss.managed.api.annotation.ManagementObject;
import org.jboss.managed.api.factory.ManagedObjectFactory;
import org.jboss.managed.plugins.factory.AbstractInstanceClassFactory;
import org.jboss.managed.plugins.factory.AbstractManagedObjectFactory;
import org.jboss.managed.plugins.factory.DefaultInstanceClassFactory;
import org.jboss.managed.spi.factory.InstanceClassFactory;
import org.jboss.metadata.plugins.loader.memory.MemoryMetaDataLoader;
import org.jboss.metadata.spi.MetaData;
import org.jboss.metadata.spi.retrieval.MetaDataRetrievalToMetaDataBridge;
import org.jboss.metatype.api.values.CollectionValue;
import org.jboss.metatype.api.values.GenericValue;
import org.jboss.metatype.api.values.MetaValue;
import org.jboss.metatype.api.values.SimpleValueSupport;
import org.jboss.test.managed.factory.AbstractManagedObjectFactoryTest;
import org.jboss.test.managed.factory.support.ManagementObjectChangedName;
import org.jboss.test.managed.factory.support.ManagementPropertySimpleManaged;
import org.jboss.test.managed.factory.support.Simple;
import org.jboss.test.managed.factory.support.SimpleUnannotated;
import org.jboss.test.managed.factory.support.amof.CustomName;
import org.jboss.test.managed.factory.support.amof.ManagementObjectWithRuntimeRef;
import org.jboss.test.managed.factory.support.amof.ManagementObjectWithRuntimeRefICF;
import org.jboss.test.managed.factory.support.amof.TestICF;
import org.jboss.test.managed.factory.support.amof.TestMOP;
import org.jboss.test.managed.factory.support.amof.TestSimpleICF;
import org.jboss.test.managed.factory.support.beans.ABeanMetaData;
import org.jboss.test.managed.factory.support.beans.AKernelDeployment;
import org.jboss.test.managed.factory.support.beans.BeanMetaDataICF;
import org.jboss.test.managed.factory.support.beans.IBeanMetaData;
import org.jboss.test.managed.factory.support.beans.IBeanMetaDataFactory;
import org.jboss.test.managed.factory.support.deployment.JmsDestination;
import org.jboss.test.managed.factory.support.deployment.JmsDestinationICF;
import org.jboss.test.managed.factory.support.deployment.JmsDestinations;
import org.jboss.test.managed.factory.support.deployment.QueueDestination;
import org.jboss.test.managed.factory.support.deployment.SecurityDomain;
import org.jboss.test.managed.factory.support.mcf.DBMSMetaData;
import org.jboss.test.managed.factory.support.mcf.DataSourceConnectionPropertyMetaData;
import org.jboss.test.managed.factory.support.mcf.LocalDSInstanceClassFactory;
import org.jboss.test.managed.factory.support.mcf.LocalDataSourceDeploymentMetaData;
import org.jboss.test.managed.factory.support.mcf.ManagedConnectionFactoryDeploymentGroup;
import org.jboss.test.managed.factory.support.mcf.ManagedConnectionFactoryDeploymentMetaData;
import org.jboss.test.managed.factory.support.mcf.NoTxConnectionFactoryDeploymentMetaData;
import org.jboss.test.managed.factory.support.mcf.NoTxICF;
import org.jboss.test.managed.factory.support.mcf.SecurityMetaData;

/**
 * Test of behavior of the AbstractManagedObjectFactory
 * 
 * @author Scott.Stark@jboss.org
 * @version $Revision: 80522 $
 */
public class AbstractManagedObjectFactoryUnitTestCase extends AbstractManagedObjectFactoryTest
{
   private ManagedObjectFactory testMOF;

   public static Test suite()
   {
      return new TestSuite(AbstractManagedObjectFactoryUnitTestCase.class);
   }

   public AbstractManagedObjectFactoryUnitTestCase(String name)
   {
      super(name);
   }

   /**
    * Test overriding the AbstractManagedObjectFactory.defaultInstanceFactory 
    */
   public void testDefaultInstanceFactory()
   {
      AbstractManagedObjectFactory mof = new AbstractManagedObjectFactory();
      mof.setDefaultInstanceFactory(new TestICF());
      TestSimpleICF icf = new TestSimpleICF();
      mof.setInstanceClassFactory(Simple.class, icf);
      testMOF = mof;

      BigDecimal bigDecimal = new BigDecimal(10);
      Simple simple = new Simple();
      simple.setBigDecimalValue(bigDecimal);

      ManagedObject managedObject = initManagedObject(simple);
      checkManagedObjectDefaults(Simple.class, managedObject);
      checkDefaultManagedProperties(managedObject, Simple.class);

      checkPropertyDefaults(managedObject, "bigDecimalValue", BigDecimal.class, bigDecimal);
      assertTrue("TestBigDecimalICF.isGetValueCalled", icf.isGetValueCalled());

      //
      managedObject = super.createManagedObject(ManagementPropertySimpleManaged.class);
      checkManagedObjectDefaults(ManagementPropertySimpleManaged.class, managedObject);
      checkDefaultManagedProperties(managedObject, ManagementPropertySimpleManaged.class);
   }

   /**
    * Test that the setDefaultManagedObjectPopulator overrides the MOF
    * default ManagedObjectPopulator
    */
   public void testDefaultManagedObjectPopulator()
   {
      AbstractManagedObjectFactory mof = new AbstractManagedObjectFactory();
      TestMOP mop = new TestMOP(mof.getConfiguration(), new TestSimpleICF(), mof.getInstanceFactories());
      mof.setDefaultManagedObjectPopulator(mop);
      testMOF = mof;

      BigDecimal bigDecimal = new BigDecimal(10);
      Simple simple = new Simple();
      simple.setBigDecimalValue(bigDecimal);

      ManagedObject managedObject = initManagedObject(simple);
      checkManagedObjectDefaults(Simple.class, managedObject);
      checkDefaultManagedProperties(managedObject, Simple.class);
      checkPropertyDefaults(managedObject, "bigDecimalValue", BigDecimal.class, bigDecimal);
      assertTrue("isPopulateManagedObjectCalled", mop.isPopulateManagedObjectCalled());
      //
      managedObject = createManagedObject(ManagementPropertySimpleManaged.class);
      checkManagedObjectDefaults(ManagementPropertySimpleManaged.class, managedObject);
      checkDefaultManagedProperties(managedObject, ManagementPropertySimpleManaged.class);
      assertTrue("isCreateObjectCalled", mop.isCreateObjectCalled());
 
   }

   public void testInstanceClassFactoryAddition()
   {
      AbstractManagedObjectFactory mof = new AbstractManagedObjectFactory();
      testMOF = mof;

      BeanMetaDataICF bicf = new BeanMetaDataICF();
      mof.addInstanceClassFactory(bicf);
      LocalDSInstanceClassFactory dsicf = new LocalDSInstanceClassFactory();
      mof.addInstanceClassFactory(dsicf);
      NoTxICF nticf = new NoTxICF();
      mof.addInstanceClassFactory(nticf);

      InstanceClassFactory icf = mof.getInstanceClassFactory(IBeanMetaData.class);
      assertEquals("IBeanMetaData ICF", bicf, icf);
      icf = mof.getInstanceClassFactory(LocalDataSourceDeploymentMetaData.class);
      assertEquals("LocalDataSourceDeploymentMetaData ICF", dsicf, icf);
      icf = mof.getInstanceClassFactory(NoTxConnectionFactoryDeploymentMetaData.class);
      assertEquals("NoTxConnectionFactoryDeploymentMetaData ICF", nticf, icf);
   }

   /**
    * Test that the MetaData repository annotations override/augment the class
    * annotations.
    */
   public void testMetaDataRepositoryOverride()
      throws Exception
   {
      MemoryMetaDataLoader memory = new MemoryMetaDataLoader();
      String annotationExpr = "@org.jboss.managed.api.annotation.ManagementObject(name=\"testMetaDataRepositoryOverride\",componentType=@org.jboss.managed.api.annotation.ManagementComponent(type=\"MCBean\", subtype=\"MetaDataTest\"))";
      ManagementObject override = (ManagementObject) AnnotationCreator.createAnnotation(annotationExpr, ManagementObject.class);
      memory.addAnnotation(override);
      MetaData metaData = new MetaDataRetrievalToMetaDataBridge(memory);

      BigDecimal bigDecimal = new BigDecimal(10);
      SimpleUnannotated simple = new SimpleUnannotated();
      simple.setBigDecimalValue(bigDecimal);

      ManagedObjectFactory mof = getMOF();
      ManagedObject managedObject = mof.initManagedObject(simple, metaData);
      assertEquals("testMetaDataRepositoryOverride", managedObject.getName());
      Map<String, Annotation> moAnnotations = managedObject.getAnnotations();
      assertEquals("MO annotations == 2;", 2, moAnnotations.size());
      ManagementObject moAnn = (ManagementObject) moAnnotations.get(ManagementObject.class.getName());
      ManagementComponent componentType = moAnn.componentType();
      assertEquals("componentType.type", "MCBean", componentType.type());
      assertEquals("componentType.subtype", "MetaDataTest", componentType.subtype());
      checkPropertyDefaults(managedObject, "bigDecimalValue", BigDecimal.class, bigDecimal);
      checkDefaultManagedProperties(managedObject, SimpleUnannotated.class);
   }

   @SuppressWarnings("unchecked")
   public void testGenericValueUpdate()
   {
      super.enableTrace("org.jboss.managed");
      AbstractManagedObjectFactory mof = new AbstractManagedObjectFactory();
      mof.setInstanceClassFactory(JmsDestination.class, new JmsDestinationICF(mof));
      testMOF = mof;

      JmsDestinations destinations = new JmsDestinations();
      List<JmsDestination> queues = new ArrayList<JmsDestination>();
      QueueDestination q1 = new QueueDestination();
      q1.setJndiName("queues/Q1");
      q1.setDomain(new SecurityDomain("java:/jaas/JMS"));
      queues.add(q1);
      destinations.setDestinations(queues);

      ManagedObject managedObject = initManagedObject(destinations);
      checkManagedObjectDefaults(JmsDestinations.class, managedObject);

      ManagedProperty destinationsMP = managedObject.getProperty("destinations");
      assertNotNull(destinationsMP);
      CollectionValue destinationsValue = (CollectionValue) destinationsMP.getValue();
      assertNotNull(destinationsValue);
      assertEquals(1, destinationsValue.getSize());
      GenericValue q1GV = (GenericValue) destinationsValue.getElements()[0];
      assertNotNull(q1GV);

      ManagedObject q1MO = (ManagedObject) q1GV.getValue();
      assertNotNull(q1MO);
      ManagedProperty domain = q1MO.getProperty("domain");
      getLog().info(domain);
      GenericValue domainGV = (GenericValue) domain.getValue();
      assertNotNull(domainGV);
      ManagedObject domainMO = (ManagedObject) domainGV.getValue();
      assertNotNull(domainMO);

      ManagedProperty domainName = domainMO.getProperty("domain");
      assertEquals(SimpleValueSupport.wrap("java:/jaas/JMS"), domainName.getValue());

      ManagedObject domainUpdateMO = initManagedObject(new SecurityDomain("java:/jaas/JMS2"));
      DefaultInstanceClassFactory icf = new DefaultInstanceClassFactory(mof);
      GenericValue domainUpdateGV = icf.getManagedObjectValue(domain, domainUpdateMO);
      assertNotNull(domainUpdateGV.getValue());
      domain.setValue(domainUpdateGV);
      // Validate this has been written through
      assertEquals("java:/jaas/JMS2", q1.getDomain().getDomain());
   }

   /**
    * Test the transformer value of the ManagementRuntimeRef annotation when
    * there is a custom ICF
    */
   public void testTransformer()
   {
      AbstractManagedObjectFactory mof = new AbstractManagedObjectFactory();
      mof.setInstanceClassFactory(ManagementObjectWithRuntimeRef.class, new ManagementObjectWithRuntimeRefICF());
      testMOF = mof;

      ManagementObjectWithRuntimeRef mowref = new ManagementObjectWithRuntimeRef();
      mowref.setRefName(new CustomName("jboss:test=testTransformer"));
      ManagedObject managedObject = initManagedObject(mowref);
      createManagedObject(ManagementObjectChangedName.class);
      assertEquals("jboss:test=testTransformer", managedObject.getComponentName());
   }

   public void testMCFHierarchy()
      throws Exception
   {
      ManagedConnectionFactoryDeploymentGroup mcf = initMCFDG();
      ManagedObjectFactory mof = getMOF();
      ManagedObject mcfMO = mof.initManagedObject(mcf, "ManagedConnectionFactoryDeploymentGroup", null);
      ManagedProperty deployments = mcfMO.getProperty("deployments");
      Object dvalue = deployments.getValue();
      assertTrue(dvalue.getClass()+"", dvalue instanceof CollectionValue);
      CollectionValue cv = (CollectionValue) dvalue;
      assertEquals(1, cv.getSize());
      Object cv0 = cv.iterator().next();
      assertTrue(cv0 instanceof GenericValue);
      GenericValue gv0 = (GenericValue) cv0;
      assertTrue(gv0.getValue() instanceof ManagedObject);
      ManagedObject mo = (ManagedObject) gv0.getValue();
      assertEquals(LocalDataSourceDeploymentMetaData.class.getName(), mo.getAttachmentName());
      assertEquals(mcfMO, mo.getParent());

      // Validate the security-domain
      ManagedProperty secDomain = mo.getProperty("security-domain");
      assertNotNull("security-domain", secDomain);
      GenericValue sdGV = (GenericValue) secDomain.getValue();
      assertNotNull("security-domain.GV", sdGV);
      ManagedObject secDomainMO = (ManagedObject) sdGV.getValue();

      assertNotNull("security-domain.MO", secDomainMO);
      assertEquals(mo, secDomainMO.getParent());
      ManagedProperty domainName = secDomainMO.getProperty("domain");
      assertNotNull("security-domain.MO.domain", domainName);
      assertEquals(SimpleValueSupport.wrap("java:/jaas/SomeDomain"), domainName.getValue());

   }

   public void testKernelDeployment()
   {
      AbstractManagedObjectFactory mof = new AbstractManagedObjectFactory();
      mof.setInstanceClassFactory(IBeanMetaData.class, new BeanMetaDataICF());
      testMOF = mof;

      AKernelDeployment ad = new AKernelDeployment();
      ABeanMetaData bean1 = new ABeanMetaData();
      bean1.setBean(Simple.class.getName());
      bean1.setName("SimpleBean#1");
      ABeanMetaData bean2 = new ABeanMetaData();
      bean2.setBean(Simple.class.getName());
      bean2.setName("SimpleBean#2");

      Simple simple1 = new Simple();
      simple1.setBigDecimalValue(new BigDecimal(123456));
      Simple simple2 = new Simple();
      simple2.setBigDecimalValue(new BigDecimal(222222));
      BeanMetaDataICF.setBean("SimpleBean#1", simple1);
      BeanMetaDataICF.setBean("SimpleBean#2", simple2);
      List<IBeanMetaDataFactory> beanFactories = new ArrayList<IBeanMetaDataFactory>();
      beanFactories.add(bean1);
      beanFactories.add(bean2);
      ad.setBeanFactories(beanFactories);

      ManagedObject mo = mof.initManagedObject(ad, "KernelDeployment", null);
      ManagedProperty beanFactoriesMP = mo.getProperty("beanFactories");
      assertNotNull("beanFactories MP", beanFactoriesMP);
      Object beanFactoriesMPValue = beanFactoriesMP.getValue();
      getLog().debug("beanFactories MP value: "+beanFactoriesMPValue);
      assertTrue(beanFactoriesMPValue instanceof CollectionValue);
      CollectionValue cv = CollectionValue.class.cast(beanFactoriesMPValue);
      MetaValue[] cvs = cv.getElements();

      assertEquals(2, cv.getSize());
      MetaValue mv0 = cvs[0];
      assertTrue(mv0 instanceof GenericValue);
      GenericValue gv0 = GenericValue.class.cast(mv0);
      Object gv0Value = gv0.getValue();
      assertTrue(gv0Value instanceof ManagedObject);
      ManagedObject mo0 = ManagedObject.class.cast(gv0Value);
      ManagedProperty bigDecimalValueMP = mo0.getProperty("bigDecimalValue");
      assertNotNull(bigDecimalValueMP);
      assertEquals(SimpleValueSupport.wrap(new BigDecimal(123456)), bigDecimalValueMP.getValue());
      bigDecimalValueMP.setValue(SimpleValueSupport.wrap(new BigDecimal(987654)));
      assertEquals(new BigDecimal(987654), simple1.getBigDecimalValue());

      MetaValue mv1 = cvs[1];
      assertTrue(mv1 instanceof GenericValue);
      GenericValue gv1 = GenericValue.class.cast(mv1);
      Object gv1Value = gv1.getValue();
      assertTrue(gv1Value instanceof ManagedObject);
      ManagedObject mo1 = ManagedObject.class.cast(gv1Value);
      ManagedProperty bigDecimalValueMP2 = mo1.getProperty("bigDecimalValue");
      assertNotNull(bigDecimalValueMP2);
      assertEquals(SimpleValueSupport.wrap(new BigDecimal(222222)), bigDecimalValueMP2.getValue());
      bigDecimalValueMP2.setValue(SimpleValueSupport.wrap(new BigDecimal(12222221)));
      assertEquals(new BigDecimal(12222221), simple2.getBigDecimalValue());
      
   }

   /**
    * Override to allow test specific ManagedObjectFactory
    */
   @Override
   protected ManagedObjectFactory getMOF()
   {
      ManagedObjectFactory mof = testMOF;
      if (mof == null)
         mof = super.getMOF();
      return mof;
   }

   protected ManagedConnectionFactoryDeploymentGroup initMCFDG()
      throws Exception
   {
      ManagedConnectionFactoryDeploymentGroup mcfs = new ManagedConnectionFactoryDeploymentGroup();
      LocalDataSourceDeploymentMetaData lds = new LocalDataSourceDeploymentMetaData();
      lds.setBackgroundValidation(true);
      lds.setBackgroundValidationMillis(5000);
      lds.setBlockingTimeoutMilliSeconds(5000);
      lds.setCheckValidConnectionSQL("select something from somewhere");
      lds.setConnectionDefinition("conn def");
      lds.setConnectionUrl("jdbc:xyz:a=b");
      DataSourceConnectionPropertyMetaData cp1 = new DataSourceConnectionPropertyMetaData();
      cp1.setName("cp1-name");
      cp1.setValue("cp1-value");
      DataSourceConnectionPropertyMetaData cp2 = new DataSourceConnectionPropertyMetaData();
      cp2.setName("cp2-name");
      cp2.setValue("cp2-value");
      DataSourceConnectionPropertyMetaData[] cps = {
        cp1, cp2    
      };
      lds.setDataSourceConnectionProperties(Arrays.asList(cps));
      lds.setDriverClass("org.jboss.jdbc.SomeDriver");
      lds.setExceptionSorterClassName("org.jboss.jdbc.SomeExceptionSorter");
      String[] depends = {"jboss:service=Naming", "jboss:service=Hypersonic,database=localDB"};
      lds.setDependsNames(Arrays.asList(depends));
      lds.setIdleTimeoutMinutes(15);
      lds.setInterleaving(false);
      lds.setMaxSize(100);
      lds.setMinSize(1);
      DBMSMetaData md = new DBMSMetaData();
      md.setTypeMapping("Hypersonic SQL");
      lds.setDBMSMetaData(md);
      lds.setNewConnectionSQL("run this on a new conn");
      lds.setPassWord("password");
      lds.setPrefill(true);
      lds.setPreparedStatementCacheSize(50);
      lds.setQueryTimeout(30000);
      lds.setUserName("user");
      // Set a SecurityMetaData to validate its MO
      SecurityMetaData smd = new SecurityMetaData();
      smd.setDomain("java:/jaas/SomeDomain");
      lds.setSecurityMetaData(smd);

      ManagedConnectionFactoryDeploymentMetaData[] mds = {lds};
      mcfs.setDeployments(Arrays.asList(mds));
      mcfs.setUrl(new URL("file:/deploy/some-ds.xml"));
      return mcfs;
   }

}
