erging from remoting-1.0 branch
authorCherian Mathew <c.mathew@bgbm.org>
Wed, 12 Feb 2014 12:48:35 +0000 (12:48 +0000)
committerCherian Mathew <c.mathew@bgbm.org>
Wed, 12 Feb 2014 12:48:35 +0000 (12:48 +0000)
- adding new confugration / controller classes for remoting
- adding aspect and related aop.xml config file
- added aspect and spring remoting jars
- updated manifest , pom and classpath for the new dependencies

21 files changed:
.gitattributes
eu.etaxonomy.taxeditor.cdmlib/.classpath
eu.etaxonomy.taxeditor.cdmlib/META-INF/MANIFEST.MF
eu.etaxonomy.taxeditor.cdmlib/META-INF/aop.xml [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/build.properties
eu.etaxonomy.taxeditor.cdmlib/lib/aspectjrt-1.7.1-sources.jar [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/lib/aspectjweaver-1.7.1-sources.jar [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/lib/spring-security-remoting-3.1.3.RELEASE.jar [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/pom.xml
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/api/lazyloading/CdmLazyLoader.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteConfiguration.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteController.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/ICdmApplicationRemoteConfiguration.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/httpInvokerServiceClients.xml [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remotingApplicationContext.xml [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_persistence_security.xml [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_services_security.xml [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/CdmApplicationRemoteControllerTest.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/RemoteLazyLoadingTest.java [new file with mode: 0644]

index e9d2265cc23aafd6488cb4ae22a38add10fd4496..a057e3836b49c2f0ec375c1680f5e325f8e94c8f 100644 (file)
@@ -140,12 +140,15 @@ eu.etaxonomy.taxeditor.bulkeditor/src/test/java/eu/etaxonomy/taxeditor/bulkedito
 eu.etaxonomy.taxeditor.cdmlib/.classpath -text
 eu.etaxonomy.taxeditor.cdmlib/.project -text
 eu.etaxonomy.taxeditor.cdmlib/META-INF/MANIFEST.MF -text
+eu.etaxonomy.taxeditor.cdmlib/META-INF/aop.xml -text
 eu.etaxonomy.taxeditor.cdmlib/README.txt -text
 eu.etaxonomy.taxeditor.cdmlib/build.properties -text
 eu.etaxonomy.taxeditor.cdmlib/lib/activation-1.1.1.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/antlr-2.7.7.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/aopalliance-1.0.jar -text
+eu.etaxonomy.taxeditor.cdmlib/lib/aspectjrt-1.7.1-sources.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/aspectjrt-1.7.1.jar -text
+eu.etaxonomy.taxeditor.cdmlib/lib/aspectjweaver-1.7.1-sources.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/aspectjweaver-1.7.1.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/avro-1.6.3.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/batik-anim-1.7.jar -text
@@ -299,6 +302,7 @@ eu.etaxonomy.taxeditor.cdmlib/lib/spring-expression-3.2.2.RELEASE.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/spring-modules-cache-0.7.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/spring-security-config-3.1.3.RELEASE.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/spring-security-core-3.1.3.RELEASE.jar -text
+eu.etaxonomy.taxeditor.cdmlib/lib/spring-security-remoting-3.1.3.RELEASE.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/stax-1.2.0.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/stax-api-1.0.1.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/unitils-core-3.3.jar -text
@@ -329,6 +333,18 @@ eu.etaxonomy.taxeditor.cdmlib/lib/xpp3_min-1.1.4c.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/xstream-1.4.4.jar -text
 eu.etaxonomy.taxeditor.cdmlib/lib/yjp-controller-api-redist-9.0.8.jar -text
 eu.etaxonomy.taxeditor.cdmlib/pom.xml -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/api/lazyloading/CdmLazyLoader.java -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteConfiguration.java -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteController.java -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/ICdmApplicationRemoteConfiguration.java -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/httpInvokerServiceClients.xml -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remotingApplicationContext.xml -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_persistence_security.xml -text
+eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_services_security.xml -text
+eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/CdmApplicationRemoteControllerTest.java -text
+eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/RemoteLazyLoadingTest.java -text
 eu.etaxonomy.taxeditor.editor/.classpath -text
 eu.etaxonomy.taxeditor.editor/.project -text
 eu.etaxonomy.taxeditor.editor/META-INF/MANIFEST.MF -text
index c6bf57c85385f86b38c9221fa053d3f27034363d..766a24b441ccf14aec065195ad69188cd7a7b28b 100644 (file)
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <classpath>
+       <classpathentry kind="src" path="src/main/java"/>
+       <classpathentry kind="src" path="src/test/java"/>
+       <classpathentry kind="src" path="src/main/resources"/>
        <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
        <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
        <classpathentry exported="true" kind="lib" path="lib/activation-1.1.1.jar"/>
@@ -30,7 +33,7 @@
        <classpathentry exported="true" kind="lib" path="lib/cdmlib-ext-3.3.1-SNAPSHOT-sources.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/cdmlib-ext-3.3.1-SNAPSHOT.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/cdmlib-io-3.3.1-SNAPSHOT-sources.jar"/>
-       <classpathentry exported="true" kind="lib" path="lib/cdmlib-io-3.3.1-SNAPSHOT.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/cdmlib-io-3.3.1-SNAPSHOT.jar" sourcepath="lib/cdmlib-io-3.3.1-SNAPSHOT-sources.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/cdmlib-model-3.3.1-SNAPSHOT-sources.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/cdmlib-model-3.3.1-SNAPSHOT.jar" sourcepath="lib/cdmlib-model-3.3.1-SNAPSHOT-sources.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/cdmlib-persistence-3.3.1-SNAPSHOT-sources.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/google-api-translate-java-0.92.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/odfdom-0.8.jar"/>
        <classpathentry exported="true" kind="lib" path="lib/h2mig_pagestore_addon.jar"/>
+       <classpathentry exported="true" kind="lib" path="lib/spring-security-remoting-3.1.3.RELEASE.jar"/>
        <classpathentry kind="output" path="target/classes"/>
 </classpath>
index 8a64331c99d5e4e9221f175ce481e80410270ee2..8ff9d9558b3f06d9598f09973eb69e57da1fd1e9 100644 (file)
@@ -124,6 +124,7 @@ Export-Package: com.google.api,
  eu.etaxonomy.cdm.print.out.pdf,
  eu.etaxonomy.cdm.print.out.taxpub,
  eu.etaxonomy.cdm.print.out.xml,
+ eu.etaxonomy.cdm.remote.api.application,
  eu.etaxonomy.cdm.remote.config,
  eu.etaxonomy.cdm.remote.controller,
  eu.etaxonomy.cdm.remote.controller.csv,
@@ -349,6 +350,7 @@ Export-Package: com.google.api,
  org.springframework.security.core.context,
  org.springframework.security.core.userdetails,
  org.springframework.security.provisioning,
+ org.springframework.security.remoting.httpinvoker,
  org.springframework.stereotype,
  org.springframework.transaction;uses:="org.springframework.core",
  org.springframework.transaction.annotation;uses:="javax.ejb,org.springframework.transaction.interceptor",
@@ -599,4 +601,6 @@ Bundle-ClassPath: .,
  lib/yjp-controller-api-redist-9.0.8.jar,
  lib/google-api-translate-java-0.92.jar,
  lib/odfdom-0.8.jar,
- lib/h2mig_pagestore_addon.jar
+ lib/h2mig_pagestore_addon.jar,
+ lib/spring-security-remoting-3.1.3.RELEASE.jar
+Import-Package: eu.etaxonomy.cdm.api.application
diff --git a/eu.etaxonomy.taxeditor.cdmlib/META-INF/aop.xml b/eu.etaxonomy.taxeditor.cdmlib/META-INF/aop.xml
new file mode 100644 (file)
index 0000000..80e76a1
--- /dev/null
@@ -0,0 +1,16 @@
+<!DOCTYPE aspectj PUBLIC
+        "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
+<aspectj>
+       <weaver options="-verbose -showWeaveInfo">
+       <include within="org.hibernate.proxy.*" />
+       <include within="org.hibernate.collection.internal.*" />
+       <include within="eu.etaxonomy.cdm.api.lazyloading.*" />
+       </weaver>
+    <aspects>
+        <!-- use only this aspect for weaving -->
+        <aspect name="eu.etaxonomy.cdm.api.lazyloading.CdmLazyLoader" />
+
+    </aspects>
+    
+</aspectj>
+             
\ No newline at end of file
index dcedb199bb50d8088914ad264cc197565231a2fd..f6b53c4d781d655664793141cdd81b3bd548ca35 100644 (file)
@@ -200,5 +200,11 @@ bin.includes = META-INF/,\
                lib/yjp-controller-api-redist-9.0.8.jar,\\r
                lib/google-api-translate-java-0.92.jar,\\r
                lib/odfdom-0.8.jar,\\r
-               lib/h2mig_pagestore_addon.jar\r
+               lib/h2mig_pagestore_addon.jar,\\r
+               lib/spring-security-remoting-3.1.3.RELEASE.jar\r
+jars.compile.order = .\r
+output.. = bin/\r
+source.. = src/main/java/,\\r
+           src/main/resources/,\\r
+           src/test/java/\r
 \r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/lib/aspectjrt-1.7.1-sources.jar b/eu.etaxonomy.taxeditor.cdmlib/lib/aspectjrt-1.7.1-sources.jar
new file mode 100644 (file)
index 0000000..21ffee7
Binary files /dev/null and b/eu.etaxonomy.taxeditor.cdmlib/lib/aspectjrt-1.7.1-sources.jar differ
diff --git a/eu.etaxonomy.taxeditor.cdmlib/lib/aspectjweaver-1.7.1-sources.jar b/eu.etaxonomy.taxeditor.cdmlib/lib/aspectjweaver-1.7.1-sources.jar
new file mode 100644 (file)
index 0000000..9625da0
Binary files /dev/null and b/eu.etaxonomy.taxeditor.cdmlib/lib/aspectjweaver-1.7.1-sources.jar differ
diff --git a/eu.etaxonomy.taxeditor.cdmlib/lib/spring-security-remoting-3.1.3.RELEASE.jar b/eu.etaxonomy.taxeditor.cdmlib/lib/spring-security-remoting-3.1.3.RELEASE.jar
new file mode 100644 (file)
index 0000000..6268e58
Binary files /dev/null and b/eu.etaxonomy.taxeditor.cdmlib/lib/spring-security-remoting-3.1.3.RELEASE.jar differ
index 4df0203a47e126979bb01a07a0d646b7ac8d06f7..dae259d3ab1e214e59dbb47027efb4e7681b1c89 100644 (file)
       <artifactId>cdmlib-ext</artifactId>\r
       <version>${cdmlib.version}</version>\r
     </dependency>\r
+       <dependency>\r
+      <groupId>org.aspectj</groupId>\r
+      <artifactId>aspectjrt</artifactId>\r
+         <version>1.7.1</version>\r
+       </dependency>\r
+         <dependency>\r
+         <groupId>org.aspectj</groupId>\r
+         <artifactId>aspectjweaver</artifactId>\r
+         <version>1.7.1</version>\r
+        </dependency>\r
     <!--  for ikey-plus \r
         TODO this should not be needed but the utils class contained  in this jar \r
         seems to be loaded as bean by spring\r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/api/lazyloading/CdmLazyLoader.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/api/lazyloading/CdmLazyLoader.java
new file mode 100644 (file)
index 0000000..512bd0f
--- /dev/null
@@ -0,0 +1,191 @@
+package eu.etaxonomy.cdm.api.lazyloading;
+
+import java.beans.Transient;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.hibernate.collection.internal.PersistentList;
+import org.hibernate.collection.internal.PersistentMap;
+import org.hibernate.collection.internal.PersistentSet;
+import org.hibernate.collection.internal.PersistentSortedMap;
+import org.hibernate.collection.internal.PersistentSortedSet;
+import org.hibernate.collection.spi.PersistentCollection;
+import org.hibernate.proxy.LazyInitializer;
+import org.springframework.beans.factory.annotation.Autowire;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.stereotype.Component;
+
+import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
+import eu.etaxonomy.cdm.api.service.ICommonService;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.PersistentMultiLanguageText;
+
+
+@Aspect
+@Component
+@Configurable(autowire = Autowire.BY_TYPE)
+public class CdmLazyLoader {
+
+
+       private Set classes = new HashSet();
+       
+       public static boolean enableWeaving = true;
+       private static Set<String> classesToIgnore = new HashSet<String>();
+       
+       
+       @Autowired
+       private ICommonService commonService;
+
+       public CdmLazyLoader() {
+               //classesToIgnore.add("eu.etaxonomy.cdm.model.common.TermVocabulary");
+               //classesToIgnore.add("eu.etaxonomy.cdm.model.common.OrderedTermVocabulary");
+               
+       }
+       
+       /**
+        *  Point cut for the 'initialize' method of the AbstractLazyInitializer.
+        *  
+        */
+       @Pointcut("execution(* org.hibernate.proxy.AbstractLazyInitializer.initialize())")
+       public void possibleEntityLazyInitializationException() {
+       }
+
+
+       /**
+        *  'Around' advice for the initialization of CDM Entity Objects
+        *  
+        */
+       @Around(value = "possibleEntityLazyInitializationException()")
+       public Object preloadEntityOnDemand(ProceedingJoinPoint pjp) throws Throwable {          
+               if(enableWeaving) {
+                       LazyInitializer ll = (LazyInitializer)pjp.getTarget();          
+                       if(ll.isUninitialized()) {                              
+                               int classid = ((Integer)ll.getIdentifier()).intValue();
+                               System.out.print("--> AspectJ Compile-Time Weaving " + ll.getEntityName() + " with id " + classid);
+                               Class clazz = (Class<? extends CdmBase>) Class.forName(ll.getEntityName());
+                               CdmBase cdmBase = CdmBase.deproxy(commonService.find(clazz,classid),clazz);
+                               ll.setImplementation(cdmBase);
+                               System.out.println("....Done");
+                       }
+               }
+               return pjp.proceed();                             
+       }
+       
+
+       /**
+        *  Point cut for the 'initialize' method of the AbstractPersistentCollection.
+        *  
+        */
+       @Pointcut("execution(protected final void org.hibernate.collection.internal.AbstractPersistentCollection.initialize(..))")
+       public void possibleCollectionLazyInitializationException() {
+       }
+       
+       /**
+        *  'Around' advice for the initialization of Collection objects
+        *  
+        */
+       @Around(value = "possibleCollectionLazyInitializationException()")
+       @Transient
+       public Object preloadCollectionOnDemand(ProceedingJoinPoint pjp) throws Throwable {              
+               if(enableWeaving) {
+                       PersistentCollection ps = (PersistentCollection) pjp.getTarget();
+                       if (ps.getOwner() != null && !classesToIgnore.contains(ps.getOwner().getClass().getName()) && !ps.wasInitialized() &&  !classes.contains(ps.getKey())) {
+                               System.out.print("--> AspectJCompile-Time Weaving " + ps.getRole());                
+                               classes.add(ps.getKey());
+                               try {
+                                       String role = ps.getRole();
+                                       String fieldName = role.substring(role.lastIndexOf(".") + 1);
+                                       System.out.print(", field : " + fieldName);
+                                       Object owner = ps.getOwner();
+
+                                       PersistentCollection col = commonService.initializeCollection(ps); 
+                                       ps.afterInitialize();
+
+                                       Class<?> clazz = ps.getClass();
+                                       if (clazz != null) {    
+                                               CollectionField cf = getCollectionField(col);
+                                               Field field = clazz.getDeclaredField(cf.getFieldName());
+                                               field.setAccessible(true);
+                                               field.set(ps, cf.getCollection());                             
+                                       }               
+                               } catch (Exception ex) {
+                                       ex.printStackTrace();
+                                       System.out.println("Error in ReattachSessionAspect : " + ex.getMessage());
+                               } finally {
+                                       classes.remove(ps.getKey());
+                                       System.out.println("....Done");
+                               }       
+                       }
+               } 
+               return pjp.proceed();
+
+       }
+       
+       private CollectionField getCollectionField(PersistentCollection pc) {
+               if(pc != null) {
+                       if(pc instanceof PersistentSet) {
+                               return new CollectionField(new HashSet((Set)pc), "set");
+                       }
+                       if(pc instanceof PersistentSortedSet) {
+                               return new CollectionField(new TreeSet((Set)pc), "set");
+                       }
+                       if(pc instanceof PersistentList) {
+                               return new CollectionField(new ArrayList((List)pc), "list");
+                       }
+                       if(pc instanceof PersistentMap || pc instanceof PersistentMultiLanguageText) {
+                               return new CollectionField(new HashMap((Map)pc), "map");
+                       }
+                       if(pc instanceof PersistentSortedMap) {
+                               return new CollectionField(new TreeMap((Map)pc), "map");
+                       }
+               }
+               return null;
+       }
+       
+       private String getCollectionFieldName(PersistentCollection pc) {
+               if(pc != null) {
+                       if(pc instanceof PersistentSet || pc instanceof PersistentSortedSet) {
+                               return "set";
+                       }
+                       
+                       if(pc instanceof PersistentList) {
+                               return "list";
+                       }
+                       if(pc instanceof PersistentMap || pc instanceof PersistentMultiLanguageText) {
+                               return "map";
+                       }
+               }
+               return null;
+       }
+       
+       private class CollectionField {
+               private Object col;
+               private String fieldName;
+               public CollectionField(Object col, String fieldName) {
+                       this.col = col;
+                       this.fieldName = fieldName;
+               }
+               
+               public Object getCollection() {
+                       return this.col;
+               }
+               
+               public String getFieldName() {
+                       return this.fieldName;
+               }
+       }
+
+}
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteConfiguration.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteConfiguration.java
new file mode 100644 (file)
index 0000000..bbd3286
--- /dev/null
@@ -0,0 +1,45 @@
+// $Id: CdmApplicationDefaultConfiguration.java 11680 2011-04-04 17:07:39Z a.mueller $\r
+/**\r
+* Copyright (C) 2007 EDIT\r
+* European Distributed Institute of Taxonomy \r
+* http://www.e-taxonomy.eu\r
+* \r
+* The contents of this file are subject to the Mozilla Public License Version 1.1\r
+* See LICENSE.TXT at the top of this package for the full license terms.\r
+*/\r
+\r
+package eu.etaxonomy.cdm.remote.api.application;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.springframework.context.ApplicationContextAware;\r
+import org.springframework.stereotype.Component;\r
+\r
+import eu.etaxonomy.cdm.api.application.CdmApplicationConfiguration;\r
+import eu.etaxonomy.cdm.api.application.CdmApplicationDefaultConfiguration;\r
+import eu.etaxonomy.cdm.api.conversation.ConversationHolder;\r
+import eu.etaxonomy.cdm.api.conversation.ConversationHolderMock;\r
+\r
+/**\r
+ * @author a.mueller\r
+ * @created 21.05.2008\r
+ * @version 1.0\r
+ */\r
+/**\r
+ * @author a.mueller\r
+ * @author j.koch\r
+ */\r
+@Component\r
+public class CdmApplicationRemoteConfiguration extends CdmApplicationConfiguration implements ICdmApplicationRemoteConfiguration, ApplicationContextAware {\r
+       \r
+       @SuppressWarnings("unused")\r
+       private static final Logger logger = Logger.getLogger(CdmApplicationRemoteConfiguration.class);\r
+\r
+       public CdmApplicationRemoteConfiguration() {\r
+       }\r
+                       \r
+       @Override\r
+       public ConversationHolder NewConversation() {\r
+               // TODO Auto-generated method stub\r
+               return new ConversationHolderMock();\r
+       }\r
+}\r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteController.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/CdmApplicationRemoteController.java
new file mode 100644 (file)
index 0000000..0e71ca7
--- /dev/null
@@ -0,0 +1,328 @@
+// $Id: CdmApplicationController.java 11680 2011-04-04 17:07:39Z a.mueller $\r
+/**\r
+ * Copyright (C) 2007 EDIT\r
+ * European Distributed Institute of Taxonomy \r
+ * http://www.e-taxonomy.eu\r
+ * \r
+ * The contents of this file are subject to the Mozilla Public License Version 1.1\r
+ * See LICENSE.TXT at the top of this package for the full license terms.\r
+ */\r
+\r
+\r
+package eu.etaxonomy.cdm.remote.api.application;\r
+\r
+import java.util.UUID;\r
+\r
+import org.apache.log4j.Logger;\r
+import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;\r
+import org.springframework.context.support.AbstractApplicationContext;\r
+import org.springframework.context.support.GenericApplicationContext;\r
+import org.springframework.core.io.ClassPathResource;\r
+import org.springframework.core.io.Resource;\r
+import org.springframework.security.access.PermissionEvaluator;\r
+import org.springframework.security.authentication.ProviderManager;\r
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;\r
+import org.springframework.security.core.Authentication;\r
+import org.springframework.security.core.context.SecurityContext;\r
+import org.springframework.security.core.context.SecurityContextHolder;\r
+import org.springframework.transaction.PlatformTransactionManager;\r
+//import org.springframework.transaction.PlatformTransactionManager;\r
+import org.springframework.transaction.TransactionStatus;\r
+\r
+import eu.etaxonomy.cdm.api.conversation.ConversationHolder;\r
+import eu.etaxonomy.cdm.api.service.IAgentService;\r
+import eu.etaxonomy.cdm.api.service.IClassificationService;\r
+import eu.etaxonomy.cdm.api.service.ICollectionService;\r
+import eu.etaxonomy.cdm.api.service.ICommonService;\r
+import eu.etaxonomy.cdm.api.service.IDatabaseService;\r
+import eu.etaxonomy.cdm.api.service.IDescriptionService;\r
+import eu.etaxonomy.cdm.api.service.IFeatureNodeService;\r
+import eu.etaxonomy.cdm.api.service.IFeatureTreeService;\r
+import eu.etaxonomy.cdm.api.service.IGrantedAuthorityService;\r
+import eu.etaxonomy.cdm.api.service.IGroupService;\r
+import eu.etaxonomy.cdm.api.service.IIdentificationKeyService;\r
+import eu.etaxonomy.cdm.api.service.ILocationService;\r
+import eu.etaxonomy.cdm.api.service.IMediaService;\r
+import eu.etaxonomy.cdm.api.service.INameService;\r
+import eu.etaxonomy.cdm.api.service.IOccurrenceService;\r
+import eu.etaxonomy.cdm.api.service.IPolytomousKeyNodeService;\r
+import eu.etaxonomy.cdm.api.service.IPolytomousKeyService;\r
+import eu.etaxonomy.cdm.api.service.IReferenceService;\r
+import eu.etaxonomy.cdm.api.service.IService;\r
+import eu.etaxonomy.cdm.api.service.ITaxonNodeService;\r
+import eu.etaxonomy.cdm.api.service.ITaxonService;\r
+import eu.etaxonomy.cdm.api.service.ITermService;\r
+import eu.etaxonomy.cdm.api.service.IUserService;\r
+import eu.etaxonomy.cdm.api.service.IVocabularyService;\r
+import eu.etaxonomy.cdm.api.service.IWorkingSetService;\r
+import eu.etaxonomy.cdm.common.monitor.IProgressMonitor;\r
+import eu.etaxonomy.cdm.common.monitor.NullProgressMonitor;\r
+import eu.etaxonomy.cdm.model.common.CdmBase;\r
+import eu.etaxonomy.cdm.model.common.DefinedTermBase;\r
+import eu.etaxonomy.cdm.persistence.hibernate.permission.ICdmPermissionEvaluator;\r
+\r
+\r
+/**\r
+ * @author a.mueller\r
+ * @author j.koch\r
+ *\r
+ */\r
+public class CdmApplicationRemoteController  implements ICdmApplicationRemoteConfiguration {\r
+       private static final Logger logger = Logger.getLogger(CdmApplicationRemoteController.class);\r
+       \r
+       public static final String DEFAULT_APPLICATION_CONTEXT_RESOURCE = "/eu/etaxonomy/cdm/remotingApplicationContext.xml";\r
+       \r
+       public AbstractApplicationContext applicationContext;\r
+       private ICdmApplicationRemoteConfiguration configuration; \r
+       private Resource applicationContextResource;\r
+       private IProgressMonitor progressMonitor;\r
+       \r
+       /**\r
+        * Constructor, opens a spring ApplicationContext with defaults\r
+        */\r
+       public static CdmApplicationRemoteController NewInstance() {\r
+               logger.info("Configure CdmApplicationRemoteController with defaults");\r
+               return new CdmApplicationRemoteController(null, null);\r
+       }\r
+       \r
+       /**\r
+        * Constructor, opens a spring ApplicationContext with given application context\r
+        * @param applicationContextResource\r
+        */\r
+       public static CdmApplicationRemoteController NewInstance(Resource applicationContextResource, IProgressMonitor progressMonitor) {\r
+               logger.info("Configure CdmApplicationRemoteController with given application context");\r
+               return new CdmApplicationRemoteController(applicationContextResource, progressMonitor);\r
+       }\r
+\r
+       /**\r
+        * Constructor, starts the application remote controller\r
+        * @param applicationContextResource\r
+        */\r
+       private CdmApplicationRemoteController(Resource applicationContextResource, IProgressMonitor progressMonitor){\r
+               logger.info("Start CdmApplicationRemoteController");\r
+               this.applicationContextResource = applicationContextResource != null ? applicationContextResource : new ClassPathResource(DEFAULT_APPLICATION_CONTEXT_RESOURCE);\r
+               this.progressMonitor = progressMonitor != null ? progressMonitor : new NullProgressMonitor();\r
+               setNewApplicationContext();\r
+       }\r
+               \r
+       /**\r
+        * Sets the application context to a new spring ApplicationContext and initializes the Controller\r
+        */\r
+       private boolean setNewApplicationContext(){\r
+               logger.info("Set new application context");\r
+               progressMonitor.beginTask("Start application context.", 6);\r
+               progressMonitor.worked(1);\r
+\r
+               GenericApplicationContext applicationContext =  new GenericApplicationContext();\r
+               \r
+               XmlBeanDefinitionReader xmlReader = new XmlBeanDefinitionReader(applicationContext);\r
+               progressMonitor.subTask("Registering resources.");\r
+               xmlReader.loadBeanDefinitions(applicationContextResource);\r
+               progressMonitor.worked(1);\r
+                               \r
+               progressMonitor.subTask("This might take a while ...");\r
+               applicationContext.refresh();\r
+               applicationContext.start();\r
+               progressMonitor.worked(1);\r
+               \r
+               progressMonitor.subTask("Cleaning up.");\r
+               setApplicationContext(applicationContext);\r
+               progressMonitor.done();\r
+               return true;\r
+       }\r
+       \r
+       /**\r
+        * Tests if some DefinedTermsAreMissing.\r
+        * @return true, if at least one is missing, else false\r
+        */\r
+       public boolean testDefinedTermsAreMissing(){\r
+               UUID englishUuid = UUID.fromString("e9f8cdb7-6819-44e8-95d3-e2d0690c3523");\r
+               DefinedTermBase<?> english = this.getTermService().find(englishUuid);\r
+               if ( english == null || ! english.getUuid().equals(englishUuid)){\r
+                       return true;\r
+               }else{\r
+                       return false;\r
+               }\r
+       }\r
+               \r
+       /**\r
+        * Sets a new application Context.\r
+        * @param ac\r
+        */\r
+       public void setApplicationContext(AbstractApplicationContext ac){\r
+               closeApplicationContext(); //closes old application context if necessary\r
+               applicationContext = ac;\r
+               applicationContext.registerShutdownHook();\r
+               init();\r
+       }\r
+       \r
+       /* (non-Javadoc)\r
+        * @see java.lang.Object#finalize()\r
+        */\r
+       public void finalize(){\r
+               close();\r
+       }\r
+       \r
+       /**\r
+        * closes the application\r
+        */\r
+       public void close(){\r
+               closeApplicationContext();\r
+       }\r
+       \r
+       /**\r
+        * closes the application context\r
+        */\r
+       private void closeApplicationContext(){\r
+               if (applicationContext != null){\r
+                       logger.info("Close ApplicationContext");\r
+                       applicationContext.close();\r
+               }\r
+       }\r
+       \r
+       private void init(){\r
+               logger.info("Init " +  this.getClass().getName() + " ... ");\r
+               if (logger.isInfoEnabled()){for (String beanName : applicationContext.getBeanDefinitionNames()){ logger.debug(beanName);}}\r
+               //TODO delete next row (was just for testing)\r
+               if (logger.isInfoEnabled()){\r
+                       logger.info("Registered Beans: ");\r
+                       String[] beanNames = applicationContext.getBeanDefinitionNames();\r
+                       for (String beanName : beanNames){\r
+                               logger.info(beanName);\r
+                       }\r
+               }\r
+               configuration = new CdmApplicationRemoteConfiguration();\r
+       }\r
+       \r
+    \r
+\r
+       /* ******  Services *********/\r
+       \r
+       public final INameService getNameService(){\r
+               return configuration.getNameService();\r
+       }\r
+\r
+       public final ITaxonService getTaxonService(){\r
+               return configuration.getTaxonService();\r
+       }\r
+       \r
+       public final IClassificationService getClassificationService(){\r
+               return configuration.getClassificationService();\r
+       }\r
+       \r
+       public final ITaxonNodeService getTaxonNodeService(){\r
+               return configuration.getTaxonNodeService();\r
+       }\r
+\r
+       public final IReferenceService getReferenceService(){\r
+               return configuration.getReferenceService();\r
+       }\r
+       \r
+       public final IAgentService getAgentService(){\r
+               return configuration.getAgentService();\r
+       }\r
+               \r
+       public final ITermService getTermService(){\r
+               return configuration.getTermService();\r
+       }\r
+\r
+       public final IDescriptionService getDescriptionService(){\r
+               return configuration.getDescriptionService();\r
+       }\r
+       \r
+       public final IOccurrenceService getOccurrenceService(){\r
+               return configuration.getOccurrenceService();\r
+       }\r
+\r
+       public final IMediaService getMediaService(){\r
+               return configuration.getMediaService();\r
+       }\r
+\r
+       public final ICommonService getCommonService(){\r
+               return configuration.getCommonService();\r
+       }\r
+       \r
+       public final ILocationService getLocationService(){\r
+               return configuration.getLocationService();\r
+       }\r
+       \r
+       public final IUserService getUserService(){\r
+               return configuration.getUserService();\r
+       }\r
+\r
+       public IGroupService getGroupService() {\r
+               return configuration.getGroupService();\r
+       }\r
+       \r
+       public final ICollectionService getCollectionService(){\r
+               return configuration.getCollectionService();\r
+       }\r
+       \r
+       public final IFeatureTreeService getFeatureTreeService(){\r
+               return configuration.getFeatureTreeService();\r
+       }\r
+       \r
+       public final IFeatureNodeService getFeatureNodeService(){\r
+               return configuration.getFeatureNodeService();\r
+       }\r
+       \r
+       public final IVocabularyService getVocabularyService(){\r
+               return configuration.getVocabularyService();\r
+       }\r
+       \r
+       public final IIdentificationKeyService getIdentificationKeyService(){\r
+               return configuration.getIdentificationKeyService();\r
+       }\r
+\r
+       public final IPolytomousKeyService getPolytomousKeyService(){\r
+               return configuration.getPolytomousKeyService();\r
+       }\r
+\r
+       public final IPolytomousKeyNodeService getPolytomousKeyNodeService(){\r
+               return configuration.getPolytomousKeyNodeService();\r
+       }\r
+       \r
+       public final IService<CdmBase> getMainService(){\r
+               return configuration.getMainService();\r
+       }\r
+       \r
+       public final IWorkingSetService getWorkingSetService(){\r
+               return configuration.getWorkingSetService();\r
+       }\r
+\r
+\r
+       @Override\r
+       public ProviderManager getAuthenticationManager() {\r
+               return configuration.getAuthenticationManager();\r
+       }\r
+\r
+\r
+\r
+       @Override\r
+       public ConversationHolder NewConversation() {\r
+               return configuration.NewConversation();\r
+       }\r
+\r
+       @Override\r
+       public Object getBean(String name) {\r
+               return configuration.getBean(name);\r
+       }\r
+\r
+       @Override\r
+       public IGrantedAuthorityService getGrantedAuthorityService() {\r
+               return configuration.getGrantedAuthorityService();\r
+       }\r
+\r
+       @Override\r
+       public ICdmPermissionEvaluator getPermissionEvaluator() {\r
+               return configuration.getPermissionEvaluator();\r
+       }\r
+\r
+       @Override\r
+       public void authenticate(String username, String password) {\r
+               configuration.authenticate(username, password);\r
+               \r
+       }\r
+\r
+\r
+}\r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/ICdmApplicationRemoteConfiguration.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/remote/api/application/ICdmApplicationRemoteConfiguration.java
new file mode 100644 (file)
index 0000000..57b5200
--- /dev/null
@@ -0,0 +1,9 @@
+package eu.etaxonomy.cdm.remote.api.application;
+
+import eu.etaxonomy.cdm.api.application.ICdmApplicationConfiguration;
+
+
+public interface ICdmApplicationRemoteConfiguration extends ICdmApplicationConfiguration {
+       
+  
+}
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java
new file mode 100644 (file)
index 0000000..adbbd46
--- /dev/null
@@ -0,0 +1,1175 @@
+
+
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.collection.internal;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.naming.NamingException;
+
+import org.hibernate.AssertionFailure;
+import org.hibernate.HibernateException;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.Session;
+import org.hibernate.collection.spi.PersistentCollection;
+import org.hibernate.engine.internal.ForeignKeys;
+import org.hibernate.engine.spi.CollectionEntry;
+import org.hibernate.engine.spi.EntityEntry;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.engine.spi.Status;
+import org.hibernate.engine.spi.TypedValue;
+import org.hibernate.internal.SessionFactoryRegistry;
+import org.hibernate.internal.util.MarkerObject;
+import org.hibernate.internal.util.collections.EmptyIterator;
+import org.hibernate.internal.util.collections.IdentitySet;
+import org.hibernate.persister.collection.CollectionPersister;
+import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.type.Type;
+import org.jboss.logging.Logger;
+
+/**
+ * Base class implementing {@link org.hibernate.collection.spi.PersistentCollection}
+ *
+ * @author Gavin King
+ */
+public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection {
+       private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class );
+
+       private static final long serialVersionUID = -7238232378593030571L;
+       
+       private transient SessionImplementor session;
+       private boolean initialized;
+       private transient List<DelayedOperation> operationQueue;
+       private transient boolean directlyAccessible;
+       private transient boolean initializing;
+       private Object owner;
+       private int cachedSize = -1;
+
+       private String role;
+       private Serializable key;
+       // collections detect changes made via their public interface and mark
+       // themselves as dirty as a performance optimization
+       private boolean dirty;
+       private Serializable storedSnapshot;
+
+       private String sessionFactoryUuid;
+       private boolean specjLazyLoad = false;
+
+       public final String getRole() {
+               return role;
+       }
+
+       public final Serializable getKey() {
+               return key;
+       }
+
+       public final boolean isUnreferenced() {
+               return role == null;
+       }
+
+       public final boolean isDirty() {
+               return dirty;
+       }
+
+       public final void clearDirty() {
+               dirty = false;
+       }
+
+       public final void dirty() {
+               dirty = true;
+       }
+
+       public final Serializable getStoredSnapshot() {
+               return storedSnapshot;
+       }
+
+       //Careful: these methods do not initialize the collection.
+
+       /**
+        * Is the initialized collection empty?
+        */
+       public abstract boolean empty();
+
+       /**
+        * Called by any read-only method of the collection interface
+        */
+       protected final void read() {
+               initialize( false );
+       }
+
+       /**
+        * Called by the {@link Collection#size} method
+        */
+       @SuppressWarnings({"JavaDoc"})
+       protected boolean readSize() {
+               if ( !initialized ) {
+                       if ( cachedSize != -1 && !hasQueuedOperations() ) {
+                               return true;
+                       }
+                       else {
+                               boolean isExtraLazy = withTemporarySessionIfNeeded(
+                                               new LazyInitializationWork<Boolean>() {
+                                                       @Override
+                                                       public Boolean doWork() {
+                                                               CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+
+                                                               if ( entry != null ) {
+                                                                       CollectionPersister persister = entry.getLoadedPersister();
+                                                                       if ( persister.isExtraLazy() ) {
+                                                                               if ( hasQueuedOperations() ) {
+                                                                                       session.flush();
+                                                                               }
+                                                                               cachedSize = persister.getSize( entry.getLoadedKey(), session );
+                                                                               return true;
+                                                                       }
+                                                                       else {
+                                                                               read();
+                                                                       }
+                                                               }
+                                                               else{
+                                                                       throwLazyInitializationExceptionIfNotConnected();
+                                                               }
+                                                               return false;
+                                                       }
+                                               }
+                               );
+                               if ( isExtraLazy ) {
+                                       return true;
+                               }
+                       }
+               }
+               return false;
+       }
+
+       public static interface LazyInitializationWork<T> {
+               public T doWork();
+       }
+
+       private <T> T withTemporarySessionIfNeeded(LazyInitializationWork<T> lazyInitializationWork) {
+               SessionImplementor originalSession = null;
+               boolean isTempSession = false;
+               boolean isJTA = false;
+
+               if ( session == null ) {
+                       if ( specjLazyLoad ) {
+                               session = openTemporarySessionForLoading();
+                               isTempSession = true;
+                       }
+                       else {
+                               throwLazyInitializationException( "could not initialize proxy - no Session" );
+                       }
+               }
+               else if ( !session.isOpen() ) {
+                       if ( specjLazyLoad ) {
+                               originalSession = session;
+                               session = openTemporarySessionForLoading();
+                               isTempSession = true;
+                       }
+                       else {
+                               throwLazyInitializationException( "could not initialize proxy - the owning Session was closed" );
+                       }
+               }
+               else if ( !session.isConnected() ) {
+                       if ( specjLazyLoad ) {
+                               originalSession = session;
+                               session = openTemporarySessionForLoading();
+                               isTempSession = true;
+                       }
+                       else {
+                               throwLazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
+                       }
+               }
+
+               if ( isTempSession ) {
+                       // TODO: On the next major release, add an
+                       // 'isJTA' or 'getTransactionFactory' method to Session.
+                       isJTA = session.getTransactionCoordinator()
+                                       .getTransactionContext().getTransactionEnvironment()
+                                       .getTransactionFactory()
+                                       .compatibleWithJtaSynchronization();
+                       
+                       if ( !isJTA ) {
+                               // Explicitly handle the transactions only if we're not in
+                               // a JTA environment.  A lazy loading temporary session can
+                               // be created even if a current session and transaction are
+                               // open (ex: session.clear() was used).  We must prevent
+                               // multiple transactions.
+                               ( ( Session) session ).beginTransaction();
+                       }
+                       
+                       session.getPersistenceContext().addUninitializedDetachedCollection(
+                                       session.getFactory().getCollectionPersister( getRole() ),
+                                       this
+                       );
+               }
+
+               try {
+                       return lazyInitializationWork.doWork();
+               }
+               finally {
+                       if ( isTempSession ) {
+                               // make sure the just opened temp session gets closed!
+                               try {
+                                       if ( !isJTA ) {
+                                               ( ( Session) session ).getTransaction().commit();
+                                       }
+                                       ( (Session) session ).close();
+                               }
+                               catch (Exception e) {
+                                       log.warn( "Unable to close temporary session used to load lazy collection associated to no session" );
+                               }
+                               session = originalSession;
+                       }
+               }
+       }
+
+       private SessionImplementor openTemporarySessionForLoading() {
+               if ( sessionFactoryUuid == null ) {
+                       throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" );
+               }
+
+               SessionFactoryImplementor sf = (SessionFactoryImplementor)
+                               SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
+               return (SessionImplementor) sf.openSession();
+       }
+
+       protected Boolean readIndexExistence(final Object index) {
+               if ( !initialized ) {
+                       Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
+                                       new LazyInitializationWork<Boolean>() {
+                                               @Override
+                                               public Boolean doWork() {
+                                                       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+                                                       CollectionPersister persister = entry.getLoadedPersister();
+                                                       if ( persister.isExtraLazy() ) {
+                                                               if ( hasQueuedOperations() ) {
+                                                                       session.flush();
+                                                               }
+                                                               return persister.indexExists( entry.getLoadedKey(), index, session );
+                                                       }
+                                                       else {
+                                                               read();
+                                                       }
+                                                       return null;
+                                               }
+                                       }
+                       );
+                       if ( extraLazyExistenceCheck != null ) {
+                               return extraLazyExistenceCheck;
+                       }
+               }
+               return null;
+       }
+
+       protected Boolean readElementExistence(final Object element) {
+               if ( !initialized ) {
+                       Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded(
+                                       new LazyInitializationWork<Boolean>() {
+                                               @Override
+                                               public Boolean doWork() {
+                                                       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+                                                       CollectionPersister persister = entry.getLoadedPersister();
+                                                       if ( persister.isExtraLazy() ) {
+                                                               if ( hasQueuedOperations() ) {
+                                                                       session.flush();
+                                                               }
+                                                               return persister.elementExists( entry.getLoadedKey(), element, session );
+                                                       }
+                                                       else {
+                                                               read();
+                                                       }
+                                                       return null;
+                                               }
+                                       }
+                       );
+                       if ( extraLazyExistenceCheck != null ) {
+                               return extraLazyExistenceCheck;
+                       }
+               }
+               return null;
+       }
+
+       protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" );
+
+       protected Object readElementByIndex(final Object index) {
+               if ( !initialized ) {
+                       class ExtraLazyElementByIndexReader implements LazyInitializationWork {
+                               private boolean isExtraLazy;
+                               private Object element;
+
+                               @Override
+                               public Object doWork() {
+                                       CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this );
+                                       CollectionPersister persister = entry.getLoadedPersister();
+                                       isExtraLazy = persister.isExtraLazy();
+                                       if ( isExtraLazy ) {
+                                               if ( hasQueuedOperations() ) {
+                                                       session.flush();
+                                               }
+                                               element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner );
+                                       }
+                                       else {
+                                               read();
+                                       }
+                                       return null;
+                               }
+                       }
+
+                       ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader();
+                       //noinspection unchecked
+                       withTemporarySessionIfNeeded( reader );
+                       if ( reader.isExtraLazy ) {
+                               return reader.element;
+                       }
+               }
+               return UNKNOWN;
+
+       }
+
+       protected int getCachedSize() {
+               return cachedSize;
+       }
+
+       private boolean isConnectedToSession() {
+               return session != null &&
+                               session.isOpen() &&
+                               session.getPersistenceContext().containsCollection( this );
+       }
+
+       /**
+        * Called by any writer method of the collection interface
+        */
+       protected final void write() {
+               initialize( true );
+               dirty();
+       }
+
+       /**
+        * Is this collection in a state that would allow us to
+        * "queue" operations?
+        */
+       @SuppressWarnings({"JavaDoc"})
+       protected boolean isOperationQueueEnabled() {
+               return !initialized &&
+                               isConnectedToSession() &&
+                               isInverseCollection();
+       }
+
+       /**
+        * Is this collection in a state that would allow us to
+        * "queue" puts? This is a special case, because of orphan
+        * delete.
+        */
+       @SuppressWarnings({"JavaDoc"})
+       protected boolean isPutQueueEnabled() {
+               return !initialized &&
+                               isConnectedToSession() &&
+                               isInverseOneToManyOrNoOrphanDelete();
+       }
+
+       /**
+        * Is this collection in a state that would allow us to
+        * "queue" clear? This is a special case, because of orphan
+        * delete.
+        */
+       @SuppressWarnings({"JavaDoc"})
+       protected boolean isClearQueueEnabled() {
+               return !initialized &&
+                               isConnectedToSession() &&
+                               isInverseCollectionNoOrphanDelete();
+       }
+
+       /**
+        * Is this the "inverse" end of a bidirectional association?
+        */
+       @SuppressWarnings({"JavaDoc"})
+       private boolean isInverseCollection() {
+               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+               return ce != null && ce.getLoadedPersister().isInverse();
+       }
+
+       /**
+        * Is this the "inverse" end of a bidirectional association with
+        * no orphan delete enabled?
+        */
+       @SuppressWarnings({"JavaDoc"})
+       private boolean isInverseCollectionNoOrphanDelete() {
+               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+               return ce != null &&
+                               ce.getLoadedPersister().isInverse() &&
+                               !ce.getLoadedPersister().hasOrphanDelete();
+       }
+
+       /**
+        * Is this the "inverse" end of a bidirectional one-to-many, or
+        * of a collection with no orphan delete?
+        */
+       @SuppressWarnings({"JavaDoc"})
+       private boolean isInverseOneToManyOrNoOrphanDelete() {
+               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+               return ce != null && ce.getLoadedPersister().isInverse() && (
+                               ce.getLoadedPersister().isOneToMany() ||
+                                               !ce.getLoadedPersister().hasOrphanDelete()
+               );
+       }
+
+       /**
+        * Queue an addition
+        */
+       @SuppressWarnings({"JavaDoc"})
+       protected final void queueOperation(DelayedOperation operation) {
+               if ( operationQueue == null ) {
+                       operationQueue = new ArrayList<DelayedOperation>( 10 );
+               }
+               operationQueue.add( operation );
+               dirty = true; //needed so that we remove this collection from the second-level cache
+       }
+
+       /**
+        * After reading all existing elements from the database,
+        * add the queued elements to the underlying collection.
+        */
+       protected final void performQueuedOperations() {
+               for ( DelayedOperation operation : operationQueue ) {
+                       operation.operate();
+               }
+       }
+
+       /**
+        * After flushing, re-init snapshot state.
+        */
+       public void setSnapshot(Serializable key, String role, Serializable snapshot) {
+               this.key = key;
+               this.role = role;
+               this.storedSnapshot = snapshot;
+       }
+
+       /**
+        * After flushing, clear any "queued" additions, since the
+        * database state is now synchronized with the memory state.
+        */
+       public void postAction() {
+               operationQueue = null;
+               cachedSize = -1;
+               clearDirty();
+       }
+
+       /**
+        * Not called by Hibernate, but used by non-JDK serialization,
+        * eg. SOAP libraries.
+        */
+       public AbstractPersistentCollection() {
+       }
+
+       protected AbstractPersistentCollection(SessionImplementor session) {
+               this.session = session;
+       }
+
+       /**
+        * return the user-visible collection (or array) instance
+        */
+       public Object getValue() {
+               return this;
+       }
+
+       /**
+        * Called just before reading any rows from the JDBC result set
+        */
+       public void beginRead() {
+               // override on some subclasses
+               initializing = true;
+       }
+
+       /**
+        * Called after reading all rows from the JDBC result set
+        */
+       public boolean endRead() {
+               //override on some subclasses
+               return afterInitialize();
+       }
+
+       public boolean afterInitialize() {
+               setInitialized();
+               //do this bit after setting initialized to true or it will recurse
+               if ( operationQueue != null ) {
+                       performQueuedOperations();
+                       operationQueue = null;
+                       cachedSize = -1;
+                       return false;
+               }
+               else {
+                       return true;
+               }
+       }
+
+       /**
+        * Initialize the collection, if possible, wrapping any exceptions
+        * in a runtime exception
+        *
+        * @param writing currently obsolete
+        *
+        * @throws LazyInitializationException if we cannot initialize
+        */
+       protected final void initialize(final boolean writing) {
+               if ( initialized ) {
+                       return;
+               }
+
+               withTemporarySessionIfNeeded(
+                               new LazyInitializationWork<Object>() {
+                                       @Override
+                                       public Object doWork() {
+                                               session.initializeCollection( AbstractPersistentCollection.this, writing );
+                                               return null;
+                                       }
+                               }
+               );
+       }
+
+       private void throwLazyInitializationExceptionIfNotConnected() {
+               if ( !isConnectedToSession() ) {
+                       throwLazyInitializationException( "no session or session was closed" );
+               }
+               if ( !session.isConnected() ) {
+                       throwLazyInitializationException( "session is disconnected" );
+               }
+       }
+
+       private void throwLazyInitializationException(String message) {
+               throw new LazyInitializationException(
+                               "failed to lazily initialize a collection" +
+                                               (role == null ? "" : " of role: " + role) +
+                                               ", " + message
+               );
+       }
+
+       protected final void setInitialized() {
+               this.initializing = false;
+               this.initialized = true;
+       }
+
+       protected final void setDirectlyAccessible(boolean directlyAccessible) {
+               this.directlyAccessible = directlyAccessible;
+       }
+
+       /**
+        * Could the application possibly have a direct reference to
+        * the underlying collection implementation?
+        */
+       public boolean isDirectlyAccessible() {
+               return directlyAccessible;
+       }
+
+       /**
+        * Disassociate this collection from the given session.
+        *
+        * @return true if this was currently associated with the given session
+        */
+       public final boolean unsetSession(SessionImplementor currentSession) {
+               prepareForPossibleSpecialSpecjInitialization();
+               if ( currentSession == this.session ) {
+                       this.session = null;
+                       return true;
+               }
+               else {
+                       return false;
+               }
+       }
+
+       protected void prepareForPossibleSpecialSpecjInitialization() {
+               if ( session != null ) {
+                       specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
+
+                       if ( specjLazyLoad && sessionFactoryUuid == null ) {
+                               try {
+                                       sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
+                               }
+                               catch (NamingException e) {
+                                       //not much we can do if this fails...
+                               }
+                       }
+               }
+       }
+
+
+       /**
+        * Associate the collection with the given session.
+        *
+        * @return false if the collection was already associated with the session
+        *
+        * @throws HibernateException if the collection was already associated
+        * with another open session
+        */
+       public final boolean setCurrentSession(SessionImplementor session) throws HibernateException {
+               if ( session == this.session ) {
+                       return false;
+               }
+               else {
+                       if ( isConnectedToSession() ) {
+                               CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this );
+                               if ( ce == null ) {
+                                       throw new HibernateException(
+                                                       "Illegal attempt to associate a collection with two open sessions"
+                                       );
+                               }
+                               else {
+                                       throw new HibernateException(
+                                                       "Illegal attempt to associate a collection with two open sessions: " +
+                                                                       MessageHelper.collectionInfoString(
+                                                                                       ce.getLoadedPersister(), this,
+                                                                                       ce.getLoadedKey(), session
+                                                                       )
+                                       );
+                               }
+                       }
+                       else {
+                               this.session = session;
+                               return true;
+                       }
+               }
+       }
+
+       /**
+        * Do we need to completely recreate this collection when it changes?
+        */
+       public boolean needsRecreate(CollectionPersister persister) {
+               return false;
+       }
+
+       /**
+        * To be called internally by the session, forcing
+        * immediate initialization.
+        */
+       public final void forceInitialization() throws HibernateException {
+               if ( !initialized ) {
+                       if ( initializing ) {
+                               throw new AssertionFailure( "force initialize loading collection" );
+                       }
+                       if ( session == null ) {
+                               throw new HibernateException( "collection is not associated with any session" );
+                       }
+                       if ( !session.isConnected() ) {
+                               throw new HibernateException( "disconnected session" );
+                       }
+                       session.initializeCollection( this, false );
+               }
+       }
+
+
+       /**
+        * Get the current snapshot from the session
+        */
+       @SuppressWarnings({"JavaDoc"})
+       protected final Serializable getSnapshot() {
+               return session.getPersistenceContext().getSnapshot( this );
+       }
+
+       /**
+        * Is this instance initialized?
+        */
+       public final boolean wasInitialized() {
+               return initialized;
+       }
+
+       public boolean isRowUpdatePossible() {
+               return true;
+       }
+
+       /**
+        * Does this instance have any "queued" additions?
+        */
+       public final boolean hasQueuedOperations() {
+               return operationQueue != null;
+       }
+
+       /**
+        * Iterate the "queued" additions
+        */
+       public final Iterator queuedAdditionIterator() {
+               if ( hasQueuedOperations() ) {
+                       return new Iterator() {
+                               int i = 0;
+
+                               public Object next() {
+                                       return operationQueue.get( i++ ).getAddedInstance();
+                               }
+
+                               public boolean hasNext() {
+                                       return i < operationQueue.size();
+                               }
+
+                               public void remove() {
+                                       throw new UnsupportedOperationException();
+                               }
+                       };
+               }
+               else {
+                       return EmptyIterator.INSTANCE;
+               }
+       }
+
+       /**
+        * Iterate the "queued" additions
+        */
+       @SuppressWarnings({"unchecked"})
+       public final Collection getQueuedOrphans(String entityName) {
+               if ( hasQueuedOperations() ) {
+                       Collection additions = new ArrayList( operationQueue.size() );
+                       Collection removals = new ArrayList( operationQueue.size() );
+                       for ( DelayedOperation operation : operationQueue ) {
+                               additions.add( operation.getAddedInstance() );
+                               removals.add( operation.getOrphan() );
+                       }
+                       return getOrphans( removals, additions, entityName, session );
+               }
+               else {
+                       return Collections.EMPTY_LIST;
+               }
+       }
+
+       /**
+        * Called before inserting rows, to ensure that any surrogate keys
+        * are fully generated
+        */
+       public void preInsert(CollectionPersister persister) throws HibernateException {
+       }
+
+       /**
+        * Called after inserting a row, to fetch the natively generated id
+        */
+       public void afterRowInsert(CollectionPersister persister, Object entry, int i) throws HibernateException {
+       }
+
+       /**
+        * get all "orphaned" elements
+        */
+       public abstract Collection getOrphans(Serializable snapshot, String entityName) throws HibernateException;
+
+       /**
+        * Get the current session
+        */
+       @SuppressWarnings({"JavaDoc"})
+       public final SessionImplementor getSession() {
+               return session;
+       }
+
+       protected final class IteratorProxy implements Iterator {
+               protected final Iterator itr;
+
+               public IteratorProxy(Iterator itr) {
+                       this.itr = itr;
+               }
+
+               public boolean hasNext() {
+                       return itr.hasNext();
+               }
+
+               public Object next() {
+                       return itr.next();
+               }
+
+               public void remove() {
+                       write();
+                       itr.remove();
+               }
+
+       }
+
+       protected final class ListIteratorProxy implements ListIterator {
+               protected final ListIterator itr;
+
+               public ListIteratorProxy(ListIterator itr) {
+                       this.itr = itr;
+               }
+
+               @SuppressWarnings({"unchecked"})
+               public void add(Object o) {
+                       write();
+                       itr.add( o );
+               }
+
+               public boolean hasNext() {
+                       return itr.hasNext();
+               }
+
+               public boolean hasPrevious() {
+                       return itr.hasPrevious();
+               }
+
+               public Object next() {
+                       return itr.next();
+               }
+
+               public int nextIndex() {
+                       return itr.nextIndex();
+               }
+
+               public Object previous() {
+                       return itr.previous();
+               }
+
+               public int previousIndex() {
+                       return itr.previousIndex();
+               }
+
+               public void remove() {
+                       write();
+                       itr.remove();
+               }
+
+               @SuppressWarnings({"unchecked"})
+               public void set(Object o) {
+                       write();
+                       itr.set( o );
+               }
+
+       }
+
+       protected class SetProxy implements java.util.Set {
+               protected final Collection set;
+
+               public SetProxy(Collection set) {
+                       this.set = set;
+               }
+
+               @SuppressWarnings({"unchecked"})
+               public boolean add(Object o) {
+                       write();
+                       return set.add( o );
+               }
+
+               @SuppressWarnings({"unchecked"})
+               public boolean addAll(Collection c) {
+                       write();
+                       return set.addAll( c );
+               }
+
+               public void clear() {
+                       write();
+                       set.clear();
+               }
+
+               public boolean contains(Object o) {
+                       return set.contains( o );
+               }
+
+               public boolean containsAll(Collection c) {
+                       return set.containsAll( c );
+               }
+
+               public boolean isEmpty() {
+                       return set.isEmpty();
+               }
+
+               public Iterator iterator() {
+                       return new IteratorProxy( set.iterator() );
+               }
+
+               public boolean remove(Object o) {
+                       write();
+                       return set.remove( o );
+               }
+
+               public boolean removeAll(Collection c) {
+                       write();
+                       return set.removeAll( c );
+               }
+
+               public boolean retainAll(Collection c) {
+                       write();
+                       return set.retainAll( c );
+               }
+
+               public int size() {
+                       return set.size();
+               }
+
+               public Object[] toArray() {
+                       return set.toArray();
+               }
+
+               @SuppressWarnings({"unchecked"})
+               public Object[] toArray(Object[] array) {
+                       return set.toArray( array );
+               }
+
+       }
+
+       protected final class ListProxy implements java.util.List {
+               protected final List list;
+
+               public ListProxy(List list) {
+                       this.list = list;
+               }
+
+               @Override
+               @SuppressWarnings({"unchecked"})
+               public void add(int index, Object value) {
+                       write();
+                       list.add( index, value );
+               }
+
+               @Override
+               @SuppressWarnings({"unchecked"})
+               public boolean add(Object o) {
+                       write();
+                       return list.add( o );
+               }
+
+               @Override
+               @SuppressWarnings({"unchecked"})
+               public boolean addAll(Collection c) {
+                       write();
+                       return list.addAll( c );
+               }
+
+               @Override
+               @SuppressWarnings({"unchecked"})
+               public boolean addAll(int i, Collection c) {
+                       write();
+                       return list.addAll( i, c );
+               }
+
+               @Override
+               public void clear() {
+                       write();
+                       list.clear();
+               }
+
+               @Override
+               public boolean contains(Object o) {
+                       return list.contains( o );
+               }
+
+               @Override
+               public boolean containsAll(Collection c) {
+                       return list.containsAll( c );
+               }
+
+               @Override
+               public Object get(int i) {
+                       return list.get( i );
+               }
+
+               @Override
+               public int indexOf(Object o) {
+                       return list.indexOf( o );
+               }
+
+               @Override
+               public boolean isEmpty() {
+                       return list.isEmpty();
+               }
+
+               @Override
+               public Iterator iterator() {
+                       return new IteratorProxy( list.iterator() );
+               }
+
+               @Override
+               public int lastIndexOf(Object o) {
+                       return list.lastIndexOf( o );
+               }
+
+               @Override
+               public ListIterator listIterator() {
+                       return new ListIteratorProxy( list.listIterator() );
+               }
+
+               @Override
+               public ListIterator listIterator(int i) {
+                       return new ListIteratorProxy( list.listIterator( i ) );
+               }
+
+               @Override
+               public Object remove(int i) {
+                       write();
+                       return list.remove( i );
+               }
+
+               @Override
+               public boolean remove(Object o) {
+                       write();
+                       return list.remove( o );
+               }
+
+               @Override
+               public boolean removeAll(Collection c) {
+                       write();
+                       return list.removeAll( c );
+               }
+
+               @Override
+               public boolean retainAll(Collection c) {
+                       write();
+                       return list.retainAll( c );
+               }
+
+               @Override
+               @SuppressWarnings({"unchecked"})
+               public Object set(int i, Object o) {
+                       write();
+                       return list.set( i, o );
+               }
+
+               @Override
+               public int size() {
+                       return list.size();
+               }
+
+               @Override
+               public List subList(int i, int j) {
+                       return list.subList( i, j );
+               }
+
+               @Override
+               public Object[] toArray() {
+                       return list.toArray();
+               }
+
+               @Override
+               @SuppressWarnings({"unchecked"})
+               public Object[] toArray(Object[] array) {
+                       return list.toArray( array );
+               }
+
+       }
+
+       /**
+        * Contract for operations which are part of a collection's operation queue.
+        */
+       protected interface DelayedOperation {
+               public void operate();
+
+               public Object getAddedInstance();
+
+               public Object getOrphan();
+       }
+
+       /**
+        * Given a collection of entity instances that used to
+        * belong to the collection, and a collection of instances
+        * that currently belong, return a collection of orphans
+        */
+       @SuppressWarnings({"JavaDoc", "unchecked"})
+       protected static Collection getOrphans(
+                       Collection oldElements,
+                       Collection currentElements,
+                       String entityName,
+                       SessionImplementor session) throws HibernateException {
+
+               // short-circuit(s)
+               if ( currentElements.size() == 0 ) {
+                       return oldElements; // no new elements, the old list contains only Orphans
+               }
+               if ( oldElements.size() == 0 ) {
+                       return oldElements; // no old elements, so no Orphans neither
+               }
+
+               final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
+               final Type idType = entityPersister.getIdentifierType();
+
+               // create the collection holding the Orphans
+               Collection res = new ArrayList();
+
+               // collect EntityIdentifier(s) of the *current* elements - add them into a HashSet for fast access
+               java.util.Set currentIds = new HashSet();
+               java.util.Set currentSaving = new IdentitySet();
+               for ( Object current : currentElements ) {
+                       if ( current != null && ForeignKeys.isNotTransient( entityName, current, null, session ) ) {
+                               EntityEntry ee = session.getPersistenceContext().getEntry( current );
+                               if ( ee != null && ee.getStatus() == Status.SAVING ) {
+                                       currentSaving.add( current );
+                               }
+                               else {
+                                       Serializable currentId = ForeignKeys.getEntityIdentifierIfNotUnsaved(
+                                                       entityName,
+                                                       current,
+                                                       session
+                                       );
+                                       currentIds.add( new TypedValue( idType, currentId, entityPersister.getEntityMode() ) );
+                               }
+                       }
+               }
+
+               // iterate over the *old* list
+               for ( Object old : oldElements ) {
+                       if ( !currentSaving.contains( old ) ) {
+                               Serializable oldId = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, old, session );
+                               if ( !currentIds.contains( new TypedValue( idType, oldId, entityPersister.getEntityMode() ) ) ) {
+                                       res.add( old );
+                               }
+                       }
+               }
+
+               return res;
+       }
+
+       public static void identityRemove(
+                       Collection list,
+                       Object object,
+                       String entityName,
+                       SessionImplementor session) throws HibernateException {
+
+               if ( object != null && ForeignKeys.isNotTransient( entityName, object, null, session ) ) {
+                       final EntityPersister entityPersister = session.getFactory().getEntityPersister( entityName );
+                       Type idType = entityPersister.getIdentifierType();
+
+                       Serializable idOfCurrent = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, object, session );
+                       Iterator itr = list.iterator();
+                       while ( itr.hasNext() ) {
+                               Serializable idOfOld = ForeignKeys.getEntityIdentifierIfNotUnsaved( entityName, itr.next(), session );
+                               if ( idType.isEqual( idOfCurrent, idOfOld, session.getFactory() ) ) {
+                                       itr.remove();
+                                       break;
+                               }
+                       }
+
+               }
+       }
+
+       public Object getIdentifier(Object entry, int i) {
+               throw new UnsupportedOperationException();
+       }
+
+       public Object getOwner() {
+               return owner;
+       }
+
+       public void setOwner(Object owner) {
+               this.owner = owner;
+       }
+
+}
+
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java
new file mode 100644 (file)
index 0000000..b906807
--- /dev/null
@@ -0,0 +1,410 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008-2011, Red Hat Inc. or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors.  All third-party contributions are
+ * distributed under license by Red Hat Inc.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA  02110-1301  USA
+ */
+package org.hibernate.proxy;
+
+import java.io.Serializable;
+
+import javax.naming.NamingException;
+
+import org.hibernate.HibernateException;
+import org.hibernate.LazyInitializationException;
+import org.hibernate.Session;
+import org.hibernate.SessionException;
+import org.hibernate.TransientObjectException;
+import org.hibernate.engine.spi.EntityKey;
+import org.hibernate.engine.spi.SessionFactoryImplementor;
+import org.hibernate.engine.spi.SessionImplementor;
+import org.hibernate.internal.SessionFactoryRegistry;
+import org.hibernate.persister.entity.EntityPersister;
+import org.jboss.logging.Logger;
+import org.springframework.beans.factory.annotation.Configurable;
+
+/**
+ * Convenience base class for lazy initialization handlers.  Centralizes the basic plumbing of doing lazy
+ * initialization freeing subclasses to acts as essentially adapters to their intended entity mode and/or
+ * proxy generation strategy.
+ *
+ * @author Gavin King
+ */
+@Configurable(dependencyCheck = true)
+public abstract class AbstractLazyInitializer implements LazyInitializer {
+       private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class );
+
+       private String entityName;
+       private Serializable id;
+       private Object target;
+       private boolean initialized;
+       private boolean readOnly;
+       private boolean unwrap;
+       private transient SessionImplementor session;
+       private Boolean readOnlyBeforeAttachedToSession;
+
+       private String sessionFactoryUuid;
+       private boolean specjLazyLoad = false;
+
+       /**
+        * For serialization from the non-pojo initializers (HHH-3309)
+        */
+       protected AbstractLazyInitializer() {
+       }
+
+       /**
+        * Main constructor.
+        *
+        * @param entityName The name of the entity being proxied.
+        * @param id The identifier of the entity being proxied.
+        * @param session The session owning the proxy.
+        */
+       protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
+               this.entityName = entityName;
+               this.id = id;
+               // initialize other fields depending on session state
+               if ( session == null ) {
+                       unsetSession();
+               }
+               else {
+                       setSession( session );
+               }
+       }
+
+       @Override
+       public final String getEntityName() {
+               return entityName;
+       }
+
+       @Override
+       public final Serializable getIdentifier() {
+               return id;
+       }
+
+       @Override
+       public final void setIdentifier(Serializable id) {
+               this.id = id;
+       }
+
+       @Override
+       public final boolean isUninitialized() {
+               return !initialized;
+       }
+
+       @Override
+       public final SessionImplementor getSession() {
+               return session;
+       }
+
+       @Override
+       public final void setSession(SessionImplementor s) throws HibernateException {
+               if ( s != session ) {
+                       // check for s == null first, since it is least expensive
+                       if ( s == null ) {
+                               unsetSession();
+                       }
+                       else if ( isConnectedToSession() ) {
+                               //TODO: perhaps this should be some other RuntimeException...
+                               throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" );
+                       }
+                       else {
+                               // s != null
+                               session = s;
+                               if ( readOnlyBeforeAttachedToSession == null ) {
+                                       // use the default read-only/modifiable setting
+                                       final EntityPersister persister = s.getFactory().getEntityPersister( entityName );
+                                       setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() );
+                               }
+                               else {
+                                       // use the read-only/modifiable setting indicated during deserialization
+                                       setReadOnly( readOnlyBeforeAttachedToSession.booleanValue() );
+                                       readOnlyBeforeAttachedToSession = null;
+                               }
+                       }
+               }
+       }
+
+       private static EntityKey generateEntityKeyOrNull(Serializable id, SessionImplementor s, String entityName) {
+               if ( id == null || s == null || entityName == null ) {
+                       return null;
+               }
+               return s.generateEntityKey( id, s.getFactory().getEntityPersister( entityName ) );
+       }
+
+       @Override
+       public final void unsetSession() {
+               prepareForPossibleSpecialSpecjInitialization();
+               session = null;
+               readOnly = false;
+               readOnlyBeforeAttachedToSession = null;
+       }
+
+       @Override
+       public final void initialize() throws HibernateException {
+               if ( !initialized ) {
+                       if ( specjLazyLoad ) {
+                               specialSpecjInitialization();
+                       }
+                       else if ( session == null ) {
+                               throw new LazyInitializationException( "could not initialize proxy - no Session" );
+                       }
+                       else if ( !session.isOpen() ) {
+                               throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" );
+                       }
+                       else if ( !session.isConnected() ) {
+                               throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" );
+                       }
+                       else {
+                               target = session.immediateLoad( entityName, id );
+                               initialized = true;
+                               checkTargetState();
+                       }
+               }
+               else {
+                       checkTargetState();
+               }
+       }
+
+       protected void specialSpecjInitialization() {
+               if ( session == null ) {
+                       //we have a detached collection thats set to null, reattach
+                       if ( sessionFactoryUuid == null ) {
+                               throw new LazyInitializationException( "could not initialize proxy - no Session" );
+                       }
+                       try {
+                               SessionFactoryImplementor sf = (SessionFactoryImplementor)
+                                               SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid );
+                               SessionImplementor session = (SessionImplementor) sf.openSession();
+                               
+                               // TODO: On the next major release, add an
+                               // 'isJTA' or 'getTransactionFactory' method to Session.
+                               boolean isJTA = session.getTransactionCoordinator()
+                                               .getTransactionContext().getTransactionEnvironment()
+                                               .getTransactionFactory()
+                                               .compatibleWithJtaSynchronization();
+                               
+                               if ( !isJTA ) {
+                                       // Explicitly handle the transactions only if we're not in
+                                       // a JTA environment.  A lazy loading temporary session can
+                                       // be created even if a current session and transaction are
+                                       // open (ex: session.clear() was used).  We must prevent
+                                       // multiple transactions.
+                                       ( ( Session) session ).beginTransaction();
+                               }
+
+                               try {
+                                       target = session.immediateLoad( entityName, id );
+                               }
+                               finally {
+                                       // make sure the just opened temp session gets closed!
+                                       try {
+                                               if ( !isJTA ) {
+                                                       ( ( Session) session ).getTransaction().commit();
+                                               }
+                                               ( (Session) session ).close();
+                                       }
+                                       catch (Exception e) {
+                                               log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" );
+                                       }
+                               }
+                               initialized = true;
+                               checkTargetState();
+                       }
+                       catch (Exception e) {
+                               e.printStackTrace();
+                               throw new LazyInitializationException( e.getMessage() );
+                       }
+               }
+               else if ( session.isOpen() && session.isConnected() ) {
+                       target = session.immediateLoad( entityName, id );
+                       initialized = true;
+                       checkTargetState();
+               }
+               else {
+                       throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" );
+               }
+       }
+
+       protected void prepareForPossibleSpecialSpecjInitialization() {
+               if ( session != null ) {
+                       specjLazyLoad = session.getFactory().getSettings().isInitializeLazyStateOutsideTransactionsEnabled();
+
+                       if ( specjLazyLoad && sessionFactoryUuid == null ) {
+                               try {
+                                       sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent();
+                               }
+                               catch (NamingException e) {
+                                       //not much we can do if this fails...
+                               }
+                       }
+               }
+       }
+
+       private void checkTargetState() {
+               if ( !unwrap ) {
+                       if ( target == null ) {
+                               getSession().getFactory().getEntityNotFoundDelegate().handleEntityNotFound( entityName, id );
+                       }
+               }
+       }
+
+       /**
+        * Getter for property 'connectedToSession'.
+        *
+        * @return Value for property 'connectedToSession'.
+        */
+       protected final boolean isConnectedToSession() {
+               return getProxyOrNull() != null;
+       }
+
+       private Object getProxyOrNull() {
+               final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
+               if ( entityKey != null && session != null && session.isOpen() ) {
+                       return session.getPersistenceContext().getProxy( entityKey );
+               }
+               return null;
+       }
+
+       @Override
+       public final Object getImplementation() {
+               initialize();
+               return target;
+       }
+
+       @Override
+       public final void setImplementation(Object target) {
+               this.target = target;
+               initialized = true;
+       }
+
+       @Override
+       public final Object getImplementation(SessionImplementor s) throws HibernateException {
+               final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() );
+               return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ));
+       }
+
+       /**
+        * Getter for property 'target'.
+        * <p/>
+        * Same as {@link #getImplementation()} except that this method will not force initialization.
+        *
+        * @return Value for property 'target'.
+        */
+       protected final Object getTarget() {
+               return target;
+       }
+
+       @Override
+       public final boolean isReadOnlySettingAvailable() {
+               return (session != null && !session.isClosed());
+       }
+
+       private void errorIfReadOnlySettingNotAvailable() {
+               if ( session == null ) {
+                       throw new TransientObjectException(
+                                       "Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
+                       );
+               }
+               if ( session.isClosed() ) {
+                       throw new SessionException(
+                                       "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session."
+                       );
+               }
+       }
+
+       @Override
+       public final boolean isReadOnly() {
+               errorIfReadOnlySettingNotAvailable();
+               return readOnly;
+       }
+
+       @Override
+       public final void setReadOnly(boolean readOnly) {
+               errorIfReadOnlySettingNotAvailable();
+               // only update if readOnly is different from current setting
+               if ( this.readOnly != readOnly ) {
+                       final EntityPersister persister = session.getFactory().getEntityPersister( entityName );
+                       if ( !persister.isMutable() && !readOnly ) {
+                               throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" );
+                       }
+                       this.readOnly = readOnly;
+                       if ( initialized ) {
+                               EntityKey key = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() );
+                               if ( key != null && session.getPersistenceContext().containsEntity( key ) ) {
+                                       session.getPersistenceContext().setReadOnly( target, readOnly );
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Get the read-only/modifiable setting that should be put in affect when it is
+        * attached to a session.
+        * <p/>
+        * This method should only be called during serialization when read-only/modifiable setting
+        * is not available (i.e., isReadOnlySettingAvailable() == false)
+        *
+        * @return null, if the default setting should be used;
+        *         true, for read-only;
+        *         false, for modifiable
+        *
+        * @throws IllegalStateException if isReadOnlySettingAvailable() == true
+        */
+       protected final Boolean isReadOnlyBeforeAttachedToSession() {
+               if ( isReadOnlySettingAvailable() ) {
+                       throw new IllegalStateException(
+                                       "Cannot call isReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
+                       );
+               }
+               return readOnlyBeforeAttachedToSession;
+       }
+
+       /**
+        * Set the read-only/modifiable setting that should be put in affect when it is
+        * attached to a session.
+        * <p/>
+        * This method should only be called during deserialization, before associating
+        * the proxy with a session.
+        *
+        * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when
+        * associated with a session; null indicates that the default should be used.
+        *
+        * @throws IllegalStateException if isReadOnlySettingAvailable() == true
+        */
+       /* package-private */
+       final void setReadOnlyBeforeAttachedToSession(Boolean readOnlyBeforeAttachedToSession) {
+               if ( isReadOnlySettingAvailable() ) {
+                       throw new IllegalStateException(
+                                       "Cannot call setReadOnlyBeforeAttachedToSession when isReadOnlySettingAvailable == true"
+                       );
+               }
+               this.readOnlyBeforeAttachedToSession = readOnlyBeforeAttachedToSession;
+       }
+
+       @Override
+       public boolean isUnwrap() {
+               return unwrap;
+       }
+
+       @Override
+       public void setUnwrap(boolean unwrap) {
+               this.unwrap = unwrap;
+       }
+}
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/httpInvokerServiceClients.xml b/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/httpInvokerServiceClients.xml
new file mode 100644 (file)
index 0000000..3c548e3
--- /dev/null
@@ -0,0 +1,430 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns="http://www.springframework.org/schema/beans"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+       xmlns:context="http://www.springframework.org/schema/context"\r
+       xmlns:tx="http://www.springframework.org/schema/tx"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd\r
+    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd\r
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">\r
+\r
+   <bean id="agentService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/agent.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IAgentService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="annotationService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/annotation.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IAnnotationService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="auditeventService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/auditevent.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IAuditEventService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="classificationService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/classification.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IClassificationService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="collectionService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/collection.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.ICollectionService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="commonService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/common.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.ICommonService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="descriptionService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/description.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IDescriptionService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="featureNodeService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/featurenode.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IFeatureNodeService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="featureTreeService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/featuretree.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IFeatureTreeService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="groupService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/group.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IGroupService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="identificationKeyService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/identificationkey.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IIdentificationKeyService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="locationService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/location.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.ILocationService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="markerService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/marker.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IMarkerService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="mediaService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/media.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IMediaService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="nameService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/name.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.INameService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="occurrenceService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/occurrence.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IOccurrenceService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="polytomousKeyNodeService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/polytomouskeynode.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IPolytomousKeyNodeService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="polytomousKeyService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/polytomouskey.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IPolytomousKeyService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="referenceService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/reference.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IReferenceService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+       \r
+<!-- \r
+   <bean id="serviceService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/service.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IService</value>\r
+               </property>\r
+       </bean>\r
+ -->\r
\r
+   <bean id="taxonNodeService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/taxonnode.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.ITaxonNodeService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="taxonService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/taxon.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.ITaxonService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="termService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/term.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.ITermService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="userService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/user.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IUserService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="vocabularyService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/vocabulary.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IVocabularyService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+   <bean id="workingSetService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/workingset.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IWorkingSetService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+       \r
+   <bean id="grantedAuthorityService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/grantedauthority.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IGrantedAuthorityService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+       \r
+   <bean id="databaseService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/database.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.IDatabaseService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+       \r
+   <bean id="lsidAuthorityService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/lsidauthoruty.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.lsid.LSIDAuthorityService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+       \r
+   <bean id="lsidMetadataService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/lsidmetadata.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.lsid.LSIDMetadataService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+       \r
+   <bean id="lsiDataService"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/lsiddata.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>eu.etaxonomy.cdm.api.service.lsid.LSIDDataService</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+       <bean id="providerManager"\r
+               class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">\r
+               <property name="serviceUrl">\r
+                       <value>http://${serverName}/${contextPath}/authenticationManager.service</value>\r
+               </property>\r
+               <property name="serviceInterface">\r
+                       <value>org.springframework.security.authentication.AuthenticationManager</value>\r
+               </property>\r
+               <property name="httpInvokerRequestExecutor">\r
+                       <bean class="org.springframework.security.remoting.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor" />\r
+               </property>\r
+       </bean>\r
+\r
+       <bean id="propertyConfigurer"\r
+               class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">\r
+               <property name="location" value="/eu/etaxonomy/cdm/config.properties" />\r
+       </bean>\r
+\r
+</beans>\r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remotingApplicationContext.xml b/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remotingApplicationContext.xml
new file mode 100644 (file)
index 0000000..9e3d1ec
--- /dev/null
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns="http://www.springframework.org/schema/beans"\r
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+       xmlns:context="http://www.springframework.org/schema/context"\r
+       xmlns:tx="http://www.springframework.org/schema/tx"\r
+       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
+    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">\r
+       \r
+       <!-- Remoting Aspect -->\r
+       <!--context:spring-configured />\r
+    <bean id="cdmlazyLoader" class="eu.etaxonomy.cdm.api.lazyloading.CdmLazyLoader"\r
+      factory-method="aspectOf" /--> \r
+\r
+        <import resource="classpath:/eu/etaxonomy/cdm/remoting_services_security.xml"/> \r
+\r
+       <import resource="classpath:/eu/etaxonomy/cdm/httpInvokerServiceClients.xml"/>\r
\r
+       <context:component-scan base-package="eu/etaxonomy/cdm/api/application">\r
+               <context:exclude-filter type="regex" expression="eu\.etaxonomy\.cdm\.api\.application\.CdmApplicationController"/>\r
+               <context:exclude-filter type="regex" expression="eu\.etaxonomy\.cdm\.api\.application\.CdmApplicationDefaultConfiguration"/>\r
+               <context:exclude-filter type="regex" expression="eu\.etaxonomy\.cdm\.remote\.api\.application\.CdmApplicationRemoteController"/>\r
+               <context:exclude-filter type="regex" expression="eu\.etaxonomy\.cdm\.remote\.api\.application\.CdmApplicationRemoteDefaultConfiguration"/>\r
+       </context:component-scan>\r
+       <!--bean id="persistentTermInitializer" class="eu.etaxonomy.cdm.database.PersistentTermInitializer">\r
+       <property name="omit" value="false" />\r
+    </bean-->\r
+       <!--bean id="remoteTermInitializer" class="eu.etaxonomy.cdm.remote.service.RemoteTermInitializer">\r
+       <property name="omit" value="false" />\r
+       </bean-->\r
+\r
+       <!-- EditGeoService was moved to ext. Therefore it will not be found by the default component scan.
+       We added it here because the Editor needs it. However, this is only a temporary solution.
+       In the future we want to pass in an application context with the editor. -->\r
+\r
+       <!-- <bean id="conversationHolder" class="eu.etaxonomy.cdm.api.conversation.ConversationHolder" scope="prototype"/> -->\r
+\r
+       <!-- TODO move to io -->\r
+<!-- \r
+       <context:component-scan base-package="eu/etaxonomy/cdm/io">\r
+               <context:exclude-filter type="regex" expression="eu\.etaxonomy\.cdm\.io\.berlinModel.*" />\r
+       </context:component-scan>\r
+ -->\r
+       <!-- enable the configuration of transactional behavior based on annotations -->\r
+       <!-- <tx:annotation-driven transaction-manager="transactionManager"/> -->\r
+\r
+</beans>\r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_persistence_security.xml b/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_persistence_security.xml
new file mode 100644 (file)
index 0000000..46916f5
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns="http://www.springframework.org/schema/beans"\r
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"\r
+  xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"\r
+  xsi:schemaLocation="http://www.springframework.org/schema/beans\r
+    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\r
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\r
+    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd\r
+    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd\r
+    ">\r
+\r
+\r
+    <!--\r
+      ============================== SECURITY ==============================\r
+    -->\r
+    <bean id="accessDecisionManager" class="eu.etaxonomy.cdm.persistence.hibernate.permission.UnanimousBasedUnrevokable">\r
+        <property name="decisionVoters">\r
+            <list>\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.GrantAlwaysVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.TaxonNodeVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.TaxonBaseVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.DescriptionBaseVoter" />\r
+                <bean class="eu.etaxonomy.cdm.persistence.hibernate.permission.voter.DescriptionElementVoter" />\r
+            </list>\r
+        </property>\r
+    </bean>\r
+\r
+    <!--\r
+        CdmPermissionEvaluator.hasPermissions() evaluates the CdmPermissions like TAXONNODE.UPDATE{20c8f083-5870-4cbd-bf56-c5b2b98ab6a7}\r
+    -->\r
+    <bean id="cdmPermissionEvaluator" class="eu.etaxonomy.cdm.persistence.hibernate.permission.CdmPermissionEvaluator">\r
+        <property name="accessDecisionManager" ref="accessDecisionManager" />\r
+    </bean>\r
+\r
+    <!-- The CdmSecurityHibernateInterceptor checks onSave() and on flushDirty() if the currently authenticated principal or token  has\r
+    sufficient permissions on the entity to be persisted -->\r
+    <bean id="securityHibernateInterceptor" class="eu.etaxonomy.cdm.persistence.hibernate.CdmSecurityHibernateInterceptor">\r
+        <property name="permissionEvaluator" ref="cdmPermissionEvaluator" />\r
+    </bean>\r
+\r
+</beans>\r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_services_security.xml b/eu.etaxonomy.taxeditor.cdmlib/src/main/resources/eu/etaxonomy/cdm/remoting_services_security.xml
new file mode 100644 (file)
index 0000000..fcadad4
--- /dev/null
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+<beans xmlns="http://www.springframework.org/schema/beans"\r
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\r
+  xmlns:context="http://www.springframework.org/schema/context"\r
+  xmlns:security="http://www.springframework.org/schema/security"\r
+  xsi:schemaLocation="\r
+    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd\r
+    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd\r
+    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd\r
+    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"\r
+    >\r
+\r
+    <import resource="classpath:/eu/etaxonomy/cdm/remoting_persistence_security.xml"/>\r
+    <!--\r
+        ======================================================================\r
+          security specific configuration\r
+        ======================================================================\r
+     -->\r
+    <security:global-method-security pre-post-annotations="enabled" run-as-manager-ref="runAsManager" >\r
+        <security:expression-handler ref="expressionHandler" />\r
+    </security:global-method-security>\r
+\r
+    <!--\r
+        To use "hasPermission()" in the Spring EL method annotations like @PreAuthorize we explicitly configure the permissionEvaluator\r
+        the cdmPermissionEvaluator is already defined in the persistence security context\r
+    -->\r
+    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">\r
+        <property name="permissionEvaluator" ref="cdmPermissionEvaluator" />\r
+    </bean>\r
+\r
+    <bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">\r
+        <property name="providers">\r
+            <list>\r
+                <ref local="daoAuthenticationProvider"/>\r
+            </list>\r
+        </property>\r
+    </bean>\r
+\r
+    <bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">\r
+        <property name="userDetailsService" ref="userService"/>\r
+        <property name="saltSource" ref="saltSource"/>\r
+        <property name="passwordEncoder" ref="passwordEncoder"/>\r
+    </bean>\r
+\r
+    <bean id="passwordEncoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>\r
+\r
+    <bean id="saltSource" class="org.springframework.security.authentication.dao.ReflectionSaltSource">\r
+        <property name="userPropertyToUse" value="getUsername"/>\r
+    </bean>\r
+\r
+    <!--\r
+        Run-As Authentication Replacement for system operations\r
+        as e.g. performed by the eu.etaxonomy.cdm.api.application.FirstDataInserter\r
+\r
+        the key must match FirstDataInserter.RUN_AS_KEY\r
+     -->\r
+    <bean id="runAsManager"\r
+        class="org.springframework.security.access.intercept.RunAsManagerImpl">\r
+      <property name="key" value="TtlCx3pgKC4l"/>\r
+    </bean>\r
+\r
+\r
+</beans>\r
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/CdmApplicationRemoteControllerTest.java b/eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/CdmApplicationRemoteControllerTest.java
new file mode 100644 (file)
index 0000000..ccb5dc7
--- /dev/null
@@ -0,0 +1,14 @@
+package eu.etaxonomy.taxeditor.remoting;
+
+import org.junit.Test;
+
+import eu.etaxonomy.cdm.remote.api.application.CdmApplicationRemoteController;
+
+public class CdmApplicationRemoteControllerTest {
+       
+       @Test
+       public void InitializeTest() {
+               CdmApplicationRemoteController carc = CdmApplicationRemoteController.NewInstance();
+       }
+
+}
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/RemoteLazyLoadingTest.java b/eu.etaxonomy.taxeditor.cdmlib/src/test/java/eu/etaxonomy/taxeditor/remoting/RemoteLazyLoadingTest.java
new file mode 100644 (file)
index 0000000..23b4315
--- /dev/null
@@ -0,0 +1,215 @@
+/**
+* Copyright (C) 2009 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.taxeditor.remoting;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.UUID;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.unitils.UnitilsJUnit4;
+import org.unitils.database.annotations.Transactional;
+import org.unitils.database.util.TransactionMode;
+import org.unitils.spring.annotation.SpringApplicationContext;
+import org.unitils.spring.annotation.SpringBeanByType;
+
+import eu.etaxonomy.cdm.api.service.ITaxonService;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.name.NonViralName;
+import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
+import eu.etaxonomy.cdm.model.taxon.Taxon;
+
+
+/**
+ * This test class is a testing ground for solving the hibernate lazy loading problem using aspects
+ *
+ * @author c.mathew
+ *
+ */
+@SpringApplicationContext("classpath:/eu/etaxonomy/cdm/remotingApplicationContext.xml")
+@Transactional(TransactionMode.DISABLED)
+public class RemoteLazyLoadingTest extends UnitilsJUnit4 {
+       
+       @SpringBeanByType
+       private ITaxonService taxonService;
+       
+       private UUID taxonUuid1 = UUID.fromString("8217ef77-2ab1-4318-bd67-ccd0cdef07c4");
+       private UUID taxonUuid2 = UUID.fromString("ef96fafa-7750-4141-b31b-1ad1daab3e76");
+       
+       /**
+        */
+       
+//     @Test
+//     public void testLazyLoading(){          
+//
+//             Taxon taxon = (Taxon)taxonService.find(taxonUuid1);
+//             System.out.println("Taxon title : " + taxon.getTitleCache());
+//             taxon.setTitleCache("Taxon Title Cache 1");             
+//             taxonService.merge(taxon);
+//             
+//             NonViralName nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class);
+//
+//             
+//             System.out.println("name : " + nvn.getTitleCache());
+//             nvn.setTitleCache("Taxon Name Title Cache 1");
+//             taxonService.merge(taxon);
+//             
+//             Reference ref = taxon.getSec();
+//             System.out.println("Secundum : " + ref.getTitleCache());
+//             
+//             Rank rank = nvn.getRank();
+//             System.out.println("rank : " + rank.getTitleCache());
+//             
+//             Set<SynonymRelationship> synRelations = taxon.getSynonymRelations();
+//             Iterator<SynonymRelationship> srItr = synRelations.iterator();
+//             while(srItr.hasNext()) {
+//                     SynonymRelationship sr = srItr.next();
+//                     System.out.println("Synonym Relationship : " + sr.getType().getTitleCache());
+//                     sr.getType().setTitleCache(sr.getType().getTitleCache() + "*");
+//             }
+//             taxonService.merge(taxon);
+//             
+//             Set<TaxonRelationship> taxonRelationsFrom = taxon.getRelationsFromThisTaxon();
+//             Iterator<TaxonRelationship> trItrFrom = taxonRelationsFrom.iterator(); 
+//             while(trItrFrom.hasNext()) {
+//                     TaxonRelationship tr = trItrFrom.next();
+//                     System.out.println("Taxon From Relationship : " + tr.getType().getTitleCache());
+//                     tr.getType().setTitleCache(tr.getType().getTitleCache() + "*");
+//             }
+//             taxonService.merge(taxon);
+//             Set<TaxonRelationship> taxonRelationsTo = taxon.getRelationsToThisTaxon();
+//             Iterator<TaxonRelationship> trItrTo = taxonRelationsTo.iterator(); 
+//             while(trItrTo.hasNext()) {
+//                     TaxonRelationship tr = trItrTo.next();
+//                     System.out.println("Taxon To Relationship : " + tr.getType().getTitleCache());
+//                     tr.getType().setTitleCache(tr.getType().getTitleCache() + "*");
+//             }
+//             taxonService.merge(taxon);
+//     }
+       
+       @Test
+       public void testCDMEntityGet() {
+               Taxon taxon = (Taxon)taxonService.find(taxonUuid1);
+               System.out.println("Taxon title : " + taxon.getTitleCache());
+               
+               NonViralName nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class); 
+               System.out.println("name : " + nvn.getTitleCache());
+               
+       }
+       
+       @Test
+       public void testCDMCollectionGet() {
+               Taxon taxon = (Taxon)taxonService.find(taxonUuid1);
+               System.out.println("Taxon title : " + taxon.getTitleCache());
+               
+               Set<SynonymRelationship> synRelations = taxon.getSynonymRelations();
+               Iterator<SynonymRelationship> srItr = synRelations.iterator();
+               while(srItr.hasNext()) {
+                       SynonymRelationship sr = srItr.next();
+                       System.out.println("Synonym Relationship : " + sr.getType().getTitleCache());                   
+               }               
+       }
+       
+       @Test
+       public void testCDMEntitySaveEager() {
+               Taxon taxon = (Taxon)taxonService.find(taxonUuid1);
+               String oldTitleCache = taxon.getTitleCache();
+               
+               System.out.println("Taxon title : " + oldTitleCache);
+
+               taxon.setTitleCache(oldTitleCache + ":updated");
+               taxonService.merge(taxon);
+               
+               Taxon taxonNew = (Taxon)taxonService.find(taxonUuid1);
+               System.out.println("New Taxon Title : " + taxonNew.getTitleCache());
+               
+               Assert.assertNotEquals("Title caches should not be equal",oldTitleCache,taxonNew.getTitleCache());
+               
+               taxonNew.setTitleCache(oldTitleCache);
+               taxonService.merge(taxonNew);
+               
+               Taxon taxonOld = (Taxon)taxonService.find(taxonUuid1);
+               System.out.println("Old Taxon Title : " + taxonOld.getTitleCache());
+               
+               Assert.assertEquals("Title caches should be equal",oldTitleCache,taxonOld.getTitleCache());
+                       
+       }
+       
+       @Test
+       public void testCDMEntitySaveLazy() {
+               Taxon taxon = (Taxon)taxonService.find(taxonUuid1);
+                               
+               NonViralName nvn = CdmBase.deproxy(taxon.getName(),NonViralName.class); 
+               String oldTitleCache = nvn.getTitleCache();
+               System.out.println("Taxon Name Title : " + oldTitleCache);
+               nvn.setTitleCache(oldTitleCache + ":updated");
+               taxonService.merge(taxon);
+               
+               Taxon taxonNew = (Taxon)taxonService.find(taxonUuid1);
+               NonViralName nvnNew = CdmBase.deproxy(taxon.getName(),NonViralName.class);                      
+               System.out.println("New Taxon Name Title : " + nvnNew.getTitleCache());
+               
+               Assert.assertNotEquals("Title caches should not be equal",oldTitleCache,nvnNew.getTitleCache());
+               
+               nvnNew.setTitleCache(oldTitleCache);
+               taxonService.merge(taxon);
+               
+               Taxon taxonOld = (Taxon)taxonService.find(taxonUuid1);
+               NonViralName nvnOld = CdmBase.deproxy(taxon.getName(),NonViralName.class);                      
+               System.out.println("Old Taxon Name Title : " + nvnNew.getTitleCache());
+               
+               Assert.assertEquals("Title caches should be equal",oldTitleCache,nvnOld.getTitleCache());               
+       }
+       
+       @Test
+       public void testCDMEntitySaveCollection() {
+               Taxon taxon = (Taxon)taxonService.find(taxonUuid1);
+               
+               Set<SynonymRelationship> synRelations = taxon.getSynonymRelations();
+               Set<String> relToTitles = new HashSet<String>();
+               Iterator<SynonymRelationship> srItr = synRelations.iterator();
+               while(srItr.hasNext()) {
+                       SynonymRelationship sr = srItr.next();
+                       System.out.println("Synonym Title Cache : " + sr.getSynonym().getTitleCache());
+                       relToTitles.add(sr.getSynonym().getTitleCache());
+                       sr.getSynonym().setTitleCache(sr.getSynonym().getTitleCache() + ":updated");
+                       
+               }
+               taxonService.merge(taxon);
+               
+               Taxon taxonNew = (Taxon)taxonService.find(taxonUuid1);          
+               Set<SynonymRelationship> synRelationsNew = taxonNew.getSynonymRelations();
+               
+               Iterator<SynonymRelationship> srItrNew = synRelationsNew.iterator();
+               Iterator<String> relToTitlesItr = relToTitles.iterator();
+               while(srItrNew.hasNext() && relToTitlesItr.hasNext()) {
+                       SynonymRelationship srNew = srItrNew.next();
+                       String relToTitle = relToTitlesItr.next();
+                       System.out.println("New Synonym Title Cache: " + srNew.getSynonym().getTitleCache());
+                       Assert.assertNotEquals("Synonym Title caches should not be equal", srNew.getSynonym().getTitleCache(), relToTitle);
+                       srNew.getSynonym().setTitleCache(relToTitle);
+               }
+               
+               Taxon taxonOld = (Taxon)taxonService.find(taxonUuid1);
+               
+               Set<SynonymRelationship> synRelationsOld = taxonNew.getSynonymRelations();
+               Iterator<SynonymRelationship> srItrOld = synRelationsOld.iterator();
+               relToTitlesItr = relToTitles.iterator();
+               while(srItrOld.hasNext() && relToTitlesItr.hasNext()) {
+                       SynonymRelationship srOld = srItrOld.next();
+                       String relToTitle = relToTitlesItr.next();
+                       System.out.println("New Synonym Title Cache: " + srOld.getSynonym().getTitleCache());
+                       Assert.assertEquals("Synonym Title caches should be equal", srOld.getSynonym().getTitleCache(), relToTitle);
+                       
+               }
+       }
+}