Hi Jean-Baptiste,
Am 16.06.2009 um 14:47 schrieb Jean-Baptiste BRIAUD -- Novlog:
> On Jun 15, 2009, at 18:54 , Andreas Junghans wrote:
>
>> Hi Jean-Baptiste,
>>
>> Am 15.06.2009 um 17:21 schrieb Jean-Baptiste BRIAUD -- Novlog:
>>
>>> Why do I have to filter java.lang.Class or
>>> java.lang.reflect.Method ?
>>
>> "class" is filtered out in the default implementation to avoid
>> unnecessary traffic and/or reference cycles.
> So, why do I have to filter it in my own filter was the real
> question :-) ?
You don't have to filter it yourself (at least that's how it should
be). A call to super.filter() should be enough.
Maybe I should provide (still) some more background info about the
filtering: The basic idea is to filter out certain attributes, not
individual classes. The parameters passed to the filter method are (1)
the object that's about to be converted and (2) a map of the object's
properties (as determined by the RPC runtime). Let's say you have the
following class:
public class MyBean {
private int x;
public void setX(int x) { this.x = x; }
public int getX() { return x; }
}
If an instance of this class is returned by an RPC call, the filter
method receives the following parameters:
- obj: The instance of MyBean
- map: A map containing the following keys and values:
- x: 1234 (the value that's returned by calling getX() on the
instance)
- class: MyBean.class (because java.lang.Object provides a
getClass() method)
The default implementation of filter() looks like this:
protected Map filter(Object obj, Map map) {
map.remove("class");
return map;
}
So the "class" attribute is removed, preventing it from being
converted to JSON form. However, if you have a bean with a getFoo()
method that returns a class object, the filter doesn't catch this. In
this case, you'd have to remove "foo" from the map too. So the whole
filtering process is based on attribute names, not class names.
>> The root of the problem
>> is that every Java object has a method "getClass" (which matches the
>> JavaBean pattern). So without filtering, the RPC implementation would
>> try to serialize the Class objects of all instances that are
>> transmitted. And since java.lang.Class has lots of getXYZ() methods
>> (some of them forming reference cycles IIRC), the JSON representation
>> quickly explodes ...
> I fully agree and because I had to filter it manually, I asked the
> question but now I guess I probably filter the wrong way !
> Here is how I did it, could you, please, revue my code :
>
> protected RemoteCallUtils getRemoteCallUtils() {
> return new RemoteCallUtils() {
>
> protected Map filter(Object obj, Map map) {
> final String className = obj.getClass().getName();
> // System.out.println("----------- filter : " +
> className);
> if ("java.lang.reflect.Method".equals(className) ||
> "java.lang.Class".equals(className) )) {
> map.clear();
> return map;
> }
> removeUnwantedProperties(map); // This method remove
> my "user land" unwanted property
> return super.filter(obj, map);
> }
>
> Should I do "my stuff" before or after the super.filter() ?
That doesn't matter. You can do your own filtering before or after (or
not call the super class at all). Please note that in your
implementation, you prevent Class objects from being transmitted, but
not the associated properties. Again, let me provide an example:
public class MyBean {
private int x;
public void setX(int x) { this.x = x; }
public int getX() { return x; }
public Class getFoo() { ... }
}
With your imlementation, the generated JSON string looks something
like this (provided you don't remove "foo" from the map in
removeUnwantedProperties() - but in this case, you wouldn't need the
extra check for java.lang.Class):
{ x: 1234, foo: {} }
The problem is that you're trying to filter by class name, but the
original design is based on filtering by attribute name. There's
nothing wrong with a class-based filter, but the current mechanism
doesn't really support it. To completely get rid of certain classes,
you'd have to iterate over the attribute map and check the classes of
the values (instead of checking the class of the obj parameter).
>> You don't have to filter java.lang.reflect.Method, at least not when
>> you're transmitting simple objects. However, if there's a getXYZ()
>> method in one of your objects that returns a java.lang.reflect.Method
>> (or array thereof), then it should be filtered. Just look at the Java
>> API documentation of Method so see why: There's stuff like
>> getReturnType() that returns a Class. And regarding Class objects,
>> see
>> above ...
> Same thing : I fully agree but I had to filter it in my own filter ...
>
>>
>> Maybe I should provide some background info about the filtering
>> mechanism. In general, when you try to convert Java objects to a
>> String representation, there are two choices:
>>
>> 1.) Explicitly specify which properties get serialized. This requires
>> some sort of meta data, e.g. the WSDL in case of web services. Or you
>> can put up a requirement that every object needs to implement a
>> specific serialization API (e.g. java.io.Serializable), which is also
>> some kind of meta data.
>>
>> 2.) Serialize all properties by default. In this case, you have to
>> provide a filter mechanism to avoid unnecessary traffic and reference
>> cycles (see above).
>>
>> Both approaches have their pros and cons.
> Same as before : I fully agree
>
>> qooxdoo's Java RPC
>> implementation uses the second one to ease development and
>> deployment.
>> If you transmit simple objects, it works quite well. However, if you
>> take existing, complicated business objects, you may have to adjust
>> the filter. You could argue that the default filter list could be
>> more
>> extensive, but this would be a never-ending story: Many JDK classes
>> are not easily serializable to a String, so you could end up with
>> thousands of filter entries that would be checked before each
>> transmission.
>>
>> You could also argue that only objects implementing
>> java.io.Serializable should be transmitted, but that would be very
>> annoying IMHO. Lots of objects in many libraries and existing
>> applications are not implementing Serializable, even when they can be
>> converted to JSON without issues.
>>
>> We could debate the different approaches all night long :-) If you
>> don't agree with the one chosen in qooxdoos Java RPC implementation,
>> take a look at the other Java RPC libraries out there. Maybe you can
>> find one that suits your needs better (or mabye not - I don't know in
>> detail how the different libs handle serialization).
>>
>> One last remark about the filtering: Even when your transmission
>> works
>> (no reference cycles), you should still take a look at the data
>> stream. In many cases, you can reduce the size of the JSON string
>> greatly by more aggressive filtering.
>
> True. That's why I'm thinking about adding one feature :
> Have both 1. and 2. approachs.
> Use annotation to tag some POJO at class level, say @C
> All the non @C classes will be kept with the current filtering
> behavior
> For those who have the @C, and only that one, filter will keep only
> the taged property (at attribute or getter level), say @A.
> Only @A in the classes who is @C will be kept.
> Benefits :
> * for that cases, the filtering do not have to be dynamic. No need to
> check at runtime and everytime if the property is to keep or reject. A
> cache could be done for that since annotation won't change.
> * this would be a comprimise between 1 and 2 : have both, keep the
> current behavior if nothing is done but allow new behavior only if you
> want
> * handle well the case of existing business object.
Using annotations is a good idea. The current RPC code started out in
a project where JDK 1.4 compatibility was required, so it wasn't
possible to use annotations. This is also the reason why the
RemoteService interface and the RemoteServiceException are used for
tagging RPC services: It's more or less a workaround for the missing
annotations.
However, JDK 1.4 doesn't seem particularly important anymore, so
adding annotations could make things cleaner and easier.
> What about the case of a class tagged @C where you would like to keep
> the dynamic filtering ability ? Use @D.
> Any property (at getter level or attribute level) that would have @D
> (only for a @C class) will go to the filter and the answer to keep or
> reject could be done based on rules or any code in the user land (aka
> a subclass of the servlet like now)
>
> In summary, 3 anotations, no changes to existing code base, no change
> to existing behavior but a way tp have a static approach (with the
> hope or more speed) when needed.
>
> Example :
>
> @C
> public class Person {
>
> @A
> String name
>
> String complicatedThingsOnlyForTheServer
>
> @D
> String ClientPromoCode
> }
>
> Because Person is @C, the "new" behavior is applied :
> * complicatedThingsOnlyForTheServer will never be JSONified, this
> become structural.
> * name will always be JSONified, this become structural.
> * ClientPromoCode may or may not be JSONified, depending on filter
> code in the RPC subclass.
>
> We may improve one thing to let the developper be as less as possible
> verbose :
> @A specify the property to kept, we could imagine to have the
> opposite : @R to reject a property.
> So, @C(defineR) will let you define the @R and the others are kept by
> default
> @C(defineA) will let you define the @A and the others are rejected by
> default
> @C is equal to @C(defineA)
>
> => depending on classes, one can only have to write the less !
In general, this is a good idea. However, I'd move @A and @R to the
getter methods (and of course find proper names for them ;-)). After
all, the transmitted values are determined by calling the getters, and
you can have a getter with no directly associated member variable (or
simply another naming standard like mName or _name).
I don't have the time to implement and test annotation support myself.
But if you could provide a patch, I can hopefully review it and send
you some feedback. Besides annotation support, an optional class-based
filter (that also removes the attributes instead of creating an empty
map) would also be useful. Oh, and you should take a look at Michael
Wyraz' refactoring:
http://qooxdoo-contrib.svn.sourceforge.net/viewvc/qooxdoo-contrib/trunk/qooxdoo-contrib/RpcJava/branches/test/
It's a more modular implementation of the RPC code and also supports
generics. Unfortunately, I never got around to give this code a proper
review (shame on me!), but it's probably a better starting point for
patches than the trunk version. Did I already mention that we badly
need an active maintainer for the Java RPC stuff? Any candidates
(Michael, Jean-Baptiste)? :-)
Regards,
Andreas J.
------------------------------------------------------------------------------
Crystal Reports - New Free Runtime and 30 Day Trial
Check out the new simplified licensing option that enables unlimited
royalty-free distribution of the report engine for externally facing
server and web deployment.
http://p.sf.net/sfu/businessobjects
_______________________________________________
qooxdoo-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/qooxdoo-devel