Tuesday Aug 21, 2007

Relatively Positioning Web UI Components

Tip on positioning Web UI components based on the width of the browser window in a NetBeans Visual Web Application or a Sun Java Studio Creator Application.[Read More]

Thursday Aug 16, 2007

SqlException: At least one parameter to the current statement is uninitialized

When you do the Using Databound Components to Access a Database tutorial, you learn how to use the Query Editor on the rowset to build the SQL statement to retrieve the desired set of data. You also learn that for every ? (parameter) in your query, you need to call setObject() on the rowset before the query is executed. Take, for example, the following query.

SELECT ALL TRAVEL.TRIP.TRIPID, 
           TRAVEL.TRIP.PERSONID, 
           TRAVEL.TRIP.DEPDATE 
FROM TRAVEL.TRIP
WHERE TRAVEL.TRIP.PERSONID = ?
   AND TRAVEL.TRIP.TRIPTYPEID = ? 
This query has two parameters, PERSONID and TRIPTYPEID. What this means is that you have to provide values for these two parameters before the rendering phase. You typically do this in the prerender() method, as shown in the following code example.
public void prerender() {
    if ( personIdDD.getSelected() == null ) {
        try {
          personDataProvider.cursorFirst();
          getSessionBean1().getTripRowSet().setObject(
            1, personDataProvider.getValue("PERSON.PERSONID"));
          getSessionBean1().getTripRowSet().setObject(
            2, "4");
          tripDataProvider.refresh();
        } catch (Exception e) {
          error("Cannot switch to person " +
              personDataProvider.getValue("PERSON.PERSONID"));
            log("Cannot switch to person " +
              personDataProvider.getValue("PERSON.PERSONID"), e);
        }
    }
}

If you forget to set the parameters before the rendering phase, you usually get the following error.

SqlException: At least one parameter to the current statement is uninitialized.

You will also get an error like this if you do not set all the parameters. Such as only calling setObject(1, somevalue) when you have two parameters in the query.

Another common error is to forget to edit the query to add the parameters. If you call setObject(1, somevalue), but your query has no parameters (no ?), you might get an error like the following.

java.lang.NullPointerException
at org.apache.derby.client.am.PreparedStatement.checkForValidParameterIndex(Unknown Source)

Similarly, if you call setObject(2, somevalue) when your query has only one parameter, the server will emit something like the following message.

SqlException: The parameter position '2' is out of range.  
The number of parameters for this prepared  statement is '1'

Monday Jul 09, 2007

Centering Web UI Components on a Page


If you would like the components to remain centered on a page when the browser is resized, try this trick.



  1. Open the Web Pages > resources > stylesheet.css style sheet and add the following style definitions. You will use these styles to define the colum styles in a three column grid panel.


    .ctrFirstColumn { 
       width: 10%;
    }

    .ctrSecondColumn {
       width: 80%;
    }

    .ctrThirdColumn {
       width: 10%;
    }



  2. Set the widths percentages to what works for your page.




  3. Save the changes, then close and reopen the IDE, so that the IDE picks up the new styles.




  4. Add a Grid Panel to the page in the top left corner.





  5. In the Properties window, click the ellipsis (...) button for the style property. Select Position, set both the Width and Height to 100%, and click OK.




  6. Set the columns property to 3.




  7. Click the ellipsis (...) button for the columnClasses. Select ctrFirstColumn and click the > button. Repeat for ctrSecondColumn and ctrThirdColumn. Click OK. These styles are applied to the columns in the grid panel in the order that you list them.




  8. Drop something into the first column, such as a blank Static Text component. As you drop each component into the Grid Panel, it occupies the first available column, so the first component that you drop goes in the first column, the second component goes in the second column, the third component goes in the third column, and the fourth component goes in a new row in the first column. A blank Static Text component is an easy way to occupy the left and right margins with a non-visible component.




  9. For the center column, drop a component from the Layout section, such as a Grid Panel or a Layout Panel. This is the component that you will use to layout all the components in the page. You can nest the layout components as necessary in order to achieve the right layout.



    You most likely want to set the width properties of the layout components to 100% or some other percentage value instead of using pixel sizes.


  10. When you add a Table component, set its width property to 100% or some other percentage value. (See the tips in the Laying out Multiple Table Components blog entry.) One thing to remember, is that whenever you work with the table layout, you have to check to make sure that the IDE did not change the width property back to a pixel width. If you find that your middle panel is not centering, check the width values of the Table components to make sure they are set to percentages.



  11. Drop a blank component, such as a blank Static Text component, to occupy the right column.

  12. Run the web app and resize the width of the browser. The components should remain centered.

Monday Jul 02, 2007

Laying Out Multiple Table Components



You can use the layout components, such as the Group Panel and the Grid Panel, to easily layout and size container components, such as the Table component. In the screen shot above, the two tables on the left are in a Group Panel, and the two on the right are in a Grid Panel. I can resize or move either panel in the Visual Designer, and the contents will resize and move in accordance (they won't resize in the Visual Designer, but they will resize in the client). The trick is to set the width property of each tables to 100% so that they resize based on the size of their container.

Because the Visual Designer does not display the widths of the tables accurately, you have to right-click on the page and choose Preview in Browser to get a feel of how the tables will actually appear in the client.

Tips:

  • Set the Group Panel component's block property to true. This encloses the panel in a <div> tag.

  • You can set the Group Panel component's separator property to <br /><br /> to put spacing between the tables.

  • Set the Table component's width property to 100%. If you make changes to the table, the IDE may reset this property so keep checking to make sure it is 100%.

  • I find it best to leave the Table Column width properties blank or set them at percentage amounts, such as 10%.

Friday Jun 22, 2007

Page Fragment Tips

7/16/07 Update: Thanks to Antonio, this blog entry is now available in Spanish.

Page fragments enable you to add a particular set of components, action handlers, and functions to a bunch of web pages. When you add a Page Fragment Box to a page, you are first given the option of choosing an existing page fragment or creating a new one, and then the IDE adds a <jsp:directive.include> tag to the main page (the page that includes the fragment). When you create a page fragment, the IDE adds both a .jspf file and a backing bean to the project.

The <jsp:directive.include> directive is not the same as, the <jsp:include> standard action. The directive causes the contents of the page fragment to be placed into the main page's .jsp before the .jsp file is translated into a servlet.

The page fragment's backing bean extends AbstractFragmentBean, which, unfortunately, does not provide the preprocess or prerender methods. It only provides the init and destroy methods. When a person lands on the page from another destination, such as by typing in the URL or clicking a link, the server instantiates the fragment's backing bean (and thus calls the init method) the first time that fragment is referenced. In general, this is likely to happen during the render response phase. On a postback (that is, when the page redisplays itself in response to a page submission) , the fact that the saved component state includes component bindings to properties in the fragment's backing bean will cause the server to instantiate the page fragment's backing bean much earlier.

Because the page fragment's init method can be called early in the lifecycle, you cannot reference or set component values in the init method. Conversely, a page fragment's init method might not be called until during or after the main page's prerender call. A workaround for this is to put methods in your page fragements and call those methods from the main page's prerender. For example, you can add a prerenderFragment method in each of your page fragments. Then for each fragment that you include in the main page, add code like the following:

public void prerender() {
      TopFragment topFragment = (TopFragment)getBean("TopFragment");
      topFragment.fragmentPrerender();
      ...
  }

Here are some code snippets to illustrate some common page fragment tasks:

Passing Data Between the Main Page and the Page Fragment

You can pass data to a page fragment through the a managed bean (request bean, session bean, application bean), or through function arguments as shown below

In the following snippit, the main page passes information to the page fragment by argument:

  public void prerender() {    
     Bike bike = getSessionBean1().getBike();
    ...
    PriceTable priceTable = (PriceTable) getBean("PriceTableFragment");
    totalsSubtotalPrice.setText(priceTable.subtotal(bike));
  }

Here, the main page sets the values in the request bean, which the page fragment then picks up and uses. This is from a web application where the user selects an item from a drop-down list in the main page, which, in turn, refreshes the trip data provider in the page fragment.

    // Page Fragment
    public void fragmentPrerender() {
        if (getRequestBean1().getPersonId() != null) {
            try {
                getSessionBean1().getTripRowSet().setObject(
                        1, getRequestBean1().getPersonId());
                tripDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch to person " +
                        getRequestBean1().getPersonId().toString());
                log("Cannot switch to person " +
                        getRequestBean1().getPersonId().toString(), e);
            }
        }
    }
    // Main Page
    public void prerender() {
        // If not a postback, set the default person
        if (getRequestBean1().getPersonId() == null) {
            try {
                personDataProvider.cursorFirst();
                getRequestBean1().setPersonId(
                  (Integer)personDataProvider.getValue("PERSON.PERSONID"));
            } catch (Exception e) {
                error("Cannot switch to person " +
                        personDataProvider.getValue("PERSON.PERSONID"));
                log("Cannot switch to person " +
                        personDataProvider.getValue("PERSON.PERSONID"), e);
            }
        }
        Fragment1 fragment1 = (Fragment1) getBean("Fragment1");
        fragment1.fragmentPrerender();
    }	

Changing Component Properties in a Page Fragment

The main page can access and modify components in the page fragment through the page fragment's bean object, as shown below:

  public void prerender() { 
    Navigation navigationFragmentBean = (    
    Navigation)getBean("Navigation");
    Hyperlink homeLink = navigationFragmentBean.getHomeLink();
    homeLink.setDisabled(true);
 }

Determining What Page the Page Fragment is Included In

In this code snippet, the page fragment changes its apperance based on what page it is being displayed in.

    public void fragmentPrerender() {
        // If they are done, show the checkout button
        String page;
        String viewId = getFacesContext().getViewRoot().getViewId();
        if (viewId == null || viewId.length() == 0 || "/".equals(viewId)) {
            page = "";
        } else {
            int pos = viewId.lastIndexOf('/');
            if (pos == viewId.length() - 1) { 
                // last char in viewId is '/'
                viewId = viewId.substring(0, pos);
                pos = viewId.lastIndexOf('/');
            }
            //ok if pos is -1, then pos+1 will be 0
            page = viewId.substring(pos+1);
        }
        checkoutButton.setRendered(FINAL_ORDER_PAGE.equals(page));
    }

Using a Tab Set in a Page Fragment for Navigation

Yossarian, a Creator/NetBeans IDE user, submitted this tutorial to the Creator Community Docs Site: Simple Page Fragment Menu Example.

Dynamically Rendering a Page Fragment Component From the Main Page

Here is a snippit where the main page dynamically renders a tree in a page fragment

    public void makeTree() {
        Fragment2 pgfrag = (Fragment2)getBean("Fragment2");
        List children = pgfrag.getDisplayTree().getChildren();
        TreeNode personNode = new TreeNode();
        personNode.setId("node1");
        personNode.setText("Shari Oln");
        children.add(personNode);
    }

Other Examples

In Winston's blog he points to Josh's web site, which uses VWP page fragments to do wonderous things. How about you? Do you have some super page fragment tips to share? If so, post them to the comments below to share with other VWP developers.

Thursday Jun 14, 2007

Using jMaki 9.3 With the NetBeans Visual Web Pack or the Sun Java Studio Creator IDE

We have been blogging about jMaki for over a year, and, during that time, the technology has gone through many iterations, making most of our posts obsolete. With the release of 9.3, there became enough changes that it was time to start reposting these mini tutorials. So, to start, here are new instructions for adding jMaki to a Visual Web Application project or a Sun Java Studio Creator project.

Note: To learn about the modifications, see Carla Mott's Big changes in jMaki blog entry.

Aug 6, 2007 Update: With the later versions, such as 9.6.1, they abstracted some of the functionalities into a separate JSF Compounds library ( jsfcompounds-0.0.2.jar), which you can download from https://ajax.dev.java.net/servlets/ProjectDocumentList?folderID=7871. You must add this jar file to your classpath, by right-clicking the Libraries node in the Projects window, choosing Add Jar/Folder, navigating to and selecting jsfcompounds-0.0.2.jar, and clicking Open. A symptom of this missing jar file is getting the following error in the JSP editor: java.lang.NoClassDefFoundError: com/truchsess/faces/compound/webapp/CompoundChildTagBase

Doing the Set Up

  1. Get the 9.3 jMaki zip. You can download it from http://ajax.dev.java.net/download.html.

    Note: There is also a NetBeans jMaki plug-in. In the past, we have written how to create a NetBeans jMaki project and copy the resources from that project into a Visual Web project. When I try it with the latest NBM, I get the following error: Unable to create an instance of jmaki.widgets.yahoo.tree.Widget. See the error log for more details. If I figure out what I am doing wrong, I will repost this with the added instrucitons.

  2. Unzip it to a folder of your choice. I will refer to this folder as jmaki-dir.

  3. Unzip the zip files in jmaki-dir/widgets for the desired widget libraries. In this mini tutorial, you will use the Tree widget from the Yahoo widget library, so, at a minimum, unzip the jmaki-yahoo zip so that you end up with jmaki-dir/widgets/resources/yahoo.

  4. Create a project (if you are using the Visual Web Pack, create a Visual Web Application). In this example, I named mine Jmaki93Demo. I will refer to to the location of this project as project-dir.

  5. Copy the supporting files to your project. You can use File > Add Existing Item to add files to your project. However, to cut down on the number of steps that I have to write up, I will explain up how to copy the files using the operating system instead of using the IDE.

    1. Copy jmaki-dir/scripts/glue.js to project-dir/web.

    2. Copy jmaki-dir/scripts/system-glue.js and jmaki-dir/scripts/jmaki.js to project-dir/web/resources.

    3. Copy jmaki-dir/core/web/resources/config.json to project-dir/web/resources.

  6. In the Projects window right-click Libraries and choose Add Jar/Folder. Then, navigate to and select jmaki-dir/lib/ajax-wrapper-comp.jar, and click OK.

    This is the jMaki tag library.

  7. Copy the desired widget resource libraries to jmaki-dir/widgets/resources. For this mini tutorial, you need to copy jmaki-dir/widgets/resources/yahoo to project-dir/web/resources.

  8. In the IDE, click the JSP button in the editing toolbar to display the JSP tags for the page.

  9. Add xmlns:a="http://jmaki/v1.0/jsf" to the <jsp:root> tag.

Adding the Yahoo Tree Widget

Now you are done setting up your project resources and you can add the Tree widget to your web page. One problem that you have to work around is that the jMaki widgets do not expose their features using the Design-Time API. What this means is that you will not be able to see the widgets in the Visual Designer. In addtion, with jMaki, you wrap the component in an <a:widget> tag. Even if you were to use a component from the Pallette, if you wrap the component in an <a:widget> tag, the Visual Designer won't see it. To make it easier to work with the widgets in the Visual Designer, I usually put them inside layout components, such as the Grid Panel. That way, I can position the widgets and be able to see in the Visual Designer where the jMaki widgets are on the page.

Note: Work is in progress to provide the jMaki widgets in component libraries that will work in the Visual Designer. We did a JavaOne Hands-On Lab that covered how this is done.

  1. Click Design in the editing toolbar to return to the Visual Designer.

  2. Drag a Grid Panel component from the Layout section of the Palette and drop it on the page.

    While in the development stage, I like to set the border property to 1 to make it easier to visualize the location of the widget on the page. You can clear the border property when you are ready to deploy.

  3. Click JSP in the editing toolbar to display the JSP tags for the page. Replace the <h:panelGrid> tag with the following (this assumes that the Grid Panel's id is gridPanel1. If not, change to match. It is OK if you change the style tag.
    <h:panelGrid binding="#{Page1.gridPanel1}" border="1" id="gridPanel1" 
    style="position: absolute; left: 48px; top: 48px">
    <a:widget name="yahoo.tree"
    value="{'root' : {
    'title' : 'Food',
    'expanded' : true,
    'children' : [
    {'title' : 'Nuts'},
    {'title' : 'Fruit',
    'children' : [
    {'title' : 'Banana'}
    ]
    }
    ]
    }
    }"/>
    </h:panelGrid>
    You now have added a jMaki tag for the Yahoo Tree widget.

  4. Click the Run button. When the page displays in the browser, view the source from the browser to see how the jMaki tags are rendered.

  5. In future blogs, we will further explore the jMaki features. To keep this blog short, we will just show how to subscribe to the Tree widget's onClick topic get the widget's value. If necessary, click Design in the editing toolbar to display the page in the Visual Designer. Drag a Script component from the Advanced section of the Palette and drop it on a blank spot on the page.

  6. Click JSP to view the JSP tags.

    • If you are using the Sun Java Studio Creator IDE, replace the <ui:script> tag as follows:
      <ui:script binding="#{Page1.script1}" id="script1">
      var handler = function(message) {
      alert('You selected ' + message.value.label);
      }
      jmaki.subscribe("/yahoo/tree/onClick", handler);
      </ui:script>
    • If you are using the NetBeans Visual Web Pack, replace the <webuijsf:script> tag as follows:
      <webuijsf:script binding="#{Page1.script1}" id="script1">
      var handler = function(message) {
      alert('You selected ' + message.value.label);
      }
      jmaki.subscribe("/yahoo/tree/onClick", handler);
      </webuijsf:script>
  7. If the script tag appears before the <a:widget> tag, cut the script tag and paste it below the <a:widget> tag, or else you might get a jMaki undefined error when you run the web application and click on a tree node..

  8. Run the web application again. When you click on a tree node (not the expansion box), you get an Alert box that shows what you just clicked.

Wednesday Apr 04, 2007

Migrating Creator Apps to VWP and Tomcat

A customer recently sent email that he was having trouble migrating the Movie Administration Sample Application, originally written for Sun Java Studio Creator, into NetBeans Visual Web Pack. When he opened the project in VWP, he resolved the missing server error by setting the target server to the Bundled Tomcat server. Upon running the application, he received the following error message:

package javax.faces does not exist

This error indicates that the J2EE packages are not available to the Tomcat web server. The fix is to add the JSF 1.1 library to the project as follows:

  1. In the Projects window, right-click the Project_Name > Libraries node and choose Add Library from the pop-up menu.
  2. In the Add Library dialog box, select JSF 1.1 and click Add Library.

A second problem, an HTTP 500 Status error, occurred when deploying the application. The server log included this error:

java.lang.NoClassDefFoundError: javax/servlet/jsp/jstl/core/Config
This is because the standard.jar/jstl.jar are needed for deploying to the Tomcat server. Here's the fix:

  • In the Projects window, right-click the Project_Name > Libraries node and add in the JSTL 1.1 library.

For more information on migrating projects, see Importing a Sun Java Studio Creator 2 Project in NetBeans Visual Web Pack 5.5.

Wednesday Feb 07, 2007

Coordinated Drop-Down Lists

I have seen a lot of inquiries in the users alias and the forum about how to do coordinated drop-down lists. Coordinated drop-downs are basically a set of nested master-detail records that follow the same pattern that we show in Using Databound Components to Access a Database. With coordinated drop-downs, whenever the user selects a new item from the master drop-down, you need to refresh the list in the detail drop-down.

In the following example, I show how to coordinate drop-down lists that contain data from the Travel database. The first drop-down lists all the PERSON records. The second drop-down shows all the TRIP records for the selected PERSON, and the third drop-down shows all the FLIGHT records for the selected TRIP. Finally, the detail data shows data from the selected FLIGHT.

The trick with coordinating multiple drop-downs, is that you need to trickle down the refreshing of the lists. A change in the first drop-down's selection requires refreshing the second list, which in turn, requires refreshing the third list, and so on.

In this example, I took the easy route. When the user makes a new selection from the PERSON drop-down, I select the first record by default and coordinate the TRIP drop-down by showing all the trips for the first-person. Then, I show all the flights for the first TRIP record, and the detail data for the first FLIGHT record.

  1. Create your Sun Java Studio Creator (JSC) or NetBeans Visual Web Pack (VWP) project.

  2. Drop three Drop Down List components, a Property Sheet component, and a Message Group component onto the page in the Visual Designer. The Message Group component is to help diagnose problems if something goes wrong.

  3. Select each component and, in the Properties window, change the id property as follows, from top to bottom:

    1. personDD
    2. tripDD
    3. flightDD


  4. Drag the PERSON table from the Travel database (in the Servers window for JSC, and in the Runtime window for VWP), and drop it on the personDD Drop Down List component in the Visual Designer.

  5. Drop the TRIP table on the tripDD component, and drop the FLIGHT table on the flightDD component.

  6. Right-click each Drop Down List component and choose Bind to Data. Make the following selections:

    1. personDD. Value Field: PERSON.PERSONID, Display Field: PERSON.NAME
    2. tripDD. Value Field: TRIP.TRIPID, Display Field: TRIP.DESTCITY
    3. flightDD. Value Field: FLIGHT.FLIGHTID, Display Field: FLIGHT.DEPAIRPORT


  7. Now, right-click each Drop Down List component and choose Auto-Submit on Change from the pop-up menu. This will cause the page to be submitted to the server each time a new selection is made from any list, and will cause the appropriate change event handlers to be called.

  8. Drag the FLIGHT table again, but this time drop it on a blank spot on the page. A dialog box appears. Choose the radio button for creating a flightRowSet in the page bean, and click OK.

    I chose to put the other queries in the session bean because I didn't want to lose the query data everytime the page is submitted. As the detail data pretty much changes on each submission, I am choosing to put this rowset in the page bean.

  9. In the Outline window, double-click the SessionBean1 > tripRowSet node to open it in the Query Editor. In the Query Editor, right-click on the PERSONID cell in the first column and choose Add Query Criteria. Select the Parameter radio button, click OK and close the Query Editor.

  10. Do the same for the flightRowSet, but add the query criteria for the TRIPID. Close the Query Editor.

  11. Now, double-click the flightRowSet in the page bean, add query criteria for FLIGHTID, and close the Query Editor.

    This query is for the detail data.

  12. Display the page in the Visual Designer (it should be displayed already), and click Java to switch to the source editor.

  13. Add the following code to the bottom, just before the final end brace:
    public void initFlightDropDown() {
        try {
            tripDataProvider.cursorFirst();
            getSessionBean1().getFlightRowSet().setObject(
                    1, tripDataProvider.getValue("TRIP.TRIPID"));
            flightDataProvider.refresh();
        } catch (Exception e) {
            error("Cannot switch to trip " +
                    tripDataProvider.getValue("TRIP.TRIPID"));
            log("Cannot switch to person " +
                    tripDataProvider.getValue("TRIP.TRIPID"), e);
        }
    }
    
    public void initFlightDetail() {
        try {
            flightDataProvider.cursorFirst();
            flightRowSet.setObject(
                    1, flightDataProvider.getValue("FLIGHT.FLIGHTID"));
            flightDataProvider.refresh();
        } catch (Exception e) {
            error("Cannot switch to flight " +
                    flightDataProvider.getValue("FLIGHT.FLIGHTID"));
            log("Cannot switch to flight " +
                    flightDataProvider.getValue("FLIGHT.FLIGHTID"), e);
        }
    }
    
  14. Add the following code shown in bold to the prerender method:
    public void prerender() {
        if ( personDD.getSelected() == null ) {
            try {
                personDataProvider.cursorFirst();
                getSessionBean1().getTripRowSet().setObject(
                        1, personDataProvider.getValue("PERSON.PERSONID"));
                tripDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch to person " +
                        personDataProvider.getValue("PERSON.PERSONID"));
                log("Cannot switch to person " +
                        personDataProvider.getValue("PERSON.PERSONID"), e);
            }
            initFlightDropDown();
            initFlightDetail();
        } else {
            try {
                // Synchronize data providers with current selections
                personDataProvider.setCursorRow(
                        personDataProvider.findFirst(
                        "PERSON.PERSONID", personDD.getSelected()));
                tripDataProvider.setCursorRow(
                        tripDataProvider.findFirst(
                        "TRIP.TRIPID", tripDD.getSelected()));
                flightDataProvider.setCursorRow(
                        flightDataProvider.findFirst(
                        "FLIGHT.FLIGHTID", flightDD.getSelected()));
    
            } catch (Exception e) {
                error("Cannot switch to selections");
                log("Cannot switch to selections", e);
            }
        }
    }
    
  15. Click Design in the editing toolbar to switch to the In the Visual Designer.

  16. Double-click the personDD Drop Down component (the top one) to cause the IDE to add a process value change handler for the component. The IDE switches to the added code. Add the following code shown in bold to the method:
    public void personDD_processValueChange(ValueChangeEvent event) {
        try {
            getSessionBean1().getTripRowSet().setObject(
                    1, personDD.getSelected());
            tripDataProvider.refresh();
        } catch (Exception e) {
            error("Cannot switch to person " +
                    personDataProvider.getValue(
                    "PERSON.PERSONID"));
            log("Cannot switch to person " +
                    personDataProvider.getValue(
                    "PERSON.PERSONID"), e);
        }
        initFlightDropDown();
        initFlightDetail();
    }
    
  17. Click Design to switch back to the Visual Designer, double-click the tripDD Drop Down List component (the middle one) to create its change event handler, and add Add the following code shown in bold to the method:
        public void tripDD_processValueChange(ValueChangeEvent event) {
            try {
                getSessionBean1().getFlightRowSet().setObject(
                        1, tripDD.getSelected());
                flightDataProvider.refresh();
            } catch (Exception e) {
                error("Cannot switch to trip " +
                        tripDataProvider.getValue(
                        "TRIP.TRIPID"));
                log("Cannot switch to trip " +
                        tripDataProvider.getValue(
                        "TRIP.TRIPID"), e);
            }
            initFlightDetail();        
        }
    
  18. Click Design one more time and double-click the flightDD Drop Down List component (the bottom one). Add the following code shown in bold the change event handler:
    public void flightDD_processValueChange(ValueChangeEvent event) {
        try {
            flightRowSet.setObject(
                    1, flightDD.getSelected());
            flightDataProvider1.refresh();
        } catch (Exception e) {
            error("Cannot switch to flight " +
                    flightDataProvider.getValue(
                    "FLIGHT.FLIGHTID"));
            log("Cannot switch to flight " +
                    flightDataProvider.getValue(
                    "FLIGHT.FLIGHTID"), e);
        } 
    }
    
  19. Take a look at the code you have added. The code in the prerender method first handles the case where the user first lands on the page and nothing has been selected. It seeds the drop-down lists with the first record from the PERSON query and synchronizes queries for the nested detail drop-down lists and the detail data. If there is a selected person, it synchronizes the cursor positions in the queries with the drop-down list selections.

    The change event handlers synchronize the appropriate detail queries. A change in PERSON selection refreshes the trip, flights, and flight detial queries. A change in TRIP selection refreshes the flights and flight detail queries, and a change in FLIGHT selection refreshes the flight detail query.

  20. The last thing we need to do is to display some detail information. Click Design to display the page and, in the Outline window, expand propertySheet1, expand section1, and select property1 (you can also select the property1 in the Visual Designer, but I find it easier to work with the property sheet in the Outline window).

  21. In the Properties window, change the label property to Airline:.

  22. Drop a Static Text component on property1 in the Outline window.

  23. Expand property1, right-click staticText1, and choose Bind to Data.

  24. Choose flightDataProvider1 (not flightDataProvider) from the drop-down list, select FLIGHT.AIRLINENAME, and click OK.

  25. If you want, you can add more Property components to the Property Sheet Section, add Static Text components to the Property components, and bind the Static Text components to other columns in the database table, as I did in the above screenshot.

  26. Now you are ready to click Run Main Project and test your coordinated drop-downs.

Tuesday Jan 30, 2007

Missing Image Bug

I just finished writing a NetBeans Visual Web Pack version of the JasperReports tutorial Generating Reports and PDFs From a Web Application. The NetBeans version should appear on the portal sometime today.

While working on this tutorial, I came across a bug that prevented the company logo from appearing in the HTML output when the application is deployed to version 9 of the Sun Java System Application Server. The bug happens when you request binary data through the FacesServlet, and the fix is in for version 9.1. Ryan and Craig were the engineers who figured out why the problem was happening. They both pointed out to me that the following statement in my code sample, which sets a relative url, is not recommended:

  exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI, "image?image="); 

The above statement results in following HTML:

  http://localhost:8080/TravelReport/faces/image?image=img_0_0_0

By using the relative URL, I was needlessly incurring, for a non-JavaServer Faces entity, the extra overhead that is associated with a JavaServer Faces request. It is better to serve up the image directly, instead of going through the JavaServer Faces lifecycle.

I am pushing out a new version of the Java Studio Creator tutorial today, which shows the correct way to pass the url (which happens to work with Sun Java Application Server 9):

   exporter.setParameter(JRHtmlExporterParameter.IMAGES_URI,
     request.getContextPath() + "/image?image="); 

While we are talking about server bugs, a customer wrote to me that they were getting a java.lang.NullPointerException from the javax.faces.component.UIComponentBase class's getRenderer method whenever they use the Table component in web applications deployed to JBoss. This is a known bug, and is documented in the Release Notes. What is happening is that when you create a Visual Web project, the classpath on the container is checked. If the IDE finds a MyFaces implementation, it gives the MyFaces implementation precedence over the JavaServer Faces Reference Implementation libraries. To prevent this problem from happening, delete or move the JBOSS_HOME/server/default/deploy/jbossweb-tomcat55.sar/jsf-lib directory before you create the Visual Web Pack project for that container. This forces the the IDE to bundle the JSF Reference Implementation with the project.

To go back to using the MyFaces run time for running non-Visual Web Pack based applications, follow these steps:

  1. In the Projects window, right-click the Libraries node and choose Add Library.
  2. In the Add Library dialog box, select the desired MyFaces binaries.
  3. Click Add Library.
  4. Make sure the JSF-RI libraries are not deployed.
  5. Modify web.xml to include the following:
  6. <listener>
      <listener-class>
        org.apache.myfaces.webapp.StartupServletContextListener
      </listener-class>
    </listener>
    

Monday Jan 08, 2007

JasperReports Tutorial: Missing Record Bug

A few customers have pointed out a bug in the Generating Reports and PDFs From a Web Application tutorial. I have to admit that I am somewhat embarrased that I never noticed that the first record is missing from the reports.

I am trying to get the fix pushed out this week. In the meantime, for those who are using code from the tutorial, you should know that you need to either call beforeFirst() or execute() on the rowset before passing the rowset to the JRResultSetDataSource constructor.

Thursday Dec 21, 2006

The Server Encountered an Internal Error

Sometimes, when testing your Visual Web application, you might get a page with the following unhelpful error:

HTTP Status 500 -

type Status report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

To find out what the real problem, go back to the NetBeans IDE and click the tab for the server's log (usually in the same area as the output window). There, you will find a more helpful description of the error. For example, you might see:

Dec 21, 2006 5:24:21 PM org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet jsp threw exception
javax.faces.el.PropertyNotFoundException: java.lang.RuntimeException:
org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot load JDBC driver class 
'org.apache.derby.jdbc.ClientDriver'
In this particular case, I had not made the database driver available to the server. I resolved the problem by copying my database's JDBC3 compliant driver to the server's common library folder. With the Tomcat server, this would be something like apache-tomcat-5.5.17\\common\\lib.

If the error is a programming error, such as a class cast error, you can search through the error output for the name of your page. If such line exists in the output, it will show the line number of the erroneous statement. Look for the names of your managed beans as well.

You can send diagnostic statements to the log from your page beans and managed beans by calling log("put your message here").

Tuesday Dec 19, 2006

Workaround for Button Component Viewed in IE7

When a web page that includes the Web UI Button component is viewed by the Internet Explorer 7 browser, the button component might be rendered with an incorrect width. This problem occurs when the Button component is placed on the page using absolute positioning.

You can use either of the following workarounds to solve this problem.

  • Specify the width property for the button, or resize the button in the Visual Designer to make the IDE assign a width to the button.
  • In the Visual Designer, nest the Button component inside a Group Panel component. By doing this, you move the absolute positioning from the button to the panel, outside the button.

Sunday Dec 17, 2006

Using the Object Array Data Provider

A forum poster recently asked for the following information:

I have a table with 5 columns, (week_no, objective, date1, date2 and add report). How do I get the date for date1( example, today's date)? How do I set the week number as auto increment by 1( example, row 1, the week_no is 1, row 2, the week_no is 2 and so on)? Lastly, how to I set the add report button to link to another JavaServer Pages file?

This scenario gives me an excellent opportunity to show how to use an Object Array Data Provider (OADP). I usually use the Object List Data Provider (OLDP), but OLDPs take a bit more work. (See the Using Hibernate tutorial for an OLDP example.) If you have a simple array of data, the OADP might be the better choice, as in this case. The following steps create a table similar to the one requested in the forum.

  1. Create a web application named ArrayTableExample.

    (If using the Visual Web Pack, be sure to create a Visual Web Application.)

  2. Create a JavaBeans object for the array. In this example, I chose to put the week number, start date, and end date in the bean. In the Projects window, right-click Source Packages > arraytableexample, choose New > Java Class, name the class WeekBean, and click Finish.
  3. Replace the class definition with the following code shown in bold.
    /\*
     \* WeekBean.java
     \*
     \*/
    
    package arraytableexample;
    
    public class WeekBean 
            extends Object implements Serializable {
        
        private int weekNumber;
        private Calendar startDate;
        private Calendar endDate;
        private int dayOfWeek; 
        
        public WeekBean(int weekNumber) {
            this.weekNumber = weekNumber;        
            Calendar workingDate = Calendar.getInstance();
            workingDate.set(java.util.Calendar.HOUR_OF_DAY, 0);
            int offset = weekNumber - 1; 
            workingDate.add(
                Calendar.DAY_OF_YEAR, 
                + (offset \* 7));
            dayOfWeek = workingDate.get(Calendar.DAY_OF_WEEK);
            startDate = (Calendar) workingDate.clone();   
            startDate.add(
            Calendar.DAY_OF_YEAR, - (dayOfWeek - Calendar.SUNDAY));
            endDate = (Calendar) workingDate.clone();
            endDate.add(
                Calendar.DAY_OF_YEAR,
                + (Calendar.SATURDAY - dayOfWeek));
        }
        
        public WeekBean() {
            this(1);
        }
            
        public int getWeekNumber() {
            return this.weekNumber;
        }
        
        public void setWeekNumber(int weekNumber) {
            this.weekNumber = weekNumber;
        }
        
        public Calendar getStartDate() {
            return this.startDate;
        }
        
        public void setStartDate(Calendar startDate) {
            this.startDate = startDate;
        }
        
        public Calendar getEndDate() {
            return endDate;
        }
        
        public void setEndDate(Calendar endDate) {
            this.endDate = endDate;
        }
    }
    
  4. Right-click in the code and choose Fix Imports. Make sure the Fully Qualified Name for Calendar is java.util.Calendar, and click OK.

  5. Close and save the file.

  6. In this example, we want the table to show the same data throughout the session, so put the array property in the session bean. In the Projects window, double-click the Session Bean node to open SessionBean1.java in the source editor.

  7. Add the following code to the bottom of the class defintion, just before the ending brace. This code also adds properties for passing the selected start and end dates to other pages.
    
        /\*\*
         \* Holds value of property weeks.
         \*/
            WeekBean[] weeks = {
                new WeekBean(1),
                new WeekBean(2),
                new WeekBean(3),
                new WeekBean(4)
            };
    
        /\*\*
         \* Getter for property weeks.
         \* @return Value of property weeks.
         \*/
        public WeekBean[] getWeeks() {
            return this.weeks;
        }
        
        /\*\*
         \* Holds value of property reportStartDate.
         \*/
        private Calendar reportStartDate;
    
        /\*\*
         \* Getter for property reportStartDate.
         \* @return Value of property reportStartDate.
         \*/
        public Calendar getReportStartDate() {
    
            return this.reportStartDate;
        }
    
        /\*\*
         \* Setter for property reportStartDate.
         \* @param reportStartDate New value of property reportStartDate.
         \*/
        public void setReportStartDate(Calendar reportStartDate) {
    
            this.reportStartDate = reportStartDate;
        }
    
        /\*\*
         \* Holds value of property reportEndDate.
         \*/
        private Calendar reportEndDate;
    
        /\*\*
         \* Getter for property reportEndDate.
         \* @return Value of property reportEndDate.
         \*/
        public Calendar getReportEndDate() {
    
            return this.reportEndDate;
        }
    
        /\*\*
         \* Setter for property reportEndDate.
         \* @param reportEndDate New value of property reportEndDate.
         \*/
        public void setReportEndDate(Calendar reportEndDate) {
    
            this.reportEndDate = reportEndDate;
        }
    
  8. Right-click in the code and choose Fix Imports. Make sure the Fully Qualified Name for Calendar is java.util.Calendar, and click OK.

  9. Close and save SessionBean1.java.

  10. View Page1 in the Visual Designer.

  11. In the Palette, expand the Data Providers section.

  12. Drag the Object Array Data Provider node and drop it on Page1.

  13. If the properties for the OADP do not appear in the Properties window, select Page1 > objectArrayDataProvider1 in the Outline window to make its properties appear.

  14. In the Properties window, change the id to weekDataProvider.

  15. Here is where it starts getting kludgy. You should be able to choose weeks from the array drop-down list, but it propably is not appearing at this point. If weeks is not in the drop-down list, choose Clean and Build Main Project from the main menu. After the build completes, close and reopen the project.

  16. In the Outline window, select Page1 > weekDataProvider.

  17. In the Properties window, select weeks (SessionBean1) from the array drop-down list.

  18. Drag a Table component from the Palette and drop it on Page1.

  19. Right-click the Table component and choose Table Layout.

  20. In the Table Layout dialog box, choose weekDataProvider from the Get Table From drop-down list.

  21. Use the Up and Down buttons to put the values in the Selected list in the following order:
    weekNumber
    startDate
    endDate
    
  22. (Optional) Set the Header Text values for the columns to Week, Start Date, and End Date.

  23. Click New to add another column.

  24. In the new column, set the Header Text to blank, select Button from the Component Type drop-down list, and set the Value Expression to Show Me.

  25. Click OK to dismiss the dialog box.

    Again, it gets kludgy. Only one row appears, which says "No items found", and there is no button. Don't worry, it will work out in the end.

  26. In the Outline window, expand table1 > tableRowGroup1 > tableColumn4, right-click button1, and choose Edit Action Event Handler.

    Page1.java opens in the source editor and scrolls to the newly added button1_action method.

  27. Add the code shown in bold to the method.
      public String button1_action() {
            getSessionBean1().setReportStartDate(
              (Calendar)getValue("#{currentRow.value['startDate']}"));
            getSessionBean1().setReportEndDate(
              (Calendar)getValue("#{currentRow.value['endDate']}"));        
            return null;
      }
    
  28. Right-click in the code and choose Fix Imports. Make sure the Fully Qualified Name for Calendar is java.util.Calendar, and click OK.

  29. Typically, you would set up page navigation to go to another page to display the report. For simplicity, click Design and drop a Static Text component on the page to display the selected start date.

  30. Right-click the Static Text component and choose Bind to Data.

  31. Click Bind to an Object, select SessionBean1 > reportStartDate, and click OK.

  32. Click Run Main Project. When the page appears, click the button in each row to verify that the action method returns the right value.

Winston has many blog entries about the dataprovider. A good place to start is his blog titled What is this Data Provider in Sun Java Studio Creator anyway?. Another good resource is Joel Brown's Weblog.

Thursday Sep 21, 2006

Using Servlets in the Java Studio Creator IDE

I am working on a tutorial on how to use JasperReports in JSF web applications. At first, like most examples out there, I built a servlet and called the servlet from my web page. I eventually figured out that it is quicker and easier to use a managed bean, and took that route instead.

However, I do see a lot of posts in the forum asking how to use servlets in JSF web applications built using the IDE, so here is what I learned.

  1. Make the servlet and put it in the web applications source package structure or add it to the library node. In this example, I called the servlet ReportGenerator.

  2. Make a mapping for the servlet. To do so, go to the Files window, expand web, expand WEB-INF, and double-click web.xml to open the xml editor.

  3. Click the Servlets button in the editing toolbar and click Add Servlet Element.

  4. Set the Servlet Name to whatever you choose (I used ReportGenerator).

  5. For the Servlet Class, browse to the source for your servlet (or type in the fully-qualified class name if using a library).

  6. Type a URL Pattern. This is how you will invoke the servlet from your web page. I used /makeReport. One thing to notice is that you need to prepend a forward slash here but you don't include the forward slash when you use it in a redirect, as you will see later. Click OK.

  7. Now you might think that you next bring up the Page Navigation editor to add a navigation mapping for makeReport. But that is not how you do it. From everything that I have read, JSF navigation entries are for navigation from a JSF view to a JSF view. So, what I did was to use a redirect, as shown in the following code:

     
        public String button1_action() {
            try {
            FacesContext facesContext = getFacesContext();
            HttpServletResponse response = 
    		(HttpServletResponse) facesContext.getExternalContext().getResponse();
            response.sendRedirect("makeReport");
            facesContext.responseComplete();
            } catch (Exception e) {
                log(e.getMessage());
                error(e.getMessage());
            }
            return null;
        }
    	


    Note that after redirecting the request, I send a responseComplete. This is extremely important. It is like passing the baton and notifying that you are dropping out of the lifecycle. If you don't complete the response, then you will get this exception:

    Exception Details: java.lang.IllegalStateException Cannot forward after response has been committed

  8. Alternatively, if you want a link instead of an action handler, you can add a link component and set it's url property to /faces + your pattern (this time include the forward slash). In my case, I set the url property to /faces/makeReport.

Wednesday Aug 30, 2006

Adding a Popup Window to a Java Studio Creator Web Page

Here is a mini-tutorial for creating a Popup window for the user to lookup values. One scenario that you might use this for is when the page visitor needs more information then can be displayed in a drop-down list.

Note: The original post of this blog entry had code that did not work in Internet Explorer. Escritorioooo posted a solution to the forum and I updated the code using Escritorioooo's solution. Thanks Escritorioooo!

  1. Create a project or open an existing one.

  2. Add a page named Popup.

  3. Add a Table component to the page.

  4. Drag VIR > Tables > State from the Servers window and drop it on the page.

  5. Right-click the Table component and choose Table Layout from the pop-up menu.

  6. In the column for the STATE.STATEID database field, set the Component Type to Hyperlink, and click OK.

  7. Select the Hyperlink component so that its properties appear in the Properties window.

  8. Set the value for the onClick property to doSave('#{currentRow.value['STATE.STATEID']}')

    (I got the value to pass to doSave() from the Value Expression in the Table Layout dialog).

  9. Click JSP in the editing toolbar to view the JavaServer Pages script.

  10. Add the following code shown in bold just under the opening tag for ui:body:

     
    <ui:body binding="#{Popup.body1}" id="body1" style="-rave-layout: grid">
    <script><![CDATA[
    function doSave(val) {
      window.opener.setVal(val);
      window.close();
    }]]></script>
    


    The Popup page is now ready for use by another page.

  11. Create a new page called Form1. Make it be the start page.

  12. Drop a Hyperlink component on the page and set the text property to State Code.

  13. Set the url property to /faces/Popup.jsp.

  14. Set the onClick property to doPopup("form1:textField1")

    Note: Here you are passing the id of the field to fill with the selected value. Determining the id can be tricky. For example, if you set the label property of the Text Field component, the id changes to form1:textField1_field.

    To determine what to use for the id, right-click in the page and choose View in Browser. When the page displays in the browser, choose View Source from the appropriate browser menu and look for the id setting for the component's <input> tag.

  15. Click the ... button for the target property to open the target property editor.

  16. In the property editor, click New and set both the Display and Value to popup. Click in some field and check that both values stuck. Then click OK.

    Later you add script to open a window named popup, and this setting causes the Popup.jsp page to display in that window.

  17. If the target property value is still blank, select popup from the combobox for that property.

  18. Drag a Text Field component and drop it under the Hyperlink component. By default, the component's id should be textField1. If not, change the id passed to doPopup() in step 14 to match. Use the View in Browser trick mentioned in Step 14 to determine the value to pass.

  19. Click JSP in the editing toolbar to view the JavaServer Pages script.

  20. Add the following code shown in bold just under the opening tag for ui:body:

     
    <ui:body binding="#{Popup.body1}" id="body1" style="-rave-layout: grid">
    <script><![CDATA[
    function doPopup(destination) {
      popup = window.open("", "popup", 
        "height=300,width=200,toolbar=no,menubar=no,scrollbars=yes");
        destinationElement=document.getElementById(destination);
      popup.focus();
    }
    
    function setVal(val){
      destinationElement.value=val;
    }]]></script>
    


    This script adds an instance field name openerDestinationElement to the popup window. The script for the Popup page uses openerDestinationElement to get to the Text Field component on the Form1 page.

  21. Run the project. Click State Code to open the popup window. Click a state code to select the code, close the popup window, and populate the text field.

Thursday Aug 17, 2006

New Hibernate Tutorial Using ObjectListDataProvider

Last night we posted our newest tutorial, Using Hibernate With the Sun Java Studio Creator IDE. This tutorial shows the essential steps necessary for adding a Hibernate framework to your project, as well as shows now to display the query results in a DropDown List component and a Table component.

This tutorial is not only helpful for Hibernate users, it is also helpful for anyone who is using a database for which compliant JDBC drivers are not available. You can use the concepts shown here to learn how to use the ObjectListDataProvider and Option arrays to feed the data from your result sets to the web application.

Note: NetBeans has a plug-in to generate the Hibernate persistence classes. You can read about it here and here.

Thursday Aug 03, 2006

jMaki, the Spry Accordion Widget, and Java Studio Creator Projects

Note: See Using jMaki 9.3 With the NetBeans Visual Web Pack or the Sun Java Studio Creator IDE for updated instructions on how to add jMaki to a project.

One of the widgets in the jMaki zip is the Spry Accordion. I thought this would be a nice way to display a page fragment menu in an application that I am building using the Sun Java Studio Creator IDE. Here is how I used the jMaki tag library to add the Accordion widget to my page fragment.

1. First, I downloaded the latest jMaki zip from https://ajax.dev.java.net/. I used 3.2.6.

2. Just like with my earlier project, I unjared the jar file and copied the necessary files to my project:

  • jmaki/resources/jmaki.js to creator-project-dir/web/resources.
  • jmaki/WEB-INF/types.properties to creator-project-dir/web/WEB-INF.
  • jmaki/WEB-INF/lib/ajax-wrapper-comp.jar to creator-project-dir/web/WEB-INF/lib.
  • jmaki/resources/spry/plainAccordion to creator-project-dir/web/resources/spry/plainAccordion.

3. Additionally, I added a resources/libs directory and copied over the Spry libraries from jmaki/resources/libs/spry.

4. I added a pageFragment, Sidebar.jspf, to my project, and in the JSP file for my page fragment, I added xmlns:a="http://java.sun.com/jmaki" to the top-level <div> tag.

5. In the page fragment, I used a Group Panel component to wrap the jMaki tag. The JSPF file looks like this:

 
	
<?xml version="1.0" encoding="UTF-8"?>
<div style="height: 400px; width: 200px; -rave-layout: grid" 
    xmlns:a="http://java.sun.com/jmaki" 
    xmlns:f="http://java.sun.com/jsf/core" 
    xmlns:ui="http://www.sun.com/web/ui">
    <f:subview id="Sidebar">
        <ui:panelGroup binding="#{Sidebar.groupPanel1}" 
            id="groupPanel1" 
            style="height: 382px; left: 0px; top: 0px; position: absolute; width: 190px">
            <f:verbatim>
                <a:ajax type="spry" name="spry.plainAccordion" />       
            </f:verbatim>
    </ui:panelGroup>
    </f:subview>
</div>

 

6. I wanted each section in the accordion to resize to the section's content. The samples that you get when you download the components from Adobe come with an AquaAccordion style that does not set a section height. To save some time, I simply copied those styles to the resources/spry/accordion/components.css file.

 
	
.AquaAccordion {
	border-left: solid 1px gray;
	border-right: solid 1px black;
	border-bottom: solid 1px gray;
	overflow: hidden;
	font-family: "Times New Roman", Times, serif;
	font-size: 16px;
}

.AquaAccordion .Tab {
	height: 24px;
	background-image: url(images/aqua-gradient.gif);
	background-repeat: repeat-x;
	background-color: #6dc1fc;
	border-top: solid 1px black;
	border-bottom: solid 1px gray;
	margin: 0px;
	padding: 2px;
	cursor: pointer;
	-moz-user-select: none;
	-khtml-user-select: none;
	text-align: center;
}

.AquaAccordion .Content {
	overflow: auto;
	margin: 0px;
	padding: 0px;
	background-image: url(images/gray-gradient.gif);
	background-repeat: repeat-x;
}

.AquaAccordion .hover {
	background-image: none;
	background-color: #33CCFF;
}

7. Now I can design my Accordion widget menu. In this simple example, I hard-coded the menu contents in my resources/spry/accordion/components.htm file. Note that because I am using the overriding styles, I am using Panel instead of AccordionPanel, Tab instead of AccordionPanelTab, and so forth.

 
	
<div id="${uuid}" class="AquaAccordion" tabindex="0">
    <div class="Panel">
        <div class="Tab">
            <div class="Label">Fruit</div>
        </div>
        <div class="Content">
            <a href="faces/FoodDetail.jsp?name=banana">Banana</a><br/>
            <a href="faces/FoodDetail.jsp?name=papaya">Papaya</a><br/>       
            <a href="faces/FoodDetail.jsp?name=grapes">Grapes</a><br/>   
        </div>
    </div>
    <div class="Panel">
        <div class="Tab">
            <div class="Label">Vegetables</div>
        </div>
        <div class="Content">
            <a href="faces/FoodDetail.jsp?name=avocado">Avocado</a><br/> 
            <a href="faces/FoodDetail.jsp?name=sprouts">Brussel Sprouts</a><br/>
            <a href="faces/FoodDetail.jsp?name=asparagus">Asparagus</a><br/>       
            <a href="faces/FoodDetail.jsp?name=onion">Onion</a><br/> 
        </div>       
    </div>
    <div class="Panel">
        <div class="Tab">
             <div class="Label">Grains and Cereal</div>
        </div>
        <div class="Content">
            <a href="faces/FoodDetail.jsp?name=rice">Rice</a><br/> 
            <a href="faces/FoodDetail.jsp?name=macaroni">Macaroni</a><br/>
        </div>       
    </div>    
</div>

 

8. To make the content sections shrink to fit the content, you must turn off animation, so I modified the widget creation statement in component.js as follows:

var acc1 = new Spry.Widget.Accordion(widget.uuid, { enableAnimation: false });

9. I can now add this page fragment to all the pages in my application.

Thursday Jun 29, 2006

Using the AJAX Rating Component

Good news. We just released a new AJAX tutorial, Using the AJAX Rating Component. In this blog, Gail interviews Matthew Bohm, the developer who authored the Rating component. Matt had some interesting things to say about the Rating component and his other contributions to the Sun Java Studio Creator IDE. Be sure to read to the end of the blog, where we ask Matt about his interests outside of work.

Gail: Why do a Rating component?
Matt: We decided to write the Rating component for two reasons. First, it is one of the components used in the Java Pet Store. Second, AJAX is intrinsically part of the UI paradigm established by Rating components on the web; that is, they almost always use AJAX and provide a great use case for demonstrating AJAX.

Gail: Briefly explain the component.
Matt: The Rating component, of course, lets the user assign a rating to an item, such as a book or movie. It displays a row of stars the user can click on. It also can display controls to indicate the user is "not interested" in the item, to clear the rating, and to toggle between normal and average mode. There are properties for specifying the text and hover texts to be displayed. When the user assigns a rating, the client sends that rating to the server via an AJAX request, and the server sends the values of several properties back to the client via the AJAX response. There are also a number of advanced properties, including those that let you execute your own client-side scripting when the user assigns a new rating, mouses over a control, or toggles the mode.

Gail: The mode toggle feature is an innovation. Can you say more about it?
Matt: When I looked at rating controls on existing web sites, I was disappointed with how they presented the user's rating and the average rating in combination. In most cases, the user and average ratings for an item were shown in separate rating instances. In the case where they were shown in the same rating instance, the user could not view the average rating after assigning a user rating. I felt we could do better. So in the Rating component for the Java Studio Creator IDE, we have a mode toggle control that lets the user alternately view the user and average rating for an item within the same rating instance.

Gail: What's another of your favorite contributions to the Java Studio Creator IDE?
Matt: My biggest contribution is definitely the invention of virtual forms. Without them, writing applications in the IDE would be much more difficult. The team's sample application developers particularly like them.

Gail: You were a technical writer in a previous life, and we owe you a thanks for writing the script for the Rating tutorial.  What made you switch to development?
Matt: Actually, my technical writing career lasted only two months or so. I had my eye on development from the start. I was working in a data warehousing group and wrote a much-needed intranet application at a time when the development team was stretched for resources. The group's director, with whom I still keep in touch,
quickly moved me into development.

Gail: Tell me something about yourself not related to work.
Matt: I write classical music. Currently I'm setting some poems by the great Argentine poet Alejandra Pizarnik for soprano and piano. Pizarnik is not so well known; she died in 1972 at age thirty-six, and her poetry is dark and surreal. My wife, who is also a poet and Argentine, introduced me to her work.

Thursday Jun 15, 2006

Table Component Sample Project

After all these months, I am finally making progress on the tutorial for the Table component. It might be a long time before I document all the steps and get the tutorial through quality assurance, editorial, and production processes. So, for those who can learn by examining code, I have decided to post the project that you build by completing the tutorial.

Here are some of the project's features:

  • Gets the data from an ObjectListDataProvider
  • Has dropdown lists, checkboxes, and buttons in the table
  • Has input fields that are not associated with the underlying data provider, so the input needs to be stored separately
  • Calculates totals from the selected table rows
  • Has dynamic footer information

Of course, everything I learned, I learned from Winston. He has posted lots of good Table component tips in his blog, so be sure to go there for advanced Table component information.

You can download the project zip from here.

Important Note: After you first open the project, build the project, then close and re-open the project. Otherwise you can get errors and the design-time mechanisms might not see the FoodListDataProvider.

Thursday Apr 20, 2006

New Tutorial on Creating Bookmarkable URLs

If you have a page that you want users to be able to bookmark, and the page requires parameters, you can use the URL to pass the parameters. Our new tutorial, Creating Bookmarkable URLs shows how to dynamically create a URL that includes the parameters and how to access the parameters from the bookmarkable page. Many thanks to Lark Fitzgerald and Craig McClanahan for doing the legwork on this tutorial and for all their technical advice.

You can send us comments on this or any other tutorial or technical article by using the Rate and Review box at the end of the tutorial or technical article (see Figure 1). We read these comments on a weekly basis and add information or make other adjustments to the tutorials based on your feedback.

Figure 1: Rate and Review Comment Box
Figure 1: Rate and Review Comment Box

Monday Mar 27, 2006

Using Checkboxes in a Table Component

Here is a one way to add a checkbox column to a table and determine which checkboxes have been checked.

  1. Add a property named selectedRows of type java.util.Set to SessionBean1.

  2. Add the following code to the init() property in SessionBean1.
    
    selectedRows = new HashSet();
    
    
  3. Add a property named selected of type boolean to SessionBean1.

  4. Change the getter and setter to the following code:
    
       /\*\*
         \* Getter for property selected
         \* @return true if checkbox in current row is selected
         \*/
        public boolean isSelected() {
            TableRowDataProvider rowData = (TableRowDataProvider) getBean("currentRow");
            if (rowData == null) {
                return false;
            } else {
                String rowId = rowData.getTableRow().getRowId();
                if (this.getSelectedRows().contains(rowId)) {
                    return true;
                } else {
                    return false;
                }
            }
        }
        
        /\*\*
         \* Setter for property selected.
         \* @param boolean; true if checkbox is selected, false if checkbox is cleared.
         \*/
        public void setSelected(boolean b) {
            TableRowDataProvider rowData = (TableRowDataProvider) getBean("currentRow");
            if (rowData != null) {
                String rowId = rowData.getTableRow().getRowId();
                if (b) {
                    this.getSelectedRows().add(rowId);
                } else {
                    this.getSelectedRows().remove(rowId);
                }
            }
        }
    
    
  5. Bind the Checkbox component's selected property to SessionBean1 > selected.

  6. Use code like the following to determine which checkboxes are selected.
    
    Iterator rows = getSessionBean1().getSelectedRows().iterator();
    while (rows.hasNext()) {
        String rowId = (String) rows.next();
        RowKey rowKey = <your table's data provider>.getRowKey(rowId);
        ... use the row key to access the data in the data provider
    }
    
    

Friday Mar 17, 2006

Iterating Through Table Data

Let's say that you want to put a footer in a column to show the column's total. To do so, you could code like the following in a property's getter method and bind the column's footer to the getter method.

        int total = 0;
        try {
            employeeDataProvider.cursorFirst();
            do {
                ... 
                total += somevalue;
                }
            } while (employeeDataProvider.cursorNext());
        } catch (Exception ex) {
            error("put your message here" +
                    ex.getMessage());
            log("put your message here: " +
                    ex.getMessage(), ex);
        }
        return total;

Sometimes, you might have some columns that are bound to the underlying data provider and some that are not. So, how do you iterate through the columns that are not bound to the data provider's fields. For those columns, you can create a hash map in the session bean that is keyed on the row id. Then you can create a getter and setter like the following and bind the column to the property (nbrServings in this example).

    public Integer getNbrServings() {
        TableRowDataProvider rowData = 
           (TableRowDataProvider) getBean("currentRow");
        if (rowData == null) {
            return new Integer(1);
        } else {
            String rowId = rowData.getTableRow().getRowId();
            Integer nbrServingsValue = 
              (Integer) this.getNbrServingsMap().get(rowId);
            if (nbrServingsValue == null) {
                return new Integer(1);
            } else {
                return  nbrServingsValue;
            }
        }
    }
    
    public void setNbrServings(Integer nbrServings) {
        TableRowDataProvider rowData = 
            (TableRowDataProvider) getBean("currentRow");
        if (rowData != null) {
            String rowId = rowData.getTableRow().getRowId();
            this.getNbrServingsMap().put(rowId, nbrServings);
        }
    }

Then add something like the following to the session bean's init method.

        nbrServingsMap = new HashMap();

You can then iterate through the has map's set of values.

Friday Mar 03, 2006

Radio Buttons in Tables

Here is a quick hack for detecting what radio button was clicked in a Table component.

Use the Table Layout dialog to add a column to the table and set its Compnent Type be Radio Button.

Add this to the page bean:

    public String getCurrentRow() {
        return tableRowGroup1.getRowKey().getRowId();
    }
   
    public void setCurrentRow(int row) {
    }
   
    private Object lastSelected="0";
   
    public Object getRBSelected() {
        String sv = (String)radioButton1.getSelectedValue();
        return sv.equals(lastSelected) ? sv : null;
    }
   
    public void setRBSelected(Object selected){
        if (selected != null) {
            lastSelected = selected;
        }
    }

In either the Outline window or the Visual Designer, select radioButton1.

In the Properties window for radioButton1, set the name property (in the Advanced section) to buttonGroup.

In the Data section for radioButton1, click the ... button for the selected property. A dialog box appears.

Select Use Binding, Click the Bind to an Object tab, select RBSelected, and click OK.

The application will now use the getRBSelected() and setRBSelected() methods to display and save user input for this component.

In the Advanced section of the Properties sheet, click the ... button for the selectedValue property.

Select Use Binding, Click the Bind to an Object tab, select currentRow, and click OK.

The application will now use the getCurrentRow() method to return the selected value for this component.

Now to find out what as been selected. In this example, the code is in a button action method:

        String aRowId = (String)RadioButton.getSelected(
                "buttonGroup");
        RowKey aRowKey = stateDataProvider.getRowKey(aRowId);
        selectedStateStaticText.setText("You Selected " +
                stateDataProvider.getValue("STATE.STATENAME",aRowKey));

If you want the selection to remain for when the user returns to the page, then you can stick the properties in a session bean. But, then you need to consider what to do if the selected row doesn't exist anymore.

Tuesday Feb 28, 2006

Displaying Multiple Fields in a Drop-Down List

The tutorials show how to display a database column in a drop-down list. But what if you want to show multiple database columns in the list. For example, the VIR database table that comes bundled with the product has a FIRSTNAME column and a LASTNAME column. You might want the drop-down list to show both fields, such as "Jane Doe".

One solution is to add an alias to the database query. Using the VIR database table as an example, you drop the VIR database table on the page, and use the Query Editor to add VIR.EMPLOYEE.FIRSTNAME || ' ' || VIR.EMPLOYEE.LASTNAME AS FULLNAME to the query statement, as shown below.

SELECT ALL VIR.EMPLOYEE.ID,
                    VIR.EMPLOYEE.FIRSTNAME,
                    VIR.EMPLOYEE.LASTNAME,
                    VIR.EMPLOYEE.EMAIL,
VIR.EMPLOYEE.FIRSTNAME || ' ' || VIR.EMPLOYEE.LASTNAME AS FULLNAME
FROM VIR.EMPLOYEE    

To test the statement, right-click in the query pane of the Query Editor and choose Run Query. A dialog pops up explaining that the the IDE's parser only knows a small subset of common SQL syntax. Click continue and the Query Editor should output the query results.

You will also see that the the visual panes in the Query Editor for this particular rowset no longer work. This is because of the same reason that the IDE's parser only knows a small subset of common SQL syntax. This problem only affects the Query Editor.

To see the query in action, drop a Drop Down List component on the page, right-click the component and choose Bind to Data. Click the Bind to Data Provider tab, choose employeeDataProvider, select EMPLOYEE.ID for the Value Field, and select FULLNAME for the Display Field.

Click OK. Test and Run.

The above query works with the bundled database. You have to adjust based on the syntax of your database. For example, for MySQL, use something like select concat(member.lastname, ", ", member.firstname).

Wednesday Feb 22, 2006

Simple Form Tutorial

Here is a very basic tutorial on creating a simple input form to add a record to a database. It leaves a lot to the reader. For example, you would want to have nicer messages for duplicate ids and you wouldn't want your error messages to display internal database information such as table names and column names. If the record was added sucessfully, you would probably want the page to navigate elsewhere.

1. Drop three Label components on the left side of the page, one under the other. Set the label text to ID:, Name:, and Description: respectively.

2. Drop three Text Field components on the page, one next to each label. Set the ids for the components to idTF, nameTF, and descriptionTF respectively.

4. Set the required property to true for each Text Field component.

6. Drop three Message components on the page, one next to each text field.

7. Ctrl-Shift-Drag from each Text Field component to its associated label.

8. Ctrl-Shift-Drag from each Text Field component to its associated message.

9. Drop a Button component on the page, under the text fields. Set its text to Add. Set its id to addButton.

10. Drop a Message Group component on the page to the right of the button. Set its showGlobalOnly property to true.

11. Access the Servers window. Drag Data Sources > Travel > Tables > TRIPTYPE and drop it on a blank spot on the page.

11. Double-click the button to access its addButton_action() method. Replace the method's body with the following code shown in bold.

    public String addButton_action() {
        if ( triptypeDataProvider.canAppendRow() ) {
            try {
                RowKey rowKey = triptypeDataProvider.appendRow();
                triptypeDataProvider.setCursorRow(rowKey);
                triptypeDataProvider.setValue("TRIPTYPE.TRIPTYPEID",
                        rowKey,  idTF.getText());
                triptypeDataProvider.setValue("TRIPTYPE.NAME",
                        rowKey, nameTF.getText());
                triptypeDataProvider.setValue("TRIPTYPE.DESCRIPTION",
                        rowKey, descriptionTF.getText());               
                triptypeDataProvider.commitChanges();

            } catch (Exception e) {
                error("Cannot append new trip type: " + e);
                triptypeDataProvider.refresh();
                return null;
            }
        } else {
            error("Cannot append trip types");
            return null;
        }
        //clear fields
        idTF.setText(null);
        nameTF.setText(null);  
        descriptionTF.setText(null);        
        return null;

12. Fix imports.

13. Run and test. It will reject existing IDs (1 through 10).

14. To see the database updates, right-click on the TRIPTYPE node in the Servers window and choose View Data.

Turning an Input Component into an Action Component

A new tutorial writer, Joe, recently joined the Creator Tutorials team. While working on the Tabset tutorial, he came up with this useful tidbit: how to make a selection from a drop-down list navigate to a different page.

1. Right-click the Drop Down List component and select Auto-Submit on Change.

2. Use the Page Navigation Editor to create links from the page with the drop-down list to the other pages. The rest of the steps assume that you have 3 links -- case1, case2, and case3.

3. Close the Page Navigation editor and return to the page with the Drop Down List component in the Visual Designer. Right-click the Drop Down List component and choose Configure Default Options. (The Delving into Components tutorial shows how to set the list items programmatically.)

4. Make the Value for each option be one of the link names that you added in the Page Navigation editor, such as case1, case2, and case3.

5. In the Visual Designer, double-click the Drop Down List component. The Java Source editor opens up on the process value change event handler for the compnent.

6. Make the body of the method similar to the following code that is shown in bold (using the correct id for your Drop Down List component).

    public void dropDown1_processValueChange(ValueChangeEvent event) {
        Application application = getApplication();
        NavigationHandler navigator = application.getNavigationHandler();
        FacesContext facesContext = getFacesContext();
        try {
            navigator.handleNavigation(facesContext, null,(String) getDropDown1().getSelected());
        } catch (Exception e) {
            log("can't navigate to " + (String) getDropDown1().getSelected());
        }
    }

7. Fix imports.

8. Run and test. You will see that the process value change doesn't get called if you try to select the first item when the page is first displayed. One way to fix that is to make the first item's Display value be something like "Choose One" and have its Value be something like "none." Then wrap the body of the process value change method in an if statement something like this.

if (!"none".equals(getDropDown2().getSelected()))

Wednesday Feb 15, 2006

Calendar Component Tips

Gail (Diva 1) is working on a Using the Calendar Component tutorial. It will be awhile until it gets published (she has lots of irons in the fire) so here are some excerpts from the tutorial.

If you don't set a minimum date, the component defaults to a minimum date of the day after tomorrow. To set your own minimum date you can add code like the following and bind the minDate and maxDate properties to these page bean properties.


private long MILLISECONDS_PER_DAY = 86400000;
private Date today = new Date();

private Date minCalDate;
    
    public Date getMinCalDate() {
        minCalDate = new Date(this.today.getTime()-MILLISECONDS_PER_DAY);
        return minCalDate;
    }
    
    private Date maxCalDate;
    
    public Date getMaxCalDate() {
        Date today = new Date();
        today.setTime(this.today.getTime());
        maxCalDate = new Date(today.getTime() + MILLISECONDS_PER_DAY \* 30);
        return maxCalDate;
    }

If you want to set a default date, you can put code like the following in the prerender method.

public void prerender() {
        // Default start date to today
        if (startCalendar.getSelectedDate() == null) {
            startCalendar.setSelectedDate(this.today);
        }   
    }

If you want to validate starting and ending dates, you can use code like the following (make these be validate event handlers for the Calendar components).

public void endCalendar_validate(FacesContext context, UIComponent component, Object value) {
        Date endDate = (Date)value;
        Date startDate = startCalendar.getSelectedDate();
        if (startDate != null) {
            if (startDate.after(endDate)) {
                throw new ValidatorException(new FacesMessage
                        ("End date must be after start date."));
            } else {
                long nbrDays =
                        ((endDate.getTime() - startDate.getTime()) /
                        MILLISECONDS_PER_DAY) + 1;
                if (nbrDays < 8 ) {
                    throw new ValidatorException(new FacesMessage
("End date must be at least one week after the start date."));
                }
            }
        }
    }

public String validateButton_action() {
            validationMsgTextField.setText(
            (String)DateFormat.getDateInstance(DateFormat.MEDIUM).format(startCalendar.getSelectedDate())
                +
                " - "
                +
                (String)DateFormat.getDateInstance(DateFormat.MEDIUM).format(endCalendar.getSelectedDate())
                + " is a valid date range."); 
        return null;
    }

Friday Feb 10, 2006

About a Page's Lifecycle and Some Tutorials Announcements

When you create a page from Creator's IDE, the IDE inserts several lifecycle methods into the Java source. These methods have Javadoc comments that explain when to use them. Unfortunately, the comments require an indepth knowledge of the JSF lifecycle.

A writer is working on an article about Creator's application model. In the meantime, here is a quick and dirty explanation of what to put in the lifecycle methods.

init. Use this method to initialize resources, such as constants and bean properties. Do not use this method to get or set component values (such as getText() and setText()). When init is called, the page's components may have values, but these values can be stale. If the page is being displayed after submission, the values will not reflect the submitted values. Therefore, the init method is not a good place to reference component values. Nor is it a good place to set component properties. This is because the values can get overwritten after the init method completes.

preprocess. If doing a postback, this method gets called after init. A postback happens when a page is submitted, such as by clicking a button or changing a value in a component for which Auto-Submit on Change is true. This method is where you can initialize stuff needed for conversions, validations, and action handling (such as a button action method). David Botteril shows a good preprocess example in his blog about conditional validation in Java Studio Creator 2. Just as with init, do not get component values in this method as they can be stale, and do not set the values because your setting can get overwritten.

prerender. The prerender method is called after conversions, validations, and action event handling happens, and after the components have been updated with all the submitted values that passed conversions and validations. Now it is finally safe to get and set component values. This method only gets called if the page is actually going to be rendered. If it is a postback where, after the form submission, the application goes to another page, this method doesn't get called. Note: The action event handlers are another place where it is safe to get and set component values.

destroy. This is called after the page is rendered. You can use it to free up resources that were necessary just for the rendering of the page. If your page uses data providers, the IDE sticks code to close the data providers in the destroy method. When a page submission redirects to a different page, there is no guarantee which page's destroy method gets called first. This is not a good place to store away values that are based on user input into request, session, or application beans. The action event methods are better places for doing that.

New and Updated Tutorials Next Week

Hopefully, by the middle of the week, you will see the following new tutorials:

  • Delving Into Components
  • Using the Page Alert Component
  • Working With Data Providers
  • Deploying to Sun Java System Application Server
  • Deploying to a BEA WebLogic Server

We also updated the following tutorials based on user feedback. These should get posted sometime next week as well.

  • Using Tree Components
  • Using Message Components
  • Using Databound Components to Access Databases
  • Using Page Fragments

We are working on tutorials about using the Calendar component about about creating a theme. If you are anxious to learn about creating themes, check out Gregory Murphy's Rough Guide: Editing Themes for Creator 2.0. We are also working on a hands-on lab proposal for JavaOne about using AJAX technology in Creator applications.

Tuesday Jan 24, 2006

Another Page Fragment Tip and a Question About Perferred Tutorial Formats

In our last blog, Gail showed you how to access page fragment properties. Sometimes, you might want a page fragment to determine what page it is being shown in. To do that, you ask the current view for the view identifier, like this:

String viewId = getFacesContext().getViewRoot().getViewId();

This returns something like "/Page1.jsp" or "/foo/Bar.jsp", and can be used to choose conditional logic based on what page you are in.

For a simple test, add a staticText component to the page fragment, and put the following in the page fragment's init method (which gets called after the prerender() method of the page that contains the page fragment).

 staticText1.setText(getFacesContext().getViewRoot().getViewId());

We have a question for you. Currently our tutorials are in HTML format. However, we have had several customer requests for the tutorials to be in PDF format. If you prefer that the tutorials are in PDF, please send us an email at CreatorDocsFeedback@Sun.Com. If we receive a sizable number of requests for PDF, then we will consider delivering future tutorial releases in both HTML and PDF.

Friday Jan 20, 2006

Aloha, Discovering via Code Completion, and a Page Fragment Tip

Chris: I love the Internet. Not only does it let me work with teams all over the world, such as when I worked with the NetBeans team in Prague, the Internet also enables me to work from anywhere. Today I am on the Big Island in Hawaii. It is a relief getting away from the constant rain and flooding in northern California. On the west side of the Big Island we have the opposite problem -- a severe drought.

I want to talk a bit about discovering how to use Creator's editing features to figure out how to solve your programming tasks on the fly. For example, someone asked in the EA forum how to figure out what column a component is in. I wasn't sure whether the person was asking about a Table component column or a database column so I answered both questions. I didn't really know the answer, so I opened the Java editor on a page that had a Table component, which was bound to a database table. To figure out how to get the Table Column that a component is in, I typed "hyperlink1.get" (where hyperlink1 is the id of a hyperlink component in a table column in my Table component) and pressed Ctrl+Space to raise the code completion box. In the list that appeared was getParent(). This seemed a likely candidate, so I chose that one. I typed ".get" and pressed Ctrl+Space again. In the list was getId(), and I chose that one.

I used the same process to discover how to get the database column associated with a component: hyperlink1.getValueBinding("text").getExpressionString().

It is kind of a bassackwards methodology, but sometimes I find the code completion method to be quicker than reading the Javadoc. If I want to learn more about a method that I have discovered using code completion, then I can look in the little Javadoc pop-up that appears above the code completion box, and I can open the various API references that are available from the Dynamic Help window. If you want to learn more, see the Using the Java Editor tutorial at http://developers.sun.com/prodtech/javatools/jscreator/ea/jsc2/learning /tutorials/javaeditor.html. My other friends for discovering and exploring are the Message Group component (where I use info(string) to send diagnostic messages at rendering time), and the server log (where I use log(string) to send lifecycle and event handling messages to the log).

Gail: This week a customer asked if he could use templates in Creator to create pages with the same header, footer and side menu. Page fragments are the perfect solution for this and the Page Fragment Box component is documented in our tutorial Using Page Fragments at http://developers.sun.com/prodtech/javatools/jscreator/ea/jsc2/learning/tutorials/pagefragments.html.

An updated version of this tutorial is coming out Wednesday, 1/25. The tutorial includes a page fragment that holds links for navigating between the pages in the application. While writing this tutorial, I discovered how to disable the link on the current page. Here are the instructions:

1. Create a new project.

2. Put a page fragment on the page (Page1). Name the page fragment Navigation.

3. Add a Hyperlink component to the page fragment. Set the hyperlink's id property to homeLink, text property to Home, and the url property to /faces/Page1.jsp.

4. Open the Java source code for Page1.

5. Add the following code to the prerender method:

public void prerender() { 
    Navigation navigationFragmentBean = (    
      Navigation)getBean("Navigation");
    Hyperlink homeLink = navigationFragmentBean.getHomeLink();
    homeLink.setDisabled(true);
 }

6. Right-click in the Java Editor and choose Fix Imports.

7. Run the application and verify that the link is disabled.

About

divas

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today