Data Models

Gives a high level view on the most important classes and aspects regarding the Product Manager data access

Model Overview

images/download/attachments/29819128/DetailModelAndListModel.png

Entity Item

The term "entity item" is used to identify a single business object which can be represented by different models.
An entity item is always a concrete real-world object which maps to a physical record in the database. (E. g. a product or structure group object.)

Additionally to that, there is also an interface EntityItem which inherits from EntityItemList. This interface has been created to unify different models in the application by the least common attributes. Since a single object can be seen just as a "special case of a list" - namely a list with just one entry - the EntityItem interface inherits from the EntityItemList interface. Therefore where ever you can use an EntityItemList, you can also use an EntityItem.

Entity Proxy

The EntityProxy represents an immutable proxy object pointing to an already persistent entity item or to an entity item that is going to be persisted soon. Depending on how it is created it contains an internal or external identifier of an entity item. Using the entity proxy you can obtain it's corresponding singleton EntityDetailModelinstance. Other then detail models, entity proxies are serializable and usually can be converted to a String representation as well as a Long representation (Long conversion is not available for entities which have a parent like StructureGroup, Product or Article.

Entity Proxy Revision

The EntityProxyRevision class combines an EntityProxy with a RevisionToken. Therefore it kind of qualifies the entity item by it's revision.

Detail Model

The EntityDetailModelinterface is a container for the actual data which needed for infrastructure needs. The detail model instances will be cached and the data will automatically be invalidated in case the entity item has been changed by someone on the network. For your convenience the EntityDetailModel also has methods to access the data of the entity item. In case it is currently in an invalidated state (thus, the internal data graph is null), the EntityDetailModel will trigger the reload of the data transparently. Modifications of entity items can (and should) only be done by modifying the detail model's data (usually by using the command framework). Exceptions to this rule only apply for specific mass data operations like the deletion of many entity items.

Caching

Loading detail models can be quite resource intensive. That is the reason why they are cached on the server and on the client. The detail model cache is managed by the EntityManager. Detail model instances are designed as singletons. For a specific product or structure group, there is only one detail model in the client or server VM.

Exclusive Access Pattern (locking)

Since a detail model is a singleton, the access to the model must be synchronized in order to be thread safe.
For example, the data of the detail model might be invalidated while you are currently working with it (because a different client modified the same object).

In order to prevent any concurrency problems you will need to obtain exclusive access on the detail model.
For this purpose, acquire and release methods are available which provide reentrant high concurrency synchronization (multiple reads, single write).

It is absolutely necessary to strictly follow the following acquire/release pattern, otherwise serious problems may arise:

Concurrent Read Access Pattern
EntityDetailModel detailModel = ...;
detailModel.acquireRead();
try
{
//read data from the detail model or it's data object
//but, only READ, write is not permitted with a read lock!
//do NOT call asynchronous events or something similar, this might cause deadlocks!
}
finally
{
detailModel.releaseRead();
}
Exclusive Write Access Pattern
EntityDetailModel detailModel = ...;
detailModel.acquireWrite();
try
{
//read or write data from/to the detail model or it's data object
//do NOT call asynchronous events or something similar, this might cause deadlocks!
detailModel.save();
}
finally
{
detailModel.releaseWrite();
}

Why is this code block so important?

If you check the acquireRead method you will see that it declares a CoreException to be thrown in case of any problem.
In case a core exception really is thrown while you acquire the read lock, you must not release the lock - or you will run into an IllegalMonitorState exception like this:

java.lang.IllegalMonitorStateException: Non-owning thread tried to release detail model for entity item 'com.heiler.ppm.std.core.entity.EntityProxyRevision@1d80945[EntityProxy=10001[|]{#}18[|],RevisionToken=RevisionToken[ID=1]]' which should never happen. Owning write thread is '<unknown>'. Number of concurrent read threads '0'. Thread tried to release is 'main'. Please make sure that you always follow the try/finally pattern with detail model's acquire and release.

You might believe, this is a very unlikely situation, because the acquireRead/acquireWrite methods surely won't throw an exception, but this is wrong.
AcquireRead will also throw a CoreException in case the current user has no read permission on this detail model - acquireWrite will throw it in case the current user has no write permission.
So you see, this might very well occur quite often.

On the other side, when the acquire methods succeed, you must make sure to call also the release methods (that's what the finally block is for).
So it's the best approach to have no other code line between the acquire call and the try.

Additionally to this most important pattern, you're also not allowed to perform an acquireWrite from within an acquireRead.
The detail model allows no lock upgrade since multiple threads can simultaneously read the detail model.

Always use the correct locking. If you really only read the detail model, then use acquireRead. AcquireWrite will give you exclusive access for the cost of performance.

Usability Shortcut for "read a single attribute"

Internally, the detail model is implemented in a thread safe manner, which means, the methods you may call on the detail model are internally synchronized in such a way that they can be treated as atomically. Like always when you have objects which are designed thread safe, multiple method calls to these objects aren't thread safe since a different thread could always interfere in between the method calls - that's why it's absolutely necessary to follow the pattern above when you call multiple methods on the detail model which must be treated together as "atomic operation" (like get data graph, modify data graph and save detail model (with the modified data graph))

However, sometimes you only need a single attribute and thats it, for this you can omit the complete acquire/release locking and just call detailModel.getFieldValue(...).

List Model

The list model is an in-memory data structure comparable to a table. A column is represented by a ListColumn and a row by a ListEntry. The field values of a list entry can be accessed with the index of a specific column.

List models can either be created from scratch using the ListModelFactory or loaded from the data storage using the ListModelLoadService

images/download/attachments/29819128/image2012-4-12_15_14_50.png

  • Non-virtual list model
    When loading a non-virtual list model, the whole content for the model (including all data/values) is loaded. This can take a long time in case of handling mass data and does not always make sense as there may be no need to access all the data in a list model all at once.

  • Virtual list model
    If using a virtual list model, the list model will not contain all data initially. Only a skeleton container of the model is loaded instead. The consequences are higher performance when obtaining the list model and a lower memory usage. If one tries to access data that is not yet available, the list model fetches the requested data (and a configured additional offset) from the database.