From 7abf8eac7ad6e753c31b833f2b4e3e0f8340b1bb Mon Sep 17 00:00:00 2001 From: Alfredo Oliviero Date: Mon, 3 Jun 2024 17:18:38 +0200 Subject: [PATCH] cache per le chiamate IS --- .../cloudcomputing/CC_MethodsListPortlet.java | 22 ++++ .../CC_MethodsMonitorPortlet.java | 1 - .../user/cloudcomputing/CC_Portlet.java | 11 +- .../user/cloudcomputing/is/CacheIsClient.java | 103 ++++++++++++++++ .../user/cloudcomputing/is/CacheIsClient.txt | 114 ++++++++++++++++++ .../user/cloudcomputing/is/IsClient.java | 28 +++-- .../cloudcomputing/is/IsClientFactory.java | 32 +++++ 7 files changed, 298 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsListPortlet.java create mode 100644 src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.java create mode 100644 src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.txt create mode 100644 src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClientFactory.java diff --git a/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsListPortlet.java b/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsListPortlet.java new file mode 100644 index 0000000..4df1fb3 --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsListPortlet.java @@ -0,0 +1,22 @@ + +package org.gcube.portlets.user.cloudcomputing; + +import java.io.IOException; + +import javax.portlet.PortletException; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import com.liferay.portal.kernel.log.LogFactoryUtil; + +public class CC_MethodsListPortlet extends CC_Portlet { + + private static com.liferay.portal.kernel.log.Log _log = LogFactoryUtil + .getLog(CC_MethodsListPortlet.class); + + public void render(RenderRequest renderRequest, RenderResponse renderResponse) + throws PortletException, IOException { + super.render(renderRequest, renderResponse); + } + +} diff --git a/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsMonitorPortlet.java b/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsMonitorPortlet.java index 17edd5a..292dbdf 100644 --- a/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsMonitorPortlet.java +++ b/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_MethodsMonitorPortlet.java @@ -17,5 +17,4 @@ public class CC_MethodsMonitorPortlet extends CC_Portlet { throws PortletException, IOException { super.render(renderRequest, renderResponse); } - } diff --git a/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_Portlet.java b/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_Portlet.java index 1b1ddde..2b694c0 100644 --- a/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_Portlet.java +++ b/src/main/java/org/gcube/portlets/user/cloudcomputing/CC_Portlet.java @@ -13,6 +13,7 @@ import javax.servlet.http.HttpServletRequest; import org.gcube.common.portal.PortalContext; import org.gcube.portlets.user.cloudcomputing.config.CC_Config; import org.gcube.portlets.user.cloudcomputing.is.IsClient; +import org.gcube.portlets.user.cloudcomputing.is.IsClientFactory; import org.gcube.portlets.user.cloudcomputing.is.IsServerConfig; import com.liferay.portal.kernel.log.LogFactoryUtil; @@ -22,7 +23,7 @@ import com.liferay.util.bridges.mvc.MVCPortlet; public class CC_Portlet extends MVCPortlet { private static com.liferay.portal.kernel.log.Log _log = LogFactoryUtil.getLog(CC_Portlet.class); - private static CC_Config configuration = null; + private CC_Config configuration = null; public final static String IS_AUTH_RESOURCE_NAME = "IAM"; public final static String IS_AUTH_CATEGORY = "Service"; @@ -59,15 +60,17 @@ public class CC_Portlet extends MVCPortlet { String host = base_url.getHost(); config.gateway = host; - IsServerConfig auth_config = IsClient.serviceConfigFromIS(IS_AUTH_RESOURCE_NAME, + IsClient isclient = IsClientFactory.getSingleton(); + + IsServerConfig auth_config = isclient.serviceConfigFromIS(IS_AUTH_RESOURCE_NAME, IS_AUTH_CATEGORY, IS_AUTH_ENTRYPOINT); config.auth_url = auth_config.getServerUrl(); - IsServerConfig ccp_config = IsClient.serviceConfigFromIS(IS_CCP_RESOURCE_NAME, + IsServerConfig ccp_config = isclient.serviceConfigFromIS(IS_CCP_RESOURCE_NAME, IS_CCP_CATEGORY, IS_CCP_ENTRYPOINT); config.ccp_url = ccp_config.getServerUrl(); - IsServerConfig cdn_config = IsClient.serviceConfigFromIS(IS_CCP_RESOURCE_NAME, + IsServerConfig cdn_config = isclient.serviceConfigFromIS(IS_CCP_RESOURCE_NAME, IS_CCP_CATEGORY, IS_CDN_ENTRYPOINT); config.cdn_url = cdn_config.getServerUrl(); diff --git a/src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.java b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.java new file mode 100644 index 0000000..0112e60 --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.java @@ -0,0 +1,103 @@ +package org.gcube.portlets.user.cloudcomputing.is; + + +import java.io.Serializable; +import java.rmi.ServerException; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.gcube.common.resources.gcore.ServiceEndpoint; + +import com.liferay.portal.kernel.cache.MultiVMPoolUtil; +import com.liferay.portal.kernel.cache.PortalCache; +import com.liferay.portal.kernel.log.LogFactoryUtil; + +/** + * Utility class to query EndPoints and search for AccessPoints from IS + * + * @author Alfredo Oliviero (ISTI - CNR) + */ + +public class CacheIsClient extends IsClient { + private static com.liferay.portal.kernel.log.Log logger = LogFactoryUtil.getLog(CacheIsClient.class); + private static CacheIsClient _singleInstance; + + // Class to represent a cache entry + + static CacheIsClient getSingleton() { + if (_singleInstance == null) { + _singleInstance = new CacheIsClient(); + } + return _singleInstance; + } + + + private static class CacheEntry implements Serializable { + List endpoints; + long timestamp; + + CacheEntry(List endpoints, long timestamp) { + this.endpoints = endpoints; + this.timestamp = timestamp; + } + } + + // Define the cache name + private static final String CACHE_NAME = "my-cache"; + private PortalCache portalCache = MultiVMPoolUtil.getCache(CACHE_NAME); + + // Method to get the cache key + private String getCacheKey(String resourceName, String category) { + return resourceName + ":" + category; + } + + // Main method with caching and locking + public synchronized List getEndpointsFromISWithCache(String resource_name, String category) throws ServerException { + String key = getCacheKey(resource_name, category); + long currentTime = System.currentTimeMillis(); + + logger.info("Request received for resource: " + resource_name + ", category: " + category); + + synchronized (key.intern()) { + logger.info("Acquired lock for key: " + key); + + // Check if the entry is present in the cache and is valid + CacheEntry cacheEntry = (CacheEntry)portalCache.get(key); + if (cacheEntry != null) { + if (currentTime - cacheEntry.timestamp <= TimeUnit.MINUTES.toMillis(10)) { + // If the cache value is still valid, return it + logger.info("Cache hit for key: " + key); + return cacheEntry.endpoints; + } else { + // Otherwise, remove the expired entry + logger.info("Cache expired for key: " + key); + portalCache.remove(key); + } + } + + // If not present in the cache or is expired, make the request + logger.info("Cache miss for key: " + key + ". Making request to external service."); + List endpoints; + try { + endpoints = super.getEndpointsFromIS(resource_name, category); + } catch (ServerException e) { + logger.error("Error fetching data from external service for key: " + key, e); + throw e; + } + + // Update the cache with the new result and timestamp + logger.info("Updating cache for key: " + key); + portalCache.put(key, new CacheEntry(endpoints, currentTime)); + + return endpoints; + } + } + // Metodo principale con caching e locking + public List getEndpointsFromIS(String resource_name, String category) throws ServerException { + logger.info("@@@ cached getEndpointsFromIS: " + resource_name + ":" + category); + + return getEndpointsFromISWithCache(resource_name, category); + } + + +} diff --git a/src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.txt b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.txt new file mode 100644 index 0000000..e1e6a60 --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/CacheIsClient.txt @@ -0,0 +1,114 @@ +package org.gcube.portlets.user.cloudcomputing.is; + +import java.rmi.ServerException; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import org.gcube.common.resources.gcore.ServiceEndpoint; + +import com.liferay.portal.kernel.log.LogFactoryUtil; + +/** + * Utility class to query EndPoints and search for AccessPoints from IS + * + * @author Alfredo Oliviero (ISTI - CNR) + */ + +public class CacheIsClient extends IsClient { + private static com.liferay.portal.kernel.log.Log logger = LogFactoryUtil.getLog(CacheIsClient.class); + + private static CacheIsClient _singleInstance = null; + + public int CACHE_DURATION_SECONDS = 1 * 10; + + static CacheIsClient getSingleton() { + if (_singleInstance == null) { + _singleInstance = new CacheIsClient(); + } + return _singleInstance; + } + + private static class CacheEntry { + List endpoints; + long timestamp; + + CacheEntry(List endpoints, long timestamp) { + this.endpoints = endpoints; + this.timestamp = timestamp; + } + } + + // Mappa per mantenere la cache + private ConcurrentHashMap cache = new ConcurrentHashMap<>(); + // Mappa per mantenere i lock + private ConcurrentHashMap locks = new ConcurrentHashMap<>(); + + // Metodo per ottenere la chiave della cache + private String getCacheKey(String resourceName, String category) { + return resourceName + ":" + category; + } + + // Metodo principale con caching e locking + public List getEndpointsFromIS(String resource_name, String category) throws ServerException { + return getEndpointsFromISWithCache(resource_name, category); + } + + + // Main method with caching and locking + public List getEndpointsFromISWithCache(String resource_name, String category) throws ServerException { + String key = getCacheKey(resource_name, category); + long currentTime = System.currentTimeMillis(); + + logger.info("Request received for resource: " + resource_name + ", category: " + category); + + // Get or create a lock for the specific key + Lock lock = locks.computeIfAbsent(key, k -> new ReentrantLock()); + logger.info("Lock object created/retrieved for key: " + key); + + // Acquire the lock + logger.info("### Acquiring lock for key: " + key); + lock.lock(); + try { + logger.info("### Lock acquired for key: " + key); + // Check if the entry is present in the cache and is valid + if (cache.containsKey(key)) { + CacheEntry cacheEntry = cache.get(key); + if (currentTime - cacheEntry.timestamp <= TimeUnit.MINUTES.toMillis(10)) { + // If the cache value is still valid, return it + logger.info(">>> Cache hit for key: " + key); + return cacheEntry.endpoints; + } else { + // Otherwise, remove the expired entry + logger.info(">>> Cache expired for key: " + key); + cache.remove(key); + } + } + + // If not present in the cache or is expired, make the request + logger.info(">>> Cache miss for key: " + key + ". Making request to external service."); + List endpoints; + try { + endpoints = getEndpointsFromIS(resource_name, category); + } catch (ServerException e) { + logger.error( "Error fetching data from external service for key: " + key, e); + throw e; + } + + // Update the cache with the new result and timestamp + logger.info("Updating cache for key: " + key); + cache.put(key, new CacheEntry(endpoints, currentTime)); + + return endpoints; + } finally { + // Release the lock + logger.info("### Releasing lock for key: " + key); + lock.unlock(); + // Remove the lock from the map to avoid memory leaks + logger.info("### Removing lock from map for key: " + key); + locks.remove(key); + } + } +} diff --git a/src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClient.java b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClient.java index d910c94..44b74f3 100644 --- a/src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClient.java +++ b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClient.java @@ -29,6 +29,15 @@ import com.liferay.portal.kernel.log.LogFactoryUtil; public class IsClient { private static com.liferay.portal.kernel.log.Log logger = LogFactoryUtil.getLog(IsClient.class); + private static IsClient _singleInstance = null; + + static IsClient getSingleton() { + if (_singleInstance == null) { + _singleInstance = new IsClient(); + } + return _singleInstance; + } + /** * obatins from IS the list of ServiceEndpoint matching the parameters * @@ -42,10 +51,10 @@ public class IsClient { * @throws Exception */ - public static ServiceEndpoint getFirstEndopintsFromIS(String resource_name, String category) + public ServiceEndpoint getFirstEndopintsFromIS(String resource_name, String category) throws ServerException { - List endpoints = getEndopintsFromIS(resource_name, category); + List endpoints = getEndpointsFromIS(resource_name, category); if (endpoints == null || endpoints.size() == 0) { logger.error("Unable to retrieve service endpoint " + resource_name); return null; @@ -55,8 +64,10 @@ public class IsClient { } - public static List getEndopintsFromIS(String resource_name, String category - /* , boolean root_service, Secret secret */) throws ServerException { + public List getEndpointsFromIS(String resource_name, String category +) throws ServerException { + logger.info("@@@ not cached getEndpointsFromIS: " + resource_name + ":" + category); + SimpleQuery query = queryFor(ServiceEndpoint.class); if (resource_name != null) { @@ -74,6 +85,7 @@ public class IsClient { ScopeProvider.instance.set(infrastructure); try { + logger.info (" IS CLIENT Requiring " + resource_name + ":"+ category); // if (root_service) { // endpoints = AuthorizedTasks.executeSafely(() -> { @@ -107,10 +119,10 @@ public class IsClient { * @throws ServerException * @throws Exception */ - public static List getAccessPointsFromIS(String resource_name, String category, + public List getAccessPointsFromIS(String resource_name, String category, String endPointName/* , boolean is_root_service, Secret secret */) throws ServerException { - List resources = getEndopintsFromIS(resource_name, category/* , is_root_service, secret */); + List resources = getEndpointsFromIS(resource_name, category/* , is_root_service, secret */); if (resources.size() == 0) { logger.error("There is no Runtime Resource having name " + resource_name + " and Category " @@ -142,7 +154,7 @@ public class IsClient { * @throws ServerException * @throws Exception */ - public static ServiceEndpoint.AccessPoint getFirstAccessPointFromIS(String resource_name, String category, + public ServiceEndpoint.AccessPoint getFirstAccessPointFromIS(String resource_name, String category, String entryPointName/* , boolean root_service, Secret secret */) throws ServerException { List access_points = getAccessPointsFromIS(resource_name, category, @@ -175,7 +187,7 @@ public class IsClient { * @return * @throws Exception */ - public static IsServerConfig serviceConfigFromIS(String resourceName, String category, String endPointName + public IsServerConfig serviceConfigFromIS(String resourceName, String category, String endPointName /* , boolean is_root_service, Secret secret */) throws ServerException { diff --git a/src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClientFactory.java b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClientFactory.java new file mode 100644 index 0000000..283913b --- /dev/null +++ b/src/main/java/org/gcube/portlets/user/cloudcomputing/is/IsClientFactory.java @@ -0,0 +1,32 @@ +package org.gcube.portlets.user.cloudcomputing.is; + +import com.liferay.portal.kernel.log.LogFactoryUtil; + +// import org.gcube.common.security.AuthorizedTasks; +// import org.gcube.common.security.secrets.Secret; + +/** + * Utility class to query EndPoints and search for AccessPoints from IS + * + * @author Alfredo Oliviero (ISTI - CNR) + */ + +public class IsClientFactory { + private static com.liferay.portal.kernel.log.Log logger = LogFactoryUtil.getLog(IsClientFactory.class); + static boolean cache_enabled = true; + + public static IsClient getSingleton(){ + return getSingleton(cache_enabled); + } + + public static IsClient getSingleton(boolean use_cache){ + if (use_cache) { + logger.info("@@@ IsClientFactory cache enabled"); + return CacheIsClient.getSingleton(); + } else { + logger.info("@@@ IsClientFactory cache not enabled"); + + return IsClient.getSingleton(); + } + } +}