Sunday, June 5, 2016

Re: JsInterop and Java collections?

As somebody that has also written js wrappers for arrays in jsinterop:

While I am not an expert I believe that what you are looking for simply it is not possible. The other software cases  you mention are copying values around (or serializing as already said). But with @jsinterop a collection must be simply __is__. There is a certain level the overlay functions can reach. Beyond that the chances is that you will have to create your own class. This is what gwt has done. GWT java.util.collection is not build around jsarray. I mean it may utilize a jsarray inside it but I am sure it has also other private members.

If you have even one private member simply your class is not a jsarray anymore...

For maps it is even more straightforward to see my point. javascript maps are Object {} with keys always strings. Can you use it to build java.util.Map<Integer, Integer>? Sure but it is not a simple js Object {} anymore.

The only reason you may want to convert a java collection to a jsarray is for feeding the data into a js library. Or convert the js output to a java collection. jsinterop shouldn't copy my stuff around. It doesn't know how to do that. But it can provide helpers to do it.

So If the collections, maps etc are not input or output only (immutable) then a copy could be straight forward (performance may be a problem since there is duplication of data).

However if the collection/map is mutable used by both input and output then you need a java class implementing Collection/Map that is jsarray/Object backed. Standardized helpers may also help in this case. Then you have the spillover of js specific classes to your program logic you mentioned earlier.

As I said before I maintain that this is unavoidable -- but I have been wrong before :-)

    Vassilis

PS: The two cases of helpers can be coerced to one with the implementation pf java Collection/Map backed by js object. Not sure how many practical cases (existing js libraries) you can cover before things go ugly really fast.






On Sun, Jun 5, 2016 at 8:39 PM, Hristo Stoyanov <hr.stoyanov@gmail.com> wrote:
Thanks Jen,
Let me try to explain the issue better:

In your example, JsArray must have a bunch of native methods mapped to the native JavaScript Array API. None of these methods bring much value to the Java developer, who for years have been very happy with the  Java  native Collection,List,Map  interfaces, and now enjoys the Java 8 additions. 

All we need is the JsInterop to map  an List (or Java array) from the Java world into plain array in the JavaScript world. Transparently. (or with the help of minor annotation or two). The same is needed for maps.

Without such transparent mapping, you are faced with some unpleasant choices:

1. You write converters and adapters as you showed in your asCollection()example. That is not going to help you if you decide to re-use your Something class on the server (e.g. on the JVM) side, as it is still cannot exist there due to the reference to JsArray. You will have to duplicate it with something like JavaSomething that has a reference to List<Person>.

2. You try something like what Paul did: define an umbrella Array interface that strictly implements and the native JavaScript Array API (plus some extra overlay methods) and then duplicate this in the Java world with Java-only mirror class like this (kudos to Paul, btw, for putting his code out there!). Then you could potentially re-use your Something class in both the JS and Java world, if it reference the umbrella Array interface instead. But now you force Java developers to learn and deal with the JavaScript array API, when they are perfectly happy with java.util.List/Collection ! This will lead to more converters in your code, for sure ...My experience over the years is that such clever tricks lead to even more kludge down the road. For example, In the old EJB 2.x model abused interfaces (remember the Home  or  Entity Bean interfaces? Or the Remote and Local ones ... Jeez ) until all this was wiped out and simplified with transparent frameworks such as JPA, JAX-B and Jackson to remove the kludge.

My hope is that JsInterop evolves towards  transparent  collections mapping, so GWT developers do not have to write or depend on plumbing code, at least not for common collections .

Btw, thank you and Paul for sharing your code and discussing JsInterop, as for GWT JsInterop projects, given the current state, your example code is the only way possible!

Cheers



On Sunday, June 5, 2016 at 5:41:05 AM UTC-7, Jens wrote:

When you think about it, other Java APIs (jaxb, jpa, Jackson) do go the extra mile to accommodate Java collections

Well kind of bad comparison right? These Java APIs either serialize/deserialize based on a defined principle or stay in Java land anyways. 
 
, and the example above looks like can be exported to js object (could use a helper annotation @JsX or two).

You don't "export" Java to JS but instead you consume JS using Java. That means you would need to have a class that implements the Map interface and takes a JS map as constructor parameter. Then you can implement the Java API on top of the JavaScript API. In some cases you can simply delegate to the JS API, in some cases you can't. 

But thats something JsInterop does not do, however nobody stops you from having something like 

@JsType(native = true)
class JsArray<T> {

 
// ...get / set / shift / slice / etc...

 
@JsOverlay
 
Collection<T> asCollection() {
   
// Java API on top of JS API
   
return new JsArrayCollectionAdapter<>(this);
 
}

 
@JsOverlay
 
List<T> asList() {
   
// Java API on top of JS API
   
return new JsArrayListAdapter<>(this);
 
}
}

@JsType(native = true)
class Something {

 
@JsProperty
 
JsArray<Person> persons;


 
@JsOverlay
 
List<Person> getPersons() {
   
return persons.asList();
 
}

 
@JsOverlay
 
void setPersons(List<Person> newPersons) {
    // might use ES6 Array.from(newPersons.toArray()) if possible, otherwise fallback to manual copying
    persons
= JsUtils.asJsArray(newPersons);
   
// alternatively do it through the adapter
   
List<Person> adapter = persons.asList();
    adapter
.clear();
    adapter
.addAll(newPersons);
 
}
}


-- J.

--
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.



--
Vassilis Virvilis

--
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