How do you call a Java method from JavaScript?

63 views
Skip to first unread message

Jim Douglas

unread,
Feb 14, 2010, 5:19:43 PM2/14/10
to Google Web Toolkit
I've been playing with some code to register oncut against a GWT
TextBox. The basic event registration seems to be working ok; I get
to the event handler function, and the "return false" part seems to be
working as designed (the cut is blocked). The problem is that I'm
trying to call back from the event handler into a Java method ... but
it's never getting there. Am I doing something obviously stupid
here? The syntax looks right, and I'm not getting any errors. But
when I run this, I get the two alerts bracketing the attempt to call
from JavaScript to Java, but that method isn't getting invoked.

Here's my test code. I added this to the end of StockWatcher
onModuleLoad():

registerOnCut(nameField.getElement());

And added these functions:

public native void registerOnCut(Element element)
/*-{
element.oncut = function()
{
$wnd.alert("about to invoke Java doCut");

$entry(this.@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut());
$wnd.alert("returned from Java doCut");
return false;
};
}-*/;

public void doCut()
{
Window.alert("made it to Java doCut");
}

Jim Douglas

unread,
Feb 15, 2010, 4:35:27 AM2/15/10
to Fabiano, Google Web Toolkit, Jim Douglas
I think that's part of it, Fabiano, and I think the other part is reconciling what 'this' means between the JavaScript event handler function and the GWT TextBox.  I've got it working like this (still using StockWatcher as a test-bed -- note that this the Window.confirm here is for testing; my actual app does something different):

public class StockWatcher implements EntryPoint {

    private static Map<Element, TextBox> m_elementToWidget = new HashMap<Element, TextBox>();

...
At the end of onModuleLoad():

    registerOnCut(nameField.getElement());

    m_elementToWidget.put(nameField.getElement(), nameField);

...
Changed the event handler functions to be static, then reconcile the Element back to the corresponding Widget:


    public native void registerOnCut(Element element)

    /*-{

        element.oncut = function(e)

        {

            if (!e) var e = window.event;

            $entry(@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut(Lcom/google/gwt/dom/client/NativeEvent;)(e));

            return false;

        };

    }-*/;


    public static void doCut(NativeEvent event)

    {

        EventTarget target = event.getEventTarget();

        if (Element.is(target))

        {

            Element element = Element.as(target);

            TextBox textbox = m_elementToWidget.get(element);

            String text = textbox.getText();

            int cursorPos = textbox.getCursorPos();

            int selectionLength = textbox.getSelectionLength();

            String selectedText = textbox.getSelectedText();

            String msg = "OK to cut selected text '"+selectedText+"' @ "+cursorPos+","+selectionLength+"?";

            if (Window.confirm(msg))

            {

                text = text.substring(0, cursorPos) + text.substring(cursorPos+selectionLength);

                textbox.setText(text);

            }

        }

    }


And for cleanup, I added this to the subclassed TextBox:


    @Override

    protected void onDetach()

    {

        super.onDetach();

        m_elementToWidget.remove(getElement());

    }


On Mon, Feb 15, 2010 at 12:29 AM, Fabiano <fta...@gmail.com> wrote:
On 14 Feb, 23:19, Jim Douglas <jdou...@basis.com> wrote:

> $entry(this.@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut());
Hi,
I'm a bit nood but I have the impression that the problem could be
related to the function argument, my proposal, not tested:

$entry(this.@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut(D));

I don't know the reason but I have found this example
http://googlewebtoolkit.blogspot.com/2008/07/getting-to-really-know-gwt-part-1-jsni.html
about functions with empty arguments:

// Set up the JS-callable signature as a global JS function.
 private native void publish() /*-{
   $wnd.formatAsCurrency =

@org.example.yourcode.format.client.DateFormatterLib::formatAsCurrency(D);
 }-*/;

it lacks $entry , but I don't think this is an issue.
I have the impression that sometimes gwt documentation in a bit
ambiguous about syntax and left too much to the user intuition.
In the main documentation there is no an example about functions with
no arguments.
Regards


Thomas Broyer

unread,
Feb 15, 2010, 6:22:18 AM2/15/10
to Google Web Toolkit

On Feb 14, 11:19 pm, Jim Douglas <jdoug...@basis.com> wrote:
> I've been playing with some code to register oncut against a GWT
> TextBox.  The basic event registration seems to be working ok; I get
> to the event handler function, and the "return false" part seems to be
> working as designed (the cut is blocked).  The problem is that I'm
> trying to call back from the event handler into a Java method ... but
> it's never getting there.  Am I doing something obviously stupid
> here?  The syntax looks right, and I'm not getting any errors.  But
> when I run this, I get the two alerts bracketing the attempt to call
> from JavaScript to Java, but that method isn't getting invoked.

that's a "scope" issue (very common in JavaScript).

> Here's my test code.  I added this to the end of StockWatcher
> onModuleLoad():
>
>     registerOnCut(nameField.getElement());
>
> And added these functions:
>
>     public native void registerOnCut(Element element)
>     /*-{
>         element.oncut = function()
>         {
>             $wnd.alert("about to invoke Java doCut");
>

> $entry(th...@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut());

The "this" is no longer your class here, as the function is a
callback. First assign it to a variable outside the function and then
pass a function to $entry(), not a reference to the method.

Which leads to:

public native void registerOnCut(Element element) /*-{

var that = this;


element.oncut = function() {
$wnd.alert("about to invoke Java doCut");

$entry(function() {

th...@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut()
();
});

$wnd.alert("returned from Java doCut");
return false;
};
}-*/;

(or you could wrap the whole 'oncut' callback in $entry).

Fabiano

unread,
Feb 15, 2010, 7:13:24 AM2/15/10
to Google Web Toolkit
On Feb 15, 10:35 am, Jim Douglas <jdou...@basis.com> wrote:
> I think that's part of it, Fabiano, and I think the other part is
> reconciling what 'this' means between the JavaScript event handler function
>

Trying to solve others issue is a good way to learn.. your final
solution is interesting.
Regards

Jim Douglas

unread,
Feb 15, 2010, 7:23:10 AM2/15/10
to Google Web Toolkit
Awesome, thanks Thomas! That version does exactly what I need.

> t...@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut()

Fabiano

unread,
Feb 15, 2010, 3:29:05 AM2/15/10
to Google Web Toolkit, Jim Douglas

Fabiano

unread,
Feb 15, 2010, 7:10:32 AM2/15/10
to Google Web Toolkit

On Feb 15, 12:22 pm, Thomas Broyer <t.bro...@gmail.com> wrote:
> On Feb 14, 11:19 pm, Jim Douglas <jdoug...@basis.com> wrote:
>
>

> t...@com.google.gwt.sample.stockwatcher.client.StockWatcher::doCut()
> ();
Hi,
this is another interesting syntax about function without arguments:
doCut() ();
Looks coherent with the google documentation.
What is the difference between doCut(D); and doCut()(); ?

Thomas Broyer

unread,
Feb 15, 2010, 4:31:37 PM2/15/10
to Google Web Toolkit

D is for 'double', not for no-arg methods:
http://java.sun.com/j2se/1.4.2/docs/guide/jni/spec/types.html#wp16432
(linked from the JSNI documentation page)

doCut() is a reference to the doCut method, while doCut()() calls the
method.

Jim Douglas

unread,
Feb 15, 2010, 5:31:51 PM2/15/10
to Google Web Toolkit
Thanks again for your help, Thomas. FWIW, here's the final version in
my application code (this particular widget subclasses TextBox):

In the constructor:
registerOnCut(getElement());

private native void registerOnCut(Element element)


/*-{
var that = this;
element.oncut = function()
{

$entry(that.@com.basis.bbj.web.gwt.client.awt.IInputEGWT::doCut()());
return false;
};
}-*/;

@SuppressWarnings("unused")
private void doCut()
{
cut(getCursorPos(), getSelectionLength());

Thomas Broyer

unread,
Feb 15, 2010, 8:17:13 PM2/15/10
to Google Web Toolkit

On 15 fév, 23:31, Jim Douglas <jdoug...@basis.com> wrote:
> Thanks again for your help, Thomas.  FWIW, here's the final version in
> my application code (this particular widget subclasses TextBox):
>
> In the constructor:
>         registerOnCut(getElement());
>
>     private native void registerOnCut(Element element)
>     /*-{
>         var that = this;
>         element.oncut = function()
>         {
>

> $entry(th...@com.basis.bbj.web.gwt.client.awt.IInputEGWT::doCut()());

You're misusing $entry here. It should "wrap" a function "reference",
so you shouldn't call doCut() and wrap its result (which would almost
be a no-op here as doCut() is void).

You'd rather write:
var that = this;

element.oncut = $entry(function() {
that.@com.basis.bbj.web.gwt.client.awt.IInputEGWT::doCut()();
return false;
});


From http://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI.html#calling
"""Notice that the reference to the exported method has been wrapped
in a call to the $entry function. This implicitly-defined function
ensures that the Java-derived method is executed with the uncaught
exception handler installed and pumps a number of other utility
services. The $entry function is reentrant-safe and should be used
anywhere that GWT-derived JavaScript may be called into from a non-GWT
context."""

Jim Douglas

unread,
Feb 15, 2010, 10:35:23 PM2/15/10
to Google Web Toolkit
I actually did read that section more than once, but the JSNI notes
are sparse, and there are no event handler samples. I just
regenerated my app in detail mode to look for similar generated code,
and I do see a few, but I don't understand it. I'll have to take this
one on faith; it's not at all clear to me what it's doing.

On Feb 15, 5:17 pm, Thomas Broyer <t.bro...@gmail.com> wrote:
> On 15 fév, 23:31, Jim Douglas <jdoug...@basis.com> wrote:
>
> > Thanks again for your help, Thomas.  FWIW, here's the final version in
> > my application code (this particular widget subclasses TextBox):
>
> > In the constructor:
> >         registerOnCut(getElement());
>
> >     private native void registerOnCut(Element element)
> >     /*-{
> >         var that = this;
> >         element.oncut = function()
> >         {
>
> > $entry(th...@com.basis.bbj.web.gwt.client.awt.IInputEGWT::doCut()());
>
> You're misusing $entry here. It should "wrap" a function "reference",
> so you shouldn't call doCut() and wrap its result (which would almost
> be a no-op here as doCut() is void).
>
> You'd rather write:
> var that = this;
> element.oncut = $entry(function() {

>    th...@com.basis.bbj.web.gwt.client.awt.IInputEGWT::doCut()();
>    return false;
>
> });
>
> Fromhttp://code.google.com/webtoolkit/doc/latest/DevGuideCodingBasicsJSNI...

Fabiano

unread,
Feb 16, 2010, 3:32:18 AM2/16/10
to Google Web Toolkit
@Thomas

Thanks for your explanation. I have got the point now.
Regards

Reply all
Reply to author
Forward
0 new messages