Module: Model - ninazeina/SXP GitHub Wiki
The model module describes all the data types, the different formats that they may need to take, whether they should be signed or not, etc. It also provides the database that records them. It provides the basic manager that deals with saving/modifying/searching for objects in the database, and an asynchronous wrapper on top of it.
- api : Interfaces for managers and entity validation
- factory : Creates the managers
- entity : The beans carrying data across the whole application.
- validator : For validating the content of an entity.
- syncManager : For storing/retrieving entities locally.
- Manager : For storing/retrieving entities asynchronously, when we go distributed.
Usually data types are provided as interfaces in the module's api, but here this is not the case: they are all in entity.
Managers deal with storing/retrieving entities. They can be synchronous for dealing with storing/retrieving entities in the local database only. Usually this should be avoided! Or asynchronous for dealing with storing/retrieving entities on the network. The asynchronous ones can be decorated to pile up different things to do upon storing/retrieving entities, see Architecture.
Validators check that the entities are well-formed.
ManagerFactory combines the different managers available to create a fully decorated manager that not only stores/retrieve entities locally, but also saves them and searches for them across the network. These further behaviours are provided as ManagerDecorator by controller/managers.
SyncManagerFactory provide undecorated, synchronous managers for dealing with storing/retrieving entities in the local database only. Usually this should be avoided!
Describes the data types of the application, together with annotations on each attributes to specify:
- the expected formats (e.g. @NotNull)
- whether this must be serialized, via JPA, when placed in the database (e.g. @Lob)
- how to wrap it in JSON forma, via JsonTool, when the controller needs it (e.g. @JsonFormat/@JsonDeserialize)
Here is an example illustrating this
package model.entity;
import ...
@XmlRootElement //convertible to XML or JSON
@Entity //convertible via JPA
public class Item {
@XmlElement(name="title")
@NotNull
@Size(min = 3, max = 255)
private String title;
@XmlElement(name="createdAt")
@NotNull
@Temporal(TemporalType.TIMESTAMP) //specify database date format
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="dd-MM-yyyy") //specify JSON date format
private Date createdAt;
@XmlElement(name="pbkey")
@NotNull
@Lob //This is too big for the database and so it needs to be serialized
@JsonSerialize(using=controller.tools.BigIntegerSerializer.class) //how to serialize
@JsonFormat(shape=JsonFormat.Shape.STRING)
private BigInteger pbkey;
...
}
Checking the formats of the entities is done by the validators. This really just wraps up javax validation, which interprets the above-described annotations and computes violations.
Provides the basic manager that deals with saving/modifying/searching for objects in the database. This basic manager is synchronous: modifying the database takes immediate effect, searching the database leads to immediate results. However, you have to take into account two things:
- in order to modify a database, you need to take the lock upon it, so that no one else enters, and then release it when you are finished;
- often you have a java object living in the RAM, which is also saved in the database. Instead of modify the database every time whenever you modify the java object in the RAM, it would be nice if this could happen automatically.
These two considerations lead to the idea there are java objects in the RAM that are being 'watched'. If you want to modify them, you should take the lock on the database, modify the objects in the RAM (which will automatically produce the same change in the database), and release the lock.
Thus, here is the interface of a SyncManager:
- begin(): take the lock upon the database
- persist(e): add an entity e to the watch list and attempt to save it from the RAM to the database. This should be done between begin() and end().
- e=find...(...): search for an entity in the database, load it in the RAM, add it to the watch list. This should be done between begin() and end().
- end(): release the lock and return the list of entities that have been successfully changed (no rollback) in the database.
- watchlist(): (TO BE DONE) return the list of entities that are being watched.
- refresh(): (TO BE DONE) regularly called, use it for periodic maintenance tasks.
The way this is implemented, this is really just a wrapping up of JPA, the javax persistence api, standard for dealing with databases.
At the level of the controller, the manager needs to be asynchronous: writing is but sending on a network, searching may eventually produce a result, but we will be notified when this happens. They can be piled up according to the Decorator Design Pattern, see Architecture.
However, you have to take into account two things:
- If a database is in the loop, you may need to take the lock upon it, and then release it when you are finished;
- often you have a java object living in the RAM, which is also somewhere on the network/database. Instead of having to manually inform the network/database every time whenever you modify the java object in the RAM, it would be nice if this could happen automatically.
These two considerations lead to the idea there are java objects in the RAM that are being 'watched'. If you want to modify them, you should take a lock, modify the objects in the RAM (which will automatically produce the same changes across the network/database), and release the lock.
Thus, here is the interface of a Manager:
- begin(): take the lock
- persist(e): add an entity e to the watch list and save it. This should be done between begin() and end().
- e=find...(..., l): search for an entity. If successful, Listener will be notified and object added to watchlist. This should be done between begin() and end().
- end(): release the lock and (TO BE DONE) return the list of entities concerned by the changes.
- watchlist(): (TO BE DONE) return the list of entities that are being watched.
- refresh(): (TO BE DONE) regularly called, use it for periodic maintenance tasks.
Here is basic use case
em.find...(ManagerListener l); //retrieve stuff
Class Listener {
notify(User u){ //called once the user is retrieved
em.begin()
u.setNick("toto")
em.end()
}
Here, we do not actually provide any such manager. We just provide a wrapper, that converts a SyncManager into a Manager.
You can edit the model to add properties(for exemple). You had to use JPA annotations : Wikipedia : JPA
Each time you edit the model, you have to delete the database (.db-XXXX folders) to avoid some conflicts. JPA will create the database again from scratch.
If the entity need to be an entire Database Table (like User and Item) add the class to src/main/ressources/META-INF/persistance.xml
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>model.entity.Item</class>
<class>model.entity.User</class>
<class>model.entity.YourClass</class>