GWT: client/server condition compilation - gwt

Is it possible in GWT to have a different implementation of a function on the client and on the server? E. g.
private static native String toFixedNative(int digits, double value) /*-{
return value.toFixed(digits);
}-*/;
public static String toFixed(int digits, double value) {
if (GWT.isClient()) {
return toFixedNative(digits, value);
} else {
String format = "%." + digits + "f";
return String.format(Locale.US, format, value);
}
}
This doesn't work currently, GWT compiler complains, that String.format function is not available in GWT. But it is not really needed, because String.format is called only when !GWT.isClient().
Is there any way to tell GWT compiler to ignore a part of the function?

With a recent version of GWT (2.7, maybe also 2.6), it should be possible to do that by moving the String.format into a method that you then annotate with #GwtIncompatible (any annotation with that name would work, package doesn't matter).
In any version of GWT, it's also possible to super source a class: provide two versions of a class for client and non-client cases. See “Overriding one package implementation with another” in http://www.gwtproject.org/doc/latest/DevGuideOrganizingProjects.html#DevGuideModuleXml
You'll find plenty of examples in GWT itself; this is also how the Java Runtime is emulated.

Related

Ability to access Dart class from native code

I am in the process of migrating from Android/JNI to Flutter/Dart/FFI to build native apps. Currently in Android I have a Java class (ScanningHelper.java) which is modified during the program lifecycle. During native initialization, I send a jobject type using which in my C/C++ code, I am able to call different member functions or even access variables directly (depending on access).
Is something similar possible with Dart FFI? The closest I have come to is possibly creating a struct in native and then using multiple functions to set/get data from native. I though have a feeling that the amount of code to be written will be high including memory clean up on Dart every time a string/const char* is returned.
Appreciate any direction to accomplish something like below without needing to duplicate implementation across both languages.
// ScanningHelper.java
public class ScanningHelper {
private String _mediaStorageDirectory;
public void setMediaStorageDirectory(String path) { _mediaStorageDirectory = path; }
public String getAppMediaStorageDirectory() { return _mediaStorageDirectory; }
public double latitude;
public double longitude;
...
byte[] image1Bytes;
}
I have some standard methods defined like getStringFromObjectMethod which allows reuse across different classes and return types;
// JNIMethods.cpp
std::string mediaDirectory = JNIMethods::getStringFromObjectMethod(jniEnv,_scanningHelper,"getAppMediaStorageDirectory"));

Passing Java Array into JavaScript (via JSNI) and back out to Java results in null value

Scenario: I have a GWT web application running within a JavaFX WebView/WebEngine. I am able to pass Strings from GWT to JavaScript to JavaFX without any issues.
Problem: When passing an array of custom objects like Data[] in the same fashion, the result on the JavaFX side is null.
An example of what Data looks like:
public class Data extends Serializable
{
char[] name;
int code;
short bar;
}
Here's the code to send the data to JavaScript:
public static native void doNativeStuff(String id, Data[] data) /*-{
$wnd.javaInterface.doStuff(id, data);
}-*/;
I've verified in the debugger that the Java object being passed in is populated with data and looks good.
Now on the JavaFX side, I have the following code to add the javaInterface to the page:
JSObject win = (JSObject) engine.executeScript("window");
win.setMember("javaInterface", new JavaInterface());
I know that this works because I'm using it for other methods that pass only Strings and they work great.
public void doStuff(String id, Data[] data)
{
// Right here, id == "validId" and data == null
if (data != null)
{
... do what is needed ...
}
}
Note that the Data object is defined and accessible on both sides.
From the GWT documentation:
Incoming Java type How it appears to JavaScript code
Java array opaque value that can only be passed back into Java code
I'm not touching it in JavaScript at all and I'm only passing it through from Java->JavaScript->Java, but the final step appears to be what is failing.
I've spent the last few hours scouring Stack Overflow, Google, GWT groups, gwtproject.org, etc. But most all of the examples only show a single argument being passed through and almost none of them show a Java Array being used.
I'd much rather just pass the object through rather than going to->from JSON, but I did give that a try out of desperation. I tried to use GSON but it doesn't work on the GWT client side. I tried to use the GWT AutoBean Framework but my Data object isn't a valid bean (I think because of no default constructor) and I cannot change that at this time.
I'm not using any Long or long values.
I've seen examples like this:
#com.google.gwt.examples.JSNIExample::staticFoo(Ljava/lang/String;)(s);
But from what I can tell that's just for going from JavaScript to GWT over JSNI. I'm trying to go the other way. I also couldn't find an example of this for multiple arguments.
I'm sure that there is just a minor tweak here that I'm missing, but I haven't been able to figure it out just yet. Please let me know if you see something that I'm missing here.
opaque value that can only be passed back into Java code
I think this means you cannot pass Java array into JavaScript code.
Agree with jat. I used to provide support for the similar needs and I had to serialize the objects myself.
And you can pass multiple arguments like this (types of arguments are given just for example):
private native void doJSAction(MyClass handler)/*-{
// do smth in JS
// then call external non-static method
handler.#com.myclient.helper.MyClass::doMyAction(Lcom/google/gwt/core/client/JavaScriptObject;Ljava/lang/String;Lcom/myclient/helper/MyClass;II)(jsNativeSmth, myString, handler, intA, intB);
}-*/;
where doMyAction is something like the following:
void doMyAction(JavaScriptObject jsObject, String s, MyClass instance, int a, int b)
I haven't played with JavaFX, but since it runs in a different VM and knows nothing about the GWT DevMode protocol (for example, a Java object is wrapped in a JS object that basically makes RPC calls to manipulate it), I am pretty sure you are going to have to serialize everything between GWT and JavaFX as Strings and primitives.

Calling GWT Java function from JavaScript

I am a newcomer to GWT and JavaScript.
There are similar question of this type I have tried to follow, but I keep failing.
I have a GWT app, I need to call a Java function from Javascript( on the onclick of a href tag, in particular.) Following is what I have done.
public class JSNITest {
public static void handleAnchorClick(int a , int b) {
Window.alert("Current row and Column is " + a + " " + b);
}
public static native void exportMyFunction()/*-{
$wnd.handleAnchorClick = function(param1,param2){
#company.package.class.JSNITest::handleAnchorClick(*)(param1,param2);
}-*/;
}
And in the HTML,
link
(a1 , a2) are two integer variables in my code.
I have also called EnclosingClass.exportMyFunction() in the entry point function.
I keep running into various kinds of exceptions(No Such class exception). Can someone please correct me?
Regards
Let me explain a bit more about exporting GWT stuff to the JS world. You have several options to do that, but I will focus on three methods.
[EDITED]
0- JsInterop: GWT maintainers are working in a new feature to easily export java methods to javascript, and wrap javascript objects. The feature was very experimental in 2.7.0 lacking some features, but in 2.8.0 will be almost functional. Please take a look to the Design Document, and other discussions in the mailing list.
[END]
1- JSNI: The first one is to write your own jsni, in this case you have to be aware about the possible mistakes you could make. Basically these mistakes are because you have to know how to deal with types. In your case if you want to get a javascript array (like you are asking in your comment below), the solution could be:
public static native void exportMyFunction()/*-{
$wnd.handleAnchorClick = #company.package.class.JSNITest::handleAnchorClick(*);
}-*/;
public static void handleAnchorClick(JsArrayMixed args) {
Window.alert("Current row and Column is " +
args.getNumber(0) + " " + args.getNumber(1));
}
public void onModuleLoad() {
exportMyFunction();
}
//javascript code
window.handleAnchorClick([1,2])
Note that JSNI only allows you to pass primitive types (except long) and JavaScriptObject objects. So when passing a javascript array, you have to receive it with a JavaScriptObject like in the example. In this case, since javascript only uses a type for numbers, args.getNumber will return always a double, and you have to convert in java.
2- gwt-exporter For exporting large projects, or when you need to handle complex objects and classes I'd rather use gwt-exporter
static class MyClass implements Exportable {
#Export("$wnd.handleAnchorClick")
public static void handleAnchorClick(double[] args) {
Window.alert("Current row and Column is " +args[0] + " " + args[1]);
}
}
public void onModuleLoad() {
GWT.create(MyClass.class);
}
//javascript code
window.handleAnchorClick([1,2])
gwt-exporter will deal with any kind of primitive types (even with long) myfunc(long[] args), with var-args myfunc(long...args), it supports method overload, and much more.
3- gwtquery Finally if you prefer gwtquery, you can use a technique to add function properties to any js object like window
// The GQuery Properties object is able to wrap a java Function object
// into an js property.
Properties wnd = window.cast();
wnd.setFunction("handleAnchorClick", new Function() {
public void f() {
// Get the js arguments[] array
JsArrayMixed args = arguments(0);
// Get the first element of the arguments[] array
JsArrayMixed ary = args.getObject(0);
Window.alert("Current row and Column is " +
ary.getNumber(0) + " " + ary.getNumber(1));
}
});
//javascript code
window.handleAnchorClick([1,2])
With gquery you can use the gwt JsArrayMixed class which always returns a number as a double, or you can use a JsCache which allows to convert numbers to any other numeric type in java ((JsCache)ary.get(1, Integer.class)
As a summary, I would rather use gwt-exporter as the first option because it is specialized in handling this problematic. As a second option, I would use gquery which is a serious complement to gwt. Finally, I would avoid to use hand-written jsni when possible, Javascript is normally a source of issues and mistakes (think that the main goal of gwt is not to deal with js).
You should consider GWT exporter. You might even consider waiting because GWT 2.8 should come out pretty soon. It was supposed to come out some time around The begging of 2015. 2015 has already begun and they're demonstrating at GWT.create right now so it should come out any day now. If you can't wait then you can either use experimental is interop, JSNI like the top answer says to or GWT exporter. JSNI is more complicated and involves a lot of boiler plate code so if you have to do a lot of js interop I recommend GWT-exporter.

Mixing Java POJOs & GWT Overlay types

In my app I use JsArray extensively to store my overlay types. I use java.util.List to store my client-side Java POJOs.
For performance reasons and to unify the way I access my model I planned to eliminate the Lists and use only JSO wrappers. Given a wrapper around a native array that can store any Java Object:
public class JsArrayObject<T> extends JavaScriptObject {
protected JsArrayObject() {}
public final native T get(int index) /*-{
return this[index];
}-*/;
public final native void push(T value) /*-{
this[this.length] = value;
}-*/;
}
Is it safe to store Java Objects this way? The doc says that when you pass a Java Object into JavaScript the result is "an opaque value accessible through special syntax". This sounds confussing to me. For instance if I push an Integer and try to get it an exception will be thrown because something different than an Object was found (at least in dev mode). The same happens with the rest of Java primitive Wrappers. Apart from the problems with Java primitive Wrappers, are there other concerns to be aware?
Many thanks
Which doc are you referring to? The one on this page?
They're talking in terms of passing Java objects into JavaScript with the intent of having JavaScript code use the methods or fields in the object. It is possible to do that, but the syntax you have to use on the JavaScript side is a bit awkward. If you've done any JSNI, you've seen it.
If you don't intend to access the Java objects from the JavaScript side you can ignore the business about the special syntax. So yes, it is safe. I'd be interested to know if it actually turns out to help performance.

How to make your GWT application plug-able?

I am writing (with my team) an GWT application, which parses and represent some domain specific language - for example, plays media presentation with text, video and UI controls. So the application has a set of components: ones - for holding model, ones - for control routines (controllers), and of course we have classes for View.
Now we have a problem - make it all plug-able, in the sense of:
should be one core plugin, which make all common stuff. This coer block must be an JavaScript file (one for every permutation)
should be ability to extend core classes, write custom ones - and compile it to separate JS file (one for every permutation)
Every plugin must registers (export it's classes etc) itself to the core platform, so it could be used.
Problems:
How to compile the custom stuff
separately ?
How to load plugins ?
For the second one problem i've found http://code.google.com/p/gwt-exporter/, that exports GWT classes to outer world, so they could be invoked from pure JS.
Also I have an idea to create new module for new plugin, so it will be compiled to separate file (first problem).
Have you an experience of building such architecture, have you some best practices in this case ?
I have experimented with this same question since GWT 1.5 and every time I come up with a more elegant solution they change the linker and break it. The only way that I have come up with that would work independent of linker design is to do exactly what you are talking about and create a new module for ever plug-in. Then use GWT exporter to create an abstract class that plugins must extend that would have an abstract method that would take the root element of the plugin passed to it by the core and populate it. The issue with this method is all plug-in modules must be added to the DOM on the initial load of the page because since 2.0 the iFrame linker relies on a page load event so dynamically added modules wont fully load. So because of this you will want to have the exported population method wrapped in runAsync so that you aren't downloading modules till you use them.
Edit:
Here is a rough example of what I am talking about. Please be aware that I haven't done any GWT in a couple years and there may be a better way of doing this by now.
public final class PluginManager
{
public static final PluginManager INSTANCE = new PluginManager();
private PluginManager()
{
}
private static native void loadPlugin( AbstractPlugin plugin )
/*-{
if (!$wnd.Plugins) {
$wnd.Plugins = {};
}
var name = plugin.#com.example.PluginManager.AbstractPlugin::getName()();
$wnd.Plugins[name] = $entry(plugin.#com.example.PluginManager.AbstractPlugin::load(Ljava/lang/String;));
}-*/;
private static native void unloadPlugin( AbstractPlugin plugin )
/*-{
if ($wnd.Plugins) {
var name = plugin.#com.example.PluginManager.AbstractPlugin::getName()();
delete $wnd.Plugins[name];
}
}-*/;
private static native JsArrayString getPlugins()
/*-{
if ($wnd.Plugins) {
return Object.keys($wnd.Plugins);
}
return undefined;
}-*/;
public static abstract class AbstractPlugin implements EntryPoint
{
#Override
public final void onModuleLoad()
{
PluginManager.INSTANCE.loadPlugin( this );
}
protected final void unload()
{
PluginManager.INSTANCE.unloadPlugin( this );
}
protected abstract String getName();
protected abstract void load( String rootPanelId );
}
}