NBDemoGWT

Building RIA with Google Web Toolkit Demo (13 Minutes)

Description

This demo is part of the NetBeans Day World Tour and is used in the session “Technologies Used in Creating Rich Internet Applications”. The demo shows how to use NetBeans to develop a web application using the Google Web Toolkit. The demo will show how to build a web page with dynamic data (tag cloud) and changing data without page refreshes.

Product Versions Supported

Points to Hit

You will be emphasizing the Google Web Toolkit technology as well as the NBGWT NetBeans plugin. Here are some points to get across to the audience.

  • Google Web Toolkit allows Java developers to create RIA without knowing Javascript and AJAX details.
  • Google Web Toolkit allows a developer to take an existing web page and add to it with GWT.
  • The Google Web Toolkit NetBeans plugin does all the hard work for you and even integrates with the GWT development environment allowing you to do debugging. The plugin has a nice feature to generate all the necessary classes for GWT asynchronous calls.

Demo Prep

  1. unzip the gwt_NBDemoGWT.zip that contains a sample project and the plugin portal database.
  2. unzip the pluginsystem_db.zip into ".netbeans-derby/".
  3. Make sure JavaDB is running on port 1527.
  4. Have the “PluginSystem” project available by unzipping pluginsystem_NBDemoGWT.zip.
  5. Make sure you have a database connection defined for the Plugin Portal database, jdbc:derby://localhost:1527/pluginsystem, userid=admin, password=admin.
  6. Make sure you have the NBGWT plugin installed.
  7. Make sure you have GWT installed and know where it's located.


Gotchas

Demo

1. Create a new project "web->web application"

  • choose "Google Web Toolkit" for the framework
  • If the "GWT Installation Folder:" is blank, click the "Browse" button and navigate to the GWT installation.
  • when the project opens,
  1. right-click on the project node and choose "Properties"
  2. Select the "Libraries" node on the left and choose "Add Project" on the right.
  3. Navigate to the "PluginSystem" project directory and choose "Add Project Jar files".
  4. Press "OK".


Explanation Point:
We need to add a JPA persistence unit to use the plugin portal database.


2. Right-click on the "Source Package" node and choose "New->Other->Folder", name the folder "META-INF".

3. Right-click on the "META-INF" package and choose "New->XML->XML Document"

  • Name the file "persistence" (the .xml will be added)
  • choose "Well-formed Document"

4. Replace the content of "persistence.xml" with this code:


<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence 
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
    <persistence-unit name="gwtdemoPU" transaction-type="RESOURCE_LOCAL">
        <provider>oracle.toplink.essentials.PersistenceProvider</provider>
        <class>org.netbeans.pluginportal.data.PluginImpl</class>
        <class>org.netbeans.pluginportal.data.CommentImpl</class>
        <class>org.netbeans.pluginportal.data.DisplayCategoryImpl</class>
        <class>org.netbeans.pluginportal.data.OtherCategoryImpl</class>
        <class>org.netbeans.pluginportal.data.RatingImpl</class>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="toplink.jdbc.user" value="admin"/>
            <property name="toplink.jdbc.password" value="admin"/>
            <property name="toplink.jdbc.url" value="jdbc:derby://localhost:1527/pluginsystem"/>
            <property name="toplink.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
        </properties>
    </persistence-unit>
</persistence>

5. In the MainEntryPoint.java file, replace the class body with the following code:


    private FlowPanel categoryPanel = new FlowPanel();

    public MainEntryPoint() {
    }

    /**
     * The entry point method, called automatically by loading a module
     * that declares an implementing class as an entry-point
     */
    public void onModuleLoad() {

        final HTML header1 = new HTML("<h1>Categories</h1>");
        final TextBox textBox = new TextBox();

        RootPanel.get().add(header1);
        RootPanel.get().add(textBox);
        categoryPanel.setWidth("400px");
        RootPanel.get().add(categoryPanel);
    }


6. Do a "Fix imports" and a "Reformat code".


Explanation Point:
Everything in the "MainEntryPoint" class will be run through the GWT compiler and turned into Javascript code. Thus this class must ONLY contain Java 1.4 constructs and only classes that are supported by this compiler. Therefore all the logic to get categories and create the tag cloud will have to be done on the server side and accessed through what Google call RPCs which are AJAX calls.


7. Run the web application and show the output so far.


Explanation Point:
Now we need to do the server-side logic. We do this by creating an asynchronous service for our "MainEntryPoint" to call.


8. On the project node, right-click and select "New->GWT RPC Service...".

  • Name the service "GWTPluginSystemService
  • Make sure "Create Usage Example Class" is NOT selected
  • Press "Finish"
Explanation Point:
First we define the service interface.


9. Open "GWTPluginSystemService.java".

10. Replace the existing method signature


    public String myMethod(String s);

with this code
    public String [[ | ]] getCategoryLinks(String [] links, String prefix);

11. Open "GWTPluginSystemServiceAsync.java".

12. Replace the existing method signature


    public void myMethod(String s, AsyncCallback callback);

with this code


    public void getCategoryLinks(String [] links, String prefix, AsyncCallback callback);

Explanation Point:
All asynchronous service interfaces MUST return "void". The mechanism works through a pass-by-reference so we will be changing the values in "links" and returning that.


13. Open "GWTPluginSystemServiceImpl.java".

14. Replace the entire class contents with the following code.


    private CategoryRange categoryRange;
    String[] links;
    String prefix;

    public String[[ | ]] getCategoryLinks(String[] inLinks, String inPrefix) {
        prefix = inPrefix;
        /**
         * Notice "links" will get the same object reference as "inLinks" and this is the reference we
         * return.  If you create a new object reference and return that, the values will NOT make it back to the client.
         */
        links = inLinks;
        getLinks();
        return links;
    }

    private void getLinks() {
        ArrayList<String> returnList = new ArrayList<String>();
        CategoryCount[] counts = this.getPluginSystem().getCategoryCounts();
        String linkStart = "<a ";
        String currentLink = "";
        for (CategoryCount currentCount : counts) {
            /**
             * Check to make sure the category name matches the prefix typed in.
             */
            if (null != currentCount.getCategoryName() && 
                    currentCount.getCategoryName().length() > 0 &&
                    !currentCount.getCategoryName().toUpperCase().startsWith(prefix.toUpperCase())) {
                continue;
            }
            currentLink = linkStart + "style=\"" + this.getLinkStyle(currentCount) + "\" href=\"http://boguslink.com\">";
            currentLink += currentCount.getCategoryName() + " </a>";
            returnList.add(currentLink);
            currentLink = "";
        }
        links = returnList.toArray(new String[0]);

        return;
    }

    private String getLinkStyle(CategoryCount count) {


        String style1 = "font-size: 12px; font-weight: normal;";
        String style2 = "font-size: 13px; font-weight: normal;";
        String style3 = "font-size: 14px; font-weight: normal;";
        String style4 = "font-size: 15px; font-weight: normal;";
        String style5 = "font-size: 16px; font-weight: normal;";
        String style6 = "font-size: 17px; font-weight: normal;";

        /**
         * For this tag cloud we will use the linear distribution algorithm with six different "buckets".
         * The algorithm is:
         *  category weight = max number/category - min number/category
         *  range = category weight/number of buckets (6).
         *  distribute the plugins based on category weight for the bucket matching the range.
         *
         */


        long weight = 0;
        long range = 0;
        if (null == categoryRange) {
            categoryRange = this.getPluginSystem().getCategoryRange();
        }

        if (null != categoryRange) {
            weight = categoryRange.getMax() - categoryRange.getMin();
            if (weight < 6) {
                /**
                 * We don't have a very big range of numbers in categories so we need to increase the
                 * weight so the range will be bigger.
                 */
                range = 1;
            } else {
                range = weight / 6;
            }
        } else {
            System.err.println("Error: category range is null!");
            return null;
        }


        /**
         * determine the rank in the cloud
         */
        if (count.getCount() >= 0 && count.getCount() <= range) {
            return style1;
        } else if (count.getCount() >= (range * 1) + 1 && count.getCount() <= (range * 1) + range) {
            return style2;
        } else if (count.getCount() >= (range * 2) + 1 && count.getCount() <= (range * 2) + range) {
            return style3;
        } else if (count.getCount() >= (range * 3) + 1 && count.getCount() <= (range * 3) + range) {
            return style4;
        } else if (count.getCount() >= (range * 4) + 1 && count.getCount() <= (range * 4) + range) {
            return style5;
        } else {
            return style6;
        }
    }
    private EntityManagerFactory emf;
    private PluginSystem pluginsystem;

    private PluginSystem getPluginSystem() {
        if (null == pluginsystem) {
            emf = Persistence.createEntityManagerFactory("gwtdemoPU");
            pluginsystem = new PluginSystemFacade(emf);
        }
        return pluginsystem;
    }

Explanation Point:
Notice that we have to put all server side code in the service implementation. This makes sense. We do, however, have to include some client code in the server piece in order to generate dynamic content that is derived from server accessible state. Remember the client code will be compiled by GWT into Javascript and the Plugin System classes as well other classes are not recognized by the GWT compiler.


16. Open "MainEntryPoint.java".

17. Add the following code after the "onModuleLoad()" method.


    /**
     * Create an asynchronous callback to handle getting the links.
     */
    final AsyncCallback linksCallback = new AsyncCallback() {

        public void onSuccess(Object result) {
            String[[ | ]] links = (String[]) result;
            addLinks(links);
        }

        public void onFailure(Throwable caught) {
        }
    };

    private void addLinks(String[] links) {
        String allLinks = "";
        /**
         * We need to concatenate all the server-side generated anchor <a> tags and
         * create one HTML tag.  Otherwise GWT wil put each HTML in a separate <DIV>.
         */
        for (int ii = 0; ii < links.length; ii++) {
            /**
             * Put   between each link to allow the cloud to wrap.
             */
            allLinks += links[Ii] + "  ";
        }
        categoryPanel.clear();
        categoryPanel.add(new HTML(allLinks));
    }

    public static GWTPluginSystemServiceAsync getService() {
        // Create the client proxy. Note that although you are creating the
        // service interface proper, you cast the result to the asynchronous
        // version of
        // the interface. The cast is always safe because the generated proxy
        // implements the asynchronous interface automatically.
        GWTPluginSystemServiceAsync service = (GWTPluginSystemServiceAsync) GWT.create(GWTPluginSystemService.class);
        // Specify the URL at which our service implementation is running.
        // Note that the target URL must reside on the same domain and port from
        // which the host page was served.
        //
        ServiceDefTarget endpoint = (ServiceDefTarget) service;
        String moduleRelativeURL = GWT.getModuleBaseURL() + "gwtpluginsystemservice";
        endpoint.setServiceEntryPoint(moduleRelativeURL);
        return service;
    }

18. Add the following code IN the "onModuleLoad()" method after the "final TextBox textBox = new TextBox();" statement.


        /**
         * Create a listener for keystrokes so we can change the tag cloud.
         */
        textBox.addKeyboardListener(new KeyboardListenerAdapter() {

            /**
             * Each time a key is pressed get a new set of links based on the
             * prefix typed.
             */
            public void onKeyUp(Widget sender, char keyCode, int modifiers) {
                getService().getCategoryLinks(new String[0], textBox.getText(), linksCallback);
            }
        });

Explanation Point:
Each time the user presses a key, the client code will invoke the asynchronous RPC with the prefix entered. The "linksCallback.onSuccess()" method will be called from the server with the results and the client code will clear the categoryPanel and re-add the links that were returned.


19. Add the following code after the "RootPanel.get().add(categoryPanel);" statement.


        getService().getCategoryLinks(new String[0], "", linksCallback);

20. Do a "Fix imports" and a "Reformat Code".

21. Run the web application and type characters in the textfield to show how the tag cloud changes based on the text typed. Point out no page refreshes are being done.

22. Set a break point on this line in "MainEntryPoint.java".



    final AsyncCallback linksCallback = new AsyncCallback() {

        public void onSuccess(Object result) {
====>       String[[ | ]] links = (String[]) result;
            addLinks(links);
        }

        public void onFailure(Throwable caught) {
        }
    };


23. Debug the web application and show how the GWT development environment is invoked and how you can step through client code in Java even though the real code is Javascript.

Demo Cleanup

None.

Not logged in. Log in, Register

By use of this website, you agree to the NetBeans Policies and Terms of Use. © 2012, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo