Wednesday, June 13, 2012

JSF and JSP Architecture Models

There are two models for building applications using the JSP and servlet technology.

The Model 1 Architecture

In this model, the target of every request is a JSP page. The JSP page does all the things to answer the request. It can use JavaBeans to access database or other services.

The Model 2 Architecture

This model follows the Model-View-Controller(MVC) design pattern. In this architecture, all the requests are received by the servlets first. The servlets act as the controller. They analyze the request, get the data into JavaBeans, and finally dispatch the requests to JSP pages. The JavaBeans act as the model. And the JSP pages act as the view.

What about JSF?

We know every JSP page is actually a servet. It will be compiled to a servlet class by the web container. Now in the JSF world, with all those JSF lifecyle phases, is it still following the Model 2 architecture? The answer is still "Yes" because the JSP file gives the layout and appearance of the page. But unlike the old jsp/servlet technology where the JSP writes out all the HTML text, in JSF, the servlet generated from the JSP page only produces some simple HTML text such as the header("Content-type", "text/html"). Mostly it just processes the tags and creates the object tree into the Context which are used to build the JSF component tree. Later on the faces servlet gets the component tree from the context and spits out the actual HTML text in the renderers of the components in the component tree.

Roughly speaking, in the traditional Model 2, the servlet does not do much. basically it just dispatches the requests to the JSP pages and its job is done. But in JSF, the servlet will execute the JSF life cycle for every request. At some point in the life cycle, the servlet will dispatch the request to the JSP. The JSP will do its job and save the the work into the conetxt. Then the execution will return to the servlet for the life cycle to finish.

The following is one actual stacktrace from a sample JSF application. It shows an error. But that is not the interest here. From the stacktrace, we can see how the whole thing works together. We can see the following:

  1. The faces servlet acts as the controller. The JSF lifecyle is executed in the controller.
  2. The splitting line in the stacktrace is the one containing the following: com.sun.faces.context.ExternalContextImpl.dispatch. This is where the faces servlet dispatches the request to the JSP page.
  3. After the faces servlet dispatches the request, the servlet generated by the JSP will take over the task. In the example, it is jsp_servlet.__index.class which is generated from the JSP page index.jsp. It is missing in this case. So an error occurs.
  4. The jsp_servlet processes the tags in the JSP page, creates the corresponding objects from the tags and stores them into the pageContext. The jsp_servlet does not write out the actual HTML text from the tags.
  5. Later on in the JSF lifecycle, the JSF renderers will write out the actual HTML text that will be sent to the browser. And these occurs in the faces servlet.
javax.servlet.ServletException: [HTTP:101249][ServletContext@11930454[app:spinner module:spinner path:/spinner spec-version:2.5]]: Servlet class jsp_servlet.__index for servlet /index.jsp could not be loaded because the requested class was not found in the classpath .
java.lang.ClassNotFoundException: jsp_servlet.__index.
 at weblogic.servlet.internal.ServletStubImpl.prepareServlet(ServletStubImpl.java:543)
 at weblogic.servlet.jsp.JspStub.prepareServlet(JspStub.java:271)
 at weblogic.servlet.jsp.JspStub.prepareServlet(JspStub.java:191)
 at weblogic.servlet.internal.ServletStubImpl.execute(ServletStubImpl.java:235)
 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:410)
 at com.sun.faces.application.ViewHandlerImpl.executePageToBuildView(ViewHandlerImpl.java:469)
 at com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:140)
 at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:110)
 at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:100)
 at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139)
 at javax.faces.webapp.FacesServlet.service(FacesServlet.java:245)
 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)
In the correct version for the example above, the class jsp_servlet._index.class is at the right location. Looking at the actual code, you can see the majority of the class is the methods of the following type:
private boolean _jsp__tagX(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, javax.servlet.jsp.PageContext pageContext, javax.servlet.jsp.tagext.JspTag activeTag, com.sun.faces.taglib.html_basic.PanelGridTag parent) throws java.lang.Throwable
    {
The method name is _jsp__tagX, where 'X' is a number. For this particular example, there are 12 such methods. X is from 0 to 11, each corresponding to an actual tag in the JSP file.

A main method in _index.java is the following:

public final class __index extends  weblogic.servlet.jsp.JspBase  implements weblogic.servlet.jsp.StaleIndicator {
...
    public void _jspService(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) 
    throws javax.servlet.ServletException, java.io.IOException {

        javax.servlet.ServletConfig config = getServletConfig();
        javax.servlet.ServletContext application = config.getServletContext();
        javax.servlet.jsp.tagext.JspTag _activeTag = null;
        java.lang.Object page = this;
        javax.servlet.jsp.PageContext pageContext = javax.servlet.jsp.JspFactory.getDefaultFactory().getPageContext(this, request, response, null, true , 8192 , true );
        response.setHeader("Content-Type", "text/html");
        javax.servlet.jsp.JspWriter out = pageContext.getOut();
        weblogic.servlet.jsp.ByteWriter bw = (weblogic.servlet.jsp.ByteWriter)out;
        bw.setInitCharacterEncoding(_WL_ORIGINAL_ENCODING, _WL_ENCODED_BYTES_OK);
        javax.servlet.jsp.JspWriter _originalOut = out;
        javax.servlet.http.HttpSession session = request.getSession( true );
        try {;
            bw.write(_wl_block0Bytes, _wl_block0);
            bw.write(_wl_block1Bytes, _wl_block1);
            bw.write(_wl_block1Bytes, _wl_block1);
            bw.write(_wl_block1Bytes, _wl_block1);

            if (_jsp__tag0(request, response, pageContext, _activeTag, null))
             return;
            bw.write(_wl_block9Bytes, _wl_block9);
        } catch (java.lang.Throwable __ee){
            if(!(__ee instanceof javax.servlet.jsp.SkipPageException)) {
                while ((out != null) && (out != _originalOut)) out = pageContext.popBody(); 
                _releaseTags(pageContext, _activeTag);
                pageContext.handlePageException(__ee);
            }
        }
    }
....

}
The method above calls _jsp_tag0() which will in turn calls _jsp_tag1(), .... The mehtod _jsp_tag0 is for the ViewTag. The method is as follows:
 private boolean _jsp__tag0(javax.servlet.ServletRequest request, javax.servlet.ServletResponse response, javax.servlet.jsp.PageContext pageContext, javax.servlet.jsp.tagext.JspTag activeTag, javax.servlet.jsp.tagext.JspTag parent) throws java.lang.Throwable
    {
        javax.servlet.jsp.tagext.JspTag _activeTag = activeTag;
        javax.servlet.jsp.JspWriter out = pageContext.getOut();
        weblogic.servlet.jsp.ByteWriter bw = (weblogic.servlet.jsp.ByteWriter) out;
         com.sun.faces.taglib.jsf_core.ViewTag __tag0 = null ;
        int __result__tag0 = 0 ;

        if (__tag0 == null ){
            __tag0 = new  com.sun.faces.taglib.jsf_core.ViewTag ();
            weblogic.servlet.jsp.DependencyInjectionHelper.inject(pageContext, __tag0);
        }
        __tag0.setPageContext(pageContext);
        __tag0.setParent(null);
        __tag0.setJspId("id0");
        _activeTag=__tag0;
        __result__tag0 = __tag0.doStartTag();

        if (__result__tag0!= javax.servlet.jsp.tagext.Tag.SKIP_BODY){
            try {
                if (__result__tag0== javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_BUFFERED) {
                    out = pageContext.pushBody();
                    bw = (weblogic.servlet.jsp.ByteWriter)out;
                    __tag0.setBodyContent(( javax.servlet.jsp.tagext.BodyContent)out);
                    __tag0.doInitBody();
                }
                do {
                    bw.write(_wl_block2Bytes, _wl_block2);

                    if (_jsp__tag1(request, response, pageContext, _activeTag, __tag0))
                     return true;
                    bw.write(_wl_block3Bytes, _wl_block3);

                    if (_jsp__tag2(request, response, pageContext, _activeTag, __tag0))
                     return true;
                    bw.write(_wl_block8Bytes, _wl_block8);
                } while (__tag0.doAfterBody()== javax.servlet.jsp.tagext.IterationTag.EVAL_BODY_AGAIN);
            } finally {
                if (__result__tag0== javax.servlet.jsp.tagext.BodyTag.EVAL_BODY_BUFFERED) {
                    out = pageContext.popBody();
                    bw = (weblogic.servlet.jsp.ByteWriter)out;
                }
            }
        }
        if (__tag0.doEndTag()== javax.servlet.jsp.tagext.Tag.SKIP_PAGE){
            _activeTag = null;
            _releaseTags(pageContext, __tag0);
            return true;
        }
        _activeTag=__tag0.getParent();
        weblogic.servlet.jsp.DependencyInjectionHelper.preDestroy(pageContext, __tag0);
        __tag0.release();
        return false;
    }

Notice that line in red "weblogic.servlet.jsp.DependencyInjectionHelper.inject(pageContext, __tag0);". This looks like to process the tag and save the values into the pageContext. The other methods _jsp_tagX() are similar.

No comments:

Post a Comment