Interface ProcessingContext

All Superinterfaces:
ApplicationContext, Context, ProcessingLifecycle
All Known Implementing Classes:
LegacyMessageSupportingContext, ResourceOverridingProcessingContext

public interface ProcessingContext extends ProcessingLifecycle, ApplicationContext, Context
Implementation of the ProcessingLifecycle adding mutable resource management operations by implementing Context.

It is recommended to construct a Context.ResourceKey instance when adding/updating/removing resources from the ProcessingContext to allow cross-referral by sharing the key or personalization when the resource should be private to a specific service.

Since:
5.0.0
Author:
Allard Buijze, Gerard Klijs, Milan Savić, Mitchell Herrijgers, Sara Pellegrini, Steven van Beelen
  • Method Details

    • withResource

      default <T> ProcessingContext withResource(Context.ResourceKey<T> key, T resource)
      Constructs a new ProcessingContext, branching off from this ProcessingContext.

      The given resource as added to the branched ProcessingContext under the given key.

      Specified by:
      withResource in interface Context
      Type Parameters:
      T - The type of resource associated with the key.
      Parameters:
      key - The key under which to register the resource in the branched ProcessingContext.
      resource - The resource to register in the branched ProcessingContext.
      Returns:
      A new ProcessingContext, branched off from this ProcessingContext.
    • putResource

      <T> T putResource(Context.ResourceKey<T> key, T resource)
      Register the given resource under the given key.
      Type Parameters:
      T - The type of resource to register under given @code.
      Parameters:
      key - The key under which to register the resource.
      resource - The resource to register.
      Returns:
      The previously registered resource, or null if none was present.
    • updateResource

      <T> T updateResource(Context.ResourceKey<T> key, UnaryOperator<@Nullable T> resourceUpdater)
      Update the resource with given key using the given resourceUpdater to describe the update. If no resource is registered with the given key, the resourceUpdater is invoked with null. Otherwise, the function is called with the currently registered resource under that key.

      The resource is replaced with the return value of the function, or removed when the function returns null.

      If the function throws an exception, the exception is rethrown to the caller.

      Type Parameters:
      T - The type of resource to update.
      Parameters:
      key - The key to update the resource for.
      resourceUpdater - The function performing the update itself.
      Returns:
      The new value associated with the key, or null when removed.
    • putResourceIfAbsent

      <T> T putResourceIfAbsent(Context.ResourceKey<T> key, T resource)
      Register the given instance under the given key if no value is currently present.
      Type Parameters:
      T - The type of resource to register under given key.
      Parameters:
      key - The key under which to register the resource.
      resource - The resource to register when nothing is present for the given key.
      Returns:
      The resource previously associated with given key.
    • computeResourceIfAbsent

      <T> T computeResourceIfAbsent(Context.ResourceKey<T> key, Supplier<T> resourceSupplier)
      If no resource is present for the given key, the given resourceSupplier is used to supply the instance to register under this key.

      The resourceSupplier MUST NOT call computeResourceIfAbsent(ResourceKey, Supplier) or putResourceIfAbsent(ResourceKey, Object) on this ProcessingContext. The backing resource store rejects re-entrant structural modification and will throw IllegalStateException (surfacing as a "Recursive update"). This matters when stacking decorators that each cache their wrapped instance per ProcessingContext: resolve the dependency on the delegate before entering the supplier, rather than from within it.

      Warning: never use this method for a resource whose construction closes over (holds a reference to) this ProcessingContext itself - construct a fresh instance directly on every call instead - unless key is guaranteed to be one of the resources every possible branch of this context overrides. A "branch" here is any ProcessingContext returned by withResource(ResourceKey, Object): a ResourceOverridingProcessingContext that overrides one specific resource key on top of a shared parent. Such a branch only intercepts computeResourceIfAbsent for its own overridden key; every other key falls through to the shared parent, ultimately the root context. If the supplied instance holds onto context, and context may be one of several sibling branches of a shared parent (for example, one branch per message being handled within a shared batch), the first branch to call this method gets its instance cached on the shared root, and every sibling branch that calls afterward receives that same stale instance back - silently operating against the wrong branch. For example, this is unsafe:

      
       // UNSAFE: MyContextAwareGateway's constructor stores a reference to "context".
       static MyContextAwareGateway forContext(ProcessingContext context) {
           return context.computeResourceIfAbsent(RESOURCE_KEY, () -> new MyContextAwareGateway(context));
       }
       
      If context is a per-message branch of a batch, the second message to call forContext receives the first message's gateway back, closed over the first message's branch. Supply a fresh instance directly instead, bypassing this resource store entirely:
      
       // SAFE: always supplies a fresh instance bound to whichever context is passed in.
       static MyContextAwareGateway forContext(ProcessingContext context) {
           return new MyContextAwareGateway(context);
       }
       

      This method remains the correct choice when the cached value does not reference context and is genuinely meant to be shared for the whole processing session, regardless of how many branches exist. For example:

      
       // SAFE: the cached ConcurrentHashMap never references "context", and is meant to be
       // shared across every branch of the same processing session.
       var managedEntities = context.computeResourceIfAbsent(managedEntitiesKey, ConcurrentHashMap::new);
       
      Type Parameters:
      T - The type of resource registered under given key.
      Parameters:
      key - The key to register the resource for.
      resourceSupplier - The function to supply the resource to register. Must not call back into the resource store of this ProcessingContext.
      Returns:
      The resource associated with the key.
    • removeResource

      <T> T removeResource(Context.ResourceKey<T> key)
      Removes the resource registered under given key.
      Type Parameters:
      T - The type of resource associated with the key.
      Parameters:
      key - The key to remove the registered resource for.
      Returns:
      The value previously associated with the key.
    • removeResource

      <T> boolean removeResource(Context.ResourceKey<T> key, T expectedResource)
      Remove the resource associated with given key if the given expectedResource is the currently associated value.
      Type Parameters:
      T - The type of resource associated with the key.
      Parameters:
      key - The key to remove the registered resource for.
      expectedResource - The expected resource to remove.
      Returns:
      true if the resource has been removed, otherwise false.