/** * Search for TraceeBackendProvider in the given classloader. The result is stored in a cache with the classloader * as (weak) key. If no backendProvider could be found a special type of collection is stored in cache and is returned. * * @param cacheCopy Working copy of the current cache (copy-on-write-cache) * @param classLoader the classloader we've to search for TraceeBackendProvider * @return A BackendProviderSet if we found at least one provider. Otherwise we return an EmptyBackendProviderSet. */ private Set<TraceeBackendProvider> getTraceeProviderFromClassloader( final Map<ClassLoader, Set<TraceeBackendProvider>> cacheCopy, final ClassLoader classLoader) { // use cache to get TraceeBackendProvider or empty results from old lookups Set<TraceeBackendProvider> classLoaderProviders = cacheCopy.get(classLoader); if (isLookupNeeded(classLoaderProviders)) { classLoaderProviders = loadProviders(classLoader); updatedCache(classLoader, classLoaderProviders); } return classLoaderProviders; }
/** * Returns the TraceeBackend. There must be exactly one Tracee implementation on the classpath. * <p/> * A call to this method may initially block to lookup the implementation with a {@link java.util.ServiceLoader}. * The call to this method from multiple threads with different class loader contexts may initially be slow * because cache writes can overwrite each other in concurrent situations and some class loader contexts may have * to be looked up multiple times. This allows the lookup mechanism to completely avoid synchronization. * <p/> * TODO: If you run a nested class loader environment (like a servlet container) and have the Tracee Api in a top * level class loader and a Tracee Implementation in a child class loader, the child class loader may not be unloaded * until a low memory situation occurs (since the SoftReference keeps the TraceeBackendProvider in memory). * It could be a solution to change the SoftReference to WeakReference but let a TraceeBackend keep a strong * reference to its TraceeBackendProvider. */ public static TraceeBackend getBackend() { return getBackend(new BackendProviderResolver()); }
/** * Find correct backend provider for the current context classloader. If no context classloader is available, a * fallback with the classloader of this resolver class is taken * * @return A bunch of TraceeBackendProvider registered and available in the current classloader */ public Set<TraceeBackendProvider> getBackendProviders() { // Create a working copy of Cache. Reference is updated upon cache update. final Map<ClassLoader, Set<TraceeBackendProvider>> cacheCopy = providersPerClassloader; // Try to determine TraceeBackendProvider by context classloader. Fallback: use classloader of class. Set<TraceeBackendProvider> providerFromContextClassLoader = getTraceeProviderFromClassloader(cacheCopy, GetClassLoader.fromContext()); if (!providerFromContextClassLoader.isEmpty()) { return providerFromContextClassLoader; } else { return getTraceeProviderFromClassloader(cacheCopy, GetClassLoader.fromClass(BackendProviderResolver.class)); } }
protected static TraceeBackend getBackend(final BackendProviderResolver resolver) { final Set<TraceeBackendProvider> backendProviders; try { backendProviders = resolver.getBackendProviders(); } catch (RuntimeException e) { throw new TraceeException("Unable to load available backend providers", e); } if (backendProviders.isEmpty()) { throw new TraceeException("Unable to find a TracEE backend provider. Make sure that you have an implementation on the classpath."); } if (backendProviders.size() > 1) { final List<Class<?>> providerClasses = new ArrayList<Class<?>>(backendProviders.size()); for (TraceeBackendProvider backendProvider : backendProviders) { providerClasses.add(backendProvider.getClass()); } final String providerClassNames = Arrays.toString(providerClasses.toArray()); throw new TraceeException("Multiple TracEE backend providers found. Don't know which one of the following to use: " + providerClassNames); } return backendProviders.iterator().next().provideBackend(); } }