HandlingCdmEntitiesTest.java / HandlingCdmEntitiesTest.xml : Added new test to descri...
authorCherian Mathew <c.mathew@bgbm.org>
Wed, 17 Sep 2014 16:13:48 +0000 (16:13 +0000)
committerCherian Mathew <c.mathew@bgbm.org>
Wed, 17 Sep 2014 16:13:48 +0000 (16:13 +0000)
cdmlib-remote-servlet.xml : added commented option to expose remoting
CdmIntegrationTest.java : added comment on how to disable transactional behaviour in tests

.gitattributes
cdmlib-persistence/src/test/java/eu/etaxonomy/cdm/test/integration/CdmIntegrationTest.java
cdmlib-remote-webapp/src/main/webapp/WEB-INF/cdmlib-remote-servlet.xml
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.java [new file with mode: 0644]
cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.xml [new file with mode: 0644]

index bc5c5a72c843c00e3bac2eccdc72cdb3f5bb162a..aa4ca0cf83a36bfacadd1d667fcedd68ef16677c 100644 (file)
@@ -2344,6 +2344,7 @@ cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/CommonServiceImplTest
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/DescriptionServiceImplTest.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/FeatureNodeServiceImplTest.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/GroupServiceImplTest.java -text
+cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/IdentifiableServiceBaseTest.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/IdentificationKeyGeneratorTTT.java -text
 cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/NameServiceImplTest.java -text
@@ -2407,6 +2408,7 @@ cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/BlankDataSet.xml
 cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/ClassificationServiceImplTest.xml -text
 cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/CommonServiceImplTest.xml -text
 cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/FeatureNodeServiceImplTest-indexing.xml -text
+cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.xml -text
 cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/IdentifiableServiceBaseTest.testUpdateTitleCache-result.xml -text
 cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/IdentifiableServiceBaseTest.xml -text
 cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/MediaDaoImplTest.xml -text
index 2bd821c63988b0dfd9691361422f6902021ee56f..566ffe993d99d1cb9719bde66d953691e48afd5b 100644 (file)
@@ -76,6 +76,8 @@ import eu.etaxonomy.cdm.test.unitils.FlatFullXmlWriter;
  * In order to create DbUnit datasets  for integration tests it is highly recommended method to use the\r
  * {@link #writeDbUnitDataSetFile(String[])} method.\r
  *\r
+ * From {@link http://www.unitils.org/tutorial-database.html}, by default every test is executed in a transaction,\r
+ * which is committed at the end of the test. This can be disabled using @Transactional(TransactionMode.DISABLED)\r
  *\r
  * @see <a href="http://www.unitils.org">unitils home page</a>\r
  *\r
index ac61c60ab40d95dce3ffdf610cf06c88c3f733f0..f08193612fda74641295c10c3a6e87a815756450 100644 (file)
@@ -19,4 +19,7 @@
     <!--
     <context:component-scan base-package="eu/etaxonomy/cdm/remote/vaadin" />
     -->
+    
+    <!-- FIXME:Remoting Expose remoting services currently only for testing -->
+<!--     <import resource="classpath:/eu/etaxonomy/cdm/remoting-services.xml"/> -->
 </beans>
\ No newline at end of file
diff --git a/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.java b/cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.java
new file mode 100644 (file)
index 0000000..eec60e9
--- /dev/null
@@ -0,0 +1,350 @@
+// $Id$
+/**
+ * Copyright (C) 2014 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+package eu.etaxonomy.cdm.api.service;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.log4j.Logger;
+import org.hibernate.LazyInitializationException;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.unitils.database.annotations.Transactional;
+import org.unitils.database.util.TransactionMode;
+import org.unitils.dbunit.annotation.DataSet;
+import org.unitils.spring.annotation.SpringBeanByType;
+
+import eu.etaxonomy.cdm.model.agent.Person;
+import eu.etaxonomy.cdm.model.agent.Team;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.name.BotanicalName;
+import eu.etaxonomy.cdm.model.name.NonViralName;
+import eu.etaxonomy.cdm.model.taxon.Taxon;
+import eu.etaxonomy.cdm.test.integration.CdmIntegrationTest;
+
+/**
+ * This test class tries to describe how hibernate works with objects (save, update, merge).
+ *
+ * @author cmathew
+ * @date 17 Sep 2014
+ *
+ */
+
+public class HandlingCdmEntitiesTest extends CdmIntegrationTest {
+
+    private static final Logger logger = Logger.getLogger(CommonServiceImplTest.class);
+
+    private static final String LIE_TEAMMEMBERS_NOSESSION = "failed to lazily initialize a collection of role: eu.etaxonomy.cdm.model.agent.Team.teamMembers, could not initialize proxy - no Session";
+    private static final String LIE_NOSESSION = "could not initialize proxy - no Session";
+
+    @SpringBeanByType
+    private ITaxonService taxonService;
+
+    @SpringBeanByType
+    private IAgentService agentService;
+
+    @SpringBeanByType
+    private ICommonService commonService;
+
+    public static final String[] includeTables = new String[]{
+        "TAXONBASE",
+        "TAXONNAMEBASE",
+        "AGENTBASE",
+        "AGENTBASE_AGENTBASE",
+        "HOMOTYPICALGROUP"
+    };
+
+    @Ignore
+    @Test
+    @Transactional(TransactionMode.DISABLED)
+    public final void createTestData() {
+        Team combAuthor = Team.NewTitledInstance("Avengers", "Avengers");
+        combAuthor.addTeamMember(Person.NewTitledInstance("Iron Man"));
+        BotanicalName name = BotanicalName.NewInstance(null, "Abies alba", null, null, null, null, null, null, null);
+        name.setCombinationAuthorTeam(combAuthor);
+        Taxon taxon = Taxon.NewInstance(name, null);
+        UUID taxonUuid = taxonService.save(taxon);
+        printDataSetWithNull(System.out,false,null,includeTables);
+    }
+
+    @Test
+    @DataSet
+    @Transactional(TransactionMode.DISABLED)
+    public void testNonTransactionalUpdateForExistingTaxon() {
+        // this method tests the updating of a 'truely' detached object which
+        // attempts to initialize a lazy loaded proxy object while trying to
+        // update the same.
+
+        // setting the TransactionMode for this method to DISABLED is important
+        // to ensure that transaction boundaries remain at the service layer calls
+        // to simulate detachment and update of the persisted object
+
+        UUID taxonUuid = UUID.fromString("23c35977-01b5-452c-9225-ecce440034e0");
+
+        // ---- loading taxon with find (uuid) ----
+
+        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
+
+        // at this point the taxonNew object is detached and all lazy loaded proxy
+        // objects in the object graph (including teamMembers) will have values of
+        // initialized=false and session=null
+
+        // since name is lazy loaded the call to getName should fail
+        try {
+            CdmBase.deproxy(taxon.getName(),NonViralName.class);
+            Assert.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
+        } catch(LazyInitializationException lie) {
+
+            if(!lie.getMessage().equals(LIE_NOSESSION)) {
+                Assert.fail("LazyInitializationException thrown, but not : " + LIE_NOSESSION);
+            }
+        }
+
+        // ---- loading taxon with find (id) ----
+
+        taxon = (Taxon)commonService.find(taxon.getClass(), taxon.getId());
+
+        // at this point the taxonNew object is detached and all lazy loaded proxy
+        // objects in the object graph (including teamMembers) will have values of
+        // initialized=false and session=null
+
+        // since name is lazy loaded the call to getName should fail
+        try {
+            CdmBase.deproxy(taxon.getName(),NonViralName.class);
+            Assert.fail("LazyInitializationException not thrown for lazy loaded Taxon.name");
+        } catch(LazyInitializationException lie) {
+
+            if(!lie.getMessage().equals(LIE_NOSESSION)) {
+                Assert.fail("LazyInitializationException thrown, but not : " + LIE_NOSESSION);
+            }
+        }
+
+        // ---- loading taxon with findTaxonByUuid ----
+
+        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
+                "name"
+        });
+
+        // loading the taxon with its taxon name object pre-initialized
+        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
+
+        // at this point the taxonNew object is detached and all lazy loaded proxy
+        // objects in the object graph (including teamMembers) will have values of
+        // initialized=false and session=null
+
+        NonViralName nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class);
+
+        // normally this call should throw a lazy loading exception since
+        // the combinationAuthorTeam object is not initialized, but
+        // the findTaxonByUuid performs this initialization (via the
+        // AdvancedBeanInitializer) since the combinationAuthorTeam
+        // is annotated with CacheUpdate
+
+        Team team = CdmBase.deproxy(nvn.getCombinationAuthorTeam(),Team.class);
+
+        // setting the protected title cache to false to ensure that
+        // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
+        // to initialize the teamMembers persistent collection which fails
+
+        team.setProtectedTitleCache(false);
+
+        try {
+            taxonService.update(taxon);
+            Assert.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
+        } catch(LazyInitializationException lie) {
+
+            if(!lie.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION)) {
+                Assert.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION);
+            }
+        }
+
+        // the above fails due to the fact that hibernate does not resolve lazy
+        // loading on a detached object until the object is persisted. The attempt
+        // to initialize teamMembers before the object graph is persisted means that
+        // the current session is not yet attached to the proxy objects and hibernate
+        // tries to use the existing session set in the teamMembers object which is
+        // null, leading to the exception
+
+        // setting the protected title cache to true to ensure that
+        // TeamDefaultCacheStrategy.getTitleCache is not called, implying
+        // that the teamMembers are not initialized, so no exception is thrown
+
+        team.setProtectedTitleCache(true);
+        taxonService.update(taxon);
+
+    }
+
+    @Test
+    @DataSet
+    public void testTransactionalUpdateAfterFindTaxonByUuidForExistingTaxon() {
+        // this method tests the updating of a detached object inside a single
+        // transaction.
+
+        // this method is transactional, meaning that a transaction is started
+        // at the start of the method and ends only with the end of the method
+
+        // since this method is transactional, any object initialized within this method
+        // will have a valid session attached to any lazy loaded proxy objects
+        // in the object graph (including teamMembers)
+
+        UUID taxonUuid = UUID.fromString("23c35977-01b5-452c-9225-ecce440034e0");
+
+        // ---- loading taxon with find (uuid) ----
+
+        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
+
+        // at this point the taxonNew object is detached and all lazy loaded proxy
+        // objects in the object graph (including teamMembers) will have a new
+        // session attached implying that all the following calls will succeed
+
+        NonViralName nvn =  CdmBase.deproxy(taxon.getName(),NonViralName.class);
+        Team team = CdmBase.deproxy(nvn.getCombinationAuthorTeam(),Team.class);
+        taxonService.update(taxon);
+
+        // ---- loading taxon with find (id) ----
+
+        taxon = (Taxon)commonService.find(taxon.getClass(), taxon.getId());
+
+        // at this point the taxonNew object is detached and all lazy loaded proxy
+        // objects in the object graph (including teamMembers) will have a new
+        // session attached implying that all the following calls will succeed
+
+        nvn =  CdmBase.deproxy(taxon.getName(),NonViralName.class);
+        team = CdmBase.deproxy(nvn.getCombinationAuthorTeam(),Team.class);
+        taxonService.update(taxon);
+
+        // ---- loading taxon with findTaxonByUuid ----
+
+        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
+                "name"
+        });
+
+        // loading the taxon with its taxon name object pre-initialized
+        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
+
+        nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class);
+        team = CdmBase.deproxy(nvn.getCombinationAuthorTeam(),Team.class);
+
+        // since a valid session is now attached to teamMembers, forcing the
+        // initializing of the teamMembers (in TeamDefaultCacheStrategy.getTitleCache)
+        // by setting the protected title cache to false does not throw an exception
+        // because the teamMember persistent collection now has a valid session,
+        // which is used to initialize the persistent collection
+
+        team.setProtectedTitleCache(false);
+        taxonService.update(taxon);
+    }
+
+    @Test
+    @Transactional(TransactionMode.DISABLED)
+    public void testNonTransactionalUpdateForNewTaxon() {
+
+        // this test is only to prove that the update problem occurs
+        // also for newly created objects (as expected)
+
+        // create / save new taxon with name and author team with team member
+
+        Team combAuthor = Team.NewTitledInstance("X-Men", "X-Men");
+        combAuthor.addTeamMember(Person.NewTitledInstance("Wolverine"));
+
+
+        BotanicalName name = BotanicalName.NewInstance(null, "Pinus Alba", null, null, null, null, null, null,  null);
+        name.setCombinationAuthorTeam(combAuthor);
+
+        Taxon taxon = Taxon.NewInstance(name, null);
+
+        UUID taxonUuid = taxonService.save(taxon);
+
+        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
+                "name"
+        });
+
+        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
+
+        NonViralName nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class);
+        Team team = CdmBase.deproxy(nvn.getCombinationAuthorTeam(),Team.class);
+        team.setProtectedTitleCache(false);
+
+        try {
+            taxonService.update(taxon);
+            Assert.fail("LazyInitializationException not thrown for lazy loaded Team.teamMembers");
+        } catch(LazyInitializationException lie) {
+
+            if(!lie.getMessage().equals(LIE_TEAMMEMBERS_NOSESSION)) {
+                Assert.fail("LazyInitializationException thrown, but not : " + LIE_TEAMMEMBERS_NOSESSION);
+            }
+        }
+
+    }
+
+    @Test
+    @DataSet
+    @Transactional(TransactionMode.DISABLED)
+    public void testNonTransactionalMergeForExistingTaxon() {
+        // this method tests the updating of a 'truely' detached object
+        // using merge
+
+        // setting the TransactionMode for this method to DISABLED is important
+        // to ensure that transaction boundaries remain at the service layer calls
+        // to simulate detachment and update of the persisted object
+
+        UUID taxonUuid = UUID.fromString("23c35977-01b5-452c-9225-ecce440034e0");
+
+        // ---- loading taxon with find (uuid) ----
+
+        Taxon taxon = (Taxon)taxonService.find(taxonUuid);
+
+        // at this point the taxonNew object is detached and all lazy loaded proxy
+        // objects in the object graph (including teamMembers) will have values of
+        // initialized=false and session=null
+
+        taxonService.merge(taxon);
+
+        // ---- loading taxon with find (id) ----
+
+        taxon = (Taxon)commonService.find(taxon.getClass(), taxon.getId());
+
+        taxonService.merge(taxon);
+
+        // ---- loading taxon with findTaxonByUuid ----
+
+        List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String[] {
+                "name"
+        });
+
+        // loading the taxon with its taxon name object pre-initialized
+        taxon = (Taxon)taxonService.findTaxonByUuid(taxonUuid, TAXON_INIT_STRATEGY);
+
+        // at this point the taxonNew object is detached and all lazy loaded proxy
+        // objects in the object graph (including teamMembers) will have values of
+        // initialized=false and session=null
+
+        NonViralName nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class);
+
+        // normally this call should throw a lazy loading exception since
+        // the combinationAuthorTeam object is not initialized, but
+        // the findTaxonByUuid performs this initialization (via the
+        // AdvancedBeanInitializer) since the combinationAuthorTeam
+        // is annotated with CacheUpdate
+
+        Team team = CdmBase.deproxy(nvn.getCombinationAuthorTeam(),Team.class);
+
+        // setting the protected title cache to false to ensure that
+        // TeamDefaultCacheStrategy.getTitleCache is called, which in turn tries
+        // to initialize the teamMembers persistent collection which fails
+        team.setProtectedTitleCache(false);
+
+
+        taxonService.merge(taxon);
+
+    }
+}
diff --git a/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.xml b/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/service/HandlingCdmEntitiesTest.xml
new file mode 100644 (file)
index 0000000..603c7ed
--- /dev/null
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<dataset>
+  <TAXONBASE DTYPE="Taxon" ID="1" CREATED="2014-09-17 10:58:57.0" UUID="23c35977-01b5-452c-9225-ecce440034e0" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Abies alba Avengers sec. ???" APPENDEDPHRASE="[null]" DOUBTFUL="false" PUBLISH="true" USENAMECACHE="false" EXCLUDED="false" TAXONSTATUSUNKNOWN="false" TAXONOMICCHILDRENCOUNT="0" UNPLACED="false" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" NAME_ID="1" SEC_ID="[null]" TAXONOMICPARENTCACHE_ID="[null]"/>
+  <TAXONNAMEBASE DTYPE="BotanicalName" ID="1" CREATED="2014-09-17 10:58:57.0" UUID="37e209e5-0752-4bd6-8328-94732102b88d" UPDATED="2014-09-17 10:58:57.495" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Abies alba Avengers" APPENDEDPHRASE="[null]" FULLTITLECACHE="Abies alba Avengers" NOMENCLATURALMICROREFERENCE="[null]" PARSINGPROBLEM="0" PROBLEMENDS="-1" PROBLEMSTARTS="-1" PROTECTEDFULLTITLECACHE="false" AUTHORSHIPCACHE="Avengers" BINOMHYBRID="false" GENUSORUNINOMIAL="Abies alba" HYBRIDFORMULA="false" INFRAGENERICEPITHET="[null]" INFRASPECIFICEPITHET="[null]" MONOMHYBRID="false" NAMECACHE="Abies alba" PROTECTEDAUTHORSHIPCACHE="false" PROTECTEDNAMECACHE="false" SPECIFICEPITHET="[null]" TRINOMHYBRID="false" NAMEAPPROBATION="[null]" SUBGENUSAUTHORSHIP="[null]" ANAMORPHIC="false" CULTIVARNAME="[null]" ACRONYM="[null]" BREED="[null]" ORIGINALPUBLICATIONYEAR="[null]" PUBLICATIONYEAR="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" HOMOTYPICALGROUP_ID="1" NOMENCLATURALREFERENCE_ID="[null]" RANK_ID="[null]" BASIONYMAUTHORTEAM_ID="[null]" COMBINATIONAUTHORTEAM_ID="1" EXBASIONYMAUTHORTEAM_ID="[null]" EXCOMBINATIONAUTHORTEAM_ID="[null]"/>
+  <AGENTBASE DTYPE="Team" ID="1" CREATED="2014-09-17 10:58:57.0" UUID="300bce50-37d8-4409-9c7a-a1b29abc365e" UPDATED="2014-09-17 10:58:57.496" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Avengers" CODE="[null]" NAME="[null]" NOMENCLATURALTITLE="Avengers" FIRSTNAME="[null]" LASTNAME="[null]" LIFESPAN_END="[null]" LIFESPAN_FREETEXT="[null]" LIFESPAN_START="[null]" PREFIX="[null]" SUFFIX="[null]" PROTECTEDNOMENCLATURALTITLECACHE="true" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ISPARTOF_ID="[null]"/>
+  <AGENTBASE DTYPE="Person" ID="2" CREATED="2014-09-17 10:58:57.0" UUID="8b99c770-0a4f-43eb-9c6f-612131eb4e56" UPDATED="2014-09-17 10:58:57.497" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Iron Man" CODE="[null]" NAME="[null]" NOMENCLATURALTITLE="Iron Man" FIRSTNAME="[null]" LASTNAME="[null]" LIFESPAN_END="[null]" LIFESPAN_FREETEXT="[null]" LIFESPAN_START="[null]" PREFIX="[null]" SUFFIX="[null]" PROTECTEDNOMENCLATURALTITLECACHE="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ISPARTOF_ID="[null]"/>
+  <AGENTBASE_AGENTBASE AGENTBASE_ID="1" TEAMMEMBERS_ID="2" SORTINDEX="0"/>
+  <HOMOTYPICALGROUP ID="1" CREATED="2014-09-17 10:58:57.0" UUID="d5d9547f-a086-48c4-9e88-a9f0d7705a82" UPDATED="2014-09-17 10:58:57.495" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]"/>
+</dataset>
\ No newline at end of file