[refactoring][beans] BeanData should be able to fork
----------------------------------------------------
Key: LABS-232
URL: https://issues.apache.org/jira/browse/LABS-232
Project: Labs
Issue Type: New Feature
Components: Magma
Affects Versions: Current
Reporter: Simone Gianni
Assignee: Simone Gianni
Fix For: Future
Currently every class has one BeanData. This class holds all magma specific
metadata about the class, giving access to PropertyInfo for each property. That
sums up introspection results, annotations and so on. Every package that needs
to add stuff will do it adding it to BeanData or PropertyInfo.
Also, all magma component communicate with the bean using a Handler. The
Handler itself uses BeanData to perform property setting/getting, validation,
formatting and the like.
Anyway, having a single representation of a class is limiting. There is the
need to change this data depending on the current context. For example, a
certain field could be visible or not depending on the role of the current
user, or the view we want to offer. Also validation could change, making some
properties required where they where not, or even adding temporary properties.
Information currently held in this structure is very wide, and goes from
validation to formatting to structural data (like, if it is or not persisted on
the database).
Currently there is a system in place, called ViewCustomizer, which is able to
modify dynamically part of it for a specific need, like hiding or expanding a
property in a specific view. While this system works correctly, it cannot be
expanded to also embrace validation, formatting and everything else. So we need
a different approach.
First of all, we need a way to "fork" different beanDatas, and provide a way to
define which one to use in a specific "scope". The problem is not forking it (a
deep clone is okay, prototype pattern), nor modifying the new "branch"
(ViewCustomizer could be extended, giving it a further extensible API, and
having people implement it). Defining the scope is a problem.
Unfortunately in java the only thing that is "scope dependent" is the stack.
So, we will probably have to pass the modified bean data, or the bean data
modifier, instead of the class/bean where we need to.
So, for example :
return new SmartForm(new WiseModifier(userBean));
To display a form using the WiseModifier. The WiseModifier class should be a
class extending a specific base class, and implementing a specific method. For
example :
public class WiseModifier extends BeanDataModifier {
public WiseModifier() {
super(User.class);
}
public void modify() {
makeRequired("name");
addFormatter("birthDay", new DateFormatter().setFormat("short"));
}
}
In this way, the modifiers will be classes, and as such (thru cglib eventually)
compiler friendly and statically determined. Also, the non modified bean data
could be obtained with a "null modifier", or simply still passing the Class
object only (this would also provide backwards compatibility, keep the easy
way, and provide an incremental way to implement this new system, cause new
methods would simply be overriding the default, class based ones).
It could also be possible, eventually using a CompoundModifier, to combine
different modifiers. So, we could have a modifier that hides a certain part of
a bean, another one that imposes a number of additional checks, and then use
one, the other, or both in a certain context.
Another important aspect could be caching of the resulting beanData. Creating a
beanData is an expensive job, cause it involves reflection, annotation parsing,
creation of a number of (mostly thread safe and stateless) objects like
converters, formatters, validators etc..
Since forking a bean data could be a lighter job (deep cloning as opposed to
reparsing), proper investigation should be done on the effective timing of the
deep cloning and manipulations. If caching is necessary, then a number of
techniques could be exploited to obtain it.
A modifier could a stateless one, thus applying always the same modifications.
In that case, cache it and stop bothering.
A modifier could have a state that is determinable. For example, a number of
private fields, and a number of if statements based solely on those private
fields. In this case, caching is still possible cause we know the state.
A modifier could have a state which is not determinable, for example an if
statement calling System.currentTimeMillis. In that case no caching is
possible, and a proper warning should be given to the user.
To limit the possibility of uncachable items, the API should be planned from
the ground up with minimal connections to the outside world, or a predefined
way of connecting to it, like getters and setters only, or constructor
parameters only and so on.
(Note that this is exactly what happens in every do-method of any web handler,
in fact the same design principles were in place for the web API where caching
plays an important role. Handlers have a predefined "protocol" with the outside
world, thru getters and setters, making the state determinable in most cases.
this means that the same caching system used in the web part could be applied
to this part if needed)
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]