Labs
  1. Labs
  2. LABS-232

[refactoring][beans] BeanData should be able to fork

    Details

    • Type: New Feature New Feature
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: Current
    • Fix Version/s: Next
    • Component/s: Magma
    • Labels:
      None

      Description

      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)

        Activity

          People

          • Assignee:
            Simone Gianni
            Reporter:
            Simone Gianni
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:

              Development