diff --git a/application/src/main/java/io/smallrye/context/application/context/propagation/ApplicationContextProvider.java b/application/src/main/java/io/smallrye/context/application/context/propagation/ApplicationContextProvider.java index 2033d393..39c55971 100644 --- a/application/src/main/java/io/smallrye/context/application/context/propagation/ApplicationContextProvider.java +++ b/application/src/main/java/io/smallrye/context/application/context/propagation/ApplicationContextProvider.java @@ -5,6 +5,7 @@ import java.util.Map; import org.eclipse.microprofile.context.ThreadContext; +import org.eclipse.microprofile.context.spi.ThreadContextController; import org.eclipse.microprofile.context.spi.ThreadContextProvider; import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; @@ -35,28 +36,40 @@ private static ClassLoader calculateSystemClassLoader() { @Override public ThreadContextSnapshot currentContext(Map props) { ClassLoader capturedTCCL = Thread.currentThread().getContextClassLoader(); - return () -> { - ClassLoader movedTCCL = Thread.currentThread().getContextClassLoader(); - if (capturedTCCL != movedTCCL) - Thread.currentThread().setContextClassLoader(capturedTCCL); - return () -> { - if (Thread.currentThread().getContextClassLoader() != movedTCCL) - Thread.currentThread().setContextClassLoader(movedTCCL); - }; + return new ThreadContextSnapshot() { + @Override + public ThreadContextController begin() { + ClassLoader movedTCCL = Thread.currentThread().getContextClassLoader(); + if (capturedTCCL != movedTCCL) + Thread.currentThread().setContextClassLoader(capturedTCCL); + return new ThreadContextController() { + @Override + public void endContext() throws IllegalStateException { + if (Thread.currentThread().getContextClassLoader() != movedTCCL) + Thread.currentThread().setContextClassLoader(movedTCCL); + } + }; + } }; } @Override public ThreadContextSnapshot clearedContext(Map props) { ClassLoader capturedTCCL = SYSTEM_CL; - return () -> { - ClassLoader movedTCCL = Thread.currentThread().getContextClassLoader(); - if (capturedTCCL != movedTCCL) - Thread.currentThread().setContextClassLoader(capturedTCCL); - return () -> { - if (Thread.currentThread().getContextClassLoader() != movedTCCL) - Thread.currentThread().setContextClassLoader(movedTCCL); - }; + return new ThreadContextSnapshot() { + @Override + public ThreadContextController begin() { + ClassLoader movedTCCL = Thread.currentThread().getContextClassLoader(); + if (capturedTCCL != movedTCCL) + Thread.currentThread().setContextClassLoader(capturedTCCL); + return new ThreadContextController() { + @Override + public void endContext() throws IllegalStateException { + if (Thread.currentThread().getContextClassLoader() != movedTCCL) + Thread.currentThread().setContextClassLoader(movedTCCL); + } + }; + } }; } diff --git a/cdi/src/main/java/io/smallrye/context/cdi/context/propagation/CdiContextProvider.java b/cdi/src/main/java/io/smallrye/context/cdi/context/propagation/CdiContextProvider.java index 26800c9f..c2521335 100644 --- a/cdi/src/main/java/io/smallrye/context/cdi/context/propagation/CdiContextProvider.java +++ b/cdi/src/main/java/io/smallrye/context/cdi/context/propagation/CdiContextProvider.java @@ -44,117 +44,133 @@ public ThreadContextSnapshot currentContext(Map map) { scopeToContextualInstances.put(context.getScope(), context.getAllContextualInstances()); } - return () -> { - // firstly we need to figure out whether given context needs activation - // if it does, then each context requires us to: - // 1) grab a bean representing it - // 2) prepare storage, in our case Map, and associate it with the context - // 3) activate the context in the snapshot and propagate instances - // 4) deactivate the context in the controller function - // if already active, we need to: - // 1) capture its state - // 2) feed it instances to propagate - // 3) restore previous state in controller function - - boolean isContextActiveOnThisThread = areContextsAlreadyActive(weldManager, scopeToContextualInstances.keySet()); - ThreadContextController controller; - if (!isContextActiveOnThisThread) { - // no active contexts yet, we will create Bound versions - BoundRequestContext requestContext = weldManager.instance() - .select(BoundRequestContext.class, BoundLiteral.INSTANCE).get(); - BoundSessionContext sessionContext = weldManager.instance() - .select(BoundSessionContext.class, BoundLiteral.INSTANCE).get(); - BoundConversationContext conversationContext = weldManager.instance() - .select(BoundConversationContext.class, BoundLiteral.INSTANCE).get(); - Map requestMap = null; - Map sessionMap = null; - - // activation of contexts that were previously active - if (scopeToContextualInstances.containsKey(requestContext.getScope())) { - requestMap = new HashMap<>(); - requestContext.associate(requestMap); - requestContext.activate(); - } - if (scopeToContextualInstances.containsKey(sessionContext.getScope())) { - sessionMap = new HashMap<>(); - sessionContext.associate(sessionMap); - sessionContext.activate(); - } - if (scopeToContextualInstances.containsKey(conversationContext.getScope())) { - // for conversation scope, we need request and session storages, only proceed when that condition is met - if (requestMap != null && sessionMap != null) { - conversationContext.associate(new MutableBoundRequest(requestMap, sessionMap)); - conversationContext.activate(); - } - } - - // propagate all contexts that have some bean in them - if (scopeToContextualInstances.get(requestContext.getScope()) != null) { - requestContext.clearAndSet(scopeToContextualInstances.get(requestContext.getScope())); - } - if (scopeToContextualInstances.get(sessionContext.getScope()) != null) { - sessionContext.clearAndSet(scopeToContextualInstances.get(sessionContext.getScope())); - } - if (scopeToContextualInstances.get(conversationContext.getScope()) != null) { - conversationContext.clearAndSet(scopeToContextualInstances.get(conversationContext.getScope())); - } - - // return ThreadContextController which deactivates scopes - controller = () -> { - // clean up contexts we previously activated by calling deactivate() on them + return new ThreadContextSnapshot() { + @Override + public ThreadContextController begin() { + // firstly we need to figure out whether given context needs activation + // if it does, then each context requires us to: + // 1) grab a bean representing it + // 2) prepare storage, in our case Map, and associate it with the context + // 3) activate the context in the snapshot and propagate instances + // 4) deactivate the context in the controller function + // if already active, we need to: + // 1) capture its state + // 2) feed it instances to propagate + // 3) restore previous state in controller function + + boolean isContextActiveOnThisThread = CdiContextProvider.this.areContextsAlreadyActive( + weldManager, + scopeToContextualInstances.keySet()); + ThreadContextController controller; + if (!isContextActiveOnThisThread) { + // no active contexts yet, we will create Bound versions + BoundRequestContext requestContext = weldManager.instance() + .select(BoundRequestContext.class, BoundLiteral.INSTANCE).get(); + BoundSessionContext sessionContext = weldManager.instance() + .select(BoundSessionContext.class, BoundLiteral.INSTANCE).get(); + BoundConversationContext conversationContext = weldManager.instance() + .select(BoundConversationContext.class, BoundLiteral.INSTANCE).get(); + Map requestMap = null; + Map sessionMap = null; + + // activation of contexts that were previously active if (scopeToContextualInstances.containsKey(requestContext.getScope())) { - requestContext.deactivate(); + requestMap = new HashMap<>(); + requestContext.associate(requestMap); + requestContext.activate(); } if (scopeToContextualInstances.containsKey(sessionContext.getScope())) { - sessionContext.deactivate(); + sessionMap = new HashMap<>(); + sessionContext.associate(sessionMap); + sessionContext.activate(); } if (scopeToContextualInstances.containsKey(conversationContext.getScope())) { - conversationContext.deactivate(); + // for conversation scope, we need request and session storages, only proceed when that condition is met + if (requestMap != null && sessionMap != null) { + conversationContext.associate(new MutableBoundRequest(requestMap, sessionMap)); + conversationContext.activate(); + } } - }; - } else { - // there are already active contexts here - // capture their contents and get references to all of them - Map, Collection>> scopeToInstancesToRestoreInTheEnd = new HashMap<>(); - Map, WeldAlterableContext> scopeToContextMap = new HashMap<>(); - for (WeldAlterableContext context : weldManager.getActiveWeldAlterableContexts()) { - scopeToInstancesToRestoreInTheEnd.put(context.getScope(), context.getAllContextualInstances()); - scopeToContextMap.put(context.getScope(), context); - } - // we work with WeldAlterableContext because the underlying implementation might differ - WeldAlterableContext requestContext = scopeToContextMap.get(RequestScoped.class); - WeldAlterableContext sessionContext = scopeToContextMap.get(SessionScoped.class); - WeldAlterableContext conversationContext = scopeToContextMap.get(ConversationScoped.class); + // propagate all contexts that have some bean in them + if (scopeToContextualInstances.get(requestContext.getScope()) != null) { + requestContext.clearAndSet(scopeToContextualInstances.get(requestContext.getScope())); + } + if (scopeToContextualInstances.get(sessionContext.getScope()) != null) { + sessionContext.clearAndSet(scopeToContextualInstances.get(sessionContext.getScope())); + } + if (scopeToContextualInstances.get(conversationContext.getScope()) != null) { + conversationContext.clearAndSet(scopeToContextualInstances.get(conversationContext.getScope())); + } - // propagate all contexts that have some bean in them - if (requestContext != null && scopeToContextualInstances.get(requestContext.getScope()) != null) { - requestContext.clearAndSet(scopeToContextualInstances.get(requestContext.getScope())); - } - if (sessionContext != null && scopeToContextualInstances.get(sessionContext.getScope()) != null) { - sessionContext.clearAndSet(scopeToContextualInstances.get(sessionContext.getScope())); - } - if (conversationContext != null && scopeToContextualInstances.get(conversationContext.getScope()) != null) { - conversationContext.clearAndSet(scopeToContextualInstances.get(conversationContext.getScope())); - } + // return ThreadContextController which deactivates scopes + controller = new ThreadContextController() { + @Override + public void endContext() throws IllegalStateException { + // clean up contexts we previously activated by calling deactivate() on them + if (scopeToContextualInstances.containsKey(requestContext.getScope())) { + requestContext.deactivate(); + } + if (scopeToContextualInstances.containsKey(sessionContext.getScope())) { + sessionContext.deactivate(); + } + if (scopeToContextualInstances.containsKey(conversationContext.getScope())) { + conversationContext.deactivate(); + } + } + }; + } else { + // there are already active contexts here + // capture their contents and get references to all of them + Map, Collection>> scopeToInstancesToRestoreInTheEnd = new HashMap<>(); + Map, WeldAlterableContext> scopeToContextMap = new HashMap<>(); + for (WeldAlterableContext context : weldManager.getActiveWeldAlterableContexts()) { + scopeToInstancesToRestoreInTheEnd.put( + context.getScope(), + context.getAllContextualInstances()); + scopeToContextMap.put(context.getScope(), context); + } + + // we work with WeldAlterableContext because the underlying implementation might differ + WeldAlterableContext requestContext = scopeToContextMap.get(RequestScoped.class); + WeldAlterableContext sessionContext = scopeToContextMap.get(SessionScoped.class); + WeldAlterableContext conversationContext = scopeToContextMap.get(ConversationScoped.class); - // return ThreadContextController which reverts state to what was previously on this thread - controller = () -> { - if (requestContext != null && scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope()) != null) { - requestContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope())); + // propagate all contexts that have some bean in them + if (requestContext != null && scopeToContextualInstances.get(requestContext.getScope()) != null) { + requestContext.clearAndSet(scopeToContextualInstances.get(requestContext.getScope())); } - if (sessionContext != null && scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope()) != null) { - sessionContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope())); + if (sessionContext != null && scopeToContextualInstances.get(sessionContext.getScope()) != null) { + sessionContext.clearAndSet(scopeToContextualInstances.get(sessionContext.getScope())); } - if (conversationContext != null - && scopeToInstancesToRestoreInTheEnd.get(conversationContext.getScope()) != null) { - conversationContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(conversationContext.getScope())); + if (conversationContext != null && scopeToContextualInstances.get(conversationContext.getScope()) != null) { + conversationContext.clearAndSet(scopeToContextualInstances.get(conversationContext.getScope())); } - }; - } - // when all is done, return the controller - return controller; + // return ThreadContextController which reverts state to what was previously on this thread + controller = new ThreadContextController() { + @Override + public void endContext() throws IllegalStateException { + if (requestContext != null + && scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope()) != null) { + requestContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope())); + } + if (sessionContext != null + && scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope()) != null) { + sessionContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope())); + } + if (conversationContext != null + && scopeToInstancesToRestoreInTheEnd.get(conversationContext.getScope()) != null) { + conversationContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get( + conversationContext.getScope())); + } + } + }; + } + + // when all is done, return the controller + return controller; + } }; } @@ -174,109 +190,125 @@ public ThreadContextSnapshot clearedContext(Map map) { activeScopes.add(context.getScope()); } - return () -> { - // firstly we need to figure out whether given context needs activation - // if it does, then each context requires us to: - // 1) grab a bean representing it - // 2) prepare storage, in our case Map, and associate it with the context - // 3) activate the context in the snapshot, no need for propagation - // 4) deactivate the context in the controller function - // if already active, we need to: - // 1) capture its state - // 2) feed it empty collections as new state - // 3) restore previous state in controller function - - boolean isContextActiveOnThisThread = areContextsAlreadyActive(weldManager, activeScopes); - ThreadContextController controller; - if (!isContextActiveOnThisThread) { - // no active contexts yet, we will create Bound versions - BoundRequestContext requestContext = weldManager.instance() - .select(BoundRequestContext.class, BoundLiteral.INSTANCE).get(); - BoundSessionContext sessionContext = weldManager.instance() - .select(BoundSessionContext.class, BoundLiteral.INSTANCE).get(); - BoundConversationContext conversationContext = weldManager.instance() - .select(BoundConversationContext.class, BoundLiteral.INSTANCE).get(); - Map requestMap = null; - Map sessionMap = null; - - // activation of contexts that were previously active - if (activeScopes.contains(requestContext.getScope())) { - requestMap = new HashMap<>(); - requestContext.associate(requestMap); - requestContext.activate(); - } - if (activeScopes.contains(sessionContext.getScope())) { - sessionMap = new HashMap<>(); - sessionContext.associate(sessionMap); - sessionContext.activate(); - } - if (activeScopes.contains(conversationContext.getScope())) { - // for conversation scope, we need request and session storages, only proceed when that condition is met - if (requestMap != null && sessionMap != null) { - conversationContext.associate(new MutableBoundRequest(requestMap, sessionMap)); - conversationContext.activate(); - } - } - - // no need for propagation, we want the storage to be empty - - // return ThreadContextController which deactivates scopes - controller = () -> { - // clean up contexts we previously activated by calling deactivate() on them + return new ThreadContextSnapshot() { + @Override + public ThreadContextController begin() { + // firstly we need to figure out whether given context needs activation + // if it does, then each context requires us to: + // 1) grab a bean representing it + // 2) prepare storage, in our case Map, and associate it with the context + // 3) activate the context in the snapshot, no need for propagation + // 4) deactivate the context in the controller function + // if already active, we need to: + // 1) capture its state + // 2) feed it empty collections as new state + // 3) restore previous state in controller function + + boolean isContextActiveOnThisThread = CdiContextProvider.this.areContextsAlreadyActive( + weldManager, + activeScopes); + ThreadContextController controller; + if (!isContextActiveOnThisThread) { + // no active contexts yet, we will create Bound versions + BoundRequestContext requestContext = weldManager.instance() + .select(BoundRequestContext.class, BoundLiteral.INSTANCE).get(); + BoundSessionContext sessionContext = weldManager.instance() + .select(BoundSessionContext.class, BoundLiteral.INSTANCE).get(); + BoundConversationContext conversationContext = weldManager.instance() + .select(BoundConversationContext.class, BoundLiteral.INSTANCE).get(); + Map requestMap = null; + Map sessionMap = null; + + // activation of contexts that were previously active if (activeScopes.contains(requestContext.getScope())) { - requestContext.deactivate(); + requestMap = new HashMap<>(); + requestContext.associate(requestMap); + requestContext.activate(); } if (activeScopes.contains(sessionContext.getScope())) { - sessionContext.deactivate(); + sessionMap = new HashMap<>(); + sessionContext.associate(sessionMap); + sessionContext.activate(); } if (activeScopes.contains(conversationContext.getScope())) { - conversationContext.deactivate(); + // for conversation scope, we need request and session storages, only proceed when that condition is met + if (requestMap != null && sessionMap != null) { + conversationContext.associate(new MutableBoundRequest(requestMap, sessionMap)); + conversationContext.activate(); + } } - }; - } else { - // there are already active contexts here - - // capture their contents and get references to all of them - Map, Collection>> scopeToInstancesToRestoreInTheEnd = new HashMap<>(); - Map, WeldAlterableContext> scopeToContextMap = new HashMap<>(); - for (WeldAlterableContext context : weldManager.getActiveWeldAlterableContexts()) { - scopeToInstancesToRestoreInTheEnd.put(context.getScope(), context.getAllContextualInstances()); - scopeToContextMap.put(context.getScope(), context); - } - // we work with WeldAlterableContext because the underlying implementation might differ - WeldAlterableContext requestContext = scopeToContextMap.get(RequestScoped.class); - WeldAlterableContext sessionContext = scopeToContextMap.get(SessionScoped.class); - WeldAlterableContext conversationContext = scopeToContextMap.get(ConversationScoped.class); + // no need for propagation, we want the storage to be empty + + // return ThreadContextController which deactivates scopes + controller = new ThreadContextController() { + @Override + public void endContext() throws IllegalStateException { + // clean up contexts we previously activated by calling deactivate() on them + if (activeScopes.contains(requestContext.getScope())) { + requestContext.deactivate(); + } + if (activeScopes.contains(sessionContext.getScope())) { + sessionContext.deactivate(); + } + if (activeScopes.contains(conversationContext.getScope())) { + conversationContext.deactivate(); + } + } + }; + } else { + // there are already active contexts here + + // capture their contents and get references to all of them + Map, Collection>> scopeToInstancesToRestoreInTheEnd = new HashMap<>(); + Map, WeldAlterableContext> scopeToContextMap = new HashMap<>(); + for (WeldAlterableContext context : weldManager.getActiveWeldAlterableContexts()) { + scopeToInstancesToRestoreInTheEnd.put( + context.getScope(), + context.getAllContextualInstances()); + scopeToContextMap.put(context.getScope(), context); + } - // clear out current storage state by passing in empty collections - if (requestContext != null & activeScopes.contains(requestContext.getScope())) { - requestContext.clearAndSet(Collections.emptySet()); - } - if (sessionContext != null && activeScopes.contains(sessionContext.getScope())) { - sessionContext.clearAndSet(Collections.emptySet()); - } - if (conversationContext != null && activeScopes.contains(conversationContext.getScope())) { - conversationContext.clearAndSet(Collections.emptySet()); - } + // we work with WeldAlterableContext because the underlying implementation might differ + WeldAlterableContext requestContext = scopeToContextMap.get(RequestScoped.class); + WeldAlterableContext sessionContext = scopeToContextMap.get(SessionScoped.class); + WeldAlterableContext conversationContext = scopeToContextMap.get(ConversationScoped.class); - // return ThreadContextController which reverts state to what was previously on this thread - controller = () -> { - if (requestContext != null && scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope()) != null) { - requestContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope())); + // clear out current storage state by passing in empty collections + if (requestContext != null & activeScopes.contains(requestContext.getScope())) { + requestContext.clearAndSet(Collections.emptySet()); } - if (sessionContext != null && scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope()) != null) { - sessionContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope())); + if (sessionContext != null && activeScopes.contains(sessionContext.getScope())) { + sessionContext.clearAndSet(Collections.emptySet()); } - if (conversationContext != null - && scopeToInstancesToRestoreInTheEnd.get(conversationContext.getScope()) != null) { - conversationContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(conversationContext.getScope())); + if (conversationContext != null && activeScopes.contains(conversationContext.getScope())) { + conversationContext.clearAndSet(Collections.emptySet()); } - }; - } - // when all is done, return the controller - return controller; + // return ThreadContextController which reverts state to what was previously on this thread + controller = new ThreadContextController() { + @Override + public void endContext() throws IllegalStateException { + if (requestContext != null + && scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope()) != null) { + requestContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(requestContext.getScope())); + } + if (sessionContext != null + && scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope()) != null) { + sessionContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get(sessionContext.getScope())); + } + if (conversationContext != null + && scopeToInstancesToRestoreInTheEnd.get(conversationContext.getScope()) != null) { + conversationContext.clearAndSet(scopeToInstancesToRestoreInTheEnd.get( + conversationContext.getScope())); + } + } + }; + } + + // when all is done, return the controller + return controller; + } }; } @@ -302,6 +334,11 @@ private boolean isCdiAvailable() { private boolean areContextsAlreadyActive(WeldManager manager, Set> scopes) { // if any of them is active, then all are, we basically need to differentiate if we are on the same thread - return scopes.stream().filter(scope -> manager.isContextActive(scope)).findAny().isPresent(); + for (Class scope : scopes) { + if (manager.isContextActive(scope)) { + return true; + } + } + return false; } } diff --git a/core/src/main/java/io/smallrye/context/SmallRyeContextManager.java b/core/src/main/java/io/smallrye/context/SmallRyeContextManager.java index 44723141..9b206da3 100644 --- a/core/src/main/java/io/smallrye/context/SmallRyeContextManager.java +++ b/core/src/main/java/io/smallrye/context/SmallRyeContextManager.java @@ -69,8 +69,7 @@ public String[] getAllProviderTypes() { } public CapturedContextState captureContext(SmallRyeThreadContext context) { - Map props = Collections.emptyMap(); - return new CapturedContextState(context, context.getPlan(), props); + return new CapturedContextState(context, context.getPlan()); } // for tests diff --git a/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java b/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java index a3479db6..2f97d497 100644 --- a/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java +++ b/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java @@ -23,6 +23,12 @@ public class SmallRyeThreadContext implements ThreadContext { private final static ThreadLocal currentThreadContext = new ThreadLocal<>(); + private final static CleanAutoCloseable NULL_THREAD_STATE = new CleanAutoCloseable() { + @Override + public void close() { + currentThreadContext.remove(); + } + }; /** * Updates the current @{link SmallRyeThreadContext} in use by the current thread, and returns an @@ -34,9 +40,18 @@ public class SmallRyeThreadContext implements ThreadContext { public static CleanAutoCloseable withThreadContext(SmallRyeThreadContext threadContext) { SmallRyeThreadContext oldValue = currentThreadContext.get(); currentThreadContext.set(threadContext); - return () -> { - currentThreadContext.set(oldValue); - }; + if (oldValue == null) { + //For restoring null values we can optimise this a little: + return NULL_THREAD_STATE; + } else { + return new CleanAutoCloseable() { + @Override + public void close() { + currentThreadContext.set(oldValue); + } + }; + } + } /** @@ -47,8 +62,16 @@ public static CleanAutoCloseable withThreadContext(SmallRyeThreadContext threadC * @param f the @{link Runnable} to invoke */ public static void withThreadContext(SmallRyeThreadContext threadContext, Runnable f) { - try (CleanAutoCloseable foo = withThreadContext(threadContext)) { + final SmallRyeThreadContext oldValue = currentThreadContext.get(); + currentThreadContext.set(threadContext); + try { f.run(); + } finally { + if (oldValue == null) { + currentThreadContext.remove(); + } else { + currentThreadContext.set(oldValue); + } } } diff --git a/core/src/main/java/io/smallrye/context/impl/ActiveContextState.java b/core/src/main/java/io/smallrye/context/impl/ActiveContextState.java index 5a565d35..34155d6a 100644 --- a/core/src/main/java/io/smallrye/context/impl/ActiveContextState.java +++ b/core/src/main/java/io/smallrye/context/impl/ActiveContextState.java @@ -1,6 +1,5 @@ package io.smallrye.context.impl; -import java.util.ArrayList; import java.util.List; import org.eclipse.microprofile.context.spi.ThreadContextController; @@ -11,21 +10,22 @@ public class ActiveContextState implements AutoCloseable { - private List activeContext; + private ThreadContextController[] activeContext; private CleanAutoCloseable activeThreadContext; public ActiveContextState(SmallRyeThreadContext threadContext, List threadContextSnapshots) { - activeContext = new ArrayList<>(threadContextSnapshots.size()); + activeContext = new ThreadContextController[threadContextSnapshots.size()]; + int i = 0; for (ThreadContextSnapshot threadContextSnapshot : threadContextSnapshots) { - activeContext.add(threadContextSnapshot.begin()); + activeContext[i++] = threadContextSnapshot.begin(); } activeThreadContext = SmallRyeThreadContext.withThreadContext(threadContext); } public void close() { // restore in reverse order - for (int i = activeContext.size() - 1; i >= 0; i--) { - activeContext.get(i).endContext(); + for (int i = activeContext.length - 1; i >= 0; i--) { + activeContext[i].endContext(); } activeThreadContext.close(); } diff --git a/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java b/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java index e69c0d9b..86e553d2 100644 --- a/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java +++ b/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java @@ -1,34 +1,19 @@ package io.smallrye.context.impl; -import java.util.LinkedList; import java.util.List; -import java.util.Map; -import org.eclipse.microprofile.context.spi.ThreadContextProvider; import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; import io.smallrye.context.SmallRyeThreadContext; public class CapturedContextState { - private List threadContextSnapshots = new LinkedList<>(); + private List threadContextSnapshots; private SmallRyeThreadContext threadContext; - public CapturedContextState(SmallRyeThreadContext threadContext, ThreadContextProviderPlan plan, - Map props) { + public CapturedContextState(SmallRyeThreadContext threadContext, ThreadContextProviderPlan plan) { this.threadContext = threadContext; - for (ThreadContextProvider provider : plan.propagatedProviders) { - ThreadContextSnapshot snapshot = provider.currentContext(props); - if (snapshot != null) { - threadContextSnapshots.add(snapshot); - } - } - for (ThreadContextProvider provider : plan.clearedProviders) { - ThreadContextSnapshot snapshot = provider.clearedContext(props); - if (snapshot != null) { - threadContextSnapshots.add(snapshot); - } - } + this.threadContextSnapshots = plan.takeThreadContextSnapshots(); } public ActiveContextState begin() { diff --git a/core/src/main/java/io/smallrye/context/impl/ThreadContextProviderPlan.java b/core/src/main/java/io/smallrye/context/impl/ThreadContextProviderPlan.java index 6c115667..2108bf45 100644 --- a/core/src/main/java/io/smallrye/context/impl/ThreadContextProviderPlan.java +++ b/core/src/main/java/io/smallrye/context/impl/ThreadContextProviderPlan.java @@ -1,8 +1,13 @@ package io.smallrye.context.impl; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Set; import org.eclipse.microprofile.context.spi.ThreadContextProvider; +import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; public class ThreadContextProviderPlan { @@ -10,10 +15,46 @@ public class ThreadContextProviderPlan { public final Set unchangedProviders; public final Set clearedProviders; + /** + * Following 3 fields are meant to optimise method #takeThreadContextSnapshots + * which is extremely hot at runtime, at cost of little extra memory for this plan. + */ + private final int snapshotInitialSize; + private final ThreadContextProvider[] propagatedProvidersFastIterable; + private final ThreadContextProvider[] clearedProvidersFastIterable; + public ThreadContextProviderPlan(Set propagatedSet, Set unchangedSet, Set clearedSet) { - this.propagatedProviders = propagatedSet; - this.unchangedProviders = unchangedSet; - this.clearedProviders = clearedSet; + this.propagatedProviders = Collections.unmodifiableSet(propagatedSet); + this.unchangedProviders = Collections.unmodifiableSet(unchangedSet); + this.clearedProviders = Collections.unmodifiableSet(clearedSet); + this.snapshotInitialSize = propagatedProviders.size() + clearedProviders.size(); + this.propagatedProvidersFastIterable = propagatedProviders.toArray(new ThreadContextProvider[0]); + this.clearedProvidersFastIterable = clearedProviders.toArray(new ThreadContextProvider[0]); + } + + /** + * This helps to optimise construction of CapturedContextState + * without exposing too many implementation details. + * Only useful for snapshots with an empty property set. + * + * @return a list of snapshots + */ + public List takeThreadContextSnapshots() { + List threadContextSnapshots = new ArrayList<>(snapshotInitialSize); + final Map props = Collections.emptyMap(); + for (ThreadContextProvider provider : propagatedProvidersFastIterable) { + ThreadContextSnapshot snapshot = provider.currentContext(props); + if (snapshot != null) { + threadContextSnapshots.add(snapshot); + } + } + for (ThreadContextProvider provider : clearedProvidersFastIterable) { + ThreadContextSnapshot snapshot = provider.clearedContext(props); + if (snapshot != null) { + threadContextSnapshots.add(snapshot); + } + } + return threadContextSnapshots; } }