Friday, September 29, 2017

Re: Problems moving JsInterop from GWT 2.7 to 2.8

We have been using this technique for more than a year and it works perfectly. We call it "Java Notation Object", hehe and you should understand the limitations, which more or less you are currently fulfilling. Just a few changes...

The most important, yes, you should use @JsType(isNative=true,namespace=GLOBAL,name="Object"), so you are actually ignoring any kind of typing, so this is more a "scheme" of where to find things than an actual typed object. This is pretty important! This is pretty similar to JSON but in Java, and these classes are not actually Java classes, are just a scheme. All these limitations are the common intersection of the feature between Java and JS.

You can do some tricks to use collections but we end up removing all of them and using arrays instead. But, to make it easy to work with, we added some @JsOverlay to return or set the Collection. For example if you have "String[] roles" you can add "@JsOverlay getRoleSet() { return Stream.of(roles).collecto(toSet()); }". Overlays are ok bc there are just like extension method or static method, so no behavior is added to the object.

This work perfectly on both sides, but there is a small annoying difference between GWT and Java, in java new Foo() will initialize primitive types and in GWT (js) will not. So we never use constructor directly, in the kind of classes we always add a static factory method and we initialize any non-nullable field, even primitives! So in java you are actually creating the object of the specified type, but in JS you are just calling "new Object()", but it is ok, bc where are using a scheme, no a type. And we never use instanceof on these schemes! This will work on the Java side, but I strongly discourage, you should not need to do that.

Finally, we never use getter and setter, don't make sense, you "cannot use inheritance" (you can use with some ticks too, for example using some int or string field as the type indicator and using, for example, the visitor pattern to traverse, this is what actually people need to do in JS so, no magic just the common intersection of features between java and json), almost everything is like final and you must not add behavior, so it is much explicit and easier to just make all field public.

I think the first you should do is removing getters and setters, and add @JsType(native,Object) to all your types, this refactor can be done almost instantly in your IDE (inline method). Then stop using Collection might not be so easy to refactor. So you might continue using it either using overlays or creating a custom jsinterop-compatible type. In this case, I recommend to not to use the whole java.util API bc it will force you to export the whole API and use generateJsInteropExports. If you are really interested or need help there just ask, but just try out avoiding maps, and using arrays. Anyways, the "Map/List own interface" should work too, just neet to make it work.

On Fri, Sep 29, 2017 at 12:53 PM Thomas Broyer <t.broyer@gmail.com> wrote:


On Friday, September 29, 2017 at 11:09:42 AM UTC+2, Jürgen Beringer wrote:
Hey,

currently  I tried the 2. time to move our application from gwt 2.7 to gwt 2.8, but I have big problems with the changes of JsInterop.

The application contains a serverside java part, running in a jetty and the client part, build with gwt, so I can reuse many software on server and client side. (It is the main part of the website  www.spreadshirt.com )
To use also the same data classes (containing only fields with getter and setter, no other methods) on both sides, I used JsInterop and it works very well with gwt 2.7 and exchanged the data as json.

Example Data class:

@JsType
public class MyObject {
   
private String id;
   
   
@JsProperty
   
public String getId() {
       
return id;
   
}
   
@JsProperty
   
public void setId(String id) {
       
this.id = id;
   
}
}


The class can be used on server side like any normal java class with any REST Framework. On gwt side I was able to convert a transmitted json to the class with:


MyObject myObject = getJsTypeObject(JsonUtils.safeEval(jsonString));
//use getter and setter to access fields

public static native <T> T getJsTypeObject(JavaScriptObject result)/*-{
    return result;
}-*/
;


And to create Objects on gwt side and send them to the server as json:

MyObject myObject = new MyObject();
myObject
.setId("1");
String jsonString = stringify(myObject);


public static final native String stringify(Object result) /*-{
    return JSON.stringify(result);
}-*/
;


I also solved the problem of List and Map with an own Interface, which is an ArrayList/HashMap on serverside and an own implemented native List/Map on gwt side.

The application has more than 100 such Objects which are heavily used on server and client side in many methods which are also be used on both sides. So implementing them twice for server and client side would be a big overhead.
Now is my question, how can I convert this gwt 2.7 code to gwt 2.8 (mainly to be able to use Java8 syntax in future). I tried multiple variants, but it seems for me, JsInterop in gwt 2.8 is not planned for such usecases which were possible in gwt 2.7 withou any problems.
I can either use a native Javascript Object in gwt (native=true) or a in GWT created Object in Javascript. But I see no solution to do both with the same Type.

I get 2 Problems
  • Casting issues: I can't use native=true because I also want to create such Objects on gwt side, so on every assignment (jsonString to typed variable) I get a cast exception and at least in superdev mode I can't deactivate the cast exceptions
If you never do "instanceof" (or expect cast exceptions) on client-side, you could probably use isNative=true,namespace=GLOBAL,name="Object"; that way, all your objects are plain old JS objects (no specific class/constructor is generated in JS), which is actually exactly what you'd expect from JSON.parse().
  • field name problems: The jsonString of the last example is {"id_g_$3":"1"} and not {"id":"1"} because the JsProperty for getter and setter works only fine with native objects. If I add JsProperty to the field itself, I can't do have JsProperty for the getter and setter, so I have to add JsIgnore to them. But then when I work with native Objects I can't use the getter and setter on gwt side. I would need JsProperty on getter, setter and the field itself, but this is also not allowed.

The "id_g_$3" is probably because you don't -generateJsInteropExports; contrary to 2.7, in 2.8, the "don't obfuscate @JsProperty/@JsMethod names" only applies when you -generateJsInteropExports.
But if you switch to isNative=true,namespace=GLOBAL,name="Object", you don't even need it.

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit+unsubscribe@googlegroups.com.
To post to this group, send email to google-web-toolkit@googlegroups.com.
Visit this group at https://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment