JSF

  • apache_maven

    Static analysis is in the verification of properties of software used in safety-critical computer systems and locating potentially vulnerable/buggy code. it is desirable to make your build fails at compile/test phases to detect faults earlier. Thanks to JSFUnit and Maven, you’ll be able to plug a JSF checker in your build with no effort.

    JSFUnit is a test framework for JSF applications. It is designed to allow complete integration testing and unit testing of JSF applications using a simplified API. JSFUnit tests run inside the container, which provides the developer full access to managed beans, the FacesContext, EL Expressions, and the internal JSF component tree. At the same time, you also have access to parsed HTML output of each client request.

    JSFUnit provides a set of unit tests for static analysis of JSF applications. Compare to JSFUnit, you can run these tests without any container, in Maven phase “test” like any regular Unit Test

    Views tests (JSFUnitStaticAnalysisViewTest.java)

    • Do any of your facelets templates or well formed JSPs reference nonexistent managed beans?
    • Do any of your templates or JSPs have EL expressions for nonexistent managed bean actions or action listeners?

    Faces-configurations tests (JSFUnitStaticAnalysisFacesConfigTest.java)

    • Are all of your session and application scoped beans Serializable?
    • Invalid Managed Bean Scope?
    • Missing Managed Bean Class?
    • Faces Configurations Class Inheritance?
    • Missing Setter Methods?
    • Duplicate Managed Bean Names?

    TLD tests (JSFUnitStaticAnalysisFacesConfigTest.java)

    • Correct Tag Attribute Types?
    • Unique Tag Names?
    • Correct Tag Inheritance?
    • Unique Tag Attributes?

    Install

    Put the following in your web project pom.xml (the pom.xml with <packaging>war</packaging>) between <dependencies> .. </dependencies>, Note that this dependency is only available in scope “test”

    <dependencies>
        <dependency>
            <groupId>org.jboss.jsfunit</groupId>
            <artifactId>jboss-jsfunit-analysis</artifactId>
            <version>1.0.0.GA</version>
            <scope>test</scope>
        </dependency>
        <!-- TLD test  dependencies  below, for
             View and facesConfig not needed-->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>maven-taglib</groupId>
            <artifactId>maven-taglib-plugin</artifactId>
            <version>1.4.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    Add the following repository in your pom, settings.xml or your Maven proxy repository (Artifactory for example).

    <Repositories>
        <Repository>
            <id>jboss</id>
            <url>http://repository.jboss.com/maven2/</url>
        </Repository>
    </Repositories>
    and
    <PluginRepositories>
        <PluginRepository>
            <id>taglib</id>
            <url>http://maven-taglib.sourceforge.net/maven</url>
        </PluginRepository>
    </PluginRepositories>

    Create now 3 test classes in /src/test/java

    JSFUnitStaticAnalysisViewTest.java

    package com.waltercedric.jsfunit;
    
    import java.util.HashSet;
    import java.util.Set;
    import org.jboss.jsfunit.analysis.AbstractViewTestCase;
    
    public class JSFUnitStaticAnalysisViewTest extends AbstractViewTestCase {
      private static Set absoluteViewPaths = new HashSet<String>() {
        {
          // add("C:/work/project/src/home.xhtml");
        }
      };
      private static Set recursiveViewPaths = new HashSet<String>() {
        {
          add("src/main/resources/pages");
          add("src/main/resources/bottom");
          add("src/main/resources/top");
          add("src/main/resources/menu");
        }
      };
      public JSFUnitStaticAnalysisViewTest() {
        super(absoluteViewPaths, recursiveViewPaths,
        "src/main/resources/META-INF/faces-config.xml");
      }
    }

    JSFUnitStaticAnalysisFacesConfigTest.java

    package com.waltercedric.jsfunit;
    
    import java.util.HashSet;
    import java.util.Set;
    import org.jboss.jsfunit.analysis.AbstractFacesConfigTestCase;
    
    public class JSFUnitStaticAnalysisFacesConfigTest extends AbstractFacesConfigTestCase {
      private static Set<String> paths = new HashSet<String>() {
        {
          add("src/main/resources/META-INF/faces-config.xml");
        }
      };
      public JSFUnitStaticAnalysisFacesConfigTest() {
        super(paths);
      }
    }

    JSFUnitStaticAnalysisTldTest.java

    package com.waltercedric.jsfunit;
    
    import java.util.HashSet;
    import java.util.Set;
    import org.jboss.jsfunit.analysis.AbstractTldTestCase;
    
    public class JSFUnitStaticAnalysisTldTest extends AbstractTldTestCase {
      private static Set<String> paths = new HashSet<String>() {
        {
          add("src/main/resources/META-INF/facelets.core.taglib.xml");
        }
      };
      public TldTestCase() {
        super(paths);
      }
    }
     

    References

  • The file web.xml inside the WEB-INF folder is the Web Application Deployment Descriptor for your application. This is an XML file describing the servlets and other components that make up your application. The file below is what I use at work. It contains better settings than the default one, plus all descriptions of parameters.

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee   http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
     
    <context-param>
    <description>
    Comma separated list of URIs of (additional) faces             config files. (e.g. /WEB-INF/my-config.xml) See JSF             1.0 PRD2,
    10.3.2 Attention: You do not need to put
    /WEB-INF/faces-config.xml in here.
    </description>
    <param-name>javax.faces.CONFIG_FILES</param-name>
    <param-value>
     /WEB-INF/faces-configs/common-faces-config.xml,
     /WEB-INF/faces-configs/page1-faces-config.xml,
     /WEB-INF/faces-configs/page2-faces-config.xml
    </param-value>
    </context-param>
    Follow Divide and Conquer rule:
    Do not put all your managed beans, navigations rules in one big file. Consider one faces-config per functions or html page.
    <context-param>
    <description>State saving method: "client" or "server"          (= default) See JSF Specification 2.5.3
    </description>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>server</param-value>
    </context-param>
    Server is recommended.
    Ive tried one day client, but HTML pages were 10Mb big!
    <context-param>
    <description>
    Only applicable if state saving method is "server"         (= default) and if
    org.apache.myfaces.SERIALIZE_STATE_IN_SESSION is           true (=default) If true (default) the serialized              state will be compressed before it is written to the session. If false the state will not be compressed.
    </description>
    <param-name>
      org.apache.myfaces.COMPRESS_STATE_IN_SESSION
    </param-name>
    <param-value>true</param-value>
    </context-param>
    You should always balanced cpu/memory usage.
    (Compressing state cost CPU but spare memory...obvious)


    <context-param>
    <description>
    Only applicable if state saving method is "server"             (= default). If true (default) the state will be               serialized to a byte stream before it is written to             the session. If false the state will not be                     serialized to a byte stream.
    </description>
    <param-name>
     org.apache.myfaces.SERIALIZE_STATE_IN_SESSION
    </param-name>
    <param-value>false</param-value>
    </context-param>
    Compressing data save memory bytes on server at the cost of more computational power.
    <context-param>
    <param-name>
    org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION
    </param-name>
    <param-value>5</param-value>
    </context-param>
    Number of back button. Default is 20. far too much. reducing this value decrease memory size.
    <context-param>
    <description>
    This parameter tells MyFaces if javascript code               should be allowed in the rendered HTML output. If             javascript is allowed, command_link anchors will have javascript code that submits the corresponding form. If javascript is not allowed, the state saving info and nested parameters will beadded as url parameters. Default: "true"
    </description>
    <param-name>org.apache.myfaces.ALLOW_JAVASCRIPT</param-name>
    <param-value>true</param-value>
    </context-param>
    <context-param>
    <param-name>org.apache.myfaces.DETECT_JAVASCRIPT</param-name>
    <param-value>false</param-value>
    </context-param>
    Nothing special here. Most of the time, webpage are full of javascript.
    <context-param>
    <description>The buffer size to set on the response         when the  ResponseWriter is generated.
    By default the value is -1, which will not assign             a buffer size on the response.
    </description>
    <param-name>facelets.BUFFER_SIZE</param-name>
    <param-value>8192</param-value>
    </context-param> 
    Try to set the buffer size to the most common page size.
    <context-param>
    <param-name>facelets.SKIP_COMMENTS</param-name>
    <param-value>true</param-value>
    </context-param>
    In production, no need to let comment in rendered code
    <context-param>
    <description>
    If true, rendered HTML code will be formatted, so             that it is "human readable". i.e. additional line             separators and whitespace will be written, that do             not influence the HTML code. Default: "true"
    </description>
    <param-name>org.apache.myfaces.PRETTY_HTML</param-name>
    <param-value>false</param-value>
    </context-param>
    Cpu waste in production.
    Beautify your code in eclipse.
    Consider instead using Firefox source chart plugin which always beautify and color the source source
    <context-param>
    <description>
    If true, a javascript function will be rendered               that is able to restore the former vertical scroll             on every request.
    Convenient feature if you have pages with long                 lists and you do not want the browser page to                 always jump to the top if you trigger a link or               button action that stays on the same
    page. Default: "false"
    </description>
    <param-name>org.apache.myfaces.AUTO_SCROLL</param-name>
    <param-value>true</param-value>
    </context-param>
     
    <context-param>
    <description>
    Validate managed beans, navigation rules and                   ensure that forms are not nested.
    </description>
    <param-name>org.apache.myfaces.VALIDATE</param-name>
    <param-value>true</param-value>
    </context-param>
    Can be turned off in production, if you have a good testing team and many deployment stage This kind of errors should not go un noticed.
    <context-param>
     <param-name>facelets.LIBRARIES</param-name>
    <param-value>
     /WEB-INF/taglib/tomahawk.taglib.xml;
     /WEB-INF/taglib/aa.taglib.xml;
     /WEB-INF/taglib/your.taglib.xml
    </param-value>
    </context-param>
    List here all 3rd party and your own tag library
    <context-param>
    <param-name>facelets.REFRESH_PERIOD</param-name>
    <param-value>2</param-value>
    </context-param>
    <context-param>
    <description>Special Debug Output for Development
    </description>
     <param-name>facelets.DEVELOPMENT</param-name>
     <param-value>false</param-value>
    </context-param>
    <context-param>
     <param-name>javax.faces.DEFAULT_SUFFIX</param-name>
     <param-value>.xhtml</param-value>
    </context-param>
    			Using a 
    			facelets.REFRESH_PERIOD >= 1 
    			means that templates 
    			will be reloaded after 
    			a given period if the
    			file has been modified 
    			on the file-system.
    			

     

     

     

     

    Obvious settings

    <filter>
    <filter-name>extensionsFilter</filter-name>
    <filter-class>
     org.apache.myfaces.webapp.filter.ExtensionsFilter
    </filter-class>
     <init-param>
      <param-name>uploadMaxFileSize</param-name>
      <param-value>100m</param-value>
    </init-param>
    <init-param>
     <param-name>uploadThresholdSize</param-name>
     <param-value>100k</param-value>
    </init-param>
    </filter>   
    Some security settings. Consider settings these values accordingly to your needs.
    <error-page>
     <error-code>404</error-code>
     <location>/error.jsp</location>
    </error-page>
           
    <error-page>
     <error-code>500</error-code>
     <location>/error.jsp</location>
    </error-page>
           
    <error-page>
     <exception-type>java.lang.Exception</exception-type>
     <location>/error.jsp</location>
    </error-page>
    Standard settings not related to JSF. Never reveal any useful informations in error pages!