Page #84 (Programming)

Debugging and Logging

Troubleshooting a JSF application can be painful. So many minor details must be just right or your application will not work. Error messages can be hard to find or nonexistent. Minor typos can give rise to an application that simply does not start or that seems to get stuck. This section contains some tips to help you out.

How Do I Decipher a Stack Trace?

When you see a screen, such as the one in Figure 13-17, count yourself lucky.

Figure 13-17. Error page


Read the first line (or the first line that seems to make some sense), and correlate it with your JSF file. In this case, there is an illegal tag (inputTaxt instead of inputText) in line 16, column 21, or, we hope, somewhere near there.

The error report may also indicate a problem with your code. For example,

  java.lang.ClassCastException      com.corejsf.UploadRenderer.decode(UploadRenderer.java:73)      javax.faces.component.UIComponentBase.decode(UIComponentBase.java:658)      javax.faces.component.UIInput.decode(UIInput.java:464)      javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:878)      javax.faces.component.UIInput.processDecodes(UIInput.java:380)      javax.faces.component.UIForm.processDecodes(UIForm.java:139)      javax.faces.component.UIComponentBase.processDecodes(UIComponentBase.java:873)      javax.faces.component.UIViewRoot.processDecodes(UIViewRoot.java:305)      com.sun.faces.lifecycle.ApplyRequestValuesPhase.execute(ApplyRequestValuesPhase.java:79)      com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:200)      com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:90)      javax.faces.webapp.FacesServlet.service(FacesServlet.java:197)      com.corejsf.UploadFilter.doFilter(UploadFilter.java:68)     

The remedy is straightforward. Have a look at line 73 of UploadRenderer.java and find out what caused the bad cast.

Tip

If your stack trace states that errors in your code are in unknown source locations, compile with debugging on. If you use Ant, add the attribute debug="true" to the javac task.


Sometimes, the situation is not so rosy. Consider this report:

  javax.servlet.ServletException: javax.faces.el.EvaluationException: Error getting property 'password' from   bean of type com.corejsf.UserBean: java.lang.NullPointerException      org.apache.jasper.runtime.PageContextImpl.doHandlePageException(PageContextImpl.java:864)      org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:800)      org.apache.jsp.index_jsp._jspService(index_jsp.java:78)      org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:133)      javax.servlet.http.HttpServlet.service(HttpServlet.java:856)      org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:311)      org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:301)      org.apache.jasper.servlet.JspServlet.service(JspServlet.java:248)      javax.servlet.http.HttpServlet.service(HttpServlet.java:856)      com.sun.faces.context.ExternalContextImpl.dispatch(ExternalContextImpl.java:322)      com.sun.faces.application.ViewHandlerImpl.renderView(ViewHandlerImpl.java:142)      com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:87)      com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:200)      com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:117)      javax.faces.webapp.FacesServlet.service(FacesServlet.java:198)     

Here, the subsystem that evaluates the expression language has wrapped an exception in the bean code inside an EvaluationException. You get to know where the EvaluationException is thrown, but that does not help you you need the location of the NullPointerException that caused it.

Your next step is to inspect the log files. In this case, the log contains a more detailed report:

  Caused by: javax.faces.el.EvaluationException: Error getting property 'password' from bean of type   com.corejsf.UserBean: java.lang.NullPointerException           at com.sun.faces.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:89)           at com.sun.faces.el.impl.ArraySuffix.evaluate(ArraySuffix.java:162)           at com.sun.faces.el.impl.ComplexValue.evaluate(ComplexValue.java:146)           at com.sun.faces.el.impl.ExpressionEvaluatorImpl.evaluate(ExpressionEvaluatorImpl.java:238)           at com.sun.faces.el.ValueBindingImpl.getValue(ValueBindingImpl.java:155)        ... 55 more   Caused by: java.lang.NullPointerException           at com.corejsf.UserBean.getPassword(UserBean.java:12)           at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)           at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)           at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)           at java.lang.reflect.Method.invoke(Method.java:324)           at com.sun.faces.el.PropertyResolverImpl.getValue(PropertyResolverImpl.java:79)           ... 59 more     

Finally, information you can use: Line 12 of UserBean.java caused the problem.

Unfortunately, sometimes the stack trace gives you no useful information at all. Here is an example of a bad case:

  javax.servlet.ServletException: Cannot find FacesContext      org.apache.jasper.runtime.PageContextImpl.doHandlePageException(PageContextImpl.java:867)      org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:800)      org.apache.jsp.index_jsp._jspService(index_jsp.java:78)      org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:133)      javax.servlet.http.HttpServlet.service(HttpServlet.java:856)      org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:311)      org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:301)      org.apache.jasper.servlet.JspServlet.service(JspServlet.java:248)      javax.servlet.http.HttpServlet.service(HttpServlet.java:856)     

What caused this error? Misalignment of the planets? No the problem was a bad URL: http://localhost:8080/login/index.jsp instead of http://localhost:8080/login/index.faces.

How Do I Avoid the "Stack Trace From Hell"?

Most Java programmers expect that the compiler finds their syntax errors. Unfortunately, when you develop a JSF application, the compiler only checks your Java source code. Mismatches between the JSF pages, managed beans, and configuration files are only detected at runtime. Slow turnaround time and undecipherable stack traces can be a serious drag on programmer productivity.

Here are some suggestions for detecting errors before you are faced with the "stack trace from hell."

  1. Use a validating XML editor for JSF pages and configuration files. The editor should alert you when you misspell element or attribute names, or do not nest elements properly. The default JSP and XML editors in Net-Beans do a fine job. In Eclipse, XMLBuddy is a reasonable choice. If you use a standalone text editor, find a plug-in, such as nXML for Emacs.

  2. Use a JSF development environment that understands managed beans and the expression language. If your IDE supports autocompletion, you will not make typos in the first place. If your IDE checks consistency between your JSF pages, managed beans, and faces-config.xml, many errors can be detected before deployment.

  3. Run the verifier. Most application servers can verify a WAR file before deployment. The verifier can find a variety of errors, such as missing classes and syntax errors in your JSF pages. With GlassFish, run the command

    glassfish/bin/verifier appname.war

    If you use NetBeans, right-click the project and choose "Verify Project" from the menu.

  4. If you use the Sun JSF implementation, force validation of the syntax of faces-config.xml and the managed objects that it defines. Add these lines to your web.xml file:

    <context-param>     <param-name>com.sun.faces.validateXml</param-name>     <param-value>true</param-value>   </context-param>   <context-param>     <param-name>com.sun.faces.verifyObjects</param-name>     <param-value>true</param-value>   </context-param>
  5. Use a debugger. Most IDEs support remote debugging of the application server. You can set breakpoints in managed beans and renderers and, with some IDEs, even in JSF pages. The setup can be complex if your IDE does not have predefined support for your application server, but it is well worth the effort.

How Do I "Hot Deploy" My Application?

Deploying an application takes time. The application server needs to shut down the old version of the application and then unpack, verify, and initialize the new version. This can be extremely distracting to the developer.

In hot deployment mode, you copy changed files to a deployment directory. The application server senses the change in the file time stamp and incrementally updates the application to use the new file. The result is much faster turnaround and increased developer productivity.

If you use an IDE such as NetBeans, it automatically uses hot deployment. But if you use your own build process, you need to understand how to enable hot deployment in your application server. Here are the rules for GlassFish:

  1. Do not create a WAR file. Instead, you use directory deployment. The layout of your build directory needs to be exactly what it would have been in the WAR file. This is sometimes called an exploded directory.

  2. Execute the command

    glassfish/bin/asadmin deploydir buildDirectoryPath

    to set the directory for your web app and to perform initial deployment. (The last component of the build directory path needs to have the same name as your web application.)

  3. By default, GlassFish uses dynamic reloading. When the files in your build directory change, wait two seconds and refresh the browser. This works well when you change a JSF page. However, other changes (such as changes in classes or configuration files) require redeployment. You can trigger redeployment by changing the time stamp of the file buildDirectoryPath/.reload (for example, with the Unix touch command).

How Do I Comment Out a Part of a JSF Page?

Sometimes, to find out which part of a JSF page causes a stack trace, you may want to use the time-honored, divide-and-conquer strategy of commenting out parts of the page to isolate the offending component.

You may find, to your surprise, that the XML comments <!-- ... --> do not pre-vent the enclosed components from being processed. Instead, the components are processed, and they are rendered, enclosed inside <!-- ... -->, so that the rendered markup does not show up in the HTML document. However, this does not help you with debugging since the same exceptions are still thrown.

This behavior seems odd, but keep in mind that most JSF pages are not real XML. They may contain non-XML constructs in JSP syntax, such as <%@ taglib ... %> directives. The JSP processor valiantly tries to parse this mess, and it leaves the XML comment delimiters alone.

There are two remedies. You can use JSP comments <%-- ... --%>. Any components inside these delimiters are not processed.

Alternatively, you can use strict XML for your JSF pages, as described in Chapter 2. If you enclose your JSF pages in a jsp:root tag, then the XML comments will work as expected.

  <?xml version="1.0"?>   <jsp:root version="2.0"      xmlns:jsp="http://java.sun.com/JSP/Page"      xmlns:f="http://java.sun.com/jsf/core"      xmlns:h="http://java.sun.com/jsf/html">      <!-- Ok to use XML comments to comment out tags -->   </jsp:root>

How Do I Find the Logs?

The details depend on your web container. GlassFish logs all messages in the file glassfish/domains/domain1/logs/server.log. You can inspect this file in a text editor, or you can use the GlassFish administration interface.

Log onto http://localhost:4848/asadmin. The default username is admin and the default password is adminadmin. After logging on, you will see a screen similar to the one in Figure 13-18.

Figure 13-18. The GlassFish administration interface


The first task in the "Common Tasks" window is "Search Log Files". Click this link to get to the log viewer (see Figure 13-19).

Figure 13-19. The GlassFish log viewer


Tomcat 5 keeps logs in the tomcat/logs directory. The standard Tomcat log files are as follows:

  • catalina.out

  • localhost_log.date.log

Here date is a date stamp, such as 2003-06-30. The catalina.out file contains all output that was sent to System.out and System.err. In the default configuration, that includes all logging messages with level INFO or higher. The localhost_log.date.log files contain the logging messages that were generated by the servlet context.

Note

Both Tomcat and the JSF reference implementation use Apache Commons logging (see http://jakarta.apache.org/commons/logging.html). This logging library is a bridge to various logging libraries, in particular the java.util.logging library that was introduced in JDK 1.4 and the Apache Log4J library (http://logging.apache.org/log4j/docs/). There are religious wars over which logging library is better and whether Commons logging is a good idea (see, for example, http://www.qos.ch/logging/thinkAgain.html). We must admit to a slight preference for java.util.logging. It may not be perfect, but it is good enough, and it is a standard part of Java.


How Do I Find Out What Parameters My Page Received?

It is often helpful to know what parameters the client sent back to the server when a form was submitted. Here is a quick and dirty method for logging the request parameters.

Insert this snippet of code on top of the JSF file that receives the request:

  <%   java.util.Enumeration e = request.getParameterNames();   while (e.hasMoreElements())   {      String n = (String) e.nextElement();      String[] v = request.getParameterValues(n);      for (int i = 0; v != null && i < v.length; i++)         java.util.logging.Logger.global.info("name=" + n + ",value=" + v[i]);   }   %>     

Then the logging output will contain entries, such as:

  Apr 2, 2004 12:50:45 PM org.apache.jsp.welcome_jsp _jspService   INFO: name=_id0,value=_id0   Apr 2, 2004 12:50:45 PM org.apache.jsp.welcome_jsp _jspService   INFO: name=_id0:_id1,value=me   Apr 2, 2004 12:50:45 PM org.apache.jsp.welcome_jsp _jspService   INFO: name=_id0:_id2,value=secret   Apr 2, 2004 12:50:45 PM org.apache.jsp.welcome_jsp _jspService   INFO: name=_id0:_id3,value=Login

If you use NetBeans, you can do a lot better than that, by having NetBeans spy on the HTTP traffic (see Figure 13-20). With NetBeans 5.5, follow these steps:

1.

Click the "Runtime" tab and expand the "Servers" node.

2.

Right-click the GlassFish entry and select "Properties".

3.

Check "Use HTTP monitor".

4.

After you run your application, select the Window -> HTTP Monitor menu option.

Figure 13-20. The NetBeans HTTP monitor


The HTTP monitor displays all traffic between the browser and the web server, and conveniently decodes the requests. This is particularly valuable when you develop your own custom components.

How Do I Turn on Logging of the JSF Container?

The JSF reference implementation contains copious logging statements whose output can be very helpful in tracking down problems with your applications.

With GlassFish, you can conveniently control logging through the administration user interface. Log onto http://localhost:4848/asadmin. The default username is admin and the default password is adminadmin.

Then click the "Application Server" node, then the "Logging" tab and then the "Log Levels child" tab. At the bottom of the screen, you will find a table with the title "Additional Module Log Level Properties". Into that table, add an entry with name javax.enterprise.resource.webcontainer.jsf and level FINEST (see Figure 13-21).

Figure 13-21. Activating JSF container logging in GlassFish


When you search the logs, be sure to set the logging level to a value finer than the default level of INFO (see Figure 13-22).

Figure 13-22. Setting the logging level in the GlassFish log viewer


These settings produce a huge amount of logging information. You can fine-tune it by selecting logging for only those children that interest you. The child loggers have suffixes

  .application       .context         .renderkit   .config            .lifecycle       .taglib

For example, if you are interested in monitoring the life cycle phases, then set only javax.enterprise.resource.webcontainer.jsf.lifecycle to FINEST.

In Tomcat, you use the standard mechanism of the java.util.logging library to configure logging output.

  1. Edit the startup script catalina.sh or catalina.bat in the tomcat/bin directory. At the top, add a line that sets the variable CATALINA_OPTS to the following parameter definition:

    -Djava.util.logging.config.file=tomcat/conf/logging.properties

    In Unix/Linux, use this syntax:

    CATALINA_OPTS="-Djava.util.logging.config.file=tomcat/conf/logging.properties"     

    In Windows, use this syntax:

    set CATALINA_OPTS=-Djava.util.logging.config.file=tomcat\conf\logging.properties     

    (As always, tomcat denotes the name of the Tomcat installation such as /usr/local/jakarta-tomcat-5.5.19 or c:\jakarta-tomcat-5.5.19.)

  2. Copy the file logging.properties from the subdirectory jre/lib inside your Java SDK to the tomcat/conf directory.

  3. Edit the file tomcat/conf/logging.properties. Locate the line

    java.util.logging.ConsoleHandler.level = INFO

    and change INFO to FINEST. At the end of the file, add a line

    javax.enterprise.resource.webcontainer.jsf=FINEST

    (If you use JSF 1.1, set com.sun.faces.level=FINEST instead.)

  4. Restart Tomcat and run a JSF application. Then inspect the file tomcat/logs/catalina.out.

To turn off JSF container logging, edit tomcat/conf/logging.properties and change com.sun.faces.level to INFO.

Caution

If Tomcat finds the Apache Log4J library on the class path, then it will use that logging library unless you specifically add the following definition to CATALINA_OPTS:

-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Jdk14Logger     


How Do I Debug a Stuck Page?

Sometimes, a JSF page seems "stuck." When you click the submit button, the page is redisplayed. Here is what you can do to debug such a page:

  • First, double-check the navigation rules to make sure that the page navigation is indeed set up properly.

  • The most common reason for a stuck page is a validation or conversion error. This is easy to check by placing a <h:messages/> tag on the page.

  • If you still do not spot the error, install a phase tracker. You saw a simple implementation in Chapter 7, but for industrial-strength spying, check out FacesTrace from http://facestrace.sourceforge.net. It gives you a visual display of the phases (see Figure 13-23).

    Figure 13-23. FacesTrace in action

FacesTrace is easy to use. Add its JAR file and the Commons Logging JAR file to your web application's WEB-INF/lib directory. Import the tag library:

<%@ taglib uri="http://sourceforge.net/projects/facestrace" prefix="ft"%>

Then add a tag

<ft:trace/>

at the end of your JSF page.

How Do I Find the Library Source?

You can download the library source from http://javaserverfaces.dev.java.net. The library source can be very helpful for troubleshooting, and to clarify opaque points of the specification.

The library code is organized in three directories:

  • jsf-api, the documented API classes whose package names start with javax.faces

  • jsf-ri, the reference implementation classes whose package names start with com.sun.faces

  • jsf-tools, the tools that mechanically generate tag handlers and renderers

To get a complete set of source files, you must run the Ant script in the jsf-tools directory.

  1. Copy the file build.properties.template to build.properties. Edit the following entries:

    jsf.build.home=path to the javaserverfaces_sources directory container.name=glassfish container.home=path to the GlassFish installation
  2. Run ant main.

  3. The missing source code is produced in the jsf-ri/build/generate directory.

Finally, it is sometimes useful to find configuration details of the JSF implementation, such as the JSF names or class names of the standard components, renderers, converters, and validators. Look inside the files jsf-ri/src/com/sun/faces/jsf-ri-config.xml and jsf-ri/build/classes/com/sun/faces/standard-xml-renderkit.xml.



Core JavaServerT Faces
Core JavaServer(TM) Faces (2nd Edition)
ISBN: 0131738860
EAN: 2147483647
Year: 2004
Pages: 84

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net