Wednesday, December 5, 2007

ALUI and Java Server Faces Portlet development - Part 2 (Continued)

In my previous ALUI JSF post ALUI and Java Server Faces Portlet development - Part 1, I talked about the problems you might encounter while using JSF and ALUI, and I also started to explain how to fix those problems. I will continue with that post...

Form Unique Id

It is standard (best) practice to make sure every ids and every javascript function names in a HTML page are unique. That way, Javascript functions are targetted correctly when called, and Javascript functions can effectively use "getElementById()" to access the right element within the portlet. When you are in a Portal environment, it is difficult to ensure that all the portlets on the page have unique ids. And thus it is possible to have a defective portlet when rendered with others on the same page, due to those kind of interferences (same element ids or same javascript function names). The probably of error reach 100% of chance if you put 2 portlets with identical content on the page.

Traditionally, with web applications where you have control over every elements and javascript function throughout the page (i.e. standard JSP), you would surely use the pt:common.namespace adaptive tag, in order to append to every JS functions and form ids the token string (i.e. $$PT_TOKEN$$)

But unfortunately, that won't work with JSF. Why is that?

First, quick reminder: It is important to note that "namespace" tag (like every ALUI Adaptive Tags) is transformed by the GATEWAY component. In other word, if you use the tag $$PT_TOKEN$$ throughout your JSF form, the remote JSF application DO see literally "$$PT_TOKEN$$", not the actual portlet IDs. When the portlet response goes through the gateway component, that is when the tag "$$PT_TOKEN$$" is changed on the fly with the real portlet id number.

Since JSF is a component oriented framework that generates the HTML markup, we don't have direct control over the HTML elements...and also, the element "name" (this is posted back to the server) is usually the same as the element "id". Thus, when you use:

<h:form id="editaddress_$$PT_TOKEN$$">
<h:inputText id="fullname" value="#{bean_backing.fullname}" />
</h:form>



You HTML markup for the response is:



<form id="editaddress_$$PT_TOKEN$$" ="">
<input id="editaddress_$$PT_TOKEN$$:fullname"
name="editaddress_$$PT_TOKEN$$:fullname" type="text" value="" />
<form>



And when you visualize the portlet source code (as rendered by the portal, where 289 is the portlet id that renders your web application), you get:



<form id="editaddress_289" ="">
<input id="editaddress_289:fullname"
name="editaddress_289:fullname" type="text" value="" />
<form>



So great, we do have unique HTML elements within each portlets, but you start to get the problem, right? Okay I am still going to explain :)



So the JSF component is waiting for a posted request attribute with literal name "street_$$PT_TOKEN$$", but actually, the posted attribute is "street_289"...and thus the JSF component cannot decode the posted value, and thus the form post won't work.



So the solution is to include the real portlet Id on the remote application side directly. I will use the same technique that I explained in the previous post: Custom renderer for the Form JSF component, overriding this time the base method "convertClientId(FacesContext context, String clientId)". Within the overridden method, I get the portlet id by simply using the IDK and then append the id to the component id.



@Override
    public String convertClientId(FacesContext context, String clientId) {
        IPortletContext ctx = PortletContextFactory.createPortletContext(
                (HttpServletRequest)context.getExternalContext().getRequest(),
                (HttpServletResponse)context.getExternalContext().getResponse()
                );
        IPortletRequest portletRequest = ctx.getRequest();
        //add the portlet id to the form
        if(portletRequest.isGatewayed())
            clientId += ctx.getRequest().getPortletID();
        return super.convertClientId(context, clientId);
    }



That's it!! That way, even if you let JSF generate the form element id, the "portlet id" WILL be appended if you are viewing the application through the portlet. And since the component is aware of the portlet id, the post will work fine. And finally, since the <h:form> is very often the top element of your JSF page, all the sub elements will also be unique since the form id is the prefix for all the children component ids.



Although you probably don't want to create a custom specific renderer for every components you want to use, this technique seems the best to me because it is completely non-intrusive within your JSF page...and thus, in the future, if BEA corrects all those JSF problems, you just disable the custom renderers and everything will work without any change.

1 comment:

  1. Hi Fabien , i have read your article , and i´m working with ADF / JSP page, i have tried to deploy the app in ALUI but when i press the buttons nothing happen, i mean my app doesn´t do any navigation at all, can you please tell me if this tips will resolve my problem , and if hit doest can you give me a step by step ... i read your blog but dind´t understand the real modification i have to do to may app.

    Best regards,
    VItor

    ReplyDelete