DWR

Offer a "lighter" bi-directional class-mapping scheme & Allow alternative class-mapping schemes

Details

  • Type: Improvement Improvement
  • Status: Resolved Resolved
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: 3.0.RC1
  • Component/s: Converters
  • Description:
    Hide

    The current class mapping scheme was designed to be compatible with JavaScript classes to be able to benefit from native constructors, instanceof support and class inheritance. Though, in some cases it is sufficient to only inspect, or set, the actual class identity and not use any of the other features. Some properties of the current class mapping scheme may then stand in the way for an efficient implementation, f ex not being able to say that an existing type-less object should be parsed as a certain class on the server (Randy's case) without actually recreating the object and calling the appropriate constructor.
     
    I have two variations on a suggestion that solves this, and they both depend on using the "$dwrclassname" or "$dwrserverclassname" properties introduced above, but on the actual instances.
     
    Variant 1: activate "light" mapping style instead of the normal mapping style
     
    This would imply a global setting in web.xml to tell DWR what class mapping scheme to use:
     
        <init-param>
            <param-name>lightClassMapping</param-name>
            <param-value>true</param-value>
        </init-param>
     
    The class mapping behaviour would change in the following way:

        * No constructor functions nor any other JavaScript class-related code generated by the DWR servlet for mapped classes.
        * An outbound object of a mapped class will get its class identity annotated as a property instead of a constructor call, ie:
              s101={name:"Donald","$dwrclassname":"db.Person"}
          instead of:
              s101=new db.Person();s101.name="Donald";
        * Inbound objects will be inspected for a "$dwrclassname" property and if there is a matching mapped class, the corresponding Java class will be instantiated. Thus, if the browser code wants to specify the class of an untyped object, it just has to add the "$dwrclassname" property on the object.

    Note a few things in the above suggestion:

       1. The "$dwrclassname" property is placed on the objects instead of on the class, as there is no class.
       2. The "$dwrclassname" property still specifies the mapped JavaScript class name, not the Java class name (ie db.Person and not mypkg.dto.Person from the previous examples).
       3. Only classes that have been set-up for conversion and also set-up for mapping through the "javascript" attribute are allowed to be created using the inbound "$dwrclassname" feature, to stop evil hackers that want to create dangerous objects on the server.

    Variant 2: activate "light" mapping style in addition to the normal mapping style
     
    This means keeping the normal mapping style on all mapped classes (with a "javascript" attribute), and using the light mapping style as a default on the remaining bean classes that are not mapped (without "javascript" attributes). This would work the same way as variant 1, but with a few differences:

        * No web.xml enabling configuration needed as this behaviour is always present.
        * Outbound data will be annotated with "$dwrserverclassname" and the Java class name, as there is no mapped JavaScript class name.
        * Inbound data will also have to use "$dwrserverclassname" instead, and specify the Java class name.
        * Security-wise we will only allow Java classes set-up for conversion to be created by inbound data, which is a little bit broader than variant 1.


    Some questions:

    > Use-cases for lighter class mapping?
    The request I'm mainly thinking about was from a long time ago on the list. I haven't saved a link to it, but the main theme of that request (AFAICR) was that:
    1) They didn't need the extra benefits provided by the JavaScript classes. They just wanted to be able to detect types on objects.
    2) The types needing class mapping were not known before run-time (hm, possibly because of some internal abstraction/plug-in framework?) so they were not able to mark the desired classes for mapping in dwr.xml before being deployed and started.
    3) Thus they needed class mapping that was "always on" and their only solution would be to enable class mapping for all converted classes, but that would potentially mean mapping a lot of classes with the associated overhead of our generated JavaScript class code.
    Continuing to Randy's case I think it can be summarized as:
    4) Make it possible to use class-mapping for client-side objects that are not created by our generated constructors (because not having control over object creation etc).
     
    > Not good to expose Java class names
    Variant 1 does not expose Java class names.
     
    > Extending signatures to solve this?
    I understand Joe's suggestion as telling the signatures system to use a certain concrete class for all calls to a certain method accepting non-concrete arguments.
    I'm not sure it's worth the effort to add this behaviour to the signatures handling as it doesn't solve handling of multiple subclasses or client-side type decisions. And the static setup possible with signatures may alternatively and easily be done by wrapping the abstract-argument method with a concrete ditto on the server.
     
    > Tweak getObjectClassName to fix Randy's case?
    Well, if this becomes an official and documented solution then we are in fact implementing half (the client-side stuff) of my "Plug-in API for class mapping" suggestion below ;-).
     
    And here's some new material for discussion:
     
    How can we let the client code know the type of outbound objects?
    In DWR 2.0 and current 3.0 there are two very different systems in effect: (1) outbound exceptions have a property naming their Java class name and, (2) mapped classes are associated with a JavaScript class (named from the mapping name in dwr.xml) by being instantiated with a generated constructor.
    My suggestion is to make it possible to attach type info much like is already done for exceptions, but for all other object types. This is by itself not a big thing, and we can choose to only attach the mapping name (and not the Java name) if that feels better security-wise.
     
    How can the client code let the server know the desired type of inbound objects?
    Currently this is done by looking up the JavaScript class that the object belongs to.
    Though, I'm sensing that we are agreeing that manually setting $dwrclassname could be allowed to override our default type-detection?
     
    Updated suggestion
    If the above is ok, and implementing a plug-in API for class mapping is not interesting at all but having a lighter class-mapping scheme is a little interesting, then we could do the following short-cut to make this really simple:

        * skip having any settings for class-mapping mode
        * move generated mapped classes to a separate file (/dwr/classes.js) [discussed in other thread]
        * the only other server-side change is to send class-mapped outbound objects like this in DWRP:
            s101=dwr.engine._createObject("Person");
          instead of like today:
            s101=new Person();
        * createObject() would look up if there is a constructor function named "Person" and
            if exists: return an object created using the constructor
            if not exists: return an Object with type as member: { "$dwrclassname": "Person" }
        * client-side type-detection should look for $dwrclassname overrides on objects sent to server

    This solution would make it possible to choose between light and full class-mapping just by deciding whether to include the generated classes from /dwr/classes.js or not.
    If the generated classes are not present then we would automatically fall back to light class-mapping using the $dwrclassname members instead.
    I think this is a coherent and symmetrical solution and I quite like it.

    Show
    The current class mapping scheme was designed to be compatible with JavaScript classes to be able to benefit from native constructors, instanceof support and class inheritance. Though, in some cases it is sufficient to only inspect, or set, the actual class identity and not use any of the other features. Some properties of the current class mapping scheme may then stand in the way for an efficient implementation, f ex not being able to say that an existing type-less object should be parsed as a certain class on the server (Randy's case) without actually recreating the object and calling the appropriate constructor.   I have two variations on a suggestion that solves this, and they both depend on using the "$dwrclassname" or "$dwrserverclassname" properties introduced above, but on the actual instances.   Variant 1: activate "light" mapping style instead of the normal mapping style   This would imply a global setting in web.xml to tell DWR what class mapping scheme to use:       <init-param>         <param-name>lightClassMapping</param-name>         <param-value>true</param-value>     </init-param>   The class mapping behaviour would change in the following way:     * No constructor functions nor any other JavaScript class-related code generated by the DWR servlet for mapped classes.     * An outbound object of a mapped class will get its class identity annotated as a property instead of a constructor call, ie:           s101={name:"Donald","$dwrclassname":"db.Person"}       instead of:           s101=new db.Person();s101.name="Donald";     * Inbound objects will be inspected for a "$dwrclassname" property and if there is a matching mapped class, the corresponding Java class will be instantiated. Thus, if the browser code wants to specify the class of an untyped object, it just has to add the "$dwrclassname" property on the object. Note a few things in the above suggestion:    1. The "$dwrclassname" property is placed on the objects instead of on the class, as there is no class.    2. The "$dwrclassname" property still specifies the mapped JavaScript class name, not the Java class name (ie db.Person and not mypkg.dto.Person from the previous examples).    3. Only classes that have been set-up for conversion and also set-up for mapping through the "javascript" attribute are allowed to be created using the inbound "$dwrclassname" feature, to stop evil hackers that want to create dangerous objects on the server. Variant 2: activate "light" mapping style in addition to the normal mapping style   This means keeping the normal mapping style on all mapped classes (with a "javascript" attribute), and using the light mapping style as a default on the remaining bean classes that are not mapped (without "javascript" attributes). This would work the same way as variant 1, but with a few differences:     * No web.xml enabling configuration needed as this behaviour is always present.     * Outbound data will be annotated with "$dwrserverclassname" and the Java class name, as there is no mapped JavaScript class name.     * Inbound data will also have to use "$dwrserverclassname" instead, and specify the Java class name.     * Security-wise we will only allow Java classes set-up for conversion to be created by inbound data, which is a little bit broader than variant 1. Some questions: > Use-cases for lighter class mapping? The request I'm mainly thinking about was from a long time ago on the list. I haven't saved a link to it, but the main theme of that request (AFAICR) was that: 1) They didn't need the extra benefits provided by the JavaScript classes. They just wanted to be able to detect types on objects. 2) The types needing class mapping were not known before run-time (hm, possibly because of some internal abstraction/plug-in framework?) so they were not able to mark the desired classes for mapping in dwr.xml before being deployed and started. 3) Thus they needed class mapping that was "always on" and their only solution would be to enable class mapping for all converted classes, but that would potentially mean mapping a lot of classes with the associated overhead of our generated JavaScript class code. Continuing to Randy's case I think it can be summarized as: 4) Make it possible to use class-mapping for client-side objects that are not created by our generated constructors (because not having control over object creation etc).   > Not good to expose Java class names Variant 1 does not expose Java class names.   > Extending signatures to solve this? I understand Joe's suggestion as telling the signatures system to use a certain concrete class for all calls to a certain method accepting non-concrete arguments. I'm not sure it's worth the effort to add this behaviour to the signatures handling as it doesn't solve handling of multiple subclasses or client-side type decisions. And the static setup possible with signatures may alternatively and easily be done by wrapping the abstract-argument method with a concrete ditto on the server.   > Tweak getObjectClassName to fix Randy's case? Well, if this becomes an official and documented solution then we are in fact implementing half (the client-side stuff) of my "Plug-in API for class mapping" suggestion below ;-).   And here's some new material for discussion:   How can we let the client code know the type of outbound objects? In DWR 2.0 and current 3.0 there are two very different systems in effect: (1) outbound exceptions have a property naming their Java class name and, (2) mapped classes are associated with a JavaScript class (named from the mapping name in dwr.xml) by being instantiated with a generated constructor. My suggestion is to make it possible to attach type info much like is already done for exceptions, but for all other object types. This is by itself not a big thing, and we can choose to only attach the mapping name (and not the Java name) if that feels better security-wise.   How can the client code let the server know the desired type of inbound objects? Currently this is done by looking up the JavaScript class that the object belongs to. Though, I'm sensing that we are agreeing that manually setting $dwrclassname could be allowed to override our default type-detection?   Updated suggestion If the above is ok, and implementing a plug-in API for class mapping is not interesting at all but having a lighter class-mapping scheme is a little interesting, then we could do the following short-cut to make this really simple:     * skip having any settings for class-mapping mode     * move generated mapped classes to a separate file (/dwr/classes.js) [discussed in other thread]     * the only other server-side change is to send class-mapped outbound objects like this in DWRP:         s101=dwr.engine._createObject("Person");       instead of like today:         s101=new Person();     * createObject() would look up if there is a constructor function named "Person" and         if exists: return an object created using the constructor         if not exists: return an Object with type as member: { "$dwrclassname": "Person" }     * client-side type-detection should look for $dwrclassname overrides on objects sent to server This solution would make it possible to choose between light and full class-mapping just by deciding whether to include the generated classes from /dwr/classes.js or not. If the generated classes are not present then we would automatically fall back to light class-mapping using the $dwrclassname members instead. I think this is a coherent and symmetrical solution and I quite like it.

Activity

Hide
Mike Wilson added a comment - 05/Sep/08 9:50 PM

I have now checked in the following enhancement:

It is now possible to use class mapping without the generated JavaScript classes. When a generated class is not included in the current page, the class-mapping system will use the "light" mapping mode for this class.
This means that mapped objects sent from server to client will be created as ordinary JavaScript Objects annotated with a $dwrClassName property to identify the object's class.

When sending objects from client to server the same property is used to attach type information on objects.
This property on instances takes precedence over class information from the JavaScript class, so the instance property may be used to override class identity for objects even when they belong to a loaded JavaScript class.

Naturally, this lighter class-mapping scheme does not support the use of instanceof to determine object/class relationships.

Show
Mike Wilson added a comment - 05/Sep/08 9:50 PM I have now checked in the following enhancement: It is now possible to use class mapping without the generated JavaScript classes. When a generated class is not included in the current page, the class-mapping system will use the "light" mapping mode for this class. This means that mapped objects sent from server to client will be created as ordinary JavaScript Objects annotated with a $dwrClassName property to identify the object's class. When sending objects from client to server the same property is used to attach type information on objects. This property on instances takes precedence over class information from the JavaScript class, so the instance property may be used to override class identity for objects even when they belong to a loaded JavaScript class. Naturally, this lighter class-mapping scheme does not support the use of instanceof to determine object/class relationships.
Mike Wilson made changes - 05/Sep/08 9:50 PM
Field Original Value New Value
Resolution Fixed [ 1 ]
Status Open [ 1 ] Resolved [ 5 ]
Assignee Joe Walker [ joe ] Mike Wilson [ mikewse ]
Fix Version/s 3.0.M2 [ 10051 ]

People

Dates

  • Created:
    11/Jul/08 5:12 PM
    Updated:
    05/Sep/08 9:50 PM
    Resolved:
    05/Sep/08 9:50 PM