This is an automated email from the ASF dual-hosted git repository. heneveld pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/brooklyn-server.git
commit 0003ba4b04435ecf5a5a470be5a3b0b95e57d166 Author: Alex Heneveld <[email protected]> AuthorDate: Fri Jun 23 16:30:21 2023 +0100 cache service lookup results saves a not insignificant time because the underlying implementations rescan classes; we invalidate the cache whenever bundles change or startup completes --- .../brooklyn/api/framework/FrameworkLookup.java | 39 ++++++++++++++++++---- .../apache/brooklyn/core/mgmt/ha/OsgiManager.java | 5 +++ .../core/workflow/steps/CustomWorkflowStep.java | 1 + .../util/core/task/BasicExecutionManager.java | 8 +++-- .../brooklyn/launcher/osgi/OsgiLauncherImpl.java | 3 ++ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/org/apache/brooklyn/api/framework/FrameworkLookup.java b/api/src/main/java/org/apache/brooklyn/api/framework/FrameworkLookup.java index 079117a4a1..829bfc8fc0 100644 --- a/api/src/main/java/org/apache/brooklyn/api/framework/FrameworkLookup.java +++ b/api/src/main/java/org/apache/brooklyn/api/framework/FrameworkLookup.java @@ -18,16 +18,14 @@ */ package org.apache.brooklyn.api.framework; +import com.google.common.collect.Maps; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.osgi.OsgiUtil; import org.osgi.framework.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.ServiceLoader; +import java.util.*; /** * A utility to fetch an instance of a class from either OSGI if available, or by a Service Loader otherwise. @@ -42,11 +40,28 @@ import java.util.ServiceLoader; * which this code does not do. Therefore this class is not suitable for use in a situation where client code needs * to take account of services coming and going, and explicitly avoid using the service when its reference count has * gone to zero. + * + * This is achieved by clients looking up services as they need, + * and maintaining a cache here. + * + * The cache is explicitly invalidated when bundles are installed and when brooklyn finishes loading. */ public class FrameworkLookup { private static final Logger LOG = LoggerFactory.getLogger(FrameworkLookup.class); + private static boolean cachingEnabled = false; + public static void setCachingEnabled(boolean cachingEnabled) { + FrameworkLookup.cachingEnabled = cachingEnabled; + } + + public static void invalidateCaches() { + cacheOfSingleInstancesWithNoLoader.clear(); + } + + static Map<Class,Maybe> cacheOfSingleInstancesWithNoLoader = Maps.newConcurrentMap(); + static Map<Class,Iterable> cacheOfAllInstancesWithNoLoader = Maps.newConcurrentMap(); + /** * Find an instance of the given class in the framework. * This first performs an OSGI lookup if the OSGI framework is available. If it is not then it falls back to @@ -72,13 +87,18 @@ public class FrameworkLookup { * @return A maybe of the instance found in the framework. */ public static <T> Maybe<T> lookup (Class<T> clazz, ClassLoader loader) { - Maybe<T> result; + if (loader==null) { + result = cacheOfSingleInstancesWithNoLoader.get(clazz); + if (result!=null) return result; + } + if (OsgiUtil.isBrooklynInsideFramework()) { result = lookupInOsgi(clazz); } else { result = lookupViaServiceLoader(clazz, loader); } + cacheOfSingleInstancesWithNoLoader.put(clazz, result); return result; } @@ -108,13 +128,20 @@ public class FrameworkLookup { * @return An iterable over the instances found in the framework. */ public static <T> Iterable<T> lookupAll(Class<T> clazz, ClassLoader loader) { - Iterable<T> result; + + if (loader==null) { + result = cacheOfAllInstancesWithNoLoader.get(clazz); + if (result!=null) return result; + } + if (OsgiUtil.isBrooklynInsideFramework()) { result = lookupAllInOsgi(clazz); } else { result = lookupAllViaServiceLoader(clazz, loader); } + cacheOfAllInstancesWithNoLoader.put(clazz, result); + return result; } diff --git a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java index 7482dd88a3..32588c6ee7 100644 --- a/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java +++ b/core/src/main/java/org/apache/brooklyn/core/mgmt/ha/OsgiManager.java @@ -37,6 +37,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle; +import org.apache.brooklyn.api.framework.FrameworkLookup; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.typereg.ManagedBundle; import org.apache.brooklyn.api.typereg.OsgiBundleWithUrl; @@ -155,6 +156,7 @@ public class OsgiManager { if (Strings.isNonBlank(result.getMetadata().getUrl())) { managedBundlesUidByUrl.put(result.getMetadata().getUrl(), result.getMetadata().getId()); } + FrameworkLookup.invalidateCaches(); } private File fileFor(ManagedBundle managedBundle) { @@ -178,6 +180,7 @@ public class OsgiManager { managedBundlesRecord.managedBundlesUidByUrl.remove(bundleMetadata.getUrl()); removeInstalledWrapperBundle(bundleMetadata); fileFor(bundleMetadata).delete(); + FrameworkLookup.invalidateCaches(); return true; } @@ -202,6 +205,7 @@ public class OsgiManager { } ManagedBundle mbBak = managedBundlesByUid.put(result.getMetadata().getId(), result.getMetadata()); + FrameworkLookup.invalidateCaches(); return Pair.of(fBak, mbBak); } @@ -221,6 +225,7 @@ public class OsgiManager { fBak.renameTo(fCached); managedBundlesByUid.put(result.getMetadata().getId(), mbBak); + FrameworkLookup.invalidateCaches(); return fCached; } diff --git a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java index 0fdd8ab981..d1925d2327 100644 --- a/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java +++ b/core/src/main/java/org/apache/brooklyn/core/workflow/steps/CustomWorkflowStep.java @@ -132,6 +132,7 @@ public class CustomWorkflowStep extends WorkflowStepDefinition implements Workfl retention = (String) input.remove("retention"); } + /** disabled or count, hashes, etc; see {@link WorkflowRetentionParser} or documentation for full info */ protected String retention; /** What to run this set of steps against, either an entity to run in that context, 'children' or 'members' to run over those, a range eg 1..10, or a list (often in a variable) to run over elements of the list */ diff --git a/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java b/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java index e2cde9a050..97e63bedce 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/task/BasicExecutionManager.java @@ -1128,6 +1128,7 @@ public class BasicExecutionManager implements ExecutionManager { private static class TaskLookup implements Supplier<Task<?>> { // this class is not meant to be serialized, but if it is, make sure exec mgr doesn't sneak in transient BasicExecutionManager mgr; + transient Task<?> cachedResult; String id; String displayName; @@ -1144,9 +1145,10 @@ public class BasicExecutionManager implements ExecutionManager { @Override public Task<?> get() { if (mgr == null) return gone(); - Task<?> result = mgr.getTask(id); - if (result != null) return result; - return gone(); + + cachedResult = mgr.getTask(id); + if (cachedResult==null) cachedResult = gone(); + return cachedResult; } private <T> Task<T> gone() { diff --git a/karaf/init/src/main/java/org/apache/brooklyn/launcher/osgi/OsgiLauncherImpl.java b/karaf/init/src/main/java/org/apache/brooklyn/launcher/osgi/OsgiLauncherImpl.java index 64edcfa702..76bc0d21af 100644 --- a/karaf/init/src/main/java/org/apache/brooklyn/launcher/osgi/OsgiLauncherImpl.java +++ b/karaf/init/src/main/java/org/apache/brooklyn/launcher/osgi/OsgiLauncherImpl.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; + +import org.apache.brooklyn.api.framework.FrameworkLookup; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.api.mgmt.ha.HighAvailabilityMode; import org.apache.brooklyn.core.BrooklynVersionService; @@ -305,6 +307,7 @@ public class OsgiLauncherImpl extends BasicLauncher<OsgiLauncherImpl> implements ((ManagementContextInternal)getManagementContext()).getBrooklynProperties().put(OsgiManager.OSGI_STARTUP_COMPLETE, true); startupTimer.stop(); LOG.info("Brooklyn initialization (part two) complete after {}", startupTimer.toString()); + FrameworkLookup.invalidateCaches(); } }
