ref #8842 replacing caching in CdmServiceRequestExecutor by CachingHttpInvokerProxyFa...
authorAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Wed, 5 Feb 2020 10:25:46 +0000 (11:25 +0100)
committerAndreas Kohlbecker <a.kohlbecker@bgbm.org>
Wed, 5 Feb 2020 11:02:12 +0000 (12:02 +0100)
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/api/application/CdmApplicationRemoteConfiguration.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/cdm/api/cache/CdmServiceCacher.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/remoting/source/CdmServerInfo.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/CdmServiceRequestExecutor.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/IRemoteInvocationTermCacher.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/RemoteInvocationTermCacher.java [new file with mode: 0644]
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/TermServiceRequestExecutor.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/session/CdmEntitySessionManager.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/session/ICdmEntitySession.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/session/ICdmEntitySessionManager.java
eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/springframework/remoting/httpinvoker/CachingHttpInvokerProxyFactoryBean.java [new file with mode: 0644]

index 7c0765ba761aa4716a547c50695069fab0f9e3be..bf6a876fa12a9dbd5743a0e1695a24bea4abe958 100644 (file)
@@ -19,8 +19,8 @@ import org.apache.log4j.Logger;
 import org.springframework.beans.BeansException;\r
 import org.springframework.context.ApplicationContext;\r
 import org.springframework.context.ApplicationContextAware;\r
+import org.springframework.remoting.httpinvoker.CachingHttpInvokerProxyFactoryBean;\r
 import org.springframework.remoting.httpinvoker.HttpComponentsHttpInvokerRequestExecutor;\r
-import org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean;\r
 import org.springframework.security.access.AccessDecisionVoter;\r
 import org.springframework.security.authentication.AuthenticationProvider;\r
 import org.springframework.security.authentication.ProviderManager;\r
@@ -92,7 +92,7 @@ import eu.etaxonomy.taxeditor.service.CachedCommonServiceImpl;
 import eu.etaxonomy.taxeditor.service.CdmAuthenticatedHttpInvokerRequestExecutor;\r
 import eu.etaxonomy.taxeditor.service.CdmServiceRequestExecutor;\r
 import eu.etaxonomy.taxeditor.service.ICachedCommonService;\r
-import eu.etaxonomy.taxeditor.service.TermServiceRequestExecutor;\r
+import eu.etaxonomy.taxeditor.service.RemoteInvocationTermCacher;\r
 import eu.etaxonomy.taxeditor.session.CdmEntitySessionManager;\r
 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;\r
 \r
@@ -103,9 +103,11 @@ import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
  *\r
  */\r
 @Component\r
-// TODO split configurations into proper @Configuration class\r
+// TODO split into CdmRepository and proper @Configuration class\r
 public class CdmApplicationRemoteConfiguration implements ICdmRepository, ApplicationContextAware  {\r
 \r
+    private static final int HTTP_READ_TIMEOUT = 0;\r
+\r
     @SuppressWarnings("unused")\r
     private static final Logger logger = Logger.getLogger(CdmApplicationRemoteConfiguration.class);\r
 \r
@@ -155,15 +157,18 @@ public class CdmApplicationRemoteConfiguration implements ICdmRepository, Applic
         } else {\r
             baseUrl = "http://" + remoteSource.getServer()  + ":" + String.valueOf(remoteSource.getPort()) + "/" + remoteSource.getContextPath();\r
         }\r
-        HttpInvokerProxyFactoryBean proxy = new HttpInvokerProxyFactoryBean();\r
-        proxy.setServiceInterface(clazz);\r
-        proxy.setServiceUrl(baseUrl + serviceSuffix);\r
+        CachingHttpInvokerProxyFactoryBean proxyFactory = new CachingHttpInvokerProxyFactoryBean();\r
+        proxyFactory.setServiceInterface(clazz);\r
+        proxyFactory.setServiceUrl(baseUrl + serviceSuffix);\r
         if(executor != null) {\r
-            executor.setReadTimeout(0);\r
-            proxy.setHttpInvokerRequestExecutor(executor);\r
+            executor.setReadTimeout(HTTP_READ_TIMEOUT);\r
+            proxyFactory.setHttpInvokerRequestExecutor(executor);\r
+        }\r
+        if(ITermService.class.isAssignableFrom(clazz)){\r
+            proxyFactory.setRemoteInvocationTermCacher(new RemoteInvocationTermCacher());\r
         }\r
-        proxy.afterPropertiesSet();\r
-        return proxy.getObject();\r
+        proxyFactory.afterPropertiesSet();\r
+        return proxyFactory.getObject();\r
     }\r
 \r
     // ****************************** APPLICATION CONTEXT *************************************************/\r
@@ -297,7 +302,7 @@ public class CdmApplicationRemoteConfiguration implements ICdmRepository, Applic
 \r
     @Override\r
     public ITermService getTermService(){\r
-        return (ITermService) getService(ITermService.class, "/remoting/term.service", new TermServiceRequestExecutor());\r
+        return (ITermService) getService(ITermService.class, "/remoting/term.service", new CdmServiceRequestExecutor());\r
     }\r
 \r
     @Override\r
index d554e612a1b2f446d57e86a481efc98f25b7b1ac..559039dd10cacf14011aaaafbdbc856821904ef8 100644 (file)
@@ -15,7 +15,7 @@ import eu.etaxonomy.cdm.config.ConfigFileUtil;
 import eu.etaxonomy.cdm.model.common.CdmBase;
 import eu.etaxonomy.cdm.model.term.DefinedTermBase;
 import eu.etaxonomy.cdm.model.term.TermBase;
-import eu.etaxonomy.taxeditor.service.TermServiceRequestExecutor;
+import eu.etaxonomy.taxeditor.service.RemoteInvocationTermCacher;
 import eu.etaxonomy.taxeditor.session.CdmEntitySession;
 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
@@ -53,7 +53,8 @@ public class CdmServiceCacher extends CdmCacher implements ICdmEntitySessionMana
 
         DefinedTermBase.setCacher(this);
         CdmTransientEntityCacher.setPermanentCacher(this);
-        TermServiceRequestExecutor.setDefaultCacher(this);
+        //TermServiceRequestExecutor.setDefaultCacher(this);
+        RemoteInvocationTermCacher.setDefaultCacher(this);
 
         cacheLoader = new CacheLoader(this);
     }
index e3dcb7808c59efd0b2fe4dd3a039d005d2e2cba8..066f0c93dd40bced2073444e58f1b32658a398a4 100644 (file)
@@ -284,6 +284,7 @@ public class CdmServerInfo {
                 return true;
             }
         } catch (CdmSourceException e) {
+            logger.error(e.getMessage(), e);
             throw new CDMServerException(e);
         }
 
index 5335475d65e19bb07aa2b464f77417df46e42467..2ed9cf696cb17adb7879118ca942cd039910d210 100644 (file)
@@ -1,106 +1,7 @@
 package eu.etaxonomy.taxeditor.service;
 
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.http.NoHttpResponseException;
-import org.apache.log4j.Logger;
-import org.springframework.remoting.httpinvoker.HttpInvokerClientConfiguration;
-import org.springframework.remoting.support.RemoteInvocation;
-import org.springframework.remoting.support.RemoteInvocationResult;
-import org.springframework.stereotype.Component;
-
-import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteController;
-import eu.etaxonomy.cdm.api.application.CdmApplicationState;
-import eu.etaxonomy.cdm.api.service.UpdateResult;
-import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
-
-@Component
+// @Component
 public class CdmServiceRequestExecutor extends CdmAuthenticatedHttpInvokerRequestExecutor {
 
-    private static final Logger logger = Logger.getLogger(CdmServiceRequestExecutor.class);
-
-    private ICdmEntitySessionManager cdmEntitySessionManager ;
-
-       private RemoteInvocation currentRemoteInvocation;
-
-       protected final static Set<String> cachableMethods = new HashSet<String>();
-
-
-
-       public CdmServiceRequestExecutor() {
-           cachableMethods.add("merge");
-           cachableMethods.add("save");
-           cachableMethods.add("findWithUpdate");
-           cachableMethods.add("loadWithUpdate");
-       }
-
-       @Override
-       protected void writeRemoteInvocation(RemoteInvocation invocation, OutputStream os) throws IOException {
-           if(cdmEntitySessionManager == null) {
-               cdmEntitySessionManager =
-                       ((CdmApplicationRemoteController)CdmApplicationState.getCurrentAppConfig()).getCdmEntitySessionManager();
-           }
-               currentRemoteInvocation = invocation;
-               super.writeRemoteInvocation(invocation, os);
-       }
-
-       @Override
-       protected RemoteInvocationResult doExecuteRequest(HttpInvokerClientConfiguration config,
-                       java.io.ByteArrayOutputStream baos)
-                                       throws java.io.IOException,
-                                       java.lang.ClassNotFoundException, NoHttpResponseException {
-               RemoteInvocationResult rir = fromCache(currentRemoteInvocation);
-
-               if(rir == null) {
-
-                   if (!(currentRemoteInvocation.getMethodName() == null) && !(config.getServiceUrl() == null)){
-                      // logger.info("Remote invoking : " + currentRemoteInvocation.getMethodName() + "@" + config.getServiceUrl());
-                   }
-                   try{
-                       rir = super.doExecuteRequest(config, baos);
-                   }catch(Exception e){
-                       if (e instanceof NoHttpResponseException){
-                          throw e;
-                       }
-                   }
-                       if(rir.getValue() != null && !rir.hasException()) {
-                           if (currentRemoteInvocation == null){
-                               logger.debug("return RemoteInvocationResult without caching" );
-                               return rir;
-                           }
-                if(cachableMethods.contains(currentRemoteInvocation.getMethodName())) {
-                    rir = new RemoteInvocationResult(cdmEntitySessionManager.load(rir.getValue(), true));
-                } else if(rir.getValue() instanceof UpdateResult){
-                    UpdateResult result = (UpdateResult)rir.getValue();
-                    if(result.isOk()){
-                        cdmEntitySessionManager.load(result, true);
-                    }
-                } else {
-                    rir = new RemoteInvocationResult(cdmEntitySessionManager.load(rir.getValue(), false));
-                }
-                       }
-                       cache(currentRemoteInvocation, rir);
-               }
-               currentRemoteInvocation = null;
-
-               return rir;
-       }
-
-       public void cache(RemoteInvocation ri, RemoteInvocationResult rir) {
-
-       }
-
-       public RemoteInvocationResult fromCache(RemoteInvocation ri) {
-           return null;
-       }
-
-        @Override
-           public void setReadTimeout(int timeout) {
-               logger.info("Read time our set to: " + timeout + " ms");
-               super.setReadTimeout(timeout);
-           }
 
 }
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/IRemoteInvocationTermCacher.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/IRemoteInvocationTermCacher.java
new file mode 100644 (file)
index 0000000..7b4b8bd
--- /dev/null
@@ -0,0 +1,33 @@
+/**
+* Copyright (C) 2020 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.service;
+
+import org.springframework.remoting.support.RemoteInvocation;
+import org.springframework.remoting.support.RemoteInvocationResult;
+
+/**
+ * @author a.kohlbecker
+ * @since Feb 4, 2020
+ *
+ */
+public interface IRemoteInvocationTermCacher {
+
+    /**
+     * @param ri
+     * @param rir
+     */
+    void cacheTerms(RemoteInvocation ri, RemoteInvocationResult rir);
+
+    /**
+     * @param ri
+     * @return
+     */
+    RemoteInvocationResult termsFromCache(RemoteInvocation ri);
+
+}
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/RemoteInvocationTermCacher.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/eu/etaxonomy/taxeditor/service/RemoteInvocationTermCacher.java
new file mode 100644 (file)
index 0000000..f1d1561
--- /dev/null
@@ -0,0 +1,56 @@
+package eu.etaxonomy.taxeditor.service;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.springframework.remoting.support.RemoteInvocation;
+import org.springframework.remoting.support.RemoteInvocationResult;
+
+import eu.etaxonomy.cdm.api.cache.CdmServiceCacher;
+import eu.etaxonomy.cdm.model.term.DefinedTermBase;
+import eu.etaxonomy.cdm.model.term.TermType;
+
+
+public class RemoteInvocationTermCacher implements IRemoteInvocationTermCacher {
+    private static final Logger logger = Logger.getLogger(RemoteInvocationTermCacher.class);
+
+       private static Map<TermType, RemoteInvocationResult> termTypeMap = new HashMap<>();
+
+       private static CdmServiceCacher cdmServiceCacher;
+
+       public static void setDefaultCacher(CdmServiceCacher csc) {
+        cdmServiceCacher = csc;
+    }
+
+       @Override
+       public void cacheTerms(RemoteInvocation ri, RemoteInvocationResult rir) {
+           if(cdmServiceCacher != null) {
+               if(ri.getMethodName().equals("listByTermType")) {
+                   if(ri.getArguments()[1] == null) {
+                       Set<DefinedTermBase<?>> terms = new HashSet<>();
+                       if(rir.getValue() != null) {
+                           terms.addAll((List<DefinedTermBase<?>>)rir.getValue());
+
+                           for(DefinedTermBase<?> term : terms) {
+                               cdmServiceCacher.load(term);
+                           }
+                           termTypeMap.put((TermType)ri.getArguments()[0], rir);
+                       }
+
+                   }
+               }
+           } else {
+               logger.info("Default CdmServiceCacher is null. Cannot cache terms");
+           }
+       }
+
+       @Override
+       public  RemoteInvocationResult termsFromCache(RemoteInvocation ri) {
+               return termTypeMap.get(ri.getArguments()[0]);
+       }
+
+}
index 714d17d242971fe31cca793326ca2f98b86e0790..bce159d303547389b7d89c673b7a54c017da0040 100644 (file)
@@ -1,17 +1,12 @@
 package eu.etaxonomy.taxeditor.service;
 
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import org.apache.log4j.Logger;
-import org.springframework.remoting.support.RemoteInvocation;
 import org.springframework.remoting.support.RemoteInvocationResult;
 
 import eu.etaxonomy.cdm.api.cache.CdmServiceCacher;
-import eu.etaxonomy.cdm.model.term.DefinedTermBase;
 import eu.etaxonomy.cdm.model.term.TermType;
 
 
@@ -26,31 +21,4 @@ public class TermServiceRequestExecutor extends CdmServiceRequestExecutor {
         cdmServiceCacher = csc;
     }
 
-       @Override
-       public void cache(RemoteInvocation ri, RemoteInvocationResult rir) {
-           if(cdmServiceCacher != null) {
-               if(ri.getMethodName().equals("listByTermType")) {
-                   if(ri.getArguments()[1] == null) {
-                       Set<DefinedTermBase<?>> terms = new HashSet<>();
-                       if(rir.getValue() != null) {
-                           terms.addAll((List<DefinedTermBase<?>>)rir.getValue());
-
-                           for(DefinedTermBase<?> term : terms) {
-                               cdmServiceCacher.load(term);
-                           }
-                           termTypeMap.put((TermType)ri.getArguments()[0], rir);
-                       }
-
-                   }
-               }
-           } else {
-               logger.info("Default CdmServiceCacher is null. Cannot cache terms");
-           }
-       }
-
-       @Override
-       public  RemoteInvocationResult fromCache(RemoteInvocation ri) {
-               return termTypeMap.get(ri.getArguments()[0]);
-       }
-
 }
index 5494552ef9b2cbcf59e8e698097b0e7ea3392b2f..8f10670aba7e35f1862f46f7dc9f4a9962b6920b 100644 (file)
@@ -86,19 +86,11 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         notifyObservers();
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#getSessions()
-     */
     @Override
     public Collection<ICdmEntitySession> getSessions() {
         return ownerSessionMap.values();
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#bind(eu.etaxonomy.taxeditor.session.ISessionEventListener)
-     */
     @Override
     public void bind(ICdmEntitySessionEnabled sessionOwner) {
         if(sessionOwner == null) {
@@ -119,10 +111,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         return ownerSessionMap.containsKey(sessionOwner);
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#load(T)
-     */
     @Override
     public <T extends Object> T load(T obj, boolean update) {
         if(tlActiveSession.get() == null) {
@@ -132,7 +120,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         }
     }
 
-
     @Override
     public <T extends CdmBase> void update() {
         if(tlActiveSession.get() != null) {
@@ -140,9 +127,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         }
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#load(T)
-     */
     @Override
     public <T extends CdmBase> T load(T cdmBase, boolean update) {
         if(tlActiveSession.get() == null) {
@@ -151,10 +135,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         return tlActiveSession.get().load(cdmBase, update);
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#load(eu.etaxonomy.cdm.api.service.UpdateResult, boolean)
-     */
     @Override
     public UpdateResult load(UpdateResult updateResult, boolean update) {
         if(tlActiveSession.get() == null) {
@@ -163,10 +143,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         return tlActiveSession.get().load(updateResult, update);
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#load(eu.etaxonomy.cdm.persistence.dto.MergeResult, boolean)
-     */
     @Override
     public MergeResult load(MergeResult mergeResult, boolean update) {
         if(tlActiveSession.get() == null) {
@@ -175,10 +151,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         return tlActiveSession.get().load(mergeResult, update);
     }
 
-
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#load(java.util.Collection)
-     */
     @Override
     public <T extends CdmBase> Collection<T> load(Collection<T> cdmBaseList, boolean update) {
         if(tlActiveSession.get() == null) {
@@ -236,9 +208,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         notifyObservers();
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#addSessionListener()
-     */
     @Override
     public void addSessionObserver(ICdmEntitySessionManagerObserver sessionObserver) {
         sessionObservers.add(sessionObserver);
@@ -250,9 +219,6 @@ public class CdmEntitySessionManager implements ICdmEntitySessionManager {
         }
     }
 
-    /* (non-Javadoc)
-     * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#isRemoting()
-     */
     @Override
     public boolean isRemoting() {
         // FIXME:Remoting stupid method to check whether we are in remoting
index 92d4d5cd0601897e5b9da076264479bfca9c1f9a..d424a557b6000b10d12b2766c8fd4415214cac88 100644 (file)
@@ -19,6 +19,9 @@ public interface ICdmEntitySession {
 
        public  <O extends Object> O load(O obj, boolean update);
 
+       /**
+        * @see eu.etaxonomy.cdm.cache.CdmTransientEntityCacher#load(Collection, boolean)
+        */
        public  <T extends CdmBase> T load(T cdmBase, boolean update);
 
        public  UpdateResult load(UpdateResult updateResult, boolean update);
index 2b890d239f155eb60a63f3db28a03603ba2f8eef..a865a53791b3c228781ca5f4d4ef9e4cce126db2 100644 (file)
@@ -20,9 +20,14 @@ public interface ICdmEntitySessionManager {
        public abstract void bind(ICdmEntitySessionEnabled sessionOwner);
 
 
+       /**
+     * @see eu.etaxonomy.cdm.cache.CdmTransientEntityCacher#load(Collection, boolean)
+     */
        public abstract <T extends Object> T load(T obj, boolean update);
 
-
+    /**
+     * @see eu.etaxonomy.cdm.cache.CdmTransientEntityCacher#load(Collection, boolean)
+     */
        public abstract <T extends CdmBase> T load(T cdmBase, boolean update);
 
 
diff --git a/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/springframework/remoting/httpinvoker/CachingHttpInvokerProxyFactoryBean.java b/eu.etaxonomy.taxeditor.cdmlib/src/main/java/org/springframework/remoting/httpinvoker/CachingHttpInvokerProxyFactoryBean.java
new file mode 100644 (file)
index 0000000..3f49d3d
--- /dev/null
@@ -0,0 +1,183 @@
+/**
+* Copyright (C) 2020 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 org.springframework.remoting.httpinvoker;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.aopalliance.intercept.MethodInvocation;
+import org.apache.log4j.Logger;
+import org.springframework.remoting.support.RemoteInvocation;
+import org.springframework.remoting.support.RemoteInvocationResult;
+
+import eu.etaxonomy.cdm.api.application.CdmApplicationRemoteController;
+import eu.etaxonomy.cdm.api.application.CdmApplicationState;
+import eu.etaxonomy.cdm.api.service.ITermService;
+import eu.etaxonomy.cdm.api.service.UpdateResult;
+import eu.etaxonomy.taxeditor.service.IRemoteInvocationTermCacher;
+import eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager;
+
+/**
+ * Extension of the <code>HttpInvokerProxyFactoryBean</code> to support caching of general CDM entities and terms.
+ * <p>
+ * <b>Performance measurement:</b></br>
+ * Supports measuring the request processing time at the client side. Setting "{@value #PROP_KEY_MEASURE_DURATION}"
+ * as system parameter enables the measurement. To make the measurements appear in the log, the log <code>Level</code>
+ * for this class needs to be set to at least {@link org.apache.log4j.Level#INFO}
+ *
+ *
+ * @author a.kohlbecker
+ * @since Feb 4, 2020
+ *
+ */
+public class CachingHttpInvokerProxyFactoryBean extends HttpInvokerProxyFactoryBean {
+
+    private static final String PROP_KEY_MEASURE_DURATION = "remoting.httpinvoker.measureDuration";
+
+    private static final Logger logger = Logger.getLogger(CachingHttpInvokerProxyFactoryBean.class);
+
+    private ICdmEntitySessionManager cdmEntitySessionManager;
+
+    private IRemoteInvocationTermCacher remoteInvocationTermCacher;
+
+    protected final static Set<String> persistingMethods = new HashSet<String>();
+
+    protected static boolean measureDuration = false;
+
+    static {
+        persistingMethods.add("merge");
+        persistingMethods.add("save");
+        persistingMethods.add("findWithUpdate");
+        persistingMethods.add("loadWithUpdate");
+        measureDuration = System.getProperty(PROP_KEY_MEASURE_DURATION) != null;
+    }
+
+    @Override
+    protected RemoteInvocationResult executeRequest(
+            RemoteInvocation invocation, MethodInvocation originalInvocation) throws Exception {
+
+        // logger.setLevel(Level.INFO);
+
+        if (!(invocation.getMethodName() == null) && !(getServiceUrl() == null) && logger.isDebugEnabled()){
+            logger.debug("Remote invoking : " + getServiceUrl() + "#" + invocation.getMethodName());
+         }
+
+        if(CdmApplicationState.getCurrentAppConfig() == null){
+            logger.debug("No application context yet, no point caching!");
+            return doExecuteRequest(invocation, originalInvocation);
+        }
+
+        // A)  if this is a term service call for term lists try to get the terms from the cache
+        if(ITermService.class.isAssignableFrom(originalInvocation.getMethod().getDeclaringClass())){
+            return handleTermRequest(invocation, originalInvocation);
+        }
+        // B) handle other service calls
+        else  {
+            return handleGeneralRequest(invocation, originalInvocation);
+        }
+    }
+
+    /**
+     * @param invocation
+     * @param originalInvocation
+     * @return
+     * @throws Exception
+     */
+    private RemoteInvocationResult handleGeneralRequest(RemoteInvocation invocation,
+            MethodInvocation originalInvocation) throws Exception {
+        RemoteInvocationResult invocationResult = doExecuteRequest(invocation, originalInvocation);
+        if(invocationResult.getValue() != null && !invocationResult.hasException()) {
+
+            if(persistingMethods.contains(invocation.getMethodName())) {
+                invocationResult = new RemoteInvocationResult(cdmEntitySessionManager().load(invocationResult.getValue(), true));
+                logger.debug("Entity cached with updating cached data");
+            } else if(invocationResult.getValue() instanceof UpdateResult){
+                UpdateResult result = (UpdateResult)invocationResult.getValue();
+                if(result.isOk()){
+                    logger.debug("Entity from UpdateResult stored in cache with updating cached data" );
+                    cdmEntitySessionManager().load(result.getCdmEntity(), true);
+                }
+            } else {
+                invocationResult = new RemoteInvocationResult(cdmEntitySessionManager().load(invocationResult.getValue(), false));
+                logger.debug("Entity cached without updating cached data" );
+            }
+
+        }
+        return  invocationResult;
+    }
+
+    /**
+     * @param invocation
+     * @param originalInvocation
+     * @return
+     * @throws Exception
+     */
+    private RemoteInvocationResult handleTermRequest(RemoteInvocation invocation, MethodInvocation originalInvocation)
+            throws Exception {
+        RemoteInvocationResult invocationResult = null;
+        if(remoteInvocationTermCacher != null){
+            invocationResult = remoteInvocationTermCacher.termsFromCache(invocation);
+            if(invocationResult == null) {
+                invocationResult = doExecuteRequest(invocation, originalInvocation);
+                remoteInvocationTermCacher.cacheTerms(invocation, invocationResult);
+                logger.debug("Term list loaded and cached");
+            } else {
+                logger.debug("Term list found in cache, not loaded");
+            }
+        } else {
+            logger.warn("Term list: No IRemoteInvocationTermCacher configured");
+            invocationResult = doExecuteRequest(invocation, originalInvocation);
+        }
+        return invocationResult;
+    }
+
+    /**
+     * @param invocation
+     * @param originalInvocation
+     * @return
+     * @throws Exception
+     */
+    private RemoteInvocationResult doExecuteRequest(RemoteInvocation invocation, MethodInvocation originalInvocation)
+            throws Exception {
+
+        double startTime = 0;
+        if(measureDuration){
+            startTime = System.currentTimeMillis();
+        }
+        RemoteInvocationResult result = super.executeRequest(invocation, originalInvocation);
+        if(measureDuration){
+            double duration = System.currentTimeMillis() - startTime;
+            logger.info(getServiceUrl() + "#" + invocation.getMethodName() + " [" + duration + " ms]");
+        }
+        return result;
+    }
+
+    /**
+     * @return the remoteInvocationTermCacher
+     */
+    public IRemoteInvocationTermCacher getRemoteInvocationTermCacher() {
+        return remoteInvocationTermCacher;
+    }
+
+    /**
+     * @param remoteInvocationTermCacher the remoteInvocationTermCacher to set
+     */
+    public void setRemoteInvocationTermCacher(IRemoteInvocationTermCacher remoteInvocationTermCacher) {
+        this.remoteInvocationTermCacher = remoteInvocationTermCacher;
+    }
+
+    private ICdmEntitySessionManager cdmEntitySessionManager(){
+        if(cdmEntitySessionManager == null) {
+            cdmEntitySessionManager =
+                    ((CdmApplicationRemoteController)CdmApplicationState.getCurrentAppConfig()).getCdmEntitySessionManager();
+        }
+        return cdmEntitySessionManager;
+    }
+
+}