Lab-5533: Java Strikes Back on Client Side: Easier Development and Deployment

Expected Duration: 100 minutes

Exercise 2: Programming with LiveConnect: Bridge between Java/Java FX and JavaScript (40 minutes)

 

This exercise shows APIs of LiveConnect, and examples of using those APIs to interact between Java/Java FX and JavaScript/DOM objects in the same web page.


Background Information

 

Introducing the LiveConnect bridge

 

Since Java SE 1.4, applet developers have been able to traverse the Document Object Model (DOM) of the document containing their applet using the Common DOM API. The Common DOM implementation has been rewritten and simplified in the new Java Plug-In, and is now both more robust and easier to use.

The bridge between the Java and JavaScript programming languages, known informally as LiveConnect, has been reimplemented. The new implementation is backward-compatible and features improved reliability, performance and cross-browser portability, for both Java calling JavaScript as well as JavaScript calling Java. Formerly Mozilla-specific LiveConnect functionality, such as the ability to call static Java methods, instantiate new Java objects and reference third-party packages from JavaScript, is now available in all browsers.

A new LiveConnect specification both documents the behavior of the Java/JavaScript bridge in the new Java Plug-In, and specifies how alternate implementations of a Java Plug-In should behave to support portability of mixed Java/JavaScript code.


Steps to Follow

 

Step 1: Interact with HTML via DOM api

  1. Click the Files Tab in the NetBeans IDE, and then expand the HelloApplet Node.
    Dom Applet Code

  2. Double-click the 'DomApplet.java' and implement method actionPerformed() as shown in the following box.
     1 /*
    2 * To change this template, choose Tools | Templates
    3 * and open the template in the editor.
    4 */

    5 package demo.applets;
    6
    7 import com.sun.java.browser.plugin2.DOM;
    8 import java.awt.FlowLayout;
    9 import java.awt.event.ActionEvent;
    10 import java.awt.event.ActionListener;
    11 import javax.swing.JApplet;
    12 import javax.swing.JButton;
    13 import org.w3c.dom.Attr;
    14 import org.w3c.dom.html.HTMLDivElement;
    15 import org.w3c.dom.html.HTMLDocument;
    16
    17 /**
    18 *
    19 * @author jason
    20 */

    21 public class DomApplet extends JApplet implements ActionListener {
    22
    23 JButton button1 = new JButton("Change Title");
    24 JButton button2 = new JButton("Hide text");
    25 JButton button3 = new JButton("Show text");
    26
    27 @Override
    28 public void init() {
    29 super.init();
    30 button1.addActionListener(this);
    31 button2.addActionListener(this);
    32 button3.addActionListener(this);
    33 this.getContentPane().setLayout(new FlowLayout());
    34 this.getContentPane().add(button1);
    35 this.getContentPane().add(button2);
    36 this.getContentPane().add(button3);
    37 }
    38
    39 public void actionPerformed(ActionEvent e) {
    40 // Set the title of the HTML page containing this applet
    41 Object source = e.getSource();
    42 if (source == button1) {
    43 HTMLDocument document = (HTMLDocument) DOM.getDocument(this);
    44 document.setTitle("Title By Applet");
    45
    46 } else if (source == button2) {
    47 //hide it
    48 HTMLDocument document = (HTMLDocument) DOM.getDocument(this);
    49 HTMLDivElement div = (HTMLDivElement) document.getElementById("testdiv");
    50 Attr myAttr = div.getAttributeNode("style");
    51 System.out.println("style value:" + div.getAttributeNode("style").getValue());
    52 myAttr.setValue("visibility:hidden");
    53 div.setAttributeNode(myAttr);
    54 } else if (source == button3) {
    55 //show it
    56 HTMLDocument document = (HTMLDocument) DOM.getDocument(this);
    57 HTMLDivElement div = (HTMLDivElement) document.getElementById("testdiv");
    58 Attr myAttr = div.getAttributeNode("style");
    59 System.out.println("style value:" + div.getAttributeNode("style").getValue());
    60 myAttr.setValue("visibility:visible");
    61 div.setAttributeNode(myAttr);
    62 }
    63 }
    64 }


    Note: the key point in the above code is line 43, 48 and 56.
    In the new plugin implemetation, the bootstrapping mechanism for acquiring the the Document object has been simplified. There is now one simple entry point which can be called from any thread:

        package com.sun.java.browser.plugin2;
       
        public class DOM {
            public static final org.w3c.dom.Document getDocument(Applet applet)
                throws org.w3c.dom.DOMException;
        }

    An applet may call this method as follows:

        org.w3c.dom.Document document =
            com.sun.java.browser.plugin2.DOM.getDocument(this);

    After getting the Document object, we can cast it into a HTMLDocument, and generally use any of the APIs in the org.w3c.dom namespace.
    For the detail DOM api, please refer to this page: http://java.sun.com/javase/6/docs/jre/api/plugin/dom/index.html.
  3. Double click the file 'dom-applet.html' shown below.

    Open Dom HTML


  4. Modify the code for file ' dom-applet.html' as shown in the following table.
     1 <!--
    2 To change this template, choose Tools | Templates
    3 and open the template in the editor.
    4 -->

    5 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    6 <html>
    7 <head>
    8 <title></title>
    9 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    10 </head>
    11 <body>
    12 <script src="deployJava.js"></script>
    13 <script>
    14 var attributes = {codebase:'.',
    15 code:'demo.applets.DomApplet.class',
    16 width:200, height:200,
    17 archive: 'dist/HelloApplet.jar',
    18 id:'DomApplet',
    19 name:'DomApplet',
    20 mayscript:'false'} ;
    21 var parameters = {fontSize:16,draggable:'true',classloader_cache:'false'} ;
    22 var version = '1.6' ;
    23 deployJava.runApplet(attributes, parameters, version);
    24 </script>
    25
    26 <hr/>
    27 <div id="testdiv" style="visibility:visible">
    28 These are some words....
    29 </div>
    30 </body>
    31 </html>



  5. Run the Applet in browser.
    Open the file explore and navigate to the directory containing the file launch-with-applet-tags.html and then double click the file, the applet will run in the browser as shown below.


    Run DOM Demo
    Try to click the button in the applet and see the effect in the browser.

Step 2: Interact with JavaScript and other Applet

  1. Click the Files Tab in the NetBeans IDE, and then expand the HelloApplet Node.
    Open Hello Applet

  2. Double-click the file 'HelloApplet.java' shown above and modify the code as following table.
     1 /*
    2 * To change this template, choose Tools | Templates
    3 * and open the template in the editor.
    4 */

    5 package demo.applets;
    6
    7 import java.applet.Applet;
    8 import java.awt.FlowLayout;
    9 import java.awt.event.ActionEvent;
    10 import java.awt.event.ActionListener;
    11 import java.awt.event.MouseEvent;
    12 import javax.swing.JApplet;
    13 import javax.swing.JButton;
    14 import javax.swing.JTextField;
    15 import netscape.javascript.JSObject;
    16
    17 /**
    18 *
    19 * @author jason
    20 */

    21 public class HelloApplet extends JApplet implements ActionListener {
    22
    23 JButton button = new JButton("Hello");
    24 JButton button2 = new JButton("Call JavaScript");
    25 JTextField name = new JTextField(10);
    26
    27 @Override
    28 public void init() {
    29 //set event handler
    30 button.addActionListener(this);
    31 button2.addActionListener(this);
    32 //add widget to the applet
    33
    34 this.getContentPane().setLayout(new FlowLayout());
    35 this.getContentPane().add(name);
    36 this.getContentPane().add(button);
    37 this.getContentPane().add(button2);
    38 }
    39
    40 public void actionPerformed(ActionEvent e) {
    41 Object target = e.getSource();
    42 System.out.println("Button clicked");
    43 if (target == button) {
    44 System.out.println("You input:" + name.getText());
    45 Applet a = this.getAppletContext().getApplet("WorldApplet");
    46 WorldApplet wa = (WorldApplet) a;
    47 wa.setLabelText(name.getText());
    48 } else if (target == button2) {
    49 System.out.println("You click call javascript");
    50 JSObject win = JSObject.getWindow(this);
    51 System.out.println(win.call("f", null));
    52 }
    53
    54 }
    55
    56 public String getNameValue() {
    57 System.out.println("getNameValue called");
    58 return name.getText();
    59 }
    60
    61 public void setLabelText(String value) {
    62 System.out.println("setLabelText called");
    63 }
    64
    65 public boolean isAppletDragStart(MouseEvent e) {
    66 // Change the drag gesture to be left-click and drag with
    67 // no modifier keys
    68 System.out.println("isAppletDragStart ");
    69 return (e.getSource() == this && e.isAltDown());
    70 }
    71
    72 }


    Note:
    The function of the above codes from line 50 to 51 is to call the JavaScript method defined in the same HTML page, and line 45 to 47 is used to call the method defined other applets.
    HelloApplet applet above get the reference to WorldApplet by calling this method (line 45) :
        Applet a = this.getAppletContext().getApplet("WorldApplet");
    Where the "WorldApplet" is the value of name attribute of the WorldApplet deployed in step 6 below.
    For more information, please refer to this page: http://java.sun.com/javase/6/webnotes/6u10/plugin2/liveconnect/index.html.
  3. Double-click the file 'WorldApplet.java' shown below
    Open World Applet
  4. Modify the code as following table.
     1 /*
    2 * To change this template, choose Tools | Templates
    3 * and open the template in the editor.
    4 */

    5
    6 package demo.applets;
    7
    8 import javax.swing.JApplet;
    9 import javax.swing.JLabel;
    10
    11 /**
    12 *
    13 * @author jason
    14 */

    15 public class WorldApplet extends JApplet {
    16 JLabel label = new JLabel("This is an world applet.");
    17
    18 @Override
    19 public void init() {
    20 this.getContentPane().add(label);
    21 }
    22
    23 public void setLabelText(String value) {
    24 label.setText(value);
    25 }
    26 }


    Note: This applet just contains a label, and provides method setLabelText to change the text on the label.
  5. Double-click on the file 'demo.html' shown below.
    Open Demo HTML

  6. Modify the code for demo.html as shown in the table below.
     1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
    2 <html>
    3 <head>
    4 <title></title>
    5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    6 <script src="deployJava.js"></script>
    7 <script language="javascript">
    8 function go1() {
    9 var value1 = HelloApplet.getNameValue();
    10 WorldApplet.setLabelText("Value from HelloApplet: " + value1);
    11 }
    12
    13 function go2() {
    14 var value1 = form1.foo.value;
    15 WorldApplet.setLabelText("Value from HTML Form: " + value1);
    16 }
    17
    18 function f() {
    19 window.alert("abc");
    20 return "abc";
    21 }
    22 </script>
    23 </head>
    24 <body>
    25 <table border="1">
    26 <tr>
    27 <td>Applet 1</td>
    28 <td>HTML FORM</td>
    29 <td>Applet 2</td>
    30 </tr>
    31 <tr>
    32 <td>
    33
    34 <script>
    35 var attributes = {codebase:'.',
    36 code:'demo.applets.HelloApplet.class',
    37 width:200, height:200,
    38 archive: 'dist/HelloApplet.jar',
    39 id:'HelloApplet',name:'HelloApplet',
    40 mayscript:'false'} ;
    41 var parameters = {fontSize:16,draggable:'true'} ;
    42 var version = '1.6' ;
    43 deployJava.runApplet(attributes, parameters, version);
    44 </script>
    45 </td>
    46 <td>
    47 <form name="form1">
    48 JavaScript Button: <input type="button" value="Put Applet 1 Value to Applet 2" onclick="go1()"/>
    49 <br/>
    50 <hr/>
    51 <input name="foo" size="10"/> <input type="button" value="Put form Value to Applet 2" onclick="go2()"/>
    52 </form>
    53 </td>
    54 <td>
    55
    56 <br/> Applet deployed with deployment tools: <br/>
    57 <script>
    58 var attributes = {codebase:'.',
    59 code:'demo.applets.WorldApplet.class',
    60 width:200, height:200,
    61 archive: 'dist/HelloApplet.jar',
    62 id:'WorldApplet',name:'WorldApplet'} ;
    63 var parameters = {fontSize:16} ;
    64 var version = '1.6' ;
    65 deployJava.runApplet(attributes, parameters, version);
    66 </script>
    67
    68 </td>
    69 <tr/>
    70 </table>
    71
    72 </body>
    73 </html>


    Note:
    In the JavaScript code above (line 8 to 16), we call the methed of applet via the name of the applet. For example, line 9 in the above html page, var value1 = HelloApplet.getNameValue();  will call the method getNameValue of the applet named HelloApplet which is deployed in line 34 to line 44.
  7. Run the Applet in browser.
    Open the file explore and navigate to the directory containing the file demo.html and then double click the file, the applet will run in the browser as shown below.
    Run Demo HTML


    In the above image, there are 3 parts. The left one is a Applet named Applet 1, the middle part is just a HTML form and in the right is a Applet named Applet 2.

    Note:

    • Click Hello button in Applet 1 will send text from Applet 1 to Applet 2.
    • Click 'Call JavaScript' Button in Applet 1 will call javaScript method defined in this HTML page.
    • Click 'Put Applet 1 Value to Applet 2' in the HTML form will get the value in the textfield in Applet 1 and then put the value into the Applet 2.
    • Click ´Put form Value to Applet 2´ in the HTML form will put the value in the textfield in the form into the Applet 2.


Step 3: Access Java FX from JavaScript

In this step, we are going to access Java FX variables and call Java FX methods from JavaScript.

  1. Open LoveLetter project in Netbeans.
  2. Double click Letter.fx to open it in the editor window. Let's study the code a little bit. In Letter.fx, we define two sprites: smurf and smurfette, by the following code.

    def smurf: Node = ImageView {
        x: (fwidth) / 2,
        y: (fheight - smurfh) / 2
        image: Image {
            url: "{__DIR__}smurf.png"
        }
        onMouseClicked: function( e: MouseEvent ):Void {
            beating.play();
        }
    }
    
    def smurfette: Node = ImageView {
        x: 10,
        y: 10
        image: Image {
            url: "{__DIR__}smurfette.png"
        }
        onMouseClicked: function( e: MouseEvent ):Void {
            sendRose();
        }
        effect: PerspectiveTransform {
        ......
        }
    }
                                                            

    And a few images representing different status of the heart of smurf

    def heartarrow: Node = ImageView {
        ......
        image: Image {
            url: "{__DIR__}heartarrow.png"
        }
    }
    
    def heartbroken: Node = ImageView {
        ......
        image: Image {
            url: "{__DIR__}heartbroken.png"
        }
    }
    
    def heartflying = ImageView {
        ......
        image: Image {
            url: "{__DIR__}heartflying.png"
        }
    }
    
    def hearts: Node = Group {
        content: [
            heart,
            heartarrow,
            heartbroken,
        ]
    }
    
                                                            

    Animations like heart beating, moving the rose, fadein, fadeout, etc. and functions to play them.

    var beating = ScaleTransition {
        byX: 1.1,
        byY: 1.1
        node: hearts
        duration: bind beatingRate
        repeatCount: Timeline.INDEFINITE
    }
    
    ......
    
    function fadeIn(node: Node) {
        FadeTransition {
            toValue: 1.0
            duration: 1.0s
            node: node
        }.play();
    }
    
    function fadeOut(node: Node) {
        FadeTransition {
            toValue: 0.0
            duration: 1.0s
            node: node
        }.play();
    }
    
    public function fallInLove() {
        fadeOut(heart);
        fadeOut(heartbroken);
        fadeIn(heartarrow);
    }
    
    public function heartBroken() {
        fadeOut(heart);
        fadeOut(heartarrow);
        fadeIn(heartbroken);
    }
    
    ......
    
    public function sendRose() {
        fadeIn(rose);
        TranslateTransition {
            fromX: 320,
            fromY: 230
            toX: 80,
            toY: 50
            node: rose
            duration: 3s
            interpolate: Interpolator.EASEBOTH
        }.play();
    }
                                                            
  3. Now, you've already had some ideas of the FX code, let's take a look at the HTML page where we deployed the FX application onto in the previous execise.

    Open MyLoveLetter.html in the <lab_root>/javaseclient/execise/page/ directory with an editor. The HTML file contains a love letter from smurf to the smurfette. And if we move the mouse over the words"HARD", "LOVE" in the letter, or click the words "ROSES", "MARRY", callback methods are called as following

    ......
    <script>
        function beating() {
            //TODO: access beating variable of Java FX
        }
    
        function fallInLove() {
            //TOOD: call fallInLove() method of Java FX
        }
    
        function sendRose() {
            //TODO: call sendRose() method of Java FX
        }
    
        function willYouMarry() {
            //TODO: call willYouMarryMe() method of Java FX
        }
    ......
    </script>
    
    
    ......
    <p><img src="images/message.png" onClick="MM_showHideLayers('layerletter','','show','layerletter-yes','','hide','layerletter-no','','hide')"></img></p>
    <div style="position: absolute; width: 100px; height: 100px; z-index: 1; left: 610px; top: 67px; visibility: hidden;" id="layerletter">
         <p><img src="images/letter.png" name="letter" width="500" height="413" border="0" usemap="#letter" id="letter">
              <map name="letter">
                   <area shape="rect" coords="153,129,209,157" href="#hard" onMouseOver="beating()">
                   <area shape="rect" coords="379,157,445,190" href="#love" onMouseOver="fallInLove()">
                   <area shape="rect" coords="379,195,447,226" href="#rose" onClick="sendRose()">
                   <area shape="rect" coords="132,324,209,356" href="#marry" onClick="willYouMarry()">
                   <area shape="rect" coords="381,8,495,94" href="#close" onClick="MM_showHideLayers('layerletter','','hide')">
              </map>
         </p>
    </div>
    ......
                                                            
  4. In order to get access to Java FX application, we need to add id parameter to the application at deploy time. Modify the javascript code in HTML page to deploy Java FX application as follows

            <script src="http://dl.javafx.com/1.1/dtfx.js"></script>
            <script>
                javafx(
                {
                    archive: "LoveLetter.jar",
                    draggable: true,
                    width: 600,
                    height: 400,
                    code: "loveletter.Letter",
                    name: "LoveLetter",
                    id: "MyApplet"
                }
            );
            </script>
                                                            
  5. Modify the callback method beating() to access FX Node variable beating as follows.

                function beating() {
                    //TODO: access beating variable of Java FX
                    var app = document.getElementById("MyApplet");
                                         app.script.beating.play();
                }
                                                            

    In JavaScript context, we can get instance of Java FX application by calling document.getElementById("<App ID>");. Variables defined in Java FX application can be access directly with <app instance>.script.<variable name>.

  6. We can also call Java FX methods through the application instance. Complete the callback methods as follows

                function fallInLove() {
                    //TOOD: call fallInLove() method of Java FX
                    var app = document.getElementById("MyApplet");
                    app.script.fallInLove();
                }
    
                function sendRose() {
                    //TODO: call sendRose() method of Java FX
                    var app = document.getElementById("MyApplet");
                    app.script.sendRose();
                }
    
                function willYouMarry() {
                    //TODO: call willYouMarryMe() method of Java FX
                    var app = document.getElementById("MyApplet");
                    app.script.willYouMarryMe();
                }
                                                            
  7. Open MyLoveLetter.html in a browser. Click the envelope icon to show the love letter. Try to move the mouse over "HARD", "LOVE", and click on "ROSES", "MARRY", you will see Java FX response the JavaScript events.

Step 3: Access JavaScript from Java FX

In this step, we're going to call JavaScript methods from Java FX code embedded in the web page.

  1. Let's take a look at some javascript code in MyLoveLetter.html. There are two javascript methods: sayYes(), sayNo() to update the content of web page without refreshing.

    <script>
    ......
                function sayYes() {
                    MM_showHideLayers('layerletter','','hide','layerletter-yes','','show','layerletter-no','','hide');
                }
    
                function sayNo() {
                    MM_showHideLayers('layerletter','','hide','layerletter-yes','','hide','layerletter-no','','show');
                }
    ......
    </script>
                                                            
  2. Double click Letter.fx in Netbeans to open it in the editor window. Method willYouMarryMe() makes the hidden button group visible.

    ......
    def yesButton: Node = SwingButton {
        text: "Yes"
        width: 70
        action: function() {
            accept();
        }
    }
    
    def noButton: Node = SwingButton {
        text: "No"
        width: 70
        action: function() {
            reject();
        }
    }
    
    def buttons: Node = HBox {
        translateX: 70
        translateY: 270
        spacing: 10
        content: [
            yesButton, noButton
        ]
        opacity: 0.0
        disable: true
    }
    ......
    
    public function willYouMarryMe() {
        buttons.disable = false;
        fadeIn(buttons);
    }
                                                            

    Once either button is clicked, callback method either accept() or reject() will be called. Besides activate some Java FX animations, we'd like to change the DOM content of the host page by calling JavaScript methods sayYes() or sayNo() perspectively.

  3. In order to use LiveConnect APIs, we need to include java plugin framework to the libraries of our Java FX applicatioin. Right click on LoveLetter project in Projects tag window, select Properties from the context menu.
  4. Select Libraries from Categories, click button Add JAR/Folder. In the filechooser, browse to directory <JRE_HOME>/lib directory, add plugin.jar.

  5. LiveConnect API defines netscape.javascript.JSObject as the delegate of JavaScript object in Java/Java FX code. So we need to add import to the code first. Add import as follows
    import netscape.javascript.JSObject;
                                                            
  6. Modify accept() and reject() methods as follows to call javascript methods

    function accept() {
        buttons.disable = true;
        fadeOut(buttons);
        heartFly();
        //TODO: call javascript method sayYes()
        var window = JSObject.getWindow(FX.getArgument("javafx.applet") as java.applet.Applet);
        window.eval("sayYes()");
    }
    
    function reject() {
        buttons.disable = true;
        fadeOut(buttons);
        heartBroken();
        //TODO: call javascript method sayNo()
        var window = JSObject.getWindow(FX.getArgument("javafx.applet") as java.applet.Applet);
        window.eval("sayNo()");
    }
                                                            

    Java FX application deployed in the browser is compiled to an Applet. The instance of Applet can be got by calling FX.getArgument("javafx.applet") method. Once we get the applet instance, the interaction with JavaScript is exactly the same as what we have introduced in the perious steps.

  7. Right click LoveLetter project in the Projects tag window of Netbeans, select Build Project from the context menu to re-create the jar file. Copy LoveLetter.jar located in <lab_root>/javaseclient/execise/LoveLetter/dist directory to <lab_root>/javaseclient/execise/page directory, replace the original one.
  8. Open MyLoveLetter.html with the browser, click the Click the envelope icon to show the love letter. Click word MARRY to show Yes and No buttons in Java FX. Click either button. Notice the content of the love letter is changed without refreshing the page.


Summary

 

In this execise, we have leant how Java/Java FX code can access the javascript context of the web it's deployed to. As well, the javascript code within the host page can also access Java/Java FX context via Liveconnect API.

With that interactibility, developers can create very rich web application mixed Java/Java FX and javascript.

 

Back to top
Next exercise