/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.jakubholy.jeeutils.jsfelcheck; import java.io.File; import java.util.Collection; import net.jakubholy.jeeutils.jsfelcheck.beanfinder.InputResource; import net.jakubholy.jeeutils.jsfelcheck.beanfinder.ManagedBeanFinder; import net.jakubholy.jeeutils.jsfelcheck.beanfinder.jsf12.Jsf12FacesConfigXmlBeanFinder; import net.jakubholy.jeeutils.jsfelcheck.expressionfinder.impl.facelets.JsfElValidatingFaceletsParser; import net.jakubholy.jeeutils.jsfelcheck.expressionfinder.impl.facelets.jsf12.MyFaces12ValidatingFaceletsParser; import net.jakubholy.jeeutils.jsfelcheck.expressionfinder.pagenodes.PageNodeListener; import net.jakubholy.jeeutils.jsfelcheck.validator.ValidatingElResolver; import net.jakubholy.jeeutils.jsfelcheck.validator.jsf12.Jsf12ValidatingElResolver; /** *

JavaDoc copied from {@link net.jakubholy.jeeutils.jsfelcheck.AbstractJsfStaticAnalyzer}; version for JSF 1.2.

* * The validator analyses JSF pages implemented with Facelets or JSP and validates that all EL * expressions reference only existing managed beans and their properties/action * methods. See the example below and * examples in the test webapps. * Of course you will need one of the JSF version specific subclasses, namely * net.jakubholy.jeeutils.jsfelcheck.JsfStaticAnalyzer. * *

Usage example

*

* You create the analyzer, tell it where it can find your managed beans and run it on your webapp: *

* *
{@code
 * JsfStaticAnalyzer jsfStaticAnalyzer = JsfStaticAnalyzer.forFacelets();
 * jsfStaticAnalyzer.withManagedBeansAndVariablesConfiguration(
 *      ManagedBeansAndVariablesConfiguration
 *      .fromClassesInPackages("net.jakubholy.jeeutils.jsfelcheck.webtest.jsf20.test.annotated")
 *      .annotatedWith(Named.class, "value")
 *      .config());
 *
 * File webappRoot = new File("src/main/webapp");
 *
 * CollectedValidationResults results = jsfStaticAnalyzer.validateElExpressions(
 *      webappRoot,
 *      new File(webappRoot, "tests/annotated"));
 *
 * assertEquals("There shall be no invalid JSF EL expressions; check System.err.out for details. FAILURE " + results.failures()
 *      , 0, results.failures().size());
 * }
* * See the test-webapp-jsf* projects that are part of this project to see examples of usage with different * versions o JSF and different configurations. You can * see them at GitHub or * download from Maven Central. * *

Configuration

*

* You usually need to configure the validator, f.ex. tell it where to find your managed beans, inform it about * the types of JSF local variables and properties that it cannot detect automatically etc. * You might prefer to just look at the examples and start using the validator over reading this * documentation and only come back to it if you find out that you need to know more. *

*

* See the with* methods - if they take an object then you usually can create it via a static * method on the object's class - it works best with static imports. *

* *

Managed Beans and Other (Top-Level) Variables

*

* You always have to inform the validator where to find your managed beans - in faces-config, Spring * application configuration XML, or as annotated beans on the classpath. *

*

* If there are some EL variables aside of managed beans and the local variables you can declare them to the * validator via {@link net.jakubholy.jeeutils.jsfelcheck.config.ManagedBeansAndVariablesConfiguration#withExtraVariable(String, Object)}. *

* *

Local Variables

*

* For local variables, such as the var produced by h:dataTable, you * must declare of what type they are as this cannot be determined based * on the code, see * {@link net.jakubholy.jeeutils.jsfelcheck.config.LocalVariableConfiguration#withLocalVariable(String, Class)}. *

*

* If there are other tags than h:dataTable for JSP or ui:repeat for Facelets that can create local variables then you * must create and register an appropriate "resolver" (a class that can extract the local variable name and type from * the tag info) for them as is done with the * dataTable - see {@link net.jakubholy.jeeutils.jsfelcheck.config.LocalVariableConfiguration#withCustomDataTableTagAlias} and * {@link net.jakubholy.jeeutils.jsfelcheck.config.LocalVariableConfiguration#withResolverForVariableProducingTag}. *

* See {@link net.jakubholy.jeeutils.jsfelcheck.expressionfinder.variables.DataTableVariableResolver} for an example. *

* *

Declaration of Types of Elements in Collections and Maps

*

* You can also help the validator by telling it what is the type of elements in collection/maps * via {@link #withPropertyTypeOverride(String, Class)}. (This is necessary even for maps/collections using * generics as the Java compiler removes the type information; you can alternatively try to play with this * * experimental generics type extractor based on the Java Compiler API.) *

* *

How it works

* We use "fake value resolversIn" for a real JSF resolver; those resolversIn do not retrieve * variables and property values from the context as JSF normally does but instead produce * a new fake value of the expected type using Mockito - thus we can check that expressions are * valid. When the type of a variable/property cannot be determined (which is often the case for * Collections, which can contain nay Object) and isn't defined via property override etc. then * we use {@link net.jakubholy.jeeutils.jsfelcheck.validator.MockObjectOfUnknownType} - * if you see it in a failed JSF EL check then you need * to declare the type to use. * *

Limitations

* * @author jakubholy.net * * @see #validateElExpressions(java.io.File, java.io.File) * @see #withManagedBeansAndVariablesConfiguration(net.jakubholy.jeeutils.jsfelcheck.config.ManagedBeansAndVariablesConfiguration) * @see CollectedValidationResults#failures() * */ public class JsfStaticAnalyzer extends AbstractJsfStaticAnalyzer { /** Instance for validation of JSP pages. */ public static JsfStaticAnalyzer forJsp() { return new JsfStaticAnalyzer(ViewType.JSP); } /** Instance for validation of Facelets pages. */ public static JsfStaticAnalyzer forFacelets() { return new JsfStaticAnalyzer(ViewType.FACELETS); } /** For tests only */ JsfStaticAnalyzer() { this(ViewType.JSP); } private JsfStaticAnalyzer(ViewType viewType) { super(viewType); LOG.info("Created JSF 1.2 JsfStaticAnalyzer"); } public static void main(String[] args) throws Exception { // SUPPRESS CHECKSTYLE (no javadoc) AbstractJsfStaticAnalyzer.main(new JsfStaticAnalyzer(), args); } @Override protected ValidatingElResolver createValidatingElResolver() { // if (! new AstAnd(0).toString().startsWith("HACKED BY JSFELCHECK ")) { // handleUnhackedElImplementationLoaded("jasper-el"); // JSF 2.0: tomcat-jasper-el // } return new Jsf12ValidatingElResolver(); } @Override protected JsfElValidatingFaceletsParser createValidatingFaceletsParser(File webappRoot, PageNodeListener pageNodeValidator) { return new MyFaces12ValidatingFaceletsParser(webappRoot, pageNodeValidator); } @Override protected ManagedBeanFinder createManagedBeanFinder( Collection facesConfigFilesToRead) { return Jsf12FacesConfigXmlBeanFinder.forResources(facesConfigFilesToRead); } }