This section contains some guidelines or rules of thumb for the proper definition of data views.
Define the property configuration recursively
You have to ensure that a bean's data view configuration is recursively reachable from the
root bean's data view configuration. For every property returning this bean, a "bridging"
data view configuration entry needs to be added. In order to prevent the cache to be
filled with unnecessary
"bridge"
properties, the association type
dynamic
might be used, for instance.
<dataview appliesTo="com.mycompany.PageBean"> <property name="content" associationType="dynamic"/> </dataview>
Why is this important?
From a data view's point of view, the process of rendering nested bean takes place as follows:
The controller computes the root bean (containing nested beans) from an incoming request
The controller invokes
DataViewFactory#loadCached(bean, name)
for this bean in order to apply a data viewThe controller passes the bean to the rendering engine (and therefore to the view templates) where the bean's properties are accessed and rendered
When accessing a bean property which is returning further beans, a data view will be applied automatically to these sub beans
In other words, the initial appliance of a data view to the root bean leads to a recursive
appliance of data views to all sub beans. Unfortunately, this is true in case that there
is a data view configuration (dataviews.xml
) for every relevant
bean/property only. Let's say there is no such configuration for the root bean, then no
data views will be applied to the sub beans automatically and these beans will be returned
as they are. As a consequence, the sub beans wouldn't be cached even if there is a data
view configuration available for them.
Example
There is a
PageBean
having a JSP template:
public interface PageBean { ArticleBean getContent(); } <cm:include target="${self.content}"/>
The template includes the rendering of an
ArticleBean
public interface ArticleBean { String getHeadline() } <c:out value="${self.headline}"/>
If there is a data view configuration for the (supposed "expensive") property "headline"
<dataview appliesTo="com.mycompany.Article"> <property name="headline"/> </dataview>
without defining a configuration for the root bean
<dataview appliesTo="com.mycompany.PageBean"> <property name="content" associationType="static"/> </dataview>
then there won't be any caching of the "headline" property.
Auto completing the data view configuration
In large projects, a recursive definition of data views might be a difficult and error-prone task. Unwanted gaps in the transitive closure and thus uncached beans may be the result. For this reason, there is a feature called "auto completion" which helps to achieve a complete transitive closure of data views.
Auto completion can be configured in the
dataviews.xml
like this:
<dataviews> ... <autocomplete> <class name="com.coremedia.objectserver.dataviews.AssumesIdentity"/> <class name="java.util.Map"/> <class name="java.util.List"/> </autocomplete> ... </dataviews>
Example 4.1. Auto completion example
This configuration causes the
DataViewFactory
to implicitly use the
association type
DYNAMIC
for all bean properties whose getter method's return
type inherit from AssumesIdentity
, Map or List and which are not already covered by a
data
view configuration. Not only properties of configured data views will be automatically completed
but also those of beans that do not have a data view configuration at all.
Caution | |
---|---|
Please note that only the getter method's return type is taken into account during auto completion, not the concrete type of an object returned from the getter at runtime. |
As a consequence of this feature, you are able to design a lean data view configuration with only a few purposeful property configurations.
But there are also some drawbacks: If there are only a few data views explicitly declared,
the
DataViewFactory
will have to create many transient ("uncached") data view
objects in order to provide closure. Thus, lots of additional objects populate the java
heap temporarily which mean more work for the garbage collector. In addition, some
synchronization is required when accessing properties. This might reduce the application's
performance. Choose the auto-completion types carefully so that all property return types
are covered on the one hand, without being too generic on the other hand. As a rule of
thumb, the super interface of your content beans (such as AssumesIdentity
)
together with
java.lang.List
and
java.lang.Map
might be a good
starting point.
Of course, there might be properties which should not be automatically completed. For this reason, a
pseudo association type
none
can be used to explicitly exclude a property
from being automatically completed.
<dataview appliesTo="com.yourcompany.YourBean"> <property name="userInfo" associationType="none"/> </dataview>
Example 4.2. Auto completion exclusion example
The property
userInfo
of
YourBean
won't be ever automatically completed and will be treated as
if there is no automatically completion and no data view configuration.
Let the controller apply a data view to its beans
A controller's contract is to compute a
ModelAndView
which contains one or
more model beans to be passed to the rendering engine. In order to make the model beans
cacheable, it's important to apply a data view to these beans within the controller.
Example
This example demonstrates a simple controller implementation snippet:
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { // compute the model bean from the request MyBean modelBean = computeBean(request); // apply a data view to this bean MyBean cachedModelBean = (MyBean) getDataViewFactory().loadCached(modelBean, null); // construct the controller's result ModelAndView result = ControllerUtils.viewOf(cachedModelBean); return result; }
Use caching only when it is reasonable
Caching with data views is for improving an application's performance: The results of property computations are stored in the heap memory in order to prevent a repeated computation when accessing the property the next time. The values are removed from the cache when they are becoming invalid or due to evictions.
The process of caching itself is not for free: Each cached entry consumes a bit of the (limited) heap space on the one hand. On the other hand, each cache read or write operation is synchronized by the cache which might lead to decreased concurrency. For this reason data view caching of a single property should be used purposeful, that is when it results in a better performance. Here are some situations where data view caching might not be worthwhile
The computation of a property is cheap.
The property value is already cached elsewhere. For instance, the Unified API is already caching its content properties: When simply delegating the content bean's property access to the content objects, the content beans need not to be cached again. Another example is a property which accesses another already cached property, for example a property
firstSentence
which performs a cheap string operation on a cached propertytext
.A cached data view will be generally invalidated or evicted immediately after it is put into the cache without or rarely being accessed in the mean time.
Make sure that it is worthwhile from a performance point of view before enabling a property to be cached by a data view.
Avoid caching of large objects
Caching with data views is especially suited for properties that consume moderate memory. In opposite, large objects (such as binary objects) shouldn't be cached by data views since the heap memory is used disproportionately.
Choose the right association type
Properties can be separated into two groups from the data view's point of view
Associating Properties: Properties which values are beans or collections of beans where data views can be applied on again.
Simple Properties: All other properties with return values such as String, Int or other objects
You do not need to define an association type for a simple property. Instead, a data view
configuration such as
<property name="propertyname" />
is sufficient.
For an associating property you have to choose between the following association types
which differ in terms of memory consumption, synchronization behavior and
invalidation/eviction behavior.
static
composition
aggregation
dynamic
Despite this different behavior, these aspects doesn't need to be considered primarily when starting to create the data view configuration. For the beginning it is sufficient to choose "static" for a cacheable property and "dynamic" for a non-cacheable property in order to make another property recursively reachable (see above). As soon as you have finished your initial data view configuration, you can do some optimizations by replacing specific association types with "aggregation" or "composition" in second step.
You can use the CoreMedia Contribution "CAE Console" to tweak your data view settings.
Do not implement property methods that use context data
In order to make a bean property cacheable you have to implement a public (non static and non final) getter method without parameters. Make sure that the method's implementation doesn't use any context data such as "current user", "current session" or similar stateful data. Otherwise, a property value is related to an arbitrary context when putting it into the cache. When reading it from the cache then, it might not fit to the reader's context.
The following example demonstrates a bad implementation where a list of content objects is filtered according to the current user's rights.
public List<ContentBean> getLinks() { List<Content> contents = getContent().getLinks("links"); List<ContentBean> result = new ArrayList<ContentBean>(); for (Content content : contents) { if (mayRead(content, getCurrentUser()) { // bad use of context data result.add(createBeanFor(content)); } } return result; }
Assume the property "links" to be cached when accessing it the first time: The cached result depends on the right of the user which accesses this property for the first time. Another user accessing this property afterwards will obtain a value which is not appropriate to the user's rights and therefore might have access to more or fewer contents than required.