I am using SmartGWT and I wish to access com.smartgwt.client.Version from JavaScript. In Firefox's Web Console, I have tried:
frames[0].$entry(Lcom_smartgwt_client_Version::getVersion()));
and
frames[0].$entry(#com.smartgwt.client.Version.getVersion());
and
frames[0].$entry(#com.smartgwt.client.Version::getVersion());
and
frames[0].$entry(#com.smartgwt.client.Version::getVersion()());
But all of them return a syntax error.
SmartGWT is deployed with my WAR and I can see other SmartGWT classes listed when I do just frames[0].
What is the right syntax to call this static Java method?
Those JSNI references do not work except in JSNI code in your java files. References to Java methods and fields in JSNI are not actually valid JavaScript, but part of the JSNI language to enable those native methods to both use Java and JavaScript. The JSNI string #com.smartgwt.client.Version::getVersion()() will be rewritten as something like $getVersion1() in PRETTY, or something just one or two characters long in OBF mode, so you can't rely on that method name being the same.
Instead, you need to export a JavaScript function from inside your application so that this external JavaScript can invoke it. Check out https://developers.google.com/web-toolkit/doc/latest/DevGuideCodingBasicsJSNI#calling for specific details on this.
Here is an example of how this might look in your application:
public native void exportGetVersion() /*-{
$wnd.getSmartGwtVersion = $entry(function() {
return #com.smartgwt.client.Version::getVersion()();
});
}-*/;
Make sure you call this function in your app somewhere to export the function - any time after that is called, you can invoke getSmartGwtVersion() from your regular JavaScript - no need to use frames or $entry.
Related
For a couple of reasons, I'm interested in writing a hybrid application which is partially coded in Java (via Google Web Toolkit) and partially coded in JavaScript. I'm planning on calling the Java library from JavaScript by using GWT Exporter.
The trouble is that this destroys a lot of the opportunities for code optimization and compression. GWT is mostly just designed to optimize the JavaScript it generates, and third-party Javascript compression libraries will probably break down when given GWT output.
Is there a way to tell the GWT compiler "hey, pass these Javascript files into your optimization pass as well"? GWT has a flag for using the Closure Compiler under the hood (which obviously supports optimizing regular javascript), so it feels like this should be possible.
Insofar as you can ask Closure to compile source, it is possible, but you have to get those 'other' sources into the code that GWT (and later closure) is compiling. Presently, this means putting that code in JSNI, otherwise it is just another file on the filesystem, and the compiler can't know what the dependencies in and out are, since it likewise can't tell when/how you are loading that file.
If I remember correctly, standard usage of dependencies in Closure is via a goog.require() method call in JavaScript at the top of your file - this both declares the dependency, and if needed loads the file. Without this, your base HTML page needs to have <script> tags for each file you might possibly use, and without actually running that page, Closure won't know what order to expect those files to load in, or how far and wide to run when compiling your source.
GWT itself (i.e. outside of Closure) only makes a very small set of optimizations to the raw JS included as JSNI in its Java sources:
com.google.gwt.dev.js.JsStaticEval - simple constant folding, and other expression simplification
com.google.gwt.dev.js.JsInliner - inline functions under a certain complexity threshold, and other assorted cleanup
com.google.gwt.dev.js.JsUnusedFunctionRemover - simple pruning of unreferenced functions/variables
com.google.gwt.dev.js.JsDuplicateCaseFolder - look for the same body in more than one case block and merge them into one fall-through case.
GWT has three primary ways to include JS source: JSNI, tags in the .gwt.xml (not supported by all linkers) and com.google.gwt.core.client.ScriptInjector to pull from a string constant or from a remote url. Only the first considers the code as actual source - the second/third let the code come from any source, and don't count on the code being statically available at compile-time. JSNI has its own limitations - it doesn't support with blocks, and must use $wnd and $doc to refer to the host page's window and document.
First, although this is not your question, you have to be aware that to use gwt-exporter you have to call GWT.create per each class you want to populate, or call the exportAll() method for exporting everything marked as exportable. That implies that you are saying to the compiler that you are going to use those classes even your JS app would eventually not use them. So you wont take advantage of the removal of unused code causing large js output. You could use code-splitting for separate code in fragments though.
Second, and related with your question, as #Colin says in his answer only code written in JSNI blocks will be optimised by the GWT compiler, but the default optimisation is very trivial, although if you use the closure compiler it is a bit stronger. I have not tried, but I think closure annotations are not allowed since probably GWT compiler removes them before passing the js to the closure compiler.
Anyway, the main problem for including those files in JSNI blocks, is that you have to copy and paste the code in your java classes manually, and then perform some other tricks for addressing $wnd, etc.
We at gwt-query have a JsniBundle generator able to take .jsfiles from filesystem or from any url, and in compile time include that code in JSNI fragments and make a couple of tricks to make it work in the iframe where GWT runs. It works for almost libraries and plugins I have used, but sometimes I had to modify the javascript source for allowing sand-boxing it.
Here you have an example of how to include jquery and highcharts:
public interface JQueryBundle extends JsniBundle {
#LibrarySource(value =
"http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js")
public void initJQuery();
}
public static abstract class HighCharts implements JsniBundle {
#LibrarySource("js/highcharts.src.js")
public abstract void initHighcharts();
public void drawChart(String id, JavaScriptObject props) {
JavaScriptObject $container = JsUtils.runJavascriptFunction(window, "$", "#" + id);
JsUtils.runJavascriptFunction($container, "highcharts", props);
}
}
public void testHighCharts() {
JQueryBundle jQuery = GWT.create(JQueryBundle.class);
HighCharts highCharts = GWT.create(HighCharts.class);
jQuery.initJQuery();
highCharts.initHighcharts();
highCharts.drawChart("chart", charData);
}
Some of the advantages of using this method are enumerated in this slide of our GWT.create-2013 presentation.
You can optimize arbitrary JavaScript code using the GWT compiler, just use the appropriately named LinkerContext.optimizeJavaScript(TreeLogger, String) method. LinkerContext objects are available inside Linkers, which are pieces of custom code run during the compilation. Here's a minimal example on how to write one:
#LinkerOrder(LinkerOrder.Order.POST)
public class ScriptOptimizer extends AbstractLinker {
#Override
public String getDescription() {
return "Optimizes external JavaScript files.";
}
#Override
public ArtifactSet link(TreeLogger logger, LinkerContext context,
ArtifactSet artifacts) throws UnableToCompleteException {
// This is some arbitrary JavaScript code you'd probably want to read
// from a static file in your classpath
String script = "var foobar = 1; for (var i = foobar; i < 5; i++) alert(1);";
// Do the optimizations
script = context.optimizeJavaScript(logger, script);
// Create an Artifact from the optimized JavaScript string
ArtifactSet newArtifacts = new ArtifactSet(artifacts);
newArtifacts.add(emitString(logger, script, "example.js"));
return newArtifacts;
}
}
Then, to include the Linker in your GWT compilation process, add this to your *.gwt.xml:
<define-linker name="scriptoptimizer"
class="com.example.ScriptOptimizer" />
<add-linker name="scriptoptimizer" />
The result will be a compiled file called example.js. You can of course generate as many files as you want or, for optimal results, concatenate all your scripts into one and compile that into a single output file.
I have some Java code written that I'd like to convert to JavaScript.
I wonder if it is possible to use the GWT compiler to compile the mentioned Java code into JavaScript code preserving all the names of the methods, variables and parameters.
I tried to compile it with code optimizations turned off using -draftCompile but the method names are mangled.
If GWT compiler can't do this, can some other tool?
Update
The Java code would have dependencies only to GWT emulated classes so the GWT compiler would definitely be able to process it.
Update 2
This Java method :
public String method()
got translated to this JavaScript funciton :
function com_client_T_$method__Lcom_client_T_2Ljava_lang_String_2()
using the compiler options :
-style DETAILED
-optimize 0
-draftCompile
So names can't be preserved. But is there a way to control how they are changed?
Clarification
Say, for example, you have a sort algorithm written in Java (or some other simple Maths utility). The method sort() takes an array of integers. and returns these integers in an array sorted. Say now, I have both Java and JavaScript applications. I want to write this method once, in Java, run it through the GWT compiler and either keep the method name the same, or have it change in a predictable way, so I can detect it and know how to change it back to sort(). I can then put that code in my JavaScript application and use it. I can also automatically re-generate it if the Java version changes. I have a very good reason technically for this, I understand the concepts of GWT at a high level, I'm just looking for an answer to this point only.
Conclusion
The answer to the main question is NO.
While method name can be somewhat preserved, its body is not usable. Method calls inside it are scattered throughout the generated file and as such, they can't be used in a JavaScript library which was the whole point of this topic.
Although you can set the compiler to output 'pretty' code, I suggest you write export functions for the classes you want to call from outside your GWT project. I believe somewhere in the GWT documentation it's detailed how to do this, but I couldn't find it so here an example I just created.
class YourClass {
public YourClass() {
...
}
public void yourMethod() {
...
}
public static YourClass create() {
return new YourClass();
}
public final static native void export() /*-{
$wnd.YourClass = function() {
this.instance = new #your.package.name.YourClass::create()()
}
var _ = $wnd.YourClass.prototype;
_.yourMethod = function() {this.instance.#your.package.name.YourClass::yourMethod()()}
}-*/;
}
EDIT
To elaborate, your code will get obfuscated like normal, but thanks to the export function, you can easily reference those functions externally. You don't have to rewrite anything from your Java class in JavaScript. You only write the references in JavaScript, so you can do this:
var myInstance = new YourClass();
myInstance.yourMethod();
Of course you have to call the static export method from somewhere in your GWT app (most likely in your EntryPoint) to make this work.
More info about referencing Java methods from JavaScript:
http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html#methods-fields
No - this isn't possible with the GWT compiler, since the GWT compiler is build to generate optimized and very performant JavaScript out of Java.
The big advantage is, that you can maintain your projekt in Java and compile it with GWT to JavaScript. So there is no need to prevent the variable-names and method-names in the JavaScript result, since all changes and work is done in the JAVA-sources.
Working in the JavaScript-output of GWT just isn't that easy and is really a lot of work!
Update:
By a hint of David, I found the Compiler-Option "-style". You can have a try with the following options:
-style=PRETTY -optimize=0
I have no idea if this will really generate "human readable" code. I think it won't, since the GWT framework will still be part of the resulting JavaScript and so it will be difficult to make changes to the JavaScript-result. Have a try and let us know ...
Maybe I can answer your second question: "If GWT compiler can't do this, can some other tool?"
I am using Java2Script for quite a while now, also on quite large projects. Integration with native JavaScript is fine, names are preserved, and after some time one can even match the generated JavaScript (in the browser debugger) with the original Java code with little effort.
Udo
You can "export" your function by writing inline JavaScript that calls it, and there is a tool gwt-exporter that does this automatically when you annotate classes and methods with #Export and similar. More information: https://code.google.com/p/gwtchismes/wiki/Tutorial_ExportingGwtLibrariesToJavascript_en
Currently I change my locale in GWT using a javascript as a native code. if the user click on changeLocale button on my GUI, i call this native method. Is there any java method/way to do that without using any native code???
Have you looked at the Showcase sample?
Redux'd, it's as easy as:
Window.Location.assign( // or replace()
Window.Location.createUrlBuilder()
.setParameter(LocaleInfo.getLocaleQueryParam(), "fr-FR")
.buildString());
I'm working on a GWT application, which should behave in a slightly different manner when it is running inside a frame and when running directly in a browser window.
The question is: how to determine at runtime whether we're in a frame or in a window?
Wrap a bit of javascript into JSNI:
public static native boolean isFrame() /*-{
return ($wnd!=$wnd.top);
}-*/;
I haven't tested this but looking at the javadoc you can try something like:
RootPanel.getBodyElement().getParentElement().getTagName()
and see if it's an iframe or whatever tag you need
I have a javascript function (very big one!) that I need its functionality in a Java (Groovy) class. It is a simple calendar converter. I can rewrite it in groovy but just want to know if it is possible to call javascript function from a java (groovy) method? I guess functional testing libraries like selenium and Canoo should have something like this, am I right?
PS: I don't want to wake up a real-world browser in order to use its JS runtime env.
Thanks,
As mentioned in the other answers, it is possible to use the Scripting API provided as part of the javax.script package, available from Java 6.
The following is a Groovy example which executes a little bit of Javascript:
import javax.script.*
manager = new ScriptEngineManager()
engine = manager.getEngineByName("JavaScript")
javascriptString = """
obj = {"value" : 42}
print(obj["value"])
"""
engine.eval(javascriptString) // prints 42
It is not necessary to call a browser to execute Javascript when using the Scripting API, but one should keep in mind that browser-specific features (probably the DOM-related functionalities) will not be available.
You can use Rhino, an implementation of JavaScript language in Java. Here is example of calling JavaScript function from java, but you can do it from groovy also.