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.

Saturday, December 1, 2007

ALUI and Java Server Faces Portlet development - Part 1

Lately I have been looking into JSF and especially the Myfaces implementation as a development framework  for ALUI portlets, Similar to ASP.NET, I personally do think JSF is also an awesome and powerful framework to develop all sort of cool portlets and applications. But similar to ASP.NET portlets, JSF portlets have some problems when integrated in ALUI portal.

Mainly, the problems lie in the fact that those 2 frameworks are component oriented: We don't have a lot of control on the rendition of those control, and sometimes, the way they render is not fully compatible with the way ALUI portal behave:

  • Some urls in generated javascript are not always successfully gatewayed.
  • The portal does not enforce generated Html components to have unique IDs throughout the portal pages (i.e. 2 portlets on the same page can possibly have 2 form tags with same ids...which would very likely mess with the functionality of the portlets)
  • If you don't enable ALUI inline refresh, the portlets will go in the "Gateway" space (or the maximized state) on any navigation event. Although that can be useful in certain case, it won't to perform portlet-to-portlet communication (since the other portlets disappear in this maximized state).
  • If you DO enable inline refresh, you will hit other problems due to the fact that the Javascript layer added to the "inline-refresh" enabled portlet can interfere with the good behavior of some of the JSF/DOTNET components...

Ok...having painted such a "Dantesque" picture of the portlet development within ALUI portal, I now want to reassure you: Because ALUI is so extensible and powerful, all those problems can be resolved, and that's the goal of this post: Showing you some of the trick to make it all work.

For you DotNet users, you are in luck :) BEA introduced the "BEA AquaLogic .NET Application Accelerator" to offer a remedy to all the problems above. Since it is all there at http://www.bea.com/framework.jsp?CNT=index.htm&FP=/content/products/more/accelerator/ I won't talk to much about it.

But what about you poor JSF users? Well unfortunately for you (and I), we will have to get to work :)

But before getting to work, some of you might say: What about the "Java Portlet Tool" (http://dev2dev.bea.com/pub/a/2006/05/java-portlet-tools.html)? Well I looked at that quite thoroughly. I must admit that it is a pretty involved and clever piece of code...and it makes things work a little better on the JSF side. I especially do like the bean support for IDK functionality. Unfortunately, the following points are making me very hesitant and uneasy about this framework:

  • Unlike "BEA AquaLogic .NET Application Accelerator", the "Java Portlet Tool" (which is written by a BEA resource) is not supported by BEA. (Even though I could go with that - after all since the Java portlet tool code is provided, we could eventually support it ourselves - I would not recommend it to my clients who pay a lot of money to get support),
  • In addition to that, the java portlet tool, which rely on the modification of the pretty involved inline refresh jsxml javascript libraries (PTXML.js), seems to have been developed and tested with Plumtree 5.x, and does not seem to have been upgraded for 6.x versions of the portal. (thus, it might or might not work with 6.x versions)
  • Finally, the java portlet tool project has not been modified since 2006, which makes me think there are not a lot of people using it, or keeping it up-to-date.

Okay...so no supported portal framework for JSF...but still I want to use inline refresh...

Does it mean that I have to develop my JAVA portlets the way I use to develop them 5 years ago, without the popular frameworks out-there? Well I personally refuse to do that, and you should too :)

So it is time to have some fun finally!

JSF Navigation

A major problem I found with the ALUI inline refresh and JSF is that HTTP POSTs with JSF navigation is not working. JSF framework navigation (similarly to struts etc...) is controlled by the framework itself, based on the navigation rules you define in the JSF config file. (faces-config.xml). In other words, when the form is posted back to the server, the framework will deal with the navigation based on the action define on the command button that triggered the HTTP POST. Nothing unusual until now, and such a navigation behavior will work fine if you don't enable inline refresh...but the next page will be displayed in the maximized state we want to avoid.

When you enable inline refresh though, the ALUI portal will intercept the button submit action and perform its own submit action using its AJAX/Javascript framework (PTXML.js). On the html side, you will see:

<form id="viewaddress" name="viewaddress" onsubmit="pt_280.formRefresh(this); return false;" method="post"

action="http://localhost:7001/portal/server.pt/gateway/PTARGS_0_0_280_377_0_43/http%3B/localhost%3B7001/MyJSFDemo/viewAddress.jsf">



We can particularly notice how the submit is intercepted: the OnSubmit event handlers of the form tag does a "return false", which is the equivalent of saying: the submit button has never been clicked, the form has never been submitted. The "pt280.formRefresh(this);" is the statement that will post the form using an AJAX call. How this works is interesting: Using JavaScript to go through the DOM, the portal reconstructs the post request using all the input fields in the form (which would have been posted to the server). But the problem is: during that reconstruction, the <input type="button" /> and <input type="submit" /> are purposely left out. Although this usually makes sense in a standard web application (those buttons don't have a particular meaning on the server side, they are just useful to trigger a post back to the server), it does not make sense in the JSF world: the button name/value pair should be posted back to the server because the navigation rules won't happen otherwise. The result is that with inline refresh, navigation initiated by a form post is NOT working.



That is inconvenient...but we can correct that easily: if we create a hidden input field that has the same name and value as the button clicked, then this hidden field WILL be included in the posted request and the navigation will then work! Great! But you don't want to manually add yourself the hidden field in the form...for the simple reason that you want the value posted only when a particular button is clicked.



So the simple fix is to use javascript to dynamically add the input field in the form:



<pt:namespace pt:token="PORTLET_ID" xmlns:pt='http://www.plumtree.com/xmlschemas/ptui/' />

function setHiddenInputPORTLET_ID(form, name, value){
var newInput = document.createElement('input');
newInput.setAttribute('type','hidden');
newInput.setAttribute('name',name);
newInput.setAttribute('value',value);
form.appendChild(newInput);
}

<input id="viewaddress:_idJsp12" name="viewaddress:_idJsp12" type="submit" value="Change"

onclick="if(typeof window.oamSetHiddenInputPORTLET_ID!='undefined'){setHiddenInputPORTLET_ID(this.form,'viewaddress:_idJsp12','Change');}" />



That way, when you click the button, the equivalent hidden input is added to the form, which will then be effectively posted by the "pt280.formRefresh(this);" call. You noticed that I also use the ALUI adaptive tag to generate a unique token (portlet id) and thus make the JavaScript function "setHiddenInput" unique (that way, no interferences can occur if multiple portlets are on the same page)



In addition, you certainly don't want to force your developers to create that by hand for every buttons, (as well as introducing in your presentation code such a specific bug fix). So in order to be the least intrusive possible, I chose to customize a little bit the rendering of the <h:commandButton> element, which is the one responsible for postback and navigation:



<h:panelGroup>

<h:commandButton action="editaddress" value="Change"></h:commandButton>

</h:panelGroup>   



To do that, you just have to create a Class that extends the HtmlButtonRenderer class, and overrides 2 methods:




  • encodeEnd to generate the javascript function setHiddenInput281()


  • buildOnClick so add the call to the generated function setHiddenInput281()



That way, your JSF page are exactly the same...but the rendering of it is a little different when viewed through the portal. You can find the full custom renderer here.



That fix alone will take care of a good majority of the problems you would have had with using JSF and inline refresh inside ALUI. On a next post, I will show you how to deal with form ids when several JSF portlets are on the same page.



So long!