12. Variable injection

12.1. Context

12.1.1. Context/WebApplicationContext

Asta4D use "Context" to manage data life cycle. The "Context" in asta4d-core supplies the basic data management mechanism and the WebApplicationContext from asta4d-web afford more powerful extending for web application development. Commonly, we will only use WebApplicationContext in our application. There are two ways to retrieve an instance of WebApplicationContext.

Example 12.1. Retrieve an instance of WebApplicationContext by static method


WebApplicationContext context = Context.getCurrentThreadContext();

      

Example 12.2. Retrieve an instance of WebApplicationContext by injection


public class PageSnippet {

    public Renderer render(WebApplicationContext context) {
        ...
    }
}

      

As the name of "getCurrentThreadContext" suggested, the context is managed per thread, and also that the context will be initialized before every http request is processed and will be cleared after every http request process has finished.

WebApplicationContext affords several methods to retrieving raw servlet apis and other helpful information:

  • getRequest

    Return the HttpServletRequest instance of current request.

  • getResponse

    Return the HttpServletResponse instance of current request.

  • getServletContext

    Return the ServletContext instance of current request.

  • getAccessURI

    Return the rewritten url of current request. For details about URL rewritting, see later chapter.

12.1.2. Scope

The context provides a mechanism to manage data by scopes. There are only 3 scopes supplied by asta4d-core's Context, but the WebApplicationContext from asta4d-web supply more scopes to help web application development.

Available scopes(with the constant name):

global(SCOPE_GLOBAL)

The data saved in global scope can be accessed cross all the contexts, you can treat it as a static object pool.

default(SCOPE_DEFAULT)

The data saved in default scope can only be access by the current context.

element attribute(SCOPE_ATTR)

This scope is related to snippet rendering. An element attribute scope is a wrapping of element attribute. When you try to retrieve data from element attribute scope, the attribute of current rendering target element will be checked and the corresponding attribute value will be returned, further, if there is no corresponding attribute in the current rendering target element's attributes, all the parents of current rendering target element will be checked recursively.

request(SCOPE_REQUEST)

The data saved in default scope can only be access in the current http request process. Note that the request scope is simply delegated to the default scope and it is different from servlet api's http request attribute.

path var(SCOPE_PATHVAR)

Path var scope represents the variables declared in url rules.

query parameter(SCOPE_QUERYPARAM)

Query parameter scope represents the query parameters specified at the part after question mark in an url.

session(SCOPE_SESSION)

Session scope represents the data saved in http session.

header(SCOPE_HEADER)

Session scope represents the header data from a http request.

cookie(SCOPE_COOKIE)

Cookie scope represents the cookie data from a http request.

flash(SCOPE_FLASH)

Asta4D affords a mechanism to allow pass data to the redirected http request, which is called flash data.

Normally, the data saved in context can be accessed by variable injection. If you need get data manually, you can do it as following:

Example 12.3. Retrieve data from context


WebApplicationContext context = Context.getCurrentThreadContext();

//from default scope
Integer count = context.get("saved-count");

//from session
Integer sessionCount = context.get(WebApplicationContext.SCOPE_SESSION, "saved-count");

      

You can implement your own customized scope or afford you customized implementation for existing scopes. You need extend from WebApplicationContext then override the method called "createMapForScope".(If you only want to split your data from default scope, you can simply specify a new scope name and a new scope will be created automatically, the created scope's life cycle is as the same as default scope)

Example 12.4. Customized memcached drived session management


public class MyContext extends WebApplicationContext {
    @Override
    protected ContextMap createMapForScope(String scope) {
        ContextMap map = null;
        switch (scope) {
        case SCOPE_SESSION: {
            HttpServletRequest request = getRequest();
            map = new MemcachedSessionMap(request);
        default:
            map = super.createMapForScope(scope);
        }
        return map;
    }
}

      

See later chapter of Asta4dServerlet for how to configure Asta4D to use the customized Context implementation instead of the default WebApplicationContext.

12.1.3. ContextBindData

Since all the data query logic are performed at snippet class, we will want to avoid duplicated query on same page rendering. The recommended solution is caching your query result by cache library such as ehcache. But Asta4D also affords you an alternative solution for some simple cases, which is called ContextBindData.

ContextBindData can be treated as a lazy load instance field for Java class. It is warranted that a ContextBindData will be initialized only once in a same Asta4D context and it will only be initialized when you first call it. There are some exceptions for the warranty about initializing only once, we will explain them later.

Example 12.5. 


public class PageSnippet {
    
    @ContextData
    private int count;

    private ContextBindData<Integer> data = new ContextBindData<Integer>(true) {
        @Override
        protected Integer buildData() {
            return count + 1;
        }

    };

    public Renderer render() {
        return Renderer.create("*", data.get());
    }
}

      

You should have noticed that we pass a "true" to the constructor of ContextBindData, which means we require the warranty that it should be only initialize once in the current context. If we pass nothing or pass a "false" to the constructor, the buildData method may be invoked multiple times in multi-thread rendering, but which is faster than warranted initialization simply because of skipping the multi-thread lock process.

Again, we recommend to use cache library to avoid duplicated query, but you can use ContextBindData as a simple, light weight alternative. See detail of Section 9.3, “The best way of implementing a snippet”.

12.2. Injection

12.2.1. @ContextData

All fields annotated by @ContextData in a snippet class will be initialized/injected automatically when the snippet class instance is initialized. Further, even the method parameters of snippet rendering method and request handle method are automatically injected, you can still customize the injection by extra @ContextData annotation.

Note that there are something different between field injection and method parameter injection. The fields of snippet class without @ContextData will not be injected, but method parameters will be always injected even without @ContextData.

Available configurations of @ContextData:

name

The name of saved data in context. If it is not specified, the field name or parameter name will be used(This requires that you reverse the parameter names when compile).

scope

The scope of saved data in context. If it is no specified, Asta4D will try to search the data in all the scopes by a predefined order:

  1. element attribute(SCOPE_ATTR)

  2. path var(SCOPE_PATHVAR)

  3. query parameter(SCOPE_QUERYPARAM)

  4. flash(SCOPE_FLASH)

  5. cookie(SCOPE_COOKIE)

  6. hearder(SCOPE_HEADER)

  7. request/default(SCOPE_REQUEST/SCOPE_DEFAULT)

  8. session(SCOPE_SESSION)

  9. global(SCOPE_GLOBAL)

This order can be configured via WebApplicationContextDataFinder#setDataSearchScopeOrder.See details at Section 12.2.3, “ContextDataFinder”

typeUnMatch

The policy of how to handle the unmatched type on data conversion. The default action is throw Exception and another two value of "DEFAULT_VALUE" and "DEFAULT_VALUE_AND_TRACE" can be specified.

There are several scope specified annotations can be used as convenience.

  • @SessionData

  • @QueryParam

  • @PathVar

  • @HeaderData

  • @CookieData

  • @FlashData

All the above annotation have the same configurable items with the original @ContextData annotation except being fixed with according data scope.

12.2.2. @ContextDataSet

A pojo class annotated by @ContextDataSet will be treated as a variable holder. If the type of a snippet class field or a method parameter is a class annotated by @ContextDataSet, when Asta4D do injection, a new instance will be created at first, then all the fields of current instance will be scanned and all the fields annotated by @ContextData will be injected as same as the snippet class field injection. @ContextDataSet can be used to allocate all of the form data into one single instance as a convenience.

Example 12.6. 


@ContextDataSet
public static class MySet {

    @ContextData
    private long f1;

    @ContextData
    private String f2;

    public long getF1() {
        return f1;
    }
    
    public String getF2() {
        return f2;
    }
}

public static class MySnippet {

    public void render(MySet holder) {
    }

}

public static class MySnippet2 {

    @ContextData
    private MySet holder;

    public void render() {
    }

}




      

Note that even you declared @ContextDataSet on the class, you still need to declare the @ContextData on the field declaration. For method parameter, it is not necessary since all the parameters are injected automatically. Further, for a declared @ContextDataSet, you still need to declare @ContextData on every field you want to be injected.


Available configurations of @ContextData:

singletonInContext

A boolean value to indicate that whether the target ContextDataSet should be singleton in a single context life cycle. The default value is false, which means a new instance would be created at every time when a ConextDataSet is required to inject.

factory

A factory class can be used to indicate how to create an instance of ContextDataSet. The default factory will call Class#newInstance() to create a new instance of ContextDataSet.

12.2.3. ContextDataFinder

Asta4D search data in context by pre configured ContextDataFinder implementation. The default implementation is WebApplicationContextDataFinder. You can supply your own search mechanism by implementing the ContextDataFinder interface or extending from WebApplicationContextDataFinder. WebApplicationContextDataFinder defines the scope search order if scope is not specified, and also returns some special data by hard coding type check:

  • Context/WebApplicationContext

  • ResourceBundleHelper

  • ParamMapResourceBundleHelper

  • HttpServletRequest

  • HttpServletResponse

  • ServletContext

  • UrlMappingRule

The first 3 types are afforded by the parent class of WebApplicationContextDataFinder: DefaultContextDataFinder. To supply your own logic, you can override the findDataInContext method and the WebApplicationContextDataFinder's source can be treated as reference implementation.

12.2.4. DataConvertor

12.2.5. ContextDataHolder