diff --git a/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/ContextManagers.java b/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/ContextManagers.java deleted file mode 100644 index d9cdd5a1..00000000 --- a/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/ContextManagers.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2016-2024 Talsma ICT - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package nl.talsmasoftware.context.core; - -import nl.talsmasoftware.context.api.Context; -import nl.talsmasoftware.context.api.ContextManager; -import nl.talsmasoftware.context.api.ContextSnapshot; - -/** - * Core implementation to allow {@link #createContextSnapshot() taking a snapshot of all contexts}. - * - *

- * Such a {@link ContextSnapshot snapshot} can be passed to a background task to allow the context to be - * {@link ContextSnapshot#reactivate() reactivated} in that background thread, until it gets - * {@link Context#close() closed} again (preferably in a try-with-resources construct). - * - * @author Sjoerd Talsma - * @since 1.1.0 - * @deprecated No longer necessary, API provides all functionality from this class now. - */ -@Deprecated -public final class ContextManagers { -// private static final Logger LOGGER = Logger.getLogger(ContextManagers.class.getName()); - - /** - * Private constructor to avoid instantiation of this class. - */ - private ContextManagers() { - throw new UnsupportedOperationException(); - } - - /** - * This method is able to create a 'snapshot' from the current - * {@link ContextManager#getActiveContextValue() active context value} - * from all known {@link ContextManager} implementations. - * - *

- * This snapshot is returned as a single object that can be temporarily - * {@link ContextSnapshot#reactivate() reactivated}. Don't forget to {@link Context#close() close} the reactivated - * context once you're done, preferably in a try-with-resources construct. - * - * @return A new snapshot that can be reactivated elsewhere (e.g. a background thread) - * within a try-with-resources construct. - */ - @SuppressWarnings("rawtypes") - public static ContextSnapshot createContextSnapshot() { - return ContextSnapshot.capture(); -// final long start = System.nanoTime(); -// final List managers = ServiceCache.cached(ContextManager.class); // Cached list is immutable -// final Object[] values = new Object[managers.size()]; -// -// for (int i = 0; i < values.length; i++) { -// values[i] = getActiveContextValue(managers.get(i)); -// } -// -// final ContextSnapshotImpl result = new ContextSnapshotImpl(managers, values); -// if (managers.isEmpty() && LOGGER.isLoggable(Level.FINER)) { -// LOGGER.finer(result + " was created but no ContextManagers were found! " -// + " Thread=" + Thread.currentThread() -// + ", ContextClassLoader=" + Thread.currentThread().getContextClassLoader()); -// } -// Timers.timed(System.nanoTime() - start, ContextManagers.class, "createContextSnapshot", null); -// return result; - } - - /** - * Clears all active contexts from the current thread. - * - *

- * Contexts that are 'stacked' (i.e. restore the previous state upon close) should be - * closed in a way that includes all 'parent' contexts as well. - * - *

- * This operation is not intended to be used by general application code as it likely breaks any 'stacked' - * active context that surrounding code may depend upon. - * Appropriate use includes thread management, where threads are reused by some pooling - * mechanism. For example, it is considered safe to clear the context when obtaining a 'fresh' thread from a - * thread pool (as no context expectations should exist at that point). - * An even better strategy would be to clear the context right before returning a used thread to the pool - * as this will allow any unclosed contexts to be garbage collected. Besides preventing contextual issues, - * this reduces the risk of memory leaks by unbalanced context calls. - */ - public static void clearActiveContexts() { - ContextManager.clearAll(); -// final long start = System.nanoTime(); -// for (ContextManager manager : ServiceCache.cached(ContextManager.class)) { -// clear(manager); -// } -// Timers.timed(System.nanoTime() - start, ContextManagers.class, "clearActiveContexts", null); - } - - /** - * Override the {@linkplain ClassLoader} used to lookup {@linkplain ContextManager contextmanagers}. - *

- * Normally, taking a snapshot uses the {@linkplain Thread#getContextClassLoader() Context ClassLoader} from the - * {@linkplain Thread#currentThread() current thread} to look up all {@linkplain ContextManager context managers}. - * It is possible to configure a fixed, single classloader in your application for looking up the context managers. - *

- * Using this method to specify a fixed classloader will only impact - * new {@linkplain ContextSnapshot context snapshots}. Existing snapshots will not be impacted. - *

- * Notes:
- *

- * - * @param classLoader The single, fixed ClassLoader to use for finding context managers. - * Specify {@code null} to restore the default behaviour. - * @since 1.0.5 - */ - public static synchronized void useClassLoader(ClassLoader classLoader) { -// ServiceCache.useClassLoader(classLoader); - ContextManager.useClassLoader(classLoader); - } - -// private static Object getActiveContextValue(ContextManager manager) { -// final long start = System.nanoTime(); -// RuntimeException error = null; -// try { -// -// final Object activeContextValue = manager.getActiveContextValue(); -// LOGGER.finest(() -> activeContextValue == null -// ? "There is no active context value for " + manager + " (in thread " + Thread.currentThread().getName() + ")." -// : "Active context value of " + manager + " in " + Thread.currentThread().getName() + ": " + activeContextValue); -// return activeContextValue; -// -// } catch (RuntimeException e) { -// LOGGER.log(Level.WARNING, e, () -> "Error obtaining active context from " + manager + " (in thread " + Thread.currentThread().getName() + ")."); -// error = e; -// return null; -// } finally { -// Timers.timed(System.nanoTime() - start, manager.getClass(), "getActiveContextValue", error); -// } -// } - -// private static void clear(ContextManager manager) { -// final long start = System.nanoTime(); -// RuntimeException error = null; -// try { -// -// manager.clear(); -// LOGGER.finest(() -> "Active context of " + manager + " was cleared."); -// -// } catch (RuntimeException e) { -// LOGGER.log(Level.WARNING, e, () -> "Error clearing active context from " + manager + "(in thread " + Thread.currentThread().getName() + ")."); -// error = e; -// } finally { -// Timers.timed(System.nanoTime() - start, manager.getClass(), "clear", error); -// } -// } - -// /** -// * Implementation of the createContextSnapshot functionality that can reactivate all values from the -// * snapshot in each corresponding {@link ContextManager}. -// */ -// @SuppressWarnings("rawtypes") -// private static final class ContextSnapshotImpl implements ContextSnapshot { -// private final List managers; -// private final Object[] values; -// -// private ContextSnapshotImpl(List managers, Object[] values) { -// this.managers = managers; -// this.values = values; -// } -// -// public Reactivation reactivate() { -// final long start = System.nanoTime(); -// RuntimeException error = null; -// final Context[] reactivatedContexts = new Context[managers.size()]; -// try { -// -// for (int i = 0; i < values.length; i++) { -// reactivatedContexts[i] = reactivate(managers.get(i), values[i]); -// } -// return new ReactivationImpl(reactivatedContexts); -// -// } catch (RuntimeException reactivationException) { -// tryClose(reactivatedContexts, reactivationException); -// ServiceCache.clear(); -// throw error = reactivationException; -// } finally { -// Timers.timed(System.nanoTime() - start, ContextSnapshot.class, "reactivate", error); -// } -// } -// -// @Override -// public String toString() { -// return "ContextSnapshot{size=" + managers.size() + '}'; -// } -// -// /** -// * Reactivates a snapshot value for a single context manager. -// * -// *

-// * This initializes a new context with the context manager -// * (normally on another thread the snapshot value was captured from). -// * -// * @param contextManager The context manager to reactivate the snapshot value for. -// * @param snapshotValue The snapshot value to be reactivated. -// * @return The context to be included in the reactivation object. -// */ -// @SuppressWarnings("unchecked") // We got the value from the managers itself. -// private static Context reactivate(ContextManager contextManager, Object snapshotValue) { -// if (snapshotValue == null) return null; -// long start = System.nanoTime(); -// RuntimeException error = null; -// try { -// -// Context reactivated = contextManager.initializeNewContext(snapshotValue); -// LOGGER.finest(() -> "Context reactivated from snapshot by " + contextManager + ": " + reactivated + "."); -// return reactivated; -// -// } catch (RuntimeException e) { -// throw error = e; -// } finally { -// Timers.timed(System.nanoTime() - start, contextManager.getClass(), "initializeNewContext", error); -// } -// } -// -// /** -// * Try to close already-reactivated contexts when a later context manager threw an exception. -// * -// * @param reactivatedContexts The contexts that were already reactivated when the error happened. -// * @param reason The error that happened. -// */ -// private static void tryClose(Context[] reactivatedContexts, Throwable reason) { -// for (Context alreadyReactivated : reactivatedContexts) { -// if (alreadyReactivated != null) { -// try { -// alreadyReactivated.close(); -// } catch (RuntimeException rte) { -// reason.addSuppressed(rte); -// } -// } -// } -// } -// } - -// /** -// * Implementation of the reactivated 'container' context that closes all reactivated contexts -// * when it is closed itself.
-// * This context contains no meaningful value in itself and purely exists to close the reactivated contexts. -// */ -// @SuppressWarnings("rawtypes") -// private static final class ReactivationImpl implements Reactivation { -// private final Context[] reactivated; -// -// private ReactivationImpl(Context[] reactivated) { -// this.reactivated = reactivated; -// } -// -// public void close() { -// RuntimeException closeException = null; -// // close in reverse order of reactivation -// for (int i = this.reactivated.length - 1; i >= 0; i--) { -// Context reactivated = this.reactivated[i]; -// if (reactivated != null) try { -// reactivated.close(); -// } catch (RuntimeException rte) { -// if (closeException == null) closeException = rte; -// else closeException.addSuppressed(rte); -// } -// } -// if (closeException != null) throw closeException; -// } -// -// @Override -// public String toString() { -// return "ContextSnapshot.Reactivation{size=" + reactivated.length + '}'; -// } -// } -} diff --git a/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/ServiceCache.java b/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/ServiceCache.java deleted file mode 100644 index 68297bff..00000000 --- a/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/ServiceCache.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2016-2024 Talsma ICT - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package nl.talsmasoftware.context.core; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.ServiceLoader; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.logging.Logger; - -/** - * Cache for resolved services. - * - *

- * This is required because the ServiceLoader itself is not thread-safe due to its internal lazy iterator. - * - *

- * Although this class is in - */ -class ServiceCache { - private static final Logger LOGGER = Logger.getLogger(ServiceCache.class.getName()); - - /** - * Internal concurrent map as cache. - */ - @SuppressWarnings("rawtypes") - private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(); - - /** - * Sometimes a single, fixed classloader may be necessary (e.g. #97) - */ - private static volatile ClassLoader classLoaderOverride = null; - - static synchronized void useClassLoader(ClassLoader classLoader) { - if (classLoaderOverride == classLoader) { - LOGGER.finest(() -> "Maintaining classloader override as " + classLoader + " (unchanged)"); - return; - } - LOGGER.fine(() -> "Updating classloader override to " + classLoader + " (was: " + classLoaderOverride + ")"); - classLoaderOverride = classLoader; - CACHE.clear(); - } - - @SuppressWarnings("unchecked") - static List cached(Class serviceClass) { - return (List) CACHE.computeIfAbsent(serviceClass, ServiceCache::load); - } - - static void clear() { - CACHE.clear(); - } - - /** - * Loads the service implementations of the requested type. - * - *

- * This method is synchronized because ServiceLoader is not thread-safe. - * Fortunately this only gets called after a cache miss, so should not affect performance. - * - *

- * The returned {@code List} will be {@linkplain Collections#unmodifiableList(List) unmodifiable}. - * - * @param serviceType The service type to load. - * @param The service type to load. - * @return Unmodifiable list of service implementations. - */ - private synchronized static List load(Class serviceType) { - final ArrayList services = new ArrayList<>(); - final ServiceLoader loader = classLoaderOverride == null - ? ServiceLoader.load(serviceType) - : ServiceLoader.load(serviceType, classLoaderOverride); - for (T service : loader) { - services.add(service); - } - services.trimToSize(); - // TODO debug logging - return Collections.unmodifiableList(services); - } -} diff --git a/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/Timers.java b/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/Timers.java deleted file mode 100644 index 0ec1366e..00000000 --- a/context-propagation-core/src/main/java/nl/talsmasoftware/context/core/Timers.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2016-2024 Talsma ICT - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package nl.talsmasoftware.context.core; - -import nl.talsmasoftware.context.api.ContextTimer; - -import java.util.concurrent.TimeUnit; -import java.util.logging.Level; -import java.util.logging.Logger; - -final class Timers { - private static final Logger TIMING_LOGGER = Logger.getLogger(Timers.class.getName()); - - static void timed(long durationNanos, Class type, String method, Throwable error) { - for (ContextTimer delegate : ServiceCache.cached(ContextTimer.class)) { - delegate.update(type, method, durationNanos, TimeUnit.NANOSECONDS, error); - } - if (TIMING_LOGGER.isLoggable(Level.FINEST)) { - TIMING_LOGGER.log(Level.FINEST, "{0}.{1}: {2,number}ns", new Object[]{type.getName(), method, durationNanos}); - } - } - -} diff --git a/context-propagation-core/src/test/java/nl/talsmasoftware/context/core/TimersTest.java b/context-propagation-core/src/test/java/nl/talsmasoftware/context/core/TimersTest.java deleted file mode 100644 index a5c23ab6..00000000 --- a/context-propagation-core/src/test/java/nl/talsmasoftware/context/core/TimersTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2016-2024 Talsma ICT - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package nl.talsmasoftware.context.core; - -import nl.talsmasoftware.context.api.ContextTimer; -import org.junit.jupiter.api.Test; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - -/** - * Test for the package-protected Timers utility class. - * - * @author Sjoerd Talsma - */ -public class TimersTest { - - @Test - public void testTimingDelegation() { - Timers.timed(TimeUnit.MILLISECONDS.toNanos(150), getClass(), "testTimingDelegation", null); - assertThat(TestContextTimer.getLastTimedMillis(getClass(), "testTimingDelegation"), is(150L)); - } - - public static class TestContextTimer implements ContextTimer { - private static final Map LAST_TIMED = new HashMap(); - - public static Long getLastTimedMillis(Class type, String method) { - return LAST_TIMED.get(type.getName() + "." + method); - } - - public void update(Class type, String method, long duration, TimeUnit unit, Throwable error) { - LAST_TIMED.put(type.getName() + "." + method, unit.toMillis(duration)); - } - - } -} diff --git a/context-propagation-core/src/test/resources/META-INF/services/nl.talsmasoftware.context.api.ContextTimer b/context-propagation-core/src/test/resources/META-INF/services/nl.talsmasoftware.context.api.ContextTimer deleted file mode 100644 index da50995f..00000000 --- a/context-propagation-core/src/test/resources/META-INF/services/nl.talsmasoftware.context.api.ContextTimer +++ /dev/null @@ -1 +0,0 @@ -nl.talsmasoftware.context.core.TimersTest$TestContextTimer diff --git a/readme.md b/readme.md index 54b45327..196a4add 100644 --- a/readme.md +++ b/readme.md @@ -131,7 +131,7 @@ context snapshots along with time spent in each individual `ContextManager`. ### Logging performance On a development machine, you can get timing for each snapshot by turning on logging -for `nl.talsmasoftware.context.core.Timers` at `FINEST` or `TRACE` level +for `nl.talsmasoftware.context.api.ContextTimer` at `FINEST` or `TRACE` level (depending on your logger of choice). Please **do not** turn this on in production as the logging overhead will most likely have a noticeable impact on your application.