/*
   Copyright The Narayana Authors
   SPDX short identifier: Apache-2.0
 */

package com.hp.mwtests.ts.arjuna.tools;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.arjuna.ats.arjuna.AtomicAction;
import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.common.recoveryPropertyManager;
import com.arjuna.ats.arjuna.coordinator.AbstractRecord;
import com.arjuna.ats.arjuna.coordinator.ActionStatus;
import com.arjuna.ats.arjuna.coordinator.RecordType;
import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeManager;
import com.arjuna.ats.arjuna.coordinator.abstractrecord.RecordTypeMap;
import com.arjuna.ats.arjuna.recovery.RecoveryDriver;
import com.arjuna.ats.arjuna.recovery.RecoveryManager;
import com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean;
import com.arjuna.ats.arjuna.tools.osb.mbean.LogRecordWrapper;
import com.arjuna.ats.arjuna.tools.osb.mbean.OSEntryBean;
import com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreBrowser;
import com.arjuna.ats.arjuna.tools.osb.mbean.ObjStoreItemMBean;
import com.arjuna.ats.arjuna.tools.osb.mbean.UidWrapper;
import com.arjuna.ats.arjuna.tools.osb.util.JMXServer;
import com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule;
import com.arjuna.ats.internal.arjuna.recovery.RecoveryManagerImple;
import com.hp.mwtests.ts.arjuna.resources.CrashRecord;

/**
 * @deprecated as of 5.0.5.Final In a subsequent release we will change packages names in order to 
 * provide a better separation between public and internal classes.
 */
@Deprecated // in order to provide a better separation between public and internal classes.
public class ObjStoreBrowserTest {
	private RecoveryManagerImple rcm;

	@Before
	public void setUp () throws Exception
	{
		recoveryPropertyManager.getRecoveryEnvironmentBean().setRecoveryBackoffPeriod(1);

		rcm = new RecoveryManagerImple(false);
		rcm.addModule(new AtomicActionRecoveryModule());
	}

	@After
	public void tearDown () throws Exception
	{
		rcm.removeAllModules(false);
		rcm.stop(false);
	}

	/**
	 * create an MBean to represent an ObjectStore
	 * @return An object that maintains MBeans representing completing transactions
	 */
	private ObjStoreBrowser createObjStoreBrowser() {
        ObjStoreBrowser osb = new ObjStoreBrowser();

		// define which object store types we are prepared to represent by mbeans
        osb.setType("com.arjuna.ats.arjuna.AtomicAction", "com.arjuna.ats.arjuna.tools.osb.mbean.ActionBean");

		return osb;
	}

	@Test
	public void testOSEntryBean() throws Exception {
		com.arjuna.common.tests.simple.EnvironmentBeanTest.testBeanByReflection(new OSEntryBean());
	}

	@Test
	public void testLogRecordWrapper() throws Exception {
		com.arjuna.common.tests.simple.EnvironmentBeanTest.testBeanByReflection(new LogRecordWrapper(Uid.nullUid()));
	}

	@Test
	public void testObjectStoreBrowser() throws Exception {
		com.arjuna.common.tests.simple.EnvironmentBeanTest.testBeanByReflection(createObjStoreBrowser());
	}

	@Test
	public void basicOSBTest () throws Exception
	{
		ObjStoreBrowser osb = new ObjStoreBrowser("os");

		osb.start();
		osb.probe();

		// there should not be any MBeans
		assertNull(osb.findUid(Uid.nullUid()));

		// listing beans of an invalid type returns null
		assertNull(osb.probe("InvalidType"));
		
		// JBTM-1230
		// This does not work on the JDBC object store as this test assumes a previous
		// run has left the "Recovery" entry on disk which won't happen in a JDBC store 
//		// TODO windows
//		if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {
//			// listing beans of a valid type returns an empty list
//			assertNotNull(osb.probe("Recovery"));
//		}

		osb.stop();
	}

	/**
	 * Create an atomic action with two participants, one of which will generate a heuristic during phase 2.
	 * The test will move the heuristic back into the prepared state and trigger recovery to replay phase 2.
	 * The test then asserts that the corresponding MBeans have been unregistered.
	 * @throws Exception if test fails unexpectedly
	 */
	@Test
	public void aaReplayTest() throws Exception {
		// TODO windows
		aaTest(true);

	}
	
	/**
	 * Similar to aaReplayTest except that the whole transaction record is removed from the object store
	 * (instead of replaying the record that generates a heuristic).
	 * @throws Exception if test fails unexpectedly
	 */
	@Test
	public void aaRemoveTest() throws Exception {
		// TODO windows
		aaTest(false);
	}

	public void aaTest(boolean replay) throws Exception {
		ObjStoreBrowser osb = createObjStoreBrowser();
		AtomicAction A = new AtomicAction();
		CrashRecord recs[] = {
				new CrashRecord(CrashRecord.CrashLocation.NoCrash, CrashRecord.CrashType.Normal),
				new CrashRecord(CrashRecord.CrashLocation.CrashInCommit, CrashRecord.CrashType.HeuristicHazard)
		};

		// register CrashRecord record type so that it is persisted in the object store correctly
		RecordTypeManager.manager().add(new RecordTypeMap() {
			public Class<? extends AbstractRecord> getRecordClass () { return CrashRecord.class;}
			public int getType () {return RecordType.USER_DEF_FIRST0;}
		});

		// create an atomic action, register crash records with it and then commit
		A.begin();

		for (CrashRecord rec : recs)
			A.add(rec);

		int outcome = A.commit();

		// the second participant should have generated a heuristic during commit
		assertEquals(ActionStatus.H_HAZARD, outcome);

		// generate MBeans representing the atomic action that was just committed
		osb.start();
		osb.probe();

		// there should be one MBean corresponding to the AtomicAction A
		UidWrapper w = osb.findUid(A.get_uid());
		assertNotNull(w);
		OSEntryBean ai = w.getMBean();
		assertNotNull(ai);

		// the MBean should wrap an ActionBean
		assertTrue(ai instanceof ActionBean);
		ActionBean actionBean = (ActionBean) ai;

		// and there should be one MBean corresponding to the CrashRecord that got the heuristic:
		int recCount = 0;
		for (CrashRecord rec : recs) {
			LogRecordWrapper lw = actionBean.getParticipant(rec);

			if (lw != null) {
				recCount += 1;

				assertTrue(lw.isHeuristic());

				// put the participant back onto the pending list
				lw.setStatus("PREPARED");
				// and check that the record is no longer in a heuristic state
				assertFalse(lw.isHeuristic());
			}
		}

		assertEquals(1, recCount);

		if (!replay) {
			actionBean.remove();
		} else {
			/*
			* prompt the recovery manager to replay the record that was
			* moved off the heuristic list and back onto the prepared list
			*/
			rcm.scan();
		}

		/*
		 * Since the recovery scan (or explicit remove request) will have successfully removed the record from
		 * the object store another probe should cause the MBean representing the record to be unregistered
		 */
		osb.probe();

		// look up the MBean and verify that it no longer exists
		w = osb.findUid(A.get_uid());
		assertNull(w);

		osb.dump(new StringBuilder());
		osb.stop();
	}

	// define an MBean interface for use in the next test
	public interface NotAnotherMBean extends ObjStoreItemMBean {}

	@Test
	public void testJMXServer() throws Exception {

		class NonCompliantBean implements NotAnotherMBean {}
		
		ObjStoreBrowser osb = createObjStoreBrowser();
		OSEntryBean bean;
		String validName = "jboss.jta:type=TestObjectStore";

		osb.start();
		osb.probe();

		bean = new OSEntryBean();

		// MalformedObjectNameException
		assertNull(JMXServer.getAgent().registerMBean("InvalidName", bean));
		assertFalse(JMXServer.getAgent().unregisterMBean("InvalidName"));

		// InstanceNotFoundException
		assertFalse(JMXServer.getAgent().unregisterMBean(validName));

		// NotCompliantMBeanException
		assertNull(JMXServer.getAgent().registerMBean(validName, new NonCompliantBean()));

		// Do it right this time
		int cnt = JMXServer.getAgent().queryNames(validName, null).size();
		assertNotNull(JMXServer.getAgent().registerMBean(validName, bean));
		assertEquals(cnt + 1, JMXServer.getAgent().queryNames(validName, null).size());

		// InstanceAlreadyExistsException
		assertNull(JMXServer.getAgent().registerMBean(validName, bean));

		// Make sure unregistering a valid bean works
		assertTrue(JMXServer.getAgent().unregisterMBean(validName));
		assertEquals(0, JMXServer.getAgent().queryNames(validName, null).size());

		osb.stop();
	}
}