Monday, May 21, 2012

The basic Code of Ajax

When developing web application using Ajax and JavaScript, the following are the three ways to start the Ajax code.
  • Use the "button" button. The following is some sample JSF code:
      <h:commondButton type="button" value="xyz" onclick="myAjaxFunction();" />
    
    Notice the button's type is "button", which means it is a push button and not a submit button. When a user activates that button, JSF does not submit the surrounding form. You can also use other non-button tags such as inputText to invoke Ajax. As long as an event causes a JavaScript event handler to run, you can put the Ajax code in that event handler to do the work.
  • Use the "submit" button. The following is some sample code used by IceFaces:
       <input type="submit" value="Submit" onclick="iceSubmit(form,this,event);return false;" />
    
    Notice that here the onclick function has to return false to disable the form submission.
  • Use the "submit" button, the JSF action, and the onclick function. The following is the sample code.
       <h:commandButton value="xyz"
     action="#{yourBean.doSomething}" onclick="return myAjaxFunction();" />
    
    Notice that this is a "submit" button because the default type of commandButton is "submit". But if the onclick function returns the value "false", the form will not be submitted, and the JSF "action" won't be invoked. If it returns true, the form will be submitted and the JSF "action" will be executed on the server side.
Now how to make the Ajax call in the web browser and get the response from the server? Basically you need to create a request object ( XMLHttpRequest for a non-IE browser or ActiveXObject for IE) , specify the parameters needed by the HTTP protocol( URL, HTTP method, etc) and send the request. You also attach a handler to the request object that will process the response from the server. There is the built-in function in the request object that will repeatedly invoke the handler with the news of the request's progress. In the handler, you check the status value and perform the action if the status indicates the response is ready.

So the special request object (XMLHttpRequest or ActiveXObject) is not just an object to send the request to the server, it is also an object that will process the response from the server using its built-in mechanism.

The sample code is below.


function myAjaxFunction(){
   var xhr;
   if (window.XMLHttpRequest){
      xhr = new XMLHttpRequest();
   }else if ( window.ActiveXObject){
      xhr = new ActiveXObject("Microsoft.XMLHTTP");
   }

   xhr.onreadystatechange = myHandler;
   xhr.open("GET", "myUrl", true);
   xhr.send(null);
}

function myHandler(){
  if ( xhr.readState == 4 ){
     if ( xhr.status == 200 ){
         var fooComponent = window.document.getElementById("fooComponentId");
         fooComponent.innerHTML = xhr.responseText;
     }
  }
}

In the above code, the URL value "myUrl" can be any URL value you want. For example, you can use "xyz.ajax". And you can use ".ajax" as the suffix for all the URLs used in Ajax. And then on the server side, you can map all such URLs to a servlet that will process all the Ajax requests. The following is a sample servlet for the Ajax request.
   public class AjaxServlet extends HttpServlet {
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
            response.setContentType("Text/plain");
            response.setHeader("Cache-Control", "no-cache");
            response.setStatus(HttpServletResponse.SC_OK);
            response.getWritter().write("some content text here");
        }
 
   }

Friday, May 11, 2012

A Case Of Debugging On JSF

The getWriter() Error

Recently I was investigating a simple JSF web application that was deployed to weblogic 10 and got the following error
]] Root cause of ServletException.
java.lang.IllegalStateException: strict servlet API: cannot call getWriter() after getOutputStream()
 at weblogic.servlet.internal.ServletResponseImpl.getWriter(ServletResponseImpl.java:309)
 at com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:186)
 at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:108)
 at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:266)
 at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:159)
 at javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)
 at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
......
Since the first line says about weblogic, I thought it must be some problem caused by the weblogic. Later on, I found the error was actually caused by the isf-impl.jar library. The second line in the stack trace is
com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:186)
The class ViewHandlerImpl is from jsf-impl-1.2.jar (Implementation-Version: 1.2-b20-FCS). The method renderView() has the following code at line 186:
 responseWriter = newWriter.cloneWithWriter(response.getWriter());
The variable "response" here is the vendor implementation specific. And here it is the weblogic ServletResponseImpl. When it calls getWriter(), error happens. The problem here is actually not the class ServletResponseImpl. The ViewHandlerImpl should not have called resonse.getWriter(). After I changed the jar from jsf-impl-1.2.jar to jsf-impl-1.2_15.jar, the application worked fine. I checked the source code of ViewHandlerImpl.java in the new jar. This time there is no call to the response.getWriter().

A lesson from this is that the final place that gives error is not necessarily the place where the error needs to be fixed. Sometimes the error is caused by the upper stream class that does not follow the rule.

Another Stack Trace

The following is the starcktrace of another error related to the jar jsf-impl-1.2.jar. I listed it here so we can see the steps that lead to the compiling of a jsp page
Error 500--Internal Server Error 
weblogic.servlet.jsp.CompilationException: Failed to compile JSP /CustomerSearch.jsp
CustomerSearch.jsp:23:128: The method setVar(String) in the type DataTableTag is not applicable for the arguments (ValueExpression)
   
                                                                                                                               ^--------^

 at weblogic.servlet.jsp.JavelinxJSPStub.reportCompilationErrorIfNeccessary(JavelinxJSPStub.java:226)
 at weblogic.servlet.jsp.JavelinxJSPStub.compilePage(JavelinxJSPStub.java:162)
 at weblogic.servlet.jsp.JspStub.prepareServlet(JspStub.java:246)
 at weblogic.servlet.jsp.JspStub.prepareServlet(JspStub.java:191)
 at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:235)
 at weblogic.servlet.internal.ServletStubImpl.onAddToMapException(ServletStubImpl.java:408)
 at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:318)
 at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:175)
 at weblogic.servlet.internal.RequestDispatcherImpl.invokeServlet(RequestDispatcherImpl.java:502)
 at weblogic.servlet.internal.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:248)
 at com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:414)
 at com.sun.faces.application.ViewHandlerImpl.executePageToBuildView(ViewHandlerImpl.java:455)
 at com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:139)
 at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:108)
 at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:266)
 at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:159)
 at javax.faces.webapp.FacesServlet.service(FacesServlet.java:266)
 at weblogic.servlet.internal.StubSecurityHelper$ServletServiceAction.run(StubSecurityHelper.java:227)
 at weblogic.servlet.internal.StubSecurityHelper.invokeServlet(StubSecurityHelper.java:125)
 at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:292)
 at weblogic.servlet.internal.TailFilter.doFilter(TailFilter.java:26)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
 at weblogic.servlet.internal.RequestEventsFilter.doFilter(RequestEventsFilter.java:27)
 at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:56)
 at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3592)
 at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)
 at weblogic.security.service.SecurityManager.runAs(SecurityManager.java:121)
 at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2202)
 at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2108)
 at weblogic.servlet.internal.ServletRequestImpl.run(ServletRequestImpl.java:1432)
 at weblogic.work.ExecuteThread.execute(ExecuteThread.java:201)
 at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)
 
We can see that the weblogic uses its own classes to compile the jsp page from the following line and the lines before it:
at weblogic.servlet.jsp.JavelinxJSPStub.compilePage
(JavelinxJSPStub.java:162)
My conclusion is the following.
  1. Weblogic has its own JSP compiler implementation to compile a JSP file.
  2. When weblogic compiles a JSP page, it refers to the tld file to generate the java code for the tags used in the JSP page. The tld files can be contained in the jsf-impl jar file. However Weblogic does not need the java class files in the jsf-impl jar file. It just needs the tld file as the specification to generate the java code for JSP.
  3. The jsf-impl jar file contains both the tld file and the tag class files.
  4. In the error case shown in the stacktrace above, the java code generated by the weblogic JSP compiler and the tld file html_basic.tld in jsf-impl-1.2.jar does not match the tag class DataTableTag.class in jsf-impl-1.2.jar. So error occurs.

More on the TLD File

According to the book "Core JavaServer Faces, 2nd edition", page 373, the JSF implementation searches for TLD files in the following locations:
  • The WEB-INF directory or one of its subdirectories
  • The META-INF directory or any JAR file in the WEB-INF/lib directory
In the jsp file, you use one of the following formats to declare the tag library:
  <%@ tablib uri="http://java.sun.com/jsf/core" prefix="f" %>
or something like the following
  <html 
 xmlns:f="http://java.sun.com/jsf/core"
 >
You can see that it does not specify the location of the TLD file. So how does the server find the exact TLD file that matches the declaraion? The answer is to use the URI. In this case, the URI is http://java.sun.com/jsf/core. The TLD file has to define the URI attribute. The following is from the jsf-core.tld file:

  <tlib-version>1.0</tlib-version>
  <jsp-version>1.2</jsp-version>
  <short-name>f</short-name>
  <uri>http://java.sun.com/jsf/core</uri>
  <description>
    The core JavaServer Faces custom actions that are independent of
    any particular RenderKit.
  </description>