Scopes of objects
Work in progress
This document is not ready yet.
Context Scope
The section describes Slice's context scope.
Design
In Guice a Scope is an object that contains providers for other classes.
First versions of Slice framework contained RequestScope that was responsible for storing/loading objects from ServletRequest objects (or creating new ones, when current were not available). This made it impossible to use Slice outside of servlet requests like in workflows and services.
ContextScope was created as solution to these problems. It works with assumption that each thread in CQ only does one action at a time - like answering to request or executing a workflow step.
For each thread separate ContextProvider can be set up, which is responsible for delivering Context implementation. A Context is basically a map that can one additional feature: it differentiates between not having object for a key and having null value for a key. It is used by ContextScope to check if new object should be created or if null value could be returned right away. Do not forget to restore old ContextProvider after use.
There are several implementations of Context available in Slice by default:
- MapContext - stores all data in internal map
- ServletRequestContext - stores all data in ServletRequest attributes, it can be used to ensure that all ContextScoped object during one request are the same
Also several ContextProvider implementations are available:
- SimpleContextProvider - provided Context can be just set by setter
- RequestContextProvider - uses service tracker to get to ContextRequstFilter and get ServletRequestContext that is always bound to request in the same thread, use instance from SliceActivator.requestContextProvider
Changing context
By default each thread is configured to use SliceActivator.requestContextProvider - to ContextScope works the same as RequestScope in Slice 1.x.
To change ContextProvider use InjectorWithContext decorator returned by InjectorsRepository.
final SimpleContextProvider simpleContextProvider = new SimpleContextProvider(); simpleContextProvider.setContext(ContextFactory.getServletRequestContext(request, response)); final InjectorWithContext injector = InjectorsRepository.getInjector(APP_NAME); injector.pushContextProvider(simpleContextProvider); try { ... } finally { injector.popContextProvider(); } |
Creating new contexts
New context should be created in a way that allows whole framework to work. For example, ResourceResolver and other object should be available in this Context.
See ContextFactory implementation for some example solutions:
Context context = new ServletRequestContext(request); context.put(Key.get(ServletRequest. class ), request); context.put(Key.get(ServletResponse. class ), response); |
In this example Slice can create its own ResourceResolver because it already has ServletRequest and ResourceResolver can be created from this.
MapContext mapContex = new MapContext(); mapContex.put(Key.get(ResourceResolver. class ), resourceResolver); |
In this example Slice has ResourceResolver from resourceResolver instance that is directly insterted into Context. No ServletRequest or ServletResponse can be used in Injector using this Context.
Mapping object to ContextScope
Objects can be mapped to context scope in two ways:
- by using @ContextScoped annotation on a class
- by using bindToContextScope method of ContextScopeModule
The second way should be used only when object of given type are directly available from ContextScope. For example, ServletRequest and ServletResponse are directly available from Context given by SliceContextFactory.