/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     06/20/2008-1.0 Guy Pelletier
//       - 232975: Failure when attribute type is generic
//     09/23/2008-1.1 Guy Pelletier
//       - 241651: JPA 2.0 Access Type support
//     01/28/2009-2.0 Guy Pelletier
//       - 248293: JPA 2.0 Element Collections (part 1)
//     02/06/2009-2.0 Guy Pelletier
//       - 248293: JPA 2.0 Element Collections (part 2)
//     03/27/2009-2.0 Guy Pelletier
//       - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
//     04/03/2009-2.0 Guy Pelletier
//       - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
//     06/02/2009-2.0 Guy Pelletier
//       - 278768: JPA 2.0 Association Override Join Table
//     06/09/2009-2.0 Guy Pelletier
//       - 249037: JPA 2.0 persisting list item index
//     02/18/2010-2.0.2 Guy Pelletier
//       - 294803: @Column(updatable=false) has no effect on @Basic mappings
//     06/18/2010-2.2 Guy Pelletier
//       - 300458: EclispeLink should throw a more specific exception than NPE
//     07/16/2010-2.2 Guy Pelletier
//       - 260296: mixed access with no Transient annotation does not result in error
package org.eclipse.persistence.testing.tests.jpa.inherited;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import junit.framework.*;

import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.weaving.PersistenceWeaved;
import org.eclipse.persistence.jpa.JpaHelper;
import org.eclipse.persistence.sessions.CopyGroup;
import org.eclipse.persistence.testing.framework.junit.JUnitTestCase;
import org.eclipse.persistence.testing.models.jpa.inherited.Accredidation;
import org.eclipse.persistence.testing.models.jpa.inherited.Becks;
import org.eclipse.persistence.testing.models.jpa.inherited.BecksTag;
import org.eclipse.persistence.testing.models.jpa.inherited.BeerConsumer;
import org.eclipse.persistence.testing.models.jpa.inherited.Birthday;
import org.eclipse.persistence.testing.models.jpa.inherited.Blue;
import org.eclipse.persistence.testing.models.jpa.inherited.Alpine;
import org.eclipse.persistence.testing.models.jpa.inherited.BlueLight;
import org.eclipse.persistence.testing.models.jpa.inherited.Bluish;
import org.eclipse.persistence.testing.models.jpa.inherited.BuildingBylaw;
import org.eclipse.persistence.testing.models.jpa.inherited.Committee;
import org.eclipse.persistence.testing.models.jpa.inherited.CommitteeDates;
import org.eclipse.persistence.testing.models.jpa.inherited.Corona;
import org.eclipse.persistence.testing.models.jpa.inherited.CoronaTag;
import org.eclipse.persistence.testing.models.jpa.inherited.ExpertBeerConsumer;
import org.eclipse.persistence.testing.models.jpa.inherited.Heineken;
import org.eclipse.persistence.testing.models.jpa.inherited.InheritedTableManager;
import org.eclipse.persistence.testing.models.jpa.inherited.Location;
import org.eclipse.persistence.testing.models.jpa.inherited.NodeImpl;
import org.eclipse.persistence.testing.models.jpa.inherited.NoiseBylaw;
import org.eclipse.persistence.testing.models.jpa.inherited.NoviceBeerConsumer;
import org.eclipse.persistence.testing.models.jpa.inherited.Official;
import org.eclipse.persistence.testing.models.jpa.inherited.OfficialEntry;
import org.eclipse.persistence.testing.models.jpa.inherited.Record;
import org.eclipse.persistence.testing.models.jpa.inherited.RedStripe;
import org.eclipse.persistence.testing.models.jpa.inherited.SerialNumber;
import org.eclipse.persistence.testing.models.jpa.inherited.Venue;
import org.eclipse.persistence.testing.models.jpa.inherited.Witness;
import org.eclipse.persistence.testing.models.jpa.inherited.ServiceTime;

public class InheritedModelJunitTest extends JUnitTestCase {
    private static BigDecimal m_blueId;
    private static Integer m_beerConsumerId;
    private static Integer m_noviceBeerConsumerId;
    private static Integer m_expertBeerConsumerId;

    private static Timestamp m_quote1Stamp;
    private static final String QUOTE_ONE = "Beer is blood";
    private static Timestamp m_quote2Stamp;
    private static final String QUOTE_TWO = "My first wife was a beer";

    public InheritedModelJunitTest() {
        super();
    }

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

    public void setUp() {
        super.setUp();
        clearCache();
    }

    public static Test suite() {
        TestSuite suite = new TestSuite();
        suite.setName("InheritedModelJunitTest");

        suite.addTest(new InheritedModelJunitTest("testSetup"));
        suite.addTest(new InheritedModelJunitTest("testCreateBlue"));
        suite.addTest(new InheritedModelJunitTest("testReadBlue"));
        suite.addTest(new InheritedModelJunitTest("testCreateBeerConsumer"));
        suite.addTest(new InheritedModelJunitTest("testCreateNoviceBeerConsumer"));
        suite.addTest(new InheritedModelJunitTest("testReadNoviceBeerConsumer"));
        suite.addTest(new InheritedModelJunitTest("testOrderColumnNoviceBeerConsumerDesignations"));
        suite.addTest(new InheritedModelJunitTest("testCreateExpertBeerConsumer"));
        suite.addTest(new InheritedModelJunitTest("testDeleteMiddleRecordOfExpertBeerConsumer"));
        suite.addTest(new InheritedModelJunitTest("testReadExpertBeerConsumer"));
        suite.addTest(new InheritedModelJunitTest("testExpertBeerConsumerRecordsCRUD"));
        suite.addTest(new InheritedModelJunitTest("testUpdateBeerConsumer"));
        suite.addTest(new InheritedModelJunitTest("testInheritedClone"));
        suite.addTest(new InheritedModelJunitTest("testCascadeRemove"));
        // 1-M map using direct key
        suite.addTest(new InheritedModelJunitTest("testHeinekenBeerConsumer"));
        // 1-M map using entity key
        suite.addTest(new InheritedModelJunitTest("testBecksBeerConsumer"));
        // 1-M map using embeddable key
        suite.addTest(new InheritedModelJunitTest("testCoronaBeerConsumer"));
        // Element collection with generic map key type, with embeddable values.
        suite.addTest(new InheritedModelJunitTest("testRedStripeExpertConsumer"));
        // Element collection with generic map key type, with embeddable values.
        suite.addTest(new InheritedModelJunitTest("testRedStripeNoviceConsumer"));
        // OrderColumn with OrderCorrectionType.EXCEPTION
        suite.addTest(new InheritedModelJunitTest("testBreakOrder_CorrectionType_EXCEPTION"));
        // OrderColumn with OrderCorrectionType.READ_WRITE
        suite.addTest(new InheritedModelJunitTest("testBreakOrder_CorrectionType_READ_WRITE"));

        suite.addTest(new InheritedModelJunitTest("testMapOrphanRemoval"));

        suite.addTest(new InheritedModelJunitTest("testSerializedElementCollectionMap"));
        suite.addTest(new InheritedModelJunitTest("testVersionUpdateOnElementCollectionChange"));

        suite.addTest(new InheritedModelJunitTest("testAddToHeinekenBeerConsumerMap"));
        suite.addTest(new InheritedModelJunitTest("testColumnUpdatableAndInsertable"));
        suite.addTest(new InheritedModelJunitTest("testColumnUpdatableAndInsertableThroughQuery"));
        suite.addTest(new InheritedModelJunitTest("testElementCollectionMapEmbeddable"));
        suite.addTest(new InheritedModelJunitTest("testMultipleIdButNonIdClassEntity"));
        suite.addTest(new InheritedModelJunitTest("testRelatedBylawWrite"));
        suite.addTest(new InheritedModelJunitTest("testInterfaceBylawWrite"));
        suite.addTest(new InheritedModelJunitTest("testEmbeddableAggregateCollectionAndAggregate"));
        suite.addTest(new InheritedModelJunitTest("testNodeImplWeaving"));
        suite.addTest(new InheritedModelJunitTest("testEmbeddaleCollectionMapEmbeddableRead"));
        suite.addTest(new InheritedModelJunitTest("testCopyMapKeyMap"));
        if (!isJPA10()) {
            suite.addTest(new InheritedModelJunitTest("testInterfaces"));
        }

        return suite;
    }

    /**
     * The setup is done as a test, both to record its failure, and to allow execution in the server.
     */
    public void testSetup() {
        new InheritedTableManager().replaceTables(JUnitTestCase.getServerSession());
        clearCache();
    }

    public void  testMultipleIdButNonIdClassEntity() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        NoiseBylaw noiseBylaw = new NoiseBylaw();
        int noiseBylawId = 0;
        BuildingBylaw buildingBylaw = new BuildingBylaw();
        int buildingBylawId = 0;

        try {
            noiseBylaw.setCity("Ottawa");
            noiseBylaw.setDescription("Can't mow your grass after 9PM!");
            em.persist(noiseBylaw);

            buildingBylaw.setCity("Ottawa");
            buildingBylaw.setDescription("Can't build without a permit");
            em.persist(buildingBylaw);

            noiseBylawId = noiseBylaw.getNumber();
            buildingBylawId = buildingBylaw.getNumber();

            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();

        // find by object entity will not work since there is no IdClass in
        // this case so we will look it up through jpql
        String jpqlString = "SELECT n FROM NoiseBylaw n WHERE n.number =" + noiseBylawId;
        NoiseBylaw refreshedNoiseBylaw = (NoiseBylaw) em.createQuery(jpqlString).getSingleResult();
        assertTrue("The noise bylaw read back did not match the original", getServerSession().compareObjects(noiseBylaw, refreshedNoiseBylaw));

        String jpqlString2 = "SELECT n FROM BuildingBylaw n WHERE n.number =" + buildingBylawId;
        BuildingBylaw refreshedBuildingBylaw = (BuildingBylaw) em.createQuery(jpqlString2).getSingleResult();
        assertTrue("The building bylaw read back did not match the original", getServerSession().compareObjects(buildingBylaw, refreshedBuildingBylaw));

        closeEntityManager(em);
    }

    public void testBecksBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        BeerConsumer initialBC = new BeerConsumer();
        int beerConsumerId = 0;

        try {
            Becks becks1  = new Becks();
            becks1.setAlcoholContent(5.1);
            BecksTag becksTag1 = new BecksTag();
            becksTag1.setCallNumber("0A.789");
            em.persist(becksTag1);

            Becks becks2  = new Becks();
            becks2.setAlcoholContent(5.1);
            BecksTag becksTag2 = new BecksTag();
            becksTag2.setCallNumber("BX.521");
            em.persist(becksTag2);

            Becks becks3  = new Becks();
            becks3.setAlcoholContent(5.1);
            BecksTag becksTag3 = new BecksTag();
            becksTag3.setCallNumber("UY.429");
            em.persist(becksTag3);

            initialBC.setName("Becks Consumer");
            initialBC.addBecksBeerToConsume(becks1, becksTag1);
            initialBC.addBecksBeerToConsume(becks2, becksTag2);
            initialBC.addBecksBeerToConsume(becks3, becksTag3);

            em.persist(initialBC);
            beerConsumerId = initialBC.getId();

            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        BeerConsumer refreshedBC = em.find(BeerConsumer.class, beerConsumerId);
        assertTrue("The beer consumer read back did not match the original", getServerSession().compareObjects(initialBC, refreshedBC));
    }

    public void testCoronaBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        BeerConsumer initialBC = new BeerConsumer();
        int beerConsumerId = 0;

        try {
            Corona corona1  = new Corona();
            corona1.setAlcoholContent(5.3);
            CoronaTag coronaTag1 = new CoronaTag();
            coronaTag1.setCode("0A");
            coronaTag1.setNumber(789);

            Corona corona2  = new Corona();
            corona2.setAlcoholContent(5.3);
            CoronaTag coronaTag2 = new CoronaTag();
            coronaTag2.setCode("BX");
            coronaTag2.setNumber(521);

            Corona corona3  = new Corona();
            corona3.setAlcoholContent(5.3);
            CoronaTag coronaTag3 = new CoronaTag();
            coronaTag3.setCode("UY");
            coronaTag3.setNumber(429);

            initialBC.setName("Corona Consumer");
            initialBC.addCoronaBeerToConsume(corona1, coronaTag1);
            initialBC.addCoronaBeerToConsume(corona2, coronaTag2);
            initialBC.addCoronaBeerToConsume(corona3, coronaTag3);

            em.persist(initialBC);
            beerConsumerId = initialBC.getId();

            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        BeerConsumer refreshedBC = em.find(BeerConsumer.class, beerConsumerId);
        assertTrue("The beer consumer read back did not match the original", getServerSession().compareObjects(initialBC, refreshedBC));
    }

    public void testCreateBlue() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        try {
            Blue blue = new Blue();
            blue.setAlcoholContent(new Float(5.3));
            em.persist(blue);
            m_blueId = blue.getId();
            blue.setUniqueKey(m_blueId.toBigInteger());
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }
            closeEntityManager(em);
            throw e;
        }

        closeEntityManager(em);
    }

    public void testOrderColumnNoviceBeerConsumerDesignations() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        NoviceBeerConsumer beerConsumer;

        try {
            // These are the designations we have right now ...
            // index: 0 - 1 - 2 - 3 - 4
            //  item: 5 - 4 - 2 - 3 - 1
           beerConsumer = em.find(NoviceBeerConsumer.class, m_noviceBeerConsumerId);

            //String stringFour = beerConsumer.getDesignations().remove(1);
            //String stringTwo = beerConsumer.getDesignations().remove(1);
           //beerConsumer.getDesignations().add(stringTwo);

            // This is what we have done
            // index: 0 - 1 - 2 - 3
            //  item: 5 - 3 - 1 - 2

            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }
            closeEntityManager(em);
            throw e;
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        BeerConsumer refreshedBC = em.find(BeerConsumer.class, m_noviceBeerConsumerId);
        assertTrue("The novice beer consumer read back did not match the original", getServerSession().compareObjects(beerConsumer, refreshedBC));
    }

    public void testReadBlue() {
        Blue blue = createEntityManager().find(Blue.class, m_blueId);
        assertTrue("Error on reading back a Blue beer", blue != null);
    }

    public void testCreateBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        try {
            BeerConsumer beerConsumer = new BeerConsumer();
            beerConsumer.setName("Blue Consumer");
            em.persist(beerConsumer);
            m_beerConsumerId = beerConsumer.getId();
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);
    }

    public void testCreateNoviceBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        NoviceBeerConsumer beerConsumer = new NoviceBeerConsumer();

        try {
            beerConsumer.setName("Novice Beer Consumer");
            beerConsumer.setIQ(100);

            beerConsumer.getAcclaims().add(1);
            beerConsumer.getAcclaims().add(2);
            beerConsumer.getAcclaims().add(3);

            beerConsumer.getAwards().put(1, 1);
            beerConsumer.getAwards().put(2, 2);
            beerConsumer.getAwards().put(3, 3);

            beerConsumer.getDesignations().add("5");
            beerConsumer.getDesignations().add("4");
            beerConsumer.getDesignations().add("2");
            beerConsumer.getDesignations().add("3");
            beerConsumer.getDesignations().add("1");

            Record record1 = new Record();
            record1.setDescription("Slowest beer ever consumed - 10 hours");
            record1.setDate(Helper.dateFromYearMonthDate(2008, 1, 1));
            record1.setLocation(new Location("Paris", "France"));
            Venue venue1 = new Venue();
            venue1.setAttendance(10);
            venue1.setName("Champs-Elysees");
            record1.setVenue(venue1);
            beerConsumer.getRecords().add(record1);

            Accredidation accredidation = new Accredidation();
            accredidation.setDetails("Superb, just superb!");
            Witness witness1 = new Witness();
            witness1.setName("Mickey Blue Eyes");
            accredidation.addWitness(witness1);
            Witness witness2 = new Witness();
            witness2.setName("Donny Trafalgo");
            accredidation.addWitness(witness2);
            beerConsumer.setAccredidation(accredidation);

            Committee committee1 = new Committee();
            committee1.setDescription("Moral labelling");
            beerConsumer.addCommittee(committee1);

            Committee committee2 = new Committee();
            committee2.setDescription("Crimes against beer");
            beerConsumer.addCommittee(committee2);

            Committee committee3 = new Committee();
            committee3.setDescription("BADD - Beers against drunk dorks");
            beerConsumer.addCommittee(committee3);

            em.persist(beerConsumer);
            m_noviceBeerConsumerId = beerConsumer.getId();
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation for a novice beer consumer: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        BeerConsumer refreshedBC = em.find(BeerConsumer.class, m_noviceBeerConsumerId);
        assertTrue("The novice beer consumer read back did not match the original", getServerSession().compareObjects(beerConsumer, refreshedBC));
    }

    public void testCreateExpertBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        ExpertBeerConsumer beerConsumer = new ExpertBeerConsumer();

        try {
            beerConsumer.setName("Expert Beer Consumer");
            beerConsumer.setIQ(110);

            beerConsumer.getAcclaims().add("A");
            beerConsumer.getAcclaims().add("B");
            beerConsumer.getAcclaims().add("C");

            // Commenting out this mapping until bug 272298 is resolved.
            // The compareObjects check below would fail in some configurations
            // as a result of this.
            //beerConsumer.getAudio().add(new byte[]{1});
            //beerConsumer.getAudio().add(new byte[]{2});
            //beerConsumer.getAudio().add(new byte[]{3});

            beerConsumer.getAwards().put("A", "A");
            beerConsumer.getAwards().put("B", "B");
            beerConsumer.getAwards().put("C", "C");

            beerConsumer.getDesignations().add("A");
            beerConsumer.getDesignations().add("B");

            m_quote1Stamp = Helper.timestampFromDate(Helper.dateFromYearMonthDate(2009, 1, 1));
            beerConsumer.getQuotes().put(m_quote1Stamp, QUOTE_ONE);
            m_quote2Stamp = Helper.timestampFromDate(Helper.dateFromYearMonthDate(2005, 7, 9));
            beerConsumer.getQuotes().put(m_quote2Stamp, QUOTE_TWO);

            Record record1 = new Record();
            record1.setDescription("Fastest beer ever consumed - 10 ms");
            record1.setDate(Helper.dateFromYearMonthDate(2009, 10, 10));
            record1.setLocation(new Location("Ottawa", "Canada"));
            Venue venue1 = new Venue();
            venue1.setAttendance(10000);
            venue1.setName("Scotiabank PLace");
            record1.setVenue(venue1);
            beerConsumer.getRecords().add(record1);

            Record record2 = new Record();
            record2.setDescription("Most beers consumed upside down - 12");
            record2.setDate(Helper.dateFromYearMonthDate(2007, 11, 11));
            record2.setLocation(new Location("Sydney", "Australia"));
            Venue venue2 = new Venue();
            venue2.setAttendance(2678);
            venue2.setName("Opera House");
            record2.setVenue(venue2);
            beerConsumer.getRecords().add(record2);

            Record record3 = new Record();
            record3.setDescription("Most beers consumed in a second - 5");
            record3.setDate(Helper.dateFromYearMonthDate(2005, 12, 12));
            record3.setLocation(new Location("Miami", "USA"));
            Venue venue3 = new Venue();
            venue3.setAttendance(63000);
            venue3.setName("Dolphin Stadium");
            record3.setVenue(venue3);
            beerConsumer.getRecords().add(record3);

            Accredidation accredidation = new Accredidation();
            accredidation.setDetails("Elite, absolutely elite!");
            Witness witness1 = new Witness();
            witness1.setName("Big Bobby");
            accredidation.addWitness(witness1);
            Witness witness2 = new Witness();
            witness2.setName("Little Bobby");
            accredidation.addWitness(witness2);
            Official official = new Official();
            official.setName("Authority Joe");
            accredidation.addOfficial(official);
            beerConsumer.setAccredidation(accredidation);

            Birthday birthday1 = new Birthday();
            birthday1.setDay(9);
            birthday1.setMonth(7);
            birthday1.setYear(2005);
            beerConsumer.addCelebration(birthday1, "Drank a 24 of Heineken");

            Birthday birthday2 = new Birthday();
            birthday2.setDay(10);
            birthday2.setMonth(7);
            birthday2.setYear(2006);
            beerConsumer.addCelebration(birthday2, "Drank a 24 of Becks");

            Committee committee1 = new Committee();
            committee1.setDescription("New beer committee");
            CommitteeDates committee1Dates = new CommitteeDates();
            committee1Dates.setStartDate("Jan 1, 2010");
            committee1.setCommitteeDates(committee1Dates);
            beerConsumer.addCommittee(committee1);

            Committee committee2 = new Committee();
            committee2.setDescription("Alcohol content regulation");
            CommitteeDates committee2Dates = new CommitteeDates();
            committee2Dates.setStartDate("Jan 1, 1970");
            committee2Dates.setEndDate("Jan 1, 2001");
            committee2.setCommitteeDates(committee2Dates);
            beerConsumer.addCommittee(committee2);

            em.persist(beerConsumer);
            m_expertBeerConsumerId = beerConsumer.getId();
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation for an expert beer consumer: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        BeerConsumer refreshedBC = em.find(BeerConsumer.class, m_expertBeerConsumerId);
        assertTrue("The expert beer consumer read back did not match the original", getServerSession().compareObjects(beerConsumer, refreshedBC));
    }

    public void testDeleteMiddleRecordOfExpertBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        try {
            ExpertBeerConsumer beerConsumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            beerConsumer.getRecords().remove(1);
            commitTransaction(em);
        } finally {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }
            closeEntityManager(em);
            clearCache();
        }
    }

    public void testHeinekenBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        BeerConsumer initialBC = new BeerConsumer();
        int beerConsumerId = 0;

        try {
            Heineken heineken1  = new Heineken();
            heineken1.setAlcoholContent(5.0);

            Heineken heineken2  = new Heineken();
            heineken2.setAlcoholContent(5.0);

            Heineken heineken3  = new Heineken();
            heineken3.setAlcoholContent(5.0);

            initialBC.setName("Heineken Consumer");
            Calendar cal = Calendar.getInstance();
            cal.set(2008, 12, 12);
            initialBC.addHeinekenBeerToConsume(heineken1, cal.getTime());
            cal.set(2009, 1, 1);
            initialBC.addHeinekenBeerToConsume(heineken2, cal.getTime());
            cal.set(2009, 2, 2);
            initialBC.addHeinekenBeerToConsume(heineken3, cal.getTime());

            em.persist(initialBC);
            beerConsumerId = initialBC.getId();

            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        BeerConsumer refreshedBC = em.find(BeerConsumer.class, beerConsumerId);
        assertTrue("The beer consumer read back did not match the original", getServerSession().compareObjects(initialBC, refreshedBC));
    }

    public void testReadNoviceBeerConsumer() {
        NoviceBeerConsumer consumer = createEntityManager().find(NoviceBeerConsumer.class, m_noviceBeerConsumerId);

        assertTrue("Error on reading back a NoviceBeerConsumer", consumer != null);

        assertTrue("IQ Level was not persisted.", consumer.getIQ() == 100);

        assertTrue("Incorrect number of acclaims returned.", consumer.getAcclaims().size() == 3);
        assertTrue("Missing acclaim - 1", consumer.getAcclaims().contains(1));
        assertTrue("Missing acclaim - 2", consumer.getAcclaims().contains(2));
        assertTrue("Missing acclaim - 3", consumer.getAcclaims().contains(3));

        assertTrue("Incorrect number of awards returned.", consumer.getAwards().size() == 3);
        Integer awardCode = consumer.getAwards().get(1);
        assertFalse("Missing award code - 1", awardCode == null);
        assertTrue("Award code 1 is incorrect", awardCode.equals(1));

        awardCode = consumer.getAwards().get(2);
        assertFalse("Missing award code - 2", awardCode == null);
        assertTrue("Award code 2 is incorrect", awardCode.equals(2));

        awardCode = consumer.getAwards().get(3);
        assertFalse("Missing award code - 3", awardCode == null);
        assertTrue("Award code 3 is incorrect", awardCode.equals(3));

        assertTrue("Incorrect number of designations returned.", consumer.getDesignations().size() == 5);
        assertTrue("Missing designation - 5 at index 0", consumer.getDesignations().get(0).equals("5"));
        assertTrue("Missing designation - 4 at index 1", consumer.getDesignations().get(1).equals("4"));
        assertTrue("Missing designation - 2 at index 2", consumer.getDesignations().get(2).equals("2"));
        assertTrue("Missing designation - 3 at index 3", consumer.getDesignations().get(3).equals("3"));
        assertTrue("Missing designation - 1 at index 4", consumer.getDesignations().get(4).equals("1"));

        assertTrue("Incorrect number of records returned.", consumer.getRecords().size() == 1);
    }

    public void testReadExpertBeerConsumer() {
        ExpertBeerConsumer consumer = createEntityManager().find(ExpertBeerConsumer.class, m_expertBeerConsumerId);

        assertTrue("Error on reading back an ExpertBeerConsumer", consumer != null);

        assertTrue("IQ Level was not persisted.", consumer.getIQ() == 110);

        assertTrue("Incorrect number of acclaims returned.", consumer.getAcclaims().size() == 3);
        assertTrue("Missing acclaim - A", consumer.getAcclaims().contains("A"));
        assertTrue("Missing acclaim - B", consumer.getAcclaims().contains("B"));
        assertTrue("Missing acclaim - C", consumer.getAcclaims().contains("C"));

        //assertTrue("Incorrect number of audio returned.", consumer.getAudio().size() == 3);
        // don't individually check them, assume they are correct.

        assertTrue("Incorrect number of awards returned.", consumer.getAwards().size() == 3);
        String awardCode = consumer.getAwards().get("A");
        assertFalse("Missing award code - A", awardCode == null);
        assertTrue("Award code A is incorrect", awardCode.equals("A"));

        awardCode = consumer.getAwards().get("B");
        assertFalse("Missing award code - B", awardCode == null);
        assertTrue("Award code B is incorrect", awardCode.equals("B"));

        awardCode = consumer.getAwards().get("C");
        assertFalse("Missing award code - C", awardCode == null);
        assertTrue("Award code C is incorrect", awardCode.equals("C"));

        assertTrue("Incorrect number of designations returned.", consumer.getDesignations().size() == 2);
        assertTrue("Missing designation - A", consumer.getDesignations().contains("A"));
        assertTrue("Missing designation - B", consumer.getDesignations().contains("B"));

        assertTrue("Incorrect number of quotes returned.", consumer.getQuotes().size() == 2);
        String quote = consumer.getQuotes().get(m_quote1Stamp);
        assertFalse("Missing quote from Jan 1, 2009", quote == null);
        assertTrue("Quote from Jan 1, 2009 was incorrect", quote.equals(QUOTE_ONE));

        quote = consumer.getQuotes().get(m_quote2Stamp);
        assertFalse("Missing quote from Jul 9, 2005", quote == null);
        assertTrue("Quote from Jul 9, 2005 was incorrect", quote.equals(QUOTE_TWO));

        assertTrue("Incorrect number of records returned.", consumer.getRecords().size() == 2);
        assertTrue("Incorrect first record", consumer.getRecords().get(0).getDescription().equals("Fastest beer ever consumed - 10 ms"));
        assertTrue("Incorrect last record", consumer.getRecords().get(1).getDescription().equals("Most beers consumed in a second - 5"));
    }

    // Bug 296606 - issues with ElementCollections
    // This test should be run after testCreateExpertBearConsumer
    // This test makes changes, so testReadExpertBeerConsumer will fail after this test.
    public void testExpertBeerConsumerRecordsCRUD() {
        String errorMsg = "";

        int nRecords;
        int nRecordsExpected = 2;

        clearCache();
        EntityManager em = createEntityManager();
        try {
            // read all Records inside transaction
            beginTransaction(em);
            ExpertBeerConsumer consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            // trigger indirection
            nRecords = consumer.getRecords().size();
            // Bug 296606 - issues with ElementCollections cause commit to fail with NPE
            // Before the bug was fixed, the workaround was to annotate Record with @ChangeTracking(ChangeTrackingType.DEFERRED)
            commitTransaction(em);
            closeEntityManager(em);
            if(nRecords != nRecordsExpected) {
                errorMsg += "wrong number of records after read; ";
            }

            // remove Record
            em = createEntityManager();
            beginTransaction(em);
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            Record recordToRemove = consumer.getRecords().iterator().next();
            consumer.getRecords().remove(recordToRemove);
            commitTransaction(em);
            closeEntityManager(em);
            nRecordsExpected--;
            // verify in cache
            em = createEntityManager();
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            closeEntityManager(em);
            nRecords = consumer.getRecords().size();
            if(nRecords != nRecordsExpected) {
                errorMsg += "cache: wrong number of records after remove; ";
            }
            // verify in db
            clearCache();
            em = createEntityManager();
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            closeEntityManager(em);
            nRecords = consumer.getRecords().size();
            if(nRecords != nRecordsExpected) {
                errorMsg += "db: wrong number of records after remove; ";
            }

            // add Record
            Record record1 = new Record();
            record1.setDescription("Original");
            record1.setDate(Helper.dateFromYearMonthDate(2009, 1, 1));
            record1.setLocation(new Location("Ottawa", "Canada"));
            Venue venue1 = new Venue();
            venue1.setAttendance(10);
            venue1.setName("Original");
            record1.setVenue(venue1);
            em = createEntityManager();
            beginTransaction(em);
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            consumer.getRecords().add(record1);
            commitTransaction(em);
            closeEntityManager(em);
            nRecordsExpected++;
            // verify in cache
            em = createEntityManager();
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            closeEntityManager(em);
            nRecords = consumer.getRecords().size();
            if(nRecords != nRecordsExpected) {
                errorMsg += "cache: wrong number of records after add; ";
            }
            // verify in db
            clearCache();
            em = createEntityManager();
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            closeEntityManager(em);
            nRecords = consumer.getRecords().size();
            if(nRecords != nRecordsExpected) {
                errorMsg += "db: wrong number of records after add; ";
            }

            // update all Records one by one.
            String newDescription = "New Description ";
            String newName = "New Name ";
            em = createEntityManager();
            beginTransaction(em);
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            int i=0;
            Iterator<Record> it = consumer.getRecords().iterator();
            while(it.hasNext()) {
                Record record = it.next();
                String index = Integer.toString(i++);
                record.setDescription(newDescription + index);
                record.getVenue().setName(newName + index );
            }
            // Bug 296606 - issues with ElementCollections cause commit to fail with NPE
            // Before the bug was fixed, the workaround was to annotate both Record and Venue with @ChangeTracking(ChangeTrackingType.DEFERRED).
            // With that workaround the test still used to fail - Record and Venue were not updated in the data base.
            // Before the bug was fixed, the workaround for that was to set property "eclipselink.weaving.internal" to "false" in persistence.xml.
            commitTransaction(em);
            // verify in cache
            em = createEntityManager();
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            closeEntityManager(em);
            nRecords = consumer.getRecords().size();
            if(nRecords != nRecordsExpected) {
                errorMsg += "cache: wrong number of records after update; ";
            }
            HashSet<String> usedDescriptions = new HashSet(nRecords);
            HashSet<String> usedNames = new HashSet(nRecords);
            it = consumer.getRecords().iterator();
            while(it.hasNext()) {
                Record record = it.next();
                String description = record.getDescription();
                if(!description.startsWith(newDescription)) {
                    errorMsg += "cache: wrong record description after update; ";
                }
                usedDescriptions.add(description);
                String name = record.getVenue().getName();
                if(!name.startsWith(newName)) {
                    errorMsg += "cache: wrong venue name after update; ";
                }
                usedNames.add(name);
            }
            if(usedDescriptions.size() != nRecords) {
                errorMsg += "cache: records with same description; ";
            }
            if(usedNames.size() != nRecords) {
                errorMsg += "cache: venues with same name; ";
            }
            // verify in db
            clearCache();
            em = createEntityManager();
            consumer = em.find(ExpertBeerConsumer.class, m_expertBeerConsumerId);
            closeEntityManager(em);
            nRecords = consumer.getRecords().size();
            if(nRecords != nRecordsExpected) {
                errorMsg += "db: wrong number of records after update; ";
            }
            usedDescriptions.clear();
            usedNames.clear();
            it = consumer.getRecords().iterator();
            while(it.hasNext()) {
                Record record = it.next();
                String description = record.getDescription();
                if(!description.startsWith(newDescription)) {
                    errorMsg += "db: wrong record description after update; ";
                }
                usedDescriptions.add(description);
                String name = record.getVenue().getName();
                if(!name.startsWith(newName)) {
                    errorMsg += "db: wrong venue name after update; ";
                }
                usedNames.add(name);
            }
            if(usedDescriptions.size() != nRecords) {
                errorMsg += "db: records with same description; ";
            }
            if(usedNames.size() != nRecords) {
                errorMsg += "db: venues with same name; ";
            }

            if(errorMsg.length() > 0) {
                fail(errorMsg);
            }
        } finally {
            if(em != null) {
                if (isTransactionActive(em)){
                    rollbackTransaction(em);
                }
                if(isOnServer()) {
                    closeEntityManager(em);
                } else if(em.isOpen()) {
                    closeEntityManager(em);
                }
            }
        }
    }

    public void testRedStripeExpertConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        ExpertBeerConsumer initialEBC = new ExpertBeerConsumer();
        initialEBC.setAccredidation(new Accredidation());
        int beerConsumerId = 0;

        try {
            RedStripe redStripe1 = new RedStripe();
            redStripe1.setAlcoholContent(5.0);
            initialEBC.addRedStripeBeersToConsume(redStripe1, "1");

            RedStripe redStripe2 = new RedStripe();
            redStripe2.setAlcoholContent(5.0);
            initialEBC.addRedStripeBeersToConsume(redStripe2, "2");

            initialEBC.setName("Expert Red Stripe Consumer");
            em.persist(initialEBC);
            beerConsumerId = initialEBC.getId();

            commitTransaction(em);
        } catch (RuntimeException e) {
            e.printStackTrace();

            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        ExpertBeerConsumer refreshedEBC = em.find(ExpertBeerConsumer.class, beerConsumerId);
        assertTrue("The expert beer consumer read back did not match the original", getServerSession().compareObjects(initialEBC, refreshedEBC));
    }

    public void testRedStripeNoviceConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        NoviceBeerConsumer initialNBC = new NoviceBeerConsumer();
        initialNBC.setAccredidation(new Accredidation());
        int beerConsumerId = 0;

        try {
            RedStripe redStripe1 = new RedStripe();
            redStripe1.setAlcoholContent(5.0);
            initialNBC.addRedStripeBeersToConsume(redStripe1, "3");

            RedStripe redStripe2 = new RedStripe();
            redStripe2.setAlcoholContent(5.0);
            initialNBC.addRedStripeBeersToConsume(redStripe2, "4");

            initialNBC.setName("Novice Red Stripe Consumer");
            em.persist(initialNBC);
            beerConsumerId = initialNBC.getId();

            commitTransaction(em);
        } catch (RuntimeException e) {
            e.printStackTrace();

            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);

        clearCache();
        em = createEntityManager();
        NoviceBeerConsumer refreshedNBC = em.find(NoviceBeerConsumer.class, beerConsumerId);
        assertTrue("The novice beer consumer read back did not match the original", getServerSession().compareObjects(initialNBC, refreshedNBC));
    }

    public void testUpdateBeerConsumer() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        try {
            BeerConsumer beerConsumer = em.find(BeerConsumer.class, m_beerConsumerId);

            // Create a detached object that mirrors the beer consumer from
            // testCreateBeerConsumer.
            BeerConsumer beerConsumerDetached = new BeerConsumer();
            beerConsumerDetached.setName(beerConsumer.getName());
            beerConsumerDetached.setId(beerConsumer.getId());
            beerConsumerDetached.setVersion(beerConsumer.getVersion());

            Blue blue = em.find(Blue.class, m_blueId);
            beerConsumerDetached.addBlueBeerToConsume(blue);

            em.merge(beerConsumerDetached);
            commitTransaction(em);
        } catch (RuntimeException e) {
            e.printStackTrace();
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during the merge of the detached beer consumer: [" + e.getMessage() + "]");
        }

        closeEntityManager(em);
    }

    public void testVersionUpdateOnElementCollectionChange() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        try {
            BeerConsumer beerConsumer = (BeerConsumer) em.createQuery("select b from BeerConsumer b join b.redStripes r").getResultList().get(0);
            int currentVersion = beerConsumer.getVersion();
            beerConsumer.getRedStripes().put("version", new RedStripe(Double.valueOf("343")));
            commitTransaction(em);
            assertTrue("Did not increment version for change to element collection", beerConsumer.getVersion() == ++currentVersion);


        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            // Re-throw exception to ensure stacktrace appears in test result.
            throw e;
        }

        closeEntityManager(em);

    }

    // Test the clone method works with LAZY attributes at multiple levels of an inheritance hierarchy
    public void testInheritedClone() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        SerialNumber serialNumber = new SerialNumber();
        em.persist(serialNumber);
        Alpine alpine = new Alpine(serialNumber);
        alpine.setBestBeforeDate(Helper.dateFromYearMonthDate(2005, 8, 21));
        alpine.setAlcoholContent(5.0);
        alpine.setClassification(Alpine.Classification.BITTER);
        alpine.addInspectionDate(new Date(System.currentTimeMillis()));
        alpine.getBeerConsumer();
        Alpine clone = null;
        try{
            clone = alpine.clone();
        } catch (CloneNotSupportedException ex){
            fail("Caught CloneNotSupportedException " + ex);
        }

        BeerConsumer consumer = new BeerConsumer();
        consumer.setName("Keith Alexander");

        BlueLight blueLight = new BlueLight();
        blueLight.setAlcoholContent(new Float(4.0));
        blueLight.setUniqueKey(new BigInteger((new Long(System.currentTimeMillis()).toString())));
        em.persist(blueLight);
        consumer.addBlueLightBeerToConsume(blueLight);
        clone.setBeerConsumer(consumer);
        consumer.addAlpineBeerToConsume(clone);
        em.persist(clone);
        em.persist(consumer);
        if (alpine.getBeerConsumer() == clone.getBeerConsumer()) {
            fail("Changing clone beerConsumer changed original.");
        }
        commitTransaction(em);
        Integer alpineId = clone.getId();
        Integer consumerId = consumer.getId();
        BigDecimal blueLightId = blueLight.getId();
        clearCache();
        closeEntityManager(em);
        em = createEntityManager();
        beginTransaction(em);
        alpine = em.find(Alpine.class, alpineId);
        try{
            clone = alpine.clone();
        } catch (CloneNotSupportedException ex){
            fail("Caught CloneNotSupportedException " + ex);
        }

        consumer = new BeerConsumer();
        consumer.setName("Frank Keith");
        clone.setBeerConsumer(consumer);
        consumer.addAlpineBeerToConsume(clone);
        clone.getBeerConsumer();
        if (alpine.getBeerConsumer() == clone.getBeerConsumer()) {
            fail("Changing clone beerConsumer changed original.");
        }
        if (alpine.getBeerConsumer() == null) {
            fail("Changing clone address reset original to null.");
        }
        if (clone.getBeerConsumer() != consumer) {
            fail("Changing clone did not work.");
        }

        try{
            consumer = em.find(BeerConsumer.class, consumerId);
            BeerConsumer consumerClone = (BeerConsumer)consumer.clone();

            Iterator<Alpine> alpineIterator = consumer.getAlpineBeersToConsume().iterator();
            while (alpineIterator.hasNext()){
                alpine = alpineIterator.next();
                assertTrue("The original beerConsumer has an alpine beer with an incorrect back pointer.", consumer == alpine.getBeerConsumer());
            }

            alpineIterator = consumerClone.getAlpineBeersToConsume().iterator();
            while (alpineIterator.hasNext()){
                alpine = alpineIterator.next();
                assertTrue("The cloned beerConsumer has an alpine beer with an incorrect back pointer.", consumerClone == alpine.getBeerConsumer());
            }

            Iterator<BlueLight> blueLightIterator = consumer.getBlueLightBeersToConsume().iterator();
            while (blueLightIterator.hasNext()){
                blueLight = blueLightIterator.next();
                assertTrue("The original beerConsumer has an BlueLight beer with an incorrect back pointer.", consumer == blueLight.getBeerConsumer());
            }

            blueLightIterator = consumerClone.getBlueLightBeersToConsume().iterator();
            while (blueLightIterator.hasNext()){
                blueLight = blueLightIterator.next();
                assertTrue("The cloned beerConsumer has an BlueLight beer with an incorrect back pointer.", consumerClone == blueLight.getBeerConsumer());
            }
        } catch (CloneNotSupportedException e){
            fail("Call to clone threw CloneNotSupportedException");
        }

        alpine = em.find(Alpine.class, clone.getId());
        em.remove(alpine);
        blueLight = em.find(BlueLight.class, blueLightId);
        em.remove(blueLight);
        consumer = em.find(BeerConsumer.class, consumerId);
        em.remove(consumer);
        commitTransaction(em);
        closeEntityManager(em);
    }

    // BUG 227345
    public void testCascadeRemove() {
        BeerConsumer beerConsumer = null;
        BlueLight blueLightPersisted = null;
        BlueLight blueLightDetached = null;
        EntityManager em = createEntityManager();

        // Create and persist the beerConsumer
        try {
            beginTransaction(em);
            beerConsumer = new BeerConsumer();
            beerConsumer.setName("Beer Man");

            blueLightPersisted = new BlueLight();
            beerConsumer.getBlueLightBeersToConsume().add(blueLightPersisted);
            blueLightPersisted.setBeerConsumer(beerConsumer);

            em.persist(beerConsumer);

            // Unique key must be set before commit.
            blueLightPersisted.setUniqueKey(blueLightPersisted.getId().toBigInteger());

            // They should be known by the EM
            assertTrue(em.contains(beerConsumer));
            assertTrue(em.contains(blueLightPersisted));
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }
            closeEntityManager(em);
            throw e;
        }

        // Create BlueLightDetached and manage the relations
        try {
            beginTransaction(em);
            blueLightDetached = new BlueLight();
            blueLightDetached.setUniqueKey(new BigDecimal(blueLightPersisted.getUniqueKey().intValue() + 1).toBigInteger());

            // Set the pointers
            beerConsumer.getBlueLightBeersToConsume().add(blueLightDetached);
            blueLightDetached.setBeerConsumer(beerConsumer);

            // And now remove the beer consumer. The remove-operation should cascade
            if (isOnServer()){
                beerConsumer = em.find(BeerConsumer.class, m_beerConsumerId);
            }
            em.remove(beerConsumer);

            // It's o.k. should be detached
            assertFalse(em.contains(blueLightDetached));
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }
            closeEntityManager(em);
            throw e;
        }

        try {
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during the remove of the BeerConsumer: [" + e.getMessage() + "]");
        }

        // Ensure neither the beer consumer nor the blue light
        // persisted/detached are available.
        assertFalse("The beer consumer was not removed", em.contains(beerConsumer));
        assertFalse("The blue light persisted was not removed even though the its owning beer comsumer was removed", em.contains(blueLightPersisted));
        assertFalse("The blue light detached was persisted even though the its owning beer comsumer was removed", em.contains(blueLightDetached));

        closeEntityManager(em);
    }

    public void testBreakOrder_CorrectionType_EXCEPTION() {
        // create BeerConsumer with designations
        EntityManager em = createEntityManager();
        beginTransaction(em);
        NoviceBeerConsumer beerConsumer = new NoviceBeerConsumer();
        beerConsumer.setAccredidation(new Accredidation());
        beerConsumer.setName("Broken order");
        beerConsumer.getDesignations().add("0");
        beerConsumer.getDesignations().add("1");
        beerConsumer.getDesignations().add("2");
        try {
            em.persist(beerConsumer);
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }
            closeEntityManager(em);
            fail("failed to create beerConsumer");
        }
        Integer id = beerConsumer.getId();

        try {

            // break the order of designations
            beginTransaction(em);
            try {
                em.createNativeQuery("UPDATE NOVICE_CONSUMER_DESIGNATIONS SET ORDER_COLUMN = null WHERE NOVICE_CONSUMER_ID = " + id +" AND ORDER_COLUMN = 0").executeUpdate();
                commitTransaction(em);
            } catch (RuntimeException e) {
                if (isTransactionActive(em)) {
                    rollbackTransaction(em);
                }
                fail("failed to break order of designations");
            } finally {
                closeEntityManager(em);
            }

            // remove beerConsumer from the cache
            clearCache();

            // read back the beer consumer, reading of the list of designations with the broken order should trigger exception
            em = createEntityManager();
            try {
                beerConsumer = em.find(NoviceBeerConsumer.class, id);
                // trigger indirection
                beerConsumer.getDesignations().size();
                fail("Exception was expected - but not thrown");
            } catch (RuntimeException e) {
                boolean isCorrectException = false;
                if(e instanceof QueryException) {
                    QueryException queryException = (QueryException)e;
                    if(((QueryException) e).getErrorCode() == QueryException.LIST_ORDER_FIELD_WRONG_VALUE) {
                        isCorrectException = true;
                    }
                }
                if(!isCorrectException) {
                    throw e;
                }
            } finally {
                closeEntityManager(em);
            }
        } finally {
            // clean up
            em = createEntityManager();
            beginTransaction(em);
            try {
                beerConsumer = em.find(NoviceBeerConsumer.class, id);
                em.remove(beerConsumer);
                commitTransaction(em);
            } catch (RuntimeException e) {
                if (isTransactionActive(em)) {
                    rollbackTransaction(em);
                }
            } finally {
                closeEntityManager(em);
            }
        }
    }

    public void testBreakOrder_CorrectionType_READ_WRITE() {
        // create BeerConsumer with committees
        EntityManager em = createEntityManager();
        beginTransaction(em);
        ExpertBeerConsumer beerConsumer = new ExpertBeerConsumer();
        beerConsumer.setName("Beer order");
        beerConsumer.setAccredidation(new Accredidation());

        Committee committee0 = new Committee();
        committee0.setDescription("Broken Order 0");
        beerConsumer.addCommittee(committee0);

        Committee committee1 = new Committee();
        committee1.setDescription("Broken Order 1");
        beerConsumer.addCommittee(committee1);

        Committee committee2 = new Committee();
        committee2.setDescription("Broken Order 2");
        beerConsumer.addCommittee(committee2);
        try {
            em.persist(beerConsumer);
            commitTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)) {
                rollbackTransaction(em);
            }
            closeEntityManager(em);
            fail("failed to create beerConsumer");
        }
        Integer id = beerConsumer.getId();

        try {

            // break the order of committees
            beginTransaction(em);
            try {
                em.createNativeQuery("UPDATE JPA_CONSUMER_COMMITTEE SET ORDER_COLUMN = null WHERE CONSUMER_ID = " + id +" AND ORDER_COLUMN = 0").executeUpdate();
                commitTransaction(em);
            } catch (RuntimeException e) {
                if (isTransactionActive(em)) {
                    rollbackTransaction(em);
                }
                fail("failed to break order of committees");
            } finally {
                closeEntityManager(em);
            }

            // remove beerConsumer from the cache
            clearCache();

            // read back the beer consumer, reading of the list of committees with the broken should fix the order "on the fly":
            // the default correction performed on the list substitutes null in the first element back to 0;
            // then alter order of committees - that would cause the list order to be updated in the db (and become valid).
            em = createEntityManager();
            beginTransaction(em);
            try {
                beerConsumer = em.find(ExpertBeerConsumer.class, id);
                // committees #1 and #2 switch their positions in the list.
                Committee committee = beerConsumer.getCommittees().remove(2);
                beerConsumer.getCommittees().add(1, committee);
                commitTransaction(em);
            } catch (RuntimeException e) {
                if (isTransactionActive(em)) {
                    rollbackTransaction(em);
                }
                closeEntityManager(em);
                fail("failed to change committees order");
            }

            // now verify that the order in the db has been corrected
            List<Number> results = em.createNativeQuery("SELECT ORDER_COLUMN FROM JPA_CONSUMER_COMMITTEE WHERE CONSUMER_ID = " +id+ " ORDER BY ORDER_COLUMN").getResultList();
            int expectedSize = beerConsumer.getCommittees().size();
            if(expectedSize != results.size()) {
                closeEntityManager(em);
                fail("read in "+ results.size() + " committees; expected " + expectedSize);
            }
            for(int i=0; i < expectedSize; i++) {
                if(results.get(i) == null || results.get(i).intValue() != i) {
                    closeEntityManager(em);
                    fail("read in list in wrong order: " + results + "; expected: 0, 1, 2");
                }
            }
            closeEntityManager(em);

        } finally {
            // clean up
            em = createEntityManager();
            beginTransaction(em);
            try {
                beerConsumer = em.find(ExpertBeerConsumer.class, id);
                em.remove(beerConsumer);
                commitTransaction(em);
            } catch (RuntimeException e) {
                if (isTransactionActive(em)) {
                    rollbackTransaction(em);
                }
            } finally {
                closeEntityManager(em);
            }
        }
    }

    public void testMapOrphanRemoval(){
        EntityManager em = createEntityManager();
        beginTransaction(em);

        BeerConsumer initialBC = new BeerConsumer();

        int beerConsumerId = 0;
        try {
            Becks becks1  = new Becks();
            becks1.setAlcoholContent(5.1);
            BecksTag becksTag1 = new BecksTag();
            becksTag1.setCallNumber("0A.789");
            em.persist(becksTag1);

            initialBC.setName("Becks Consumer");
            initialBC.addBecksBeerToConsume(becks1, becksTag1);

            em.persist(initialBC);
            beerConsumerId = initialBC.getId();

            em.flush();

            clearCache();

            BeerConsumer refreshedBC = em.find(BeerConsumer.class, beerConsumerId);
            refreshedBC.getBecksBeersToConsume().remove(becksTag1);

            em.flush();

            clearCache();
            becksTag1 = (BecksTag)em.find(BecksTag.class, becksTag1.getId());
            assertTrue("Key was deleted when it should not be.", becksTag1 != null);
            becks1 = (Becks)em.find(Becks.class, becks1.getId());
            assertTrue("Orphan removal did not remove the orphan", becks1 == null);

            rollbackTransaction(em);
        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            closeEntityManager(em);
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        }

    }

    public void testSerializedElementCollectionMap(){
        EntityManager em = createEntityManager();
        beginTransaction(em);

        try{
            BeerConsumer initialBC = new BeerConsumer();
            em.persist(initialBC);

            SerialNumber serialNumber = new SerialNumber();
            em.persist(serialNumber);

            initialBC.addCommentLookup(serialNumber, "A test comment");

            em.flush();
            em.clear();

            initialBC = em.find(BeerConsumer.class, initialBC.getId());
            serialNumber = em.find(SerialNumber.class, serialNumber.getNumber());

            assertTrue("The serialized map was not properly retrieved after persist.", initialBC.getCommentLookup().size() == 1);
            assertTrue("The serialized map did not contain the proper entry.", initialBC.getCommentLookup().get(serialNumber) != null);
        } finally {
            rollbackTransaction(em);
            closeEntityManager(em);
        }
    }

    // this method is a test for the fix for bug 299847, prior to the fix, it fails when
    // weaving is disabled
    public void testAddToHeinekenBeerConsumerMap() {
        EntityManager em = createEntityManager();
        beginTransaction(em);

        int beerConsumerId = 0;
        int heinekenId = 0;
        Calendar cal = Calendar.getInstance();
        cal.set(2008, 12, 12);
        try {
            BeerConsumer initialBC = new BeerConsumer();
            initialBC.setName("Heineken Consumer");
            em.persist(initialBC);
            Heineken heineken1  = new Heineken();
            heineken1.setAlcoholContent(5.0);
            em.persist(heineken1);
            commitTransaction(em);

            beerConsumerId = initialBC.getId();
            heinekenId = heineken1.getId();

            clearCache();

            beginTransaction(em);

            initialBC = em.find(BeerConsumer.class, initialBC.getId());
            heineken1 = em.find(Heineken.class, heineken1.getId());
            initialBC.addHeinekenBeerToConsume(heineken1, cal.getTime());
            commitTransaction(em);

            clearCache();

            initialBC = em.find(BeerConsumer.class, initialBC.getId());
            heineken1 = em.find(Heineken.class, heineken1.getId());
            assertTrue("The beer consumer is not appropriately associated with the heineken.", heineken1.getBeerConsumer() != null);
            assertTrue("The heineken is not appropriately associated with the beer consumer.", initialBC.getHeinekenBeersToConsume().size() == 1);
        } catch (RuntimeException e) {
            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        } finally {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }
            beginTransaction(em);
            BeerConsumer consumer = em.find(BeerConsumer.class, beerConsumerId);
            Heineken heineken = em.find(Heineken.class, heinekenId);
            if (consumer != null){
                em.remove(consumer);
            }
            if (heineken != null){
                em.remove(heineken);
            }

            commitTransaction(em);
            closeEntityManager(em);
        }
    }

    public void testColumnUpdatableAndInsertable() {
        EntityManager em = createEntityManager();

        try {
            // Create an official
            beginTransaction(em);

            OfficialEntry officialEntry = new OfficialEntry();
            em.persist(officialEntry);

            Official initialOfficial = new Official();
            initialOfficial.setName("Gui Pelletier"); // insertable=true, updatable=false
            initialOfficial.setAge(25); // insertable=false, updatable=true
            initialOfficial.setSalary(50000); // insertable=true, updatable=false
            initialOfficial.setBonus(10000); // insertable=false, updatable=true

            // Tests multiple mappings to the same column. The M-1 is read only
            // and the basic is the writable mapping.
            initialOfficial.setOfficialEntry(officialEntry); // insertable=false, updatable=false
            initialOfficial.setOfficialEntryId(officialEntry.getId()); // insertable=true, updatable=true

            ServiceTime service = new ServiceTime();
            service.setStartDate("Jan 1, 2008"); // insertable=true, updatable=false
            service.setEndDate("Jul 1, 2010"); // insertable=false, updatable=true
            initialOfficial.setServiceTime(service);

            em.persist(initialOfficial);

            commitTransaction(em);

            // Close the EM, clear cache and get new EM.
            closeEntityManager(em);
            clearCache();
            em = createEntityManager();

            // Read the official and verify its content
            beginTransaction(em);
            Official official = em.find(Official.class, initialOfficial.getId());
            assertTrue("The name was not inserted", official.getName().equals("Gui Pelletier"));
            assertTrue("The age was inserted", official.getAge() == null);
            assertTrue("The salary was not inserted", official.getSalary() == 50000);
            assertTrue("The bonus was inserted", official.getBonus() == null);
            assertTrue("The official entry was not inserted", official.getOfficialEntryId() == officialEntry.getId());
            assertTrue("The embeddable start date was not inserted", official.getServiceTime().getStartDate().equals("Jan 1, 2008"));
            assertTrue("The embeddable end date was inserted", official.getServiceTime().getEndDate() == null);

            // Change the updatable=false fields:
            official.setName("Guy Pelletier");
            official.setSalary(100000);
            official.getServiceTime().setStartDate("Jan 30, 2008");

            // Update the insertable=false fields:
            official.setAge(25);
            official.setBonus(10000);
            official.getServiceTime().setEndDate("Jul 1, 2010");

            commitTransaction(em);

            // Close the EM, clear cache and get new EM.
            closeEntityManager(em);
            clearCache();
            em = createEntityManager();

            // The refreshed official at this point should not have had any
            // update changes to name but age should now be updated.
            Official refreshedOfficial = em.find(Official.class, initialOfficial.getId());
            assertTrue("The refreshedOfficial did not match the original", getServerSession().compareObjects(initialOfficial, refreshedOfficial));

        } catch (RuntimeException e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            fail("An exception was caught during create operation: [" + e.getMessage() + "]");
        } finally {
            closeEntityManager(em);
        }
    }

    public void testColumnUpdatableAndInsertableThroughQuery() {
        if ((JUnitTestCase.getServerSession()).getPlatform().isSymfoware()) {
            getServerSession().logMessage("Test testColumnUpdatableAndInsertableThroughQuery skipped for this platform, "
                    + "Symfoware doesn't support UpdateAll/DeleteAll on multi-table objects (see rfe 298193).");
            return;
        }

        EntityManager em = createEntityManager();

        try {
            // Create an official
            beginTransaction(em);
            Official initialOfficial = new Official();
            initialOfficial.setName("Gui Pelletier");
            em.persist(initialOfficial);
            commitTransaction(em);

            // Close the EM, clear cache and get new EM.
            closeEntityManager(em);
            clearCache();
            em = createEntityManager();

            // Update the official using a named query.
            beginTransaction(em);
            Query query = em.createNamedQuery("UpdateOfficalName");
            query.setParameter("name", "Guy");
            query.setParameter("id", initialOfficial.getId());
            query.executeUpdate();
            Official modifiedOfficial = em.find(Official.class, initialOfficial.getId());
            assertTrue("The name was not updated after executing the named query", modifiedOfficial.getName().equals("Guy"));
            commitTransaction(em);

            // Close the EM, clear cache and get new EM.
            closeEntityManager(em);
            clearCache();
            em = createEntityManager();

            Official refreshedOfficial = em.find(Official.class, modifiedOfficial.getId());
            assertTrue("The refreshedOfficial did not match the modified", getServerSession().compareObjects(modifiedOfficial, refreshedOfficial));

        } catch (Exception e) {
            if (isTransactionActive(em)){
                rollbackTransaction(em);
            }

            fail("Update query failed: " + e.getMessage());
        } finally {
            closeEntityManager(em);
        }
    }

    public void testElementCollectionMapEmbeddable(){
        EntityManager em = createEntityManager();

        try {
            // Create an official
            beginTransaction(em);
            BeerConsumer consumer = new BeerConsumer();
            consumer.setName("Lionel");
            RedStripe rs = new RedStripe();
            rs.setAlcoholContent(4.5);
            consumer.addRedStripeByAlcoholContent(rs);
            em.persist(consumer);
            em.flush();

            rs = new RedStripe();
            rs.setAlcoholContent(3.5);
            consumer.addRedStripeByAlcoholContent(rs);
            em.flush();
        } finally {
            rollbackTransaction(em);
        }
    }

    // Bug 282523
    public void testRelatedBylawWrite(){
        EntityManager em = createEntityManager();

        try {
            beginTransaction(em);
            BuildingBylaw law = new BuildingBylaw();
            law.setCity("EastTown");
            law.setDescription("No west facing property.");

            BuildingBylaw relatedLaw = new BuildingBylaw();
            relatedLaw.setCity("EastTown");
            relatedLaw.setDescription("No square houses");

            law.setRelatedByLaw(relatedLaw);

            em.persist(law);
            em.persist(relatedLaw);

            em.flush();
            em.clear();

            String jpqlString = "SELECT b FROM BuildingBylaw b WHERE b.number =" + law.getNumber();
            law = (BuildingBylaw) em.createQuery(jpqlString).getSingleResult();

            assertNotNull("BuildingBylaw not properly written.", law);
            assertNotNull("ManyToOne with target type != actual type not properly written.", law.getRelatedByLaw());
        } finally {
            rollbackTransaction(em);
        }
    }

    // Bug 282523
    public void testInterfaceBylawWrite(){
        EntityManager em = createEntityManager();

        try {
            beginTransaction(em);
            BuildingBylaw law = new BuildingBylaw();
            law.setCity("EastTown");
            law.setDescription("No west facing property.");

            BuildingBylaw relatedLaw = new BuildingBylaw();
            relatedLaw.setCity("EastTown");
            relatedLaw.setDescription("No square houses");

            law.setReference(relatedLaw);

            em.persist(law);
            em.persist(relatedLaw);

            em.flush();
            em.clear();

            String jpqlString = "SELECT b FROM BuildingBylaw b WHERE b.number =" + law.getNumber();
            law = (BuildingBylaw) em.createQuery(jpqlString).getSingleResult();

            assertNotNull("BuildingBylaw not properly written.", law);
            assertNotNull("ManyToOne with target type != actual type not properly written.", law.getReference());
        } finally {
            rollbackTransaction(em);
        }
    }

    // Bug 334175
    public void testEmbeddableAggregateCollectionAndAggregate(){
        EntityManager em = createEntityManager();
        beginTransaction(em);
        BeerConsumer consumer = new BeerConsumer();
        consumer.setName("Lionel");
        RedStripe rs = new RedStripe();
        rs.setAlcoholContent(4.5);
        consumer.addRedStripeByAlcoholContent(rs);
        em.persist(consumer);

        Official official = new Official();
        official.setName("George");
        rs = new RedStripe();
        rs.setAlcoholContent(4.6);
        official.setLastRedStripeConsumed(rs);
        em.persist(official);

        em.flush();
        em.clear();
        clearCache();

        consumer = em.find(BeerConsumer.class, consumer.getId());
        assertNotNull("BeerConsumer had null red stripes.", consumer.getRedStripes());
        assertTrue("BeerConsumer had wrong number of red stripes.", consumer.getRedStripesByAlcoholContent().size() == 1);
        assertTrue("BeerConsumer had wrong red stripe.", consumer.getRedStripesByAlcoholContent().get(4.5) != null);

        official = em.find(Official.class, official.getId());
        assertNotNull("Official had null red stripe.", official.getLastRedStripeConsumed());
        assertTrue("Official had the wrong red stripe", official.getLastRedStripeConsumed().getAlcoholContent() == 4.6);

        rollbackTransaction(em);

    }


    // Bug 370975
    public void testNodeImplWeaving(){
        if (isWeavingEnabled()) {
            Class[] interfaces = NodeImpl.class.getInterfaces();
            boolean found = false;
            for (Class c : interfaces){
                if (c == PersistenceWeaved.class){
                    found = true;
                }
            }
            assertTrue("NodeImpl was not weaved.", found);
        }
    }

    // Bug 384527
    public void testEmbeddaleCollectionMapEmbeddableRead(){
        EntityManager em = createEntityManager();

        try {
            beginTransaction(em);
            BeerConsumer consumer = new BeerConsumer();
            consumer.setName("Lionel");
            RedStripe rs = new RedStripe();
            rs.setAlcoholContent(4.5);
            consumer.addRedStripeByAlcoholContent(rs);
            rs = new RedStripe();
            rs.setAlcoholContent(5.0);
            consumer.addRedStripeByAlcoholContent(rs);
            em.persist(consumer);
            em.flush();

            clearCache();
            em.clear();
            consumer = em.find(BeerConsumer.class, consumer.getId());
            Integer version = consumer.getVersion();
            consumer.getRedStripesByAlcoholContent().get(4.5);
            em.flush();

            clearCache();
            em.clear();

            consumer = em.find(BeerConsumer.class, consumer.getId());
            assertTrue("The version was updated on a read.", consumer.getVersion().equals(version));
        } finally {
            rollbackTransaction(em);
        }
    }

    public void testInterfaces(){
        // ensure weaving has occured
        EntityManager em = createEntityManager();
        em.getCriteriaBuilder();
        Class[] testClasses = new Class[]{BeerConsumer.class, Alpine.class, NoviceBeerConsumer.class, Bluish.class, Blue.class, Corona.class, ExpertBeerConsumer.class, Heineken.class};

        for (int index = 0;index<testClasses.length;index++){
            Class[] interfaces = testClasses[index].getInterfaces();
            Type[] genericInterfaces = testClasses[index].getGenericInterfaces();

            if (interfaces.length != genericInterfaces.length){
                fail("Weaving failed to correctly update interfaces for " + testClasses[index]);
            }

            for (int i=0;i<interfaces.length;i++){
                String comparisonString = null;
                if (genericInterfaces[i] instanceof Class<?>){
                    comparisonString = ((Class<?>)genericInterfaces[i]).getCanonicalName();
                } else if (genericInterfaces[i] instanceof ParameterizedType){
                    comparisonString = ((Class<?>)((ParameterizedType)genericInterfaces[i]).getRawType()).getCanonicalName();
                }

                assertEquals("Mismatched interface on " + testClasses[index] + ": " + interfaces[i].getCanonicalName() + " != " + comparisonString, interfaces[i].getCanonicalName(), comparisonString);
            }
        }
    }

    // Bug 406957 - Copy fails on AggregateCollectionMapping and on map with @MapKeyColumn
    public void testCopyMapKeyMap() {
        Calendar cal = Calendar.getInstance();
        BeerConsumer consumer = new BeerConsumer();
        consumer.setId(1);
        consumer.setName("A");
        Heineken heineken  = new Heineken();
        heineken.setAlcoholContent(5.0);
        heineken.setId(11);
        Date date = cal.getTime();
        consumer.addHeinekenBeerToConsume(heineken, date);
        EntityManager em = createEntityManager();
        CopyGroup copyAll = new CopyGroup();
        copyAll.cascadeAllParts();
        BeerConsumer consumerCopy = (BeerConsumer)JpaHelper.getEntityManager(em).copy(consumer, copyAll);
        if (consumerCopy.getHeinekenBeersToConsume().size() != consumer.getHeinekenBeersToConsume().size()) {
            fail("consumerCopy.getHeinekenBeersToConsume().size() = " + consumerCopy.getHeinekenBeersToConsume().size() + "; "+consumer.getHeinekenBeersToConsume().size()+" was expected");
        }
        if (consumerCopy.getHeinekenBeersToConsume() == consumer.getHeinekenBeersToConsume()) {
            fail("consumerCopy.getHeinekenBeersToConsume() == consumer.getHeinekenBeersToConsume()");
        }
        Map.Entry<Date, Heineken> entry = (Map.Entry<Date, Heineken>)consumerCopy.getHeinekenBeersToConsume().entrySet().iterator().next();
        Date dateCopy = entry.getKey();
        Heineken heinekenCopy = entry.getValue();
        if (!date.equals(dateCopy)) {
            fail("!date.equals(dateCopy)");
        }
        if (heineken == heinekenCopy) {
            fail("heineken == heinekenCopy");
        }
        if (heineken.getAlcoholContent() != heinekenCopy.getAlcoholContent()) {
            fail("heineken.getAlcoholContent() != heinekenCopy.getAlcoholContent()");
        }
    }
}
