NBDemoWicket

Building RIA with Wicket Demo (? Minutes)

Description

This presentation 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 Wicket that includes the use of AJAX and a tag cloud.

Product Versions Supported

  • NetBeans 6.0 M10
  • The following Wicket Plugins:
  • WicketSupport” version 0.81 dated 9/16/2007
  • Wicket 1.3 as a Library” version 1.0 dated 9/16/2007

Points to Hit

You will be emphasizing the Wicket technology in this demo. Here are some points to get across to the audience.

  • Wicket allows a non-Java EE Java developer to very quickly create RIA.
  • A developer can use AJAX without knowing Javascript.
  • You can retrofit existing static web pages with Wicket.
  • Whenever possible, show off the HTML editor code completion and syntax highlighting.

Demo Prep

  1. unzip the attached zip ria_wicket_NBDemoWicket.zip that contains a sample project, the code snippets and the plugin portal database.
  2. unzip the pluginsystem_db_NBDemoWicket.zip into ".netbeans-derby/". The "pluginsystem_db_NBDemoWicket.zip" is the plugin portal database files that need to be unzipped into the directory that JavaDB is looking at for databases. You can see this directory setting in NetBeans by going to "Tools->JavaDB Database->Settings...". The directory the "pluginsystem_db_NBDemoWicket.zip" should be unzipped into is the "Database Location:" setting.
  3. Download and install the plugins from the plugin portal. You can get them here http://plugins.netbeans.org/PluginPortal/faces/PluginDetailPage.jsp?pluginid=3586
  4. Open the code snippet file and be ready to copy/paste from it.
  5. Make sure you have the Plugin Portal database installed on the machine.
  6. Make sure JavaDB is running on port 1527.
  7. Have the “PluginSystem” project available.
  8. Make sure you have a database connection defined for the Plugin Portal database, jdbc:derby://localhost:1527/pluginsystem, userid=admin, password=admin.


Gotchas

Sometimes you might need to do a “clean and build” if you get an error deploying the Wicket web application that looks something like this.

“Error during incremental deploy: org.netbeans.modules.masterfs.filebasedfs.utils.FSException: Cannot create file...”

I really only saw this when a made class changes but you might run into it during the demo.

Demo

1. First run a completed version of the demo to explain the web application that you will build. Show the tag cloud with all the Plugin Portal categories and show how typing something in the text field limits the words in the tag cloud.

2. Create a Wicket project by doing the following.

  • Create a project and choose the “web” category and the “web application” project type and press “Next”.
  • Type “wicketdemo” in the project name and take the rest of the defaults on this page. Press “Next”.
  • On the “Frameworks” window, select “Wicket 1.3” and take the rest of the defaults for the framework. Press “Finish”.
Explanation Point:
NetBeans provides a web frameworks API that allows you to plug specific web application framework details into the project.


3. At this point “index.jsp” will be the default page opened. Close “index.jsp” and open “Home.html” and “Home.java” under the source folder.

Explanation Point:
For each and every web page in Wicket, there MUST be a corresponding Java class with the same name as the web page. The HTML file and the Java file must be in the same directory.


4. Run the web application by clicking the green arrow or right-clicking on the project and choosing “Run”.

5. Now demonstrate how easy it is to add content to a web page. In the “Home.html” file, add the following line just below the “<span wicket:id='mainNavigation'/>” line.

<span wicket:id='label'>this will be replaced</span>

6.Open the “Home.java” file and add the following code in the constructor.

   Label label = new Label("label","Plugin Portal Categories");
   add(label);
Explanation Point:
The first parameter of all visual components is the “id” of the component. This corresponds to the “wicket:id” in the HTML.


7.Show the results by running the web application by clicking the green arrow or right-clicking on the project and choosing “Run”.

8.Next build the tag cloud panel. This demonstrates how Wicket supports adding dynamic content.

  1. . Right-click on the “wicketdemo” project node and choose “New->Other...”.
  2. . Choose category “wicket” and “file type” of "Panel".
  3. . Give it a class name of “CloudPanel” and choose the “com.myapp.wicket” package and press “Finish”.

4.Before you start the “CloudPanel” we will need to make the Plugin System data available. To do this we will first create a Persistance Unit then we will create a project dependency.

Explanation Point:
The Plugin System is the back end of the Plugin Portal. It contains all the business logic around maintaining the Plugin data. The Plugin System uses Java Persistence API (JPA) as a persistence technology. Therefore in order to use the Plugin System, we need define a Persistence Unit so we can provide an EntityManagerFactory for the Plugin System.


5.Select the “Source Packages” node, right-click and choose “New->Folder...”. Name the folder “META-INF”, take the rest of the defaults and click “Finish”.

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

  • Name the file "persistence" (the .xml will be added)

7. choose "Well-formed Document"

8. 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>

Explanation Point:
The default persistence unit is created to be used in a Java EE 5 supported container that supports JPA. The default is meant to be used inside a Java EE 5 supported component that will “inject” the details. The supported components are EJB3 and JavaServer Faces Managed Beans. Since we will be using neither, we need to supply the JDBC connection information.


9.Now we need to include the Plugin System API to use. Open the project properties (right-click on project node->properties).

10.Choose “Libraries” and “Add Project...”. Navigate to the “PluginSystem” project and click “Add Project JAR Files”.

11.Choose “Add Library” and add the “Java DB Driver”, and “TopLink Essentials”.

12.Choose “Add Jar” and navigate to the Glassfish_install_dir/lib” directory and select the “javaee.jar” file.

Explanation Point:
We need the Java DB, Toplink Essentials and the “javaee.jar” file for the JPA classes.


13. Now lets add the Wicket panel. In the “CloudPanel.html” file, add the following tags.

     <wicket:panel>
     </wicket:panel>

14.Type the following code inside the <wicket:panel> tags showing the code completion INSIDE the “style” attribute.

     <div style="width: 300px">
     </div>
Explanation Point:
We will put the actual tag cloud inside a “div” and limit the width to force it to wrap the text.


15.Now paste the following code inside the “div” tags.

                <span wicket:id="cloud">
                    <a wicket:id="link" style="text-decoration: none"> 
                        <!--tag cloud dynamic contents go here -->
                        <span wicket:id="text" style="unknown">caption</span>
                    </a>
                </span>
Explanation Point:
The “cloud” will correspond to a Wicket “repeater”, the way to generate dynamic content in Wicket. The dynamic content will be filled in where the comment denotes. Notice the “style=”unknown” on the “text” component. This style will be filled in by the Java code and will give the tag cloud the different size fonts. Notice the “style=”text-decoration: none””. This turns off the underlines on the links in the tag cloud.


16.Now we need to add the corresponding code in the Java class. In the “CloudPanel.java” file, add the following code.


Add this declaration for a member variable.

    /**
     * Mark the following as transient so Wicket won't try to serialize them.
     */
    private transient EntityManagerFactory emf;
    private transient PluginSystem pluginsystem;
Explanation Point:
We want to mark these as “transient” because we don't want Wicket to serialize them into the session.


Replace the constructor with this code.

    public CloudPanel(String id) {
        super(id);

        buildPanel();
    }

Add a method to access the Plugin System.

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

Add the following code.

    private void buildPanel() {

        CategoryCount[] counts = getPluginSystem().getCategoryCounts();
        List<CategoryCount> countsList = Arrays.asList(counts);

        final DataView repeater = new DataView("cloud", new ListDataProvider(countsList)) {

            protected void populateItem(final Item item) {

                MyLink link = new MyLink("link");
Explanation Point:
MyLink is an inner class that defines a simple type based on the abstract “Link”. We can't use an anonymous inner class here because we need to use the object reference in other places.


                item.add(link);


                String categoryName = ((CategoryCount) item.getModelObject()).getCategoryName();


                Label label = new Label("text", ((CategoryCount) item.getModelObject()).getCategoryName());
Explanation Point:
“Item” will wrap the actual type as it's model. In this case the type passed to the “ListDataProvider” is “CategoryCount”.
                       Model model = new Model() {
                            @Override
                              public Object getObject() {
                                return getLinkStyle((CategoryCount) item.getModelObject());
                            }
                        };

                        AttributeModifier am = new AttributeModifier("style", model);
                        label.add(am);
                        link.add(label); 
                    }
         }; 

        /**
         * Add the repeater to the cloud panel.
         */
        add(repeater);
    }


    protected Component getCloudPanel() {
        return this;
    }

    private String getLinkStyle(CategoryCount count) {
Explanation Point:
This method implements a “Linear Distribution” tag cloud algorithm.


        String style1 = "font-size: 10px; font-weight: normal;";
        String style2 = "font-size: 11px; font-weight: normal;";
        String style3 = "font-size: 12px; font-weight: normal;";
        String style4 = "font-size: 13px; font-weight: normal;";
        String style5 = "font-size: 14px; font-weight: normal;";
        String style6 = "font-size: 15px; 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.
         *
         */

        CategoryRange categoryRange = getPluginSystem().getCategoryRange();
        long weight = 0;
        long range = 0;
        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;
        }
    }

    /**
     * Extend the abstract Link class to create a simple Link type.
     */
    class MyLink extends Link {

        public MyLink(String id) {
            super(id);
        }

        public void onClick() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }


17. Now we need to add the CloudPanel to the main page, Home.html. Edit Home.html and add the following line after the last .

<span wicket:id='cloud-panel'/>

18. Edit Home.java and add the following lines in the constructor.

       CloudPanel cloud = new CloudPanel("cloud-panel");
       add(cloud);

19. Now “Run” the web application to see the results so far. The page should look something like this.

20. Next we will demonstrate how easy Wicket makes AJAX. First open the “CloudPanel.html” file and add the following lines above the “
”.
            <form wicket:id="form">
                Category: <input type="text" wicket:id="field" size="50"/><br/>
                Values:<span wicket:id="selectedValue"></span><br/>
            </form>
Explanation Point:
We will create a textfield that the user will use to limit the entries in the tag cloud. As the user types, only the words that match the prefix the user is typing will show up in the tag cloud.


21. Edit “CloudPanel.java” and add the following code anywhere in the class.

    
    private String currentPrefix;

    public String getCurrentPrefix() {
        return currentPrefix;
    }

    public void setCurrentPrefix(String currentPrefix) {
        this.currentPrefix = currentPrefix;
    }
Explanation Point:
The getter method for “currentPrefix” will be used by the AJAX callback mechanism.


Add the following code at the top of the “buildPanel” method.

        Form form = new Form("form");
        add(form);

        final TextField field = new TextField("field", new Model(""));
        form.add(field);

        final Label label = new Label("selectedValue", new Model(""));
        label.setOutputMarkupId(true);
        form.add(label);

Add the following code after this line of code

   String categoryName = ((CategoryCount) item.getModelObject()).getCategoryName();
      in the “populateItem” method.
   String categoryStart = getCurrentPrefix();
   if (null == categoryName) {
      link.setVisible(false);
      return;
   }
    /**
     * If the category doesn't begin with the typed in prefix, don't add it to the cloud.
     */
   if (null != categoryStart && null != categoryName) {
     if (!categoryName.toUpperCase().startsWith(categoryStart.toUpperCase())) {
        link.setVisible(false);
        return;
     }
  }

Add the following code after “add(repeater)”

        /**
         *  We need to set the output markup id so it can be used as an AJAX target
         */
        setOutputMarkupId(true);
        /**
         *  Now set up the AJAX handling
         */
        OnChangeAjaxBehavior onChangeAjaxBehavior = new OnChangeAjaxBehavior() {

            @Override
            protected void onUpdate(AjaxRequestTarget target) {
                setCurrentPrefix(field.getModelObjectAsString());
                target.addComponent(getCloudPanel());
            }
        };
        field.add(onChangeAjaxBehavior);
Explanation Point:
That's all there is to using AJAX. First you set up an “OnChangeAjaxBehavior” overriding the “onUpdate” method. You set the target to be the component that will be updated when the event (change) is fired. You must also add the “OnChangeAjaxBehavior” to the component that will use the event.


22. Now run the web app again and show how typing in the text field limits what is shown in the tag cloud. For example, typing “To” will result in only “Tools” showing.


Demo Cleanup

If you want to use the same project name, “wicketdemo”, you will need to rename, move, or delete the current copy in the project folder you will use for the demo.

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