Jakub is a Java EE developer since 2005 and occasionally a project manager, working currently with Iterate AS. He's highly interested in developer productivity (and tools like Maven and AOP/AspectJ), web frameworks, Java portals, testing and performance and works a lot with IBM technologies. A native to Czech Republic, he lives now in Oslo, Norway. Jakub is a DZone MVB and is not an employee of DZone and has posted 149 posts at DZone. You can read more from them at their website. View Full User Profile

Validating JSF EL Expressions in JSF Pages with static-jsfexpression-validator

06.23.2011
| 6627 views |
  • submit to reddit

Update: Version 0.9.3 with new group/artifactId released on 7/25 including native support for JSF 1.2 (reflected below in the pom snippet).
Update: Version 0.9.4 with function tolerance for JSF 1.2 released on 7/28 (it doesn't check functions are OK but checks their parameters etc.)

static-jsfexpression-validator is utility for verifying that EL expressions in JSF pages, such as #{bean.property}, are correct, that means that they don’t reference undefined managed beans and nonexistent getters or action methods. The purpose is to make JSF-based web applications safer to refactor as the change of a method name will lead to the detection of an invalid expression without need for extensive manual UI tests. It can be run statically, for example from a test. Currently it builds on the JSF implementation v. 1.1 but can be in few hours (or days) modified to support newer version of JSF. How does it work?

  1. Defined managed beans (name + type) are extracted from faces-config files and/or Spring application context files
  2. JSP pages are parsed by Jasper, Tomcat’s JSP parser
  3. For each JSF tag:
  4. If it defines local variables, they are recorded (such as var in h:dataTable)
  5. All JSF EL expressions in the tag’s attributes are validated by a real EL resolver using two magic classes, namely custom VariableResolver and PropertyResolver, that – instead of looking up managed bean instances and invoking their getters – fabricate “fake values” of the expected types so that the “resolution” of an expression can proceed. The effect is that the existence of the referenced properties and action methods is verified against the target classes.
    • Sometimes it is not possible to determine the type of a JSF variable or property (e.g. when it’s a Collection element), in which case it is necessary to declare it beforehand.
    • You can also manually declare extra variables (managed beans) and override the detected type of properties.

Minimal Setup

Add this dependency to your Maven/Ivy/…:

<dependency>
<groupId>net.jakubholy.jeeutils.jsfelcheck</groupId>
<artifactId>static-jsfexpression-validator-jsf11</artifactId>
<!-- <artifactId>static-jsfexpression-validator-jsf12</artifactId> -->
<!-- <artifactId>static-jsfexpression-validator-jsf20</artifactId> - now only reuses 1.2 -->
<version>0.9.3</version>
<scope>test</scope>
</dependency>

Alternatively, you can fetch static-jsfexpression-validator-jsf11-0.9.3.jar (or -jsf12- or -jsf20-) and its dependencies yourself, see the Appendix A.

Run it:

java -cp static-jsfexpression-validator-jsf11-0.9.3.jar:... net.jakubholy.jeeutils.jsfelcheck.JsfStaticAnalyzer --jspRoot /path/to/jsp/files/dir

Alternatively, run it from a Java class to be able to configure everything:

public class JsfElValidityTest {
   @Test
    public void should_have_only_defined_beans_and_valid_properties_in_jsf_el_expressions() throws Exception {
        JsfStaticAnalyzer jsfStaticAnalyzer = new JsfStaticAnalyzer();
        jsfStaticAnalyzer.setFacesConfigFiles(Collections.singleton(new File("web/WEB-INF/faces-config.xml")));
        Map<String, Class<?>> none = Collections.emptyMap();
        CollectedValidationResults results = jsfStaticAnalyzer.validateElExpressions("web", none, none, none);
        assertEquals("There shall be no invalid JSF EL expressions; check System.err/.out for details. FAILURE " + results.failures()
                , 0, results.failures().size());
    }
}

Run it and check the standard error and output for results, which should ideally look something like this:

INFO: >>> STARTED FOR '/someFile.jsp #############################################
...
>>> LOCAL VARIABLES THAT YOU MUST DECLARE TYPE FOR [0] #########################################

>>> FAILED JSF EL EXPRESSIONS [0] #########################################
(Set logging to fine for class net.jakubholy.jeeutils.jsfelcheck.validator.ValidatingJsfElResolver to se failure details and stacktraces)
>>> TOTAL EXCLUDED EXPRESIONS: 0 by filters: []
>>> TOTAL EXPRESSIONS CHECKED: 5872 (FAILED: 0, IGNORED EXPRESSIONS: 0) IN 0min 25s

Standard Usage

Normally you will need to configure the validator because you will have cases where property type etc. cannot be derived automatically.

Declaring Local Variable Types, Extra Variables, Property Type Overrides

Local Variables – h:dataTable etc.

If your JSP includes a JSF tag that declares a new local variable (typically h:dataTable), like vegetable in the example below:

<h:dataTable value="#{vegetarion.favouriteVegetable}" var="vegetable">
   <h:column>
       <h:outputText value="#{vegetable.name}" escape="false"/>
   </h:column>
   ...
</h:dataTable>

where favouriteVegetable is a Collection of Vegetables then you must tell the validator what type of objects the collection contains:

Map<String, Class<?>> localVariableTypes = new Hashtable<String, Class<?>>();
localVariableTypes.put("vegetarion.favouriteVegetable", Vegetable.class);
jsfStaticAnalyzer.validateElExpressions("web", localVariableTypes, extraVariables, propertyTypeOverrides);

The failure to do so would be indicated by a number of failed expression validations and a suggestion to register type for this variable:

>>> LOCAL VARIABLES THAT YOU MUST DECLARE TYPE FOR [6] #########################################
Declare component type of 'vegetarion.favouriteVegetable' assigned to the variable vegetable (file /favourites.jsp, tag line 109)
>>> FAILED JSF EL EXPRESSIONS [38] #########################################
(Set logging to fine for class net.jakubholy.jeeutils.jsfelcheck.validator.ValidatingJsfElResolver to se failure details and stacktraces)
FailedValidationResult [failure=InvalidExpressionException [Invalid EL expression '#{vegetable.name}': PropertyNotFoundException - 
Property 'name' not found on class net.jakubholy.jeeutils.jsfelcheck.expressionfinder.impl.jasper.variables.ContextVariableRegistry$Error_YouMustDelcareTypeForThisVariable$$EnhancerByMockitoWithCGLIB$$3c8d0e8f]; expression=#{vegetable.name}, file=/favourites.jsp, tagLine=118]
Defining Variables Not in faces-config

Variable: the first element of an EL expression.

If you happen to be using a variable that is not a managed bean defined in faces-config (or Spring config file), for example because you create it manually, you need to declare it and its type:

Map<String, Class<?>> extraVariables = new Hashtable<String, Class<?>>();
localVariableTypes.put("myMessages", Map.class);
jsfStaticAnalyzer.validateElExpressions("web", localVariableTypes, extraVariables, propertyTypeOverrides);

Expressions like #{myMessages['whatever.key']} would be now OK.

Overriding the Detected Type of Properties, Especially for Collection Elements

Property: any but the first segment of an EL expression (#{variable.propert1.property2['property3]….}).

Sometimes you need to explicitely tell the validator the type of a property. This is necessary if the poperty is an object taken from a Collection, where the type is unknown at the runtime, but it may be useful also at other times.

If you had:

<h:outputText value="#{vegetableMap['carrot'].color}"/>

then you’d need to declare the type like this:

Map<String, Class<?>> propertyTypeOverrides = new Hashtable<String, Class<?>>();
propertyTypeOverrides.put("vegetableMap.*", Vegetable.class);
//or just for 1 key: propertyTypeOverrides.put("vegetableMap.carrot", Vegetable.class);
jsfStaticAnalyzer.validateElExpressions("web", localVariableTypes, extraVariables, propertyTypeOverrides);

Using the .* syntax you indicate that all elements contained in the Collection/Map are of the given type. You can also override the type of a single property, whether it is contained in a collection or not, as shown on the third line.

Excluding/Including Selected Expressions for Validation

You may supply the validator with filters that determine which expressions should be checked or ignored. This may be useful mainly if you it is not possible to check them, for example because a variable iterates over a collection with incompatible objects.

The ignored expressions are added to a separate report and the number of ignored expressions together with the filters responsible for them is printed.

Example: ignore all expressions for the variable evilCollection:

jsfStaticAnalyzer.addElExpressionFilter(new ElExpressionFilter(){
   @Override public boolean accept(ParsedElExpression expression) {
       if (expression.size() == 1
          && expression.iterator().next().equals("evilCollection")) {
      return false;
       }
       return true;
   }

   @Override public String toString() {
       return "ExcludeEvilCollectionWithIncompatibleObjects";
   }
});

(I admit that the interface should be simplified.)

Other Configuration

In JsfStaticAnalyzer:

  • setFacesConfigFiles(Collection<File>): faces-config files where to look for defined managed beans; null/empty not to read any
  • setSpringConfigFiles(Collection<File>) Spring applicationContext files where to look for defined managed beans; null/empty not to read any
  • setSuppressOutput(boolean) – do not print to System.err/.out – used if you want to process the produced CollectedValidationResults on your own
  • setJspsToIncludeCommaSeparated(String) – normally all JSPs under the jspDir are processed, you can force processing only the ones you want by supplying thier names here (JspC setting)
  • setPrintCorrectExpressions(boolean) – set to true to print all the correctly validated JSF EL expressions

Understanding the Results

jsfStaticAnalyzer.validateElExpressions prints the results into the standard output and error and also returnes them in a CollectedValidationResults with the following content:

  • ResultsIterable<FailedValidationResult> failures() – expressions whose validation wasn’t successful
  • ResultsIterable<SuccessfulValidationResult> goodResults() – expressions validated successfully
  • ResultsIterable<ExpressionRejectedByFilterResult> excluded() – expressions ignored due to a filter
  • Collection<DeclareTypeOfVariableException> – local variables (h:dataTable’s var) for which you need to declare their type

The ResultsIterable have size() and the individual *Result classes contain enough information to describe the problem (the expression, exception, location, …).

Now we will look how the results appear in the output.

Unknown managed bean (variable)

FailedValidationResult [failure=InvalidExpressionException [Invalid EL expression
'#{messages['message.res.ok']}': VariableNotFoundException - 
No variable 'messages' among the predefined ones.]; expression=#{messages['message.res.ok']}, file=/sample_failures.jsp, tagLine=20]

Solution: Fix it or add the variable to the extraVariables map parameter.

Invalid property (no corresponding getter found on the variable/previous property)

a) Invalid property on a correct target object class

This kind of failures is the raison d’être of this tool.

FailedValidationResult [failure=InvalidExpressionException 
[Invalid EL expression '#{segment.departureDateXXX}': PropertyNotFoundException - 
Property 'departureDateXXX' not found on class example.Segment$$EnhancerByMockitoWithCGLIB$$5eeba04];
 expression=#{segment.departureDateXXX}, file=/sample_failures.jsp, tagLine=92]

Solution: Fix it, i.e. correct the expression to reference an existing property of the class. If the validator is using different class then it should then you might need to define a propertyTypeOverride.

b) Invalid property on an unknown target object class – MockObjectOfUnknownType
FailedValidationResult [failure=InvalidExpressionException 
[Invalid EL expression '#{carList[1].price}': PropertyNotFoundException -
 Property 'price' not found on class net.jakubholy.jeeutils.jsfelcheck.validator.MockObjectOfUnknownType$$EnhancerByMockitoWithCGLIB$$9fa876d1]; 
expression=#{carList[1].price}, file=/cars.jsp, tagLine=46]

Solution: carList is clearly a List whose element type cannot be determined and you must therefore declare it via the propertyTypeOverrides map property.

Local variable without defined type

FailedValidationResult [failure=InvalidExpressionException
 [Invalid EL expression '   #{traveler.name}': PropertyNotFoundException -
 Property 'name' not found on class net.jakubholy.jeeutils.jsfelcheck.expressionfinder.impl.jasper.variables.ContextVariableRegistry$Error_YouMustDelcareTypeForThisVariable$$EnhancerByMockitoWithCGLIB$$b8a846b2]; 
expression=   #{traveler.name}, file=/worldtravels.jsp, tagLine=118]

Solution: Declare the type via the localVariableTypes map parameter.

More Documentation

Check the JavaDoc, especially in JsfStaticAnalyzer.

Limitations

  1. Currently only local variables defined by h:dataTable‘s var are recognized. To add support for others you’d need create and register a class similar to DataTableVariableResolver
  2. Handling of included files isn’t perfect, the don’t know about local variables defined in the including file. But we have all info needed to implement this. Static includes are handled by the Jasper parser (though it likely parses the included files also as top-level files, if they are on its search path).

Future

It depends on my project’s needs, your feedback and your contributions :-) .

Where to Get It

From the project’s GitHub or from the project’s Maven Central repository, snapshots also may appear in the Sonatype snapshots repo.

Appendices

A. Dependencies of v.0.9.0 (also mostly similar for later versions):

(Note: Spring is not really needed if you haven’t Spring-managed JSF beans.)

aopalliance:aopalliance:jar:1.0:compile
commons-beanutils:commons-beanutils:jar:1.6:compile
commons-collections:commons-collections:jar:2.1:compile
commons-digester:commons-digester:jar:1.5:compile
commons-io:commons-io:jar:1.4:compile
commons-logging:commons-logging:jar:1.0:compile
javax.faces:jsf-api:jar:1.1_02:compile
javax.faces:jsf-impl:jar:1.1_02:compile
org.apache.tomcat:annotations-api:jar:6.0.29:compile
org.apache.tomcat:catalina:jar:6.0.29:compile
org.apache.tomcat:el-api:jar:6.0.29:compile
org.apache.tomcat:jasper:jar:6.0.29:compile
org.apache.tomcat:jasper-el:jar:6.0.29:compile
org.apache.tomcat:jasper-jdt:jar:6.0.29:compile
org.apache.tomcat:jsp-api:jar:6.0.29:compile
org.apache.tomcat:juli:jar:6.0.29:compile
org.apache.tomcat:servlet-api:jar:6.0.29:compile
org.mockito:mockito-all:jar:1.8.5:compile
org.springframework:spring-beans:jar:2.5.6:compile
org.springframework:spring-context:jar:2.5.6:compile
org.springframework:spring-core:jar:2.5.6:compile
xml-apis:xml-apis:jar:1.0.b2:compile

 

From http://theholyjava.wordpress.com/2011/06/22/validating-jsf-el-expressions-in-jsf-pages-with-static-jsfexpression-validator/

Published at DZone with permission of Jakub Holý, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Sirikant Noori replied on Sun, 2012/01/15 - 12:38pm

it means that you haven’t provided valid directory where your JSP pages are located (when you call jsfStaticAnalyzer.validateElExpressions(“/path/to/jsp/directory”, …)).

Henk De Boer replied on Sat, 2012/01/21 - 7:00am

By itself this is REALLY nice, but to be really useable it does needs to be taken to the next level. Have you compared it with the validation functionality offered by e.g. JBoss Tools and bare WTP in Eclipse?

 

where favouriteVegetable is a Collection of Vegetables then you must tell the validator what type of objects the collection contains:

 

Why?  The only thing you need to know is that "var" links to an entry in "#{vegetarion.favouriteVegetable}". The validator should be able to resolve what "vegetarion" is and then look up the type of its favouriteVegetable itself.

Also, I'm not sure you should still be talking about JSP in combination with JSF in 2011/2012, as this has been totally deprecated in favor of Facelets. The article mentions entries in faces-config.xml, but beans can of course also be defined using annotations.

Then, increasingly CDI is being used in combination with JSF, so it really would need support for that as well.

So again, really cool project, but as a JSP/faces-config.xml based solution that needs help to resolve things that the validator should easily be able to resolve itself, is practically of only very limited use (IMHO).

 

 

Jakub Holý replied on Sun, 2012/01/29 - 9:53am in response to: Henk De Boer

Thank you for your comment, Henk. I share many of your opinions :-)

  1. No, I haven't compared it with JBoss Tools/WTP. I looked briefly into WTP in the hope of being to reuse it but it wasn't possible. The advantage of this project/tool is that it runs outside of an IDE, e.g. on a CI server.
  2. If favouriteVegetable is Collection<Vegetable> then you at runtime have no way to find that our as the type info of generics is removed during compilation. Yes, extracting the info from the source code would be cool. I welcome ideas on how to do that easily :-) (Again, notice this tool is intended to run at a CI server)
  3. Regarding JSP you're right thoug many of us enterprise devs are still stuck with prehistorical technologies. However Facelets support is now (v.0.9.7) integrated on the basic level (parsing & checking) though there are still some things to do.
  4. Right, fortunately that is easy to achive by using Reflections - with them you can tweak the discovery of any annotated bean to suite your particular setup and frameworks (and then just register the beans with the validator). I plan on providing an example soon 

The key point: No, it isn't JSP/faces-config only :-)

Jakub Holý replied on Fri, 2012/02/10 - 1:15pm in response to: Jakub Holý

Regarding #2 it's possible but non-trivial, see proof of concept at http://theholyjava.wordpress.com/2012/02/07/using-java-compiler-tree-api-to-extract-generics-types/

 

#4 support for aut-detecting managed bean has just been added (not yet released) - https://github.com/jakubholynet/static-jsfexpression-validator/commit/706b44d0a298af769c0bcd80bc0d1c821c3dc18a (and previous ones)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.