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
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
*\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
} 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
\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
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;
DefinedTermBase.setCacher(this);
CdmTransientEntityCacher.setPermanentCacher(this);
- TermServiceRequestExecutor.setDefaultCacher(this);
+ //TermServiceRequestExecutor.setDefaultCacher(this);
+ RemoteInvocationTermCacher.setDefaultCacher(this);
cacheLoader = new CacheLoader(this);
}
return true;
}
} catch (CdmSourceException e) {
+ logger.error(e.getMessage(), e);
throw new CDMServerException(e);
}
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);
- }
}
--- /dev/null
+/**
+* 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);
+
+}
--- /dev/null
+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]);
+ }
+
+}
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;
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]);
- }
-
}
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) {
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) {
}
}
-
@Override
public <T extends CdmBase> void update() {
if(tlActiveSession.get() != null) {
}
}
- /* (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) {
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) {
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) {
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) {
notifyObservers();
}
- /* (non-Javadoc)
- * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#addSessionListener()
- */
@Override
public void addSessionObserver(ICdmEntitySessionManagerObserver sessionObserver) {
sessionObservers.add(sessionObserver);
}
}
- /* (non-Javadoc)
- * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionManager#isRemoting()
- */
@Override
public boolean isRemoting() {
// FIXME:Remoting stupid method to check whether we are in remoting
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);
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);
--- /dev/null
+/**
+* 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;
+ }
+
+}