Skip to content

Context Managers

When launching jobs and steps, instances of JobExecution and StepExecution are used by the Summer Batch engine and persisted in the repository. These executions each have a dedicated context that can be used to store data and process variables. In order to give access to these contexts in a convenient way, several classes were added to the Summer Batch: ContextManager, ContextManagerUnityLoader, AbstractExecutionListener and AbstractService.

ContextManager class

Contexts are basically dictionaries. ContextManager simply enables to store and retrieve from the dictionary and gives basic utility methods (particularly related to counter management). There is a single ContextManager class for both job context and step context but two instances will be available, one for the job and the other for the step.

It implements Summer.Batch.Extra.IContextManager interface, whose code is given below

Example 7.20. IContextManager interface contract

using Summer.Batch.Infrastructure.Item;

namespace Summer.Batch.Extra
{
    /// <summary>
    /// Interface for context manager
    /// </summary>
    public interface IContextManager
    {
        /// <summary>
        /// Accessors for the context
        /// </summary>
        ExecutionContext Context { get; set; }

        /// <summary>
        /// Stores an object inside the cache
        /// </summary>
        /// <param name="key">object key to store</param>
        /// <param name="record">object to store</param>
        void PutInContext(object key, object record);

        /// <summary>
        /// Check if the key is in the cache
        /// </summary>
        /// <param name="key">key of the object to retrieve</param>
        /// <returns>whether it is in the cache</returns>
        bool ContainsKey(object key);

        /// <summary>
        /// Retrieves an object from the cache
        /// </summary>
        /// <param name="key">key of the object to retrieve</param>
        /// <returns>retrieved object</returns>
        object GetFromContext(object key);

        /// <summary>
        /// Clears the cache
        /// </summary>
        void Empty();

        /// <summary>
        /// Dumps the cache content
        /// </summary>
        /// <returns>the content of the cache as a string</returns>
        string Dump();

        /// <summary>
        /// Sets the value of a named counter
        /// </summary>
        /// <param name="counter">the name of the counter</param>
        /// <param name="value">the new value of the the named counter</param>
        void SetCounter(string counter, long value);

        /// <summary>
        /// Returns the value of a named counter
        /// </summary>
        /// <param name="counter">the name of the counter</param>
        ///<returns>the value of the the named counter</returns>
        long GetCounter(string counter);

        /// <summary>
        ///  Increments the value of a counter by one. If this counter does not yet
        /// exist, it is first created with a value of 0 (thus, the new value is 1).
        /// </summary>
        /// <param name="counter">the name of the counter</param>
        void IncrementCounter(string counter);

        /// <summary>
        ///  Decrements the value of a counter by one. If this counter does not yet
        /// exist, it is first created with a value of 0 (thus, the new value is -1).
        /// </summary>
        /// <param name="counter">the name of the counter</param>
        void DecrementCounter(string counter);
    }
}           

Note

Despite being an object for internal compatibility, the key should be a string which can be converted to a string if it is not.

Caution

If you use PutInContext method to store specfic type of object defined in your own project, context managers will cause serializationexception.(see section Serialization to resolve it)

ContextManagerUnityLoader

In order to use context managers, one should extend a dedicated UnityLoader: ContextManagerUnityLoader, that will register in the unity container two instances of ContextManager, one for the job and one for the step. The keys used for these registrations are BatchConstants.JobContextManagerName and BatchConstants.StepContextManagerName. As usual, this class must be extended and LoadArtifacts must be implemented with all job artifacts registrations. If a registration should declare a ContextManager as a property, it can be done through usual unity wiring.

Example 7.21. Unity wiring example for ContextManager

container.StepScopeRegistration<IMyInterface, IMyClass>("MyKey")
    .Property("JobContextManager").Reference<IContextManager>(BatchConstants.JobContextManagerName)
    .Property("StepContextManager").Reference<IContextManager>(BatchConstants.StepContextManagerName)
    .Register();

Note

Unity registration can also be done through automatic injection, which is the strategy used in AbstractExecutionListener and AbstractService.

AbstractExecutionListener and AbstractService

AbstractExecutionListener class must be extended by the Processor classes. It supplies access to the StepContextManager and JobContextManager properties and adds a StepListener capability: in the BeforeStep method, the job and step context managers are linked to the current job execution context and step execution context. Then, the context managers can be used in processor code. Even other services involved in processing can access these context managers by extending AbstractService. In any case, with these base classes, properties named StepContextManager and JobContextManager will be available and usable.

Caution

If you do not use AbstractExecutionListener, context managers will not be linked to correct contexts, and even if they are injected, they will remain empty shells and not work properly.