Статьи

Проверка выражений EL JSF на страницах JSF с помощью static-jsfexpression-validator

Обновление: версия 0.9.3 с новым group / artifactId, выпущенным 7/25, включая встроенную поддержку JSF 1.2 (отражено ниже во фрагменте pom).
Обновление: версия 0.9.4 с допуском функций для JSF 1.2, выпущенная 7/28 (она не проверяет работоспособность функций, но проверяет их параметры и т. Д.)

static-jsfexpression-validator — это утилита для проверки правильности выражений EL на страницах JSF, таких как # {bean.property}, что означает, что они не ссылаются на неопределенные управляемые компоненты и несуществующие методы получения или методы действий. Цель состоит в том, чтобы сделать основанные на JSF веб-приложения более безопасными для рефакторинга, поскольку изменение имени метода приведет к обнаружению недопустимого выражения без необходимости расширенных ручных тестов пользовательского интерфейса. Его можно запускать статически, например, из теста. В настоящее время он основан на реализации JSF v. 1.1, но может быть изменен в течение нескольких часов (или дней) для поддержки более новой версии JSF. Как это работает?

  1. Определенные управляемые bean-компоненты (имя + тип) извлекаются из файловface-config и / или контекстных файлов приложения Spring
  2. JSP-страницы анализируются Jasper , анализатором JSP Tomcat.
  3. Для каждого тега JSF:
  4. Если он определяет локальные переменные, они записываются (например, var в h: dataTable )
  5. Все выражения JSF EL в атрибутах тега проверяются реальным распознавателем EL с использованием двух магических классов, а именно пользовательских VariableResolver и PropertyResolver, которые — вместо поиска экземпляров управляемых компонентов и вызова их методов получения — производят «поддельные значения» ожидаемых типов, так что что «разрешение» выражения может продолжаться. Эффект заключается в том, что существование указанных свойств и методов действия сверяется с целевыми классами.

    • Иногда невозможно определить тип переменной или свойства JSF (например, когда это элемент Collection), и в этом случае необходимо объявить его заранее.
    • Вы также можете вручную объявить дополнительные переменные (управляемые компоненты) и переопределить обнаруженный тип свойств.

Минимальная настройка

Добавьте эту зависимость к вашему 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>

Кроме того, вы можете самостоятельно получить  static-jsfexpression-validator-jsf11-0.9.3.jar (или -jsf12- или -jsf20-) и его зависимости, см. Приложение A.

Запустить его:

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

В качестве альтернативы, запустите его из класса Java, чтобы иметь возможность настроить все:

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());
    }
}

Запустите его и проверьте стандартную ошибку и выводите результаты, которые в идеале должны выглядеть примерно так:

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

Стандартное использование

Обычно вам необходимо настроить валидатор, потому что у вас будут случаи, когда тип свойства и т. Д. Не могут быть получены автоматически.

Объявление типов локальных переменных, дополнительных переменных, переопределений типов свойств

Локальные переменные — h: dataTable и т. Д.

Если ваш JSP включает тег JSF, который объявляет новую локальную переменную (обычно h: dataTable), как vegetable в примере ниже:

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

где favouriteVegetable — это Коллекция овощей, тогда вы должны сообщить валидатору, какой тип объектов содержит коллекция:

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

Невыполнение этого будет указываться рядом неудачных проверок выражений и предложением зарегистрировать тип для этой переменной:

>>> 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]

Определение переменных Не в лицах-конфигурации

Переменная: первый элемент выражения EL.

Если вы используете переменную, которая не является управляемым bean-компонентом, определенным вface-config (или конфигурационном файле Spring), например, потому что вы создаете ее вручную, вам нужно объявить ее и ее тип:

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

Выражения типа # {myMessages [‘what.key’]} теперь будут в порядке.

Переопределение обнаруженного типа свойств, особенно для элементов коллекции

Свойство: любой, кроме первого сегмента выражения EL (# {variable.propert1.property2 [‘property3]….}).

Иногда вам нужно явно сообщить валидатору тип свойства. Это необходимо, если poperty — это объект, взятый из Collection, тип которого неизвестен во время выполнения, но может быть полезен и в других случаях.

Если у тебя есть:

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

тогда вам нужно объявить тип следующим образом:

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);

Используя синтаксис. *, Вы указываете, что все элементы, содержащиеся в коллекции / карте, имеют заданный тип. Вы также можете переопределить тип отдельного свойства, независимо от того, содержится оно в коллекции или нет, как показано в третьей строке.

Исключая / Включая выбранные выражения для проверки

Вы можете предоставить валидатору фильтры, которые определяют, какие выражения следует проверять или игнорировать. Это может быть полезно в основном, если вы не можете проверить их, например, потому что переменная перебирает коллекцию с несовместимыми объектами.

Игнорируемые выражения добавляются в отдельный отчет, и выводится количество игнорируемых выражений вместе с ответственными за них фильтрами.

Пример: игнорировать все выражения для переменной 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";
   }
});

(Я признаю, что интерфейс должен быть упрощен.)

Другая конфигурация

В JsfStaticAnalyzer:

  • setFacesConfigFiles (Collection <File>): файлыface-config, где искать определенные управляемые bean-компоненты; null / empty не читать
  • setSpringConfigFiles (Collection <File>) Spring файлы applicationContext, где искать определенные управляемые bean-компоненты; null / empty не читать
  • setSuppressOutput (boolean) — не печатать в System.err / .out — используется, если вы хотите обработать полученные CollectedValidationResults самостоятельно
  • setJspsToIncludeCommaSeparated (String) — обычно обрабатываются все JSP в jspDir, вы можете принудительно обработать только те, которые хотите, указав здесь свои имена (настройка JspC)
  • setPrintCorrectExpressions (boolean) — установить в true, чтобы печатать все правильно проверенные выражения JSF EL

Понимание результатов

jsfStaticAnalyzer.validateElExpressions печатает результаты в стандартный вывод и ошибку, а также возвращает их в CollectedValidationResults со следующим содержимым:

  • ResultsIterable <FailedValidationResult> failures () — выражения, проверка которых не прошла успешно
  • ResultsIterable <SuccessfulValidationResult> goodResults () — выражения успешно проверены
  • ResultsIterable <ExpressionRejectedByFilterResult> exclude () — выражения игнорируются из-за фильтра
  • Коллекция <DeclareTypeOfVariableException> — локальные переменные (h: dataTable’s var), для которых вам нужно объявить их тип

У ResultsIterable есть size (), а отдельные * классы Result содержат достаточно информации для описания проблемы (выражение, исключение, местоположение и т. Д.).

Теперь посмотрим, как результаты появятся в выводе.

Неизвестный управляемый компонент (переменная)

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]

Решение : исправьте это или добавьте переменную в параметр карты extraVariables.

Неверное свойство (не найден соответствующий геттер для переменной / предыдущего свойства)

а) Неверное свойство в правильном классе целевого объекта

Этот вид неудач является смыслом существования этого инструмента.

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]

Решение : Исправьте это, то есть исправьте выражение для ссылки на существующее свойство класса. Если валидатор использует другой класс, тогда он должен определить свойство propertyTypeOverride.

б) Недопустимое свойство для неизвестного класса целевого объекта — 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]

Решение : carList — это явно List, тип элемента которого не может быть определен, и поэтому вы должны объявить его с помощью свойства map propertyTypeOverrides.

Локальная переменная без определенного типа

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]

Решение . Объявите тип с помощью параметра map localVariableTypes.

Больше документации

Проверьте JavaDoc, особенно в JsfStaticAnalyzer .

Ограничения

  1. В настоящее время только локальные переменные , определенные ч: DataTable «S вар распознаются. Чтобы добавить поддержку других, вам нужно создать и зарегистрировать класс, аналогичный DataTableVariableResolver.
  2. Обработка включенных файлов не идеальна, не знаю о локальных переменных, определенных во включаемом файле. Но у нас есть вся информация, необходимая для реализации этого. Статические включения обрабатываются анализатором Jasper (хотя он, вероятно, анализирует включенные файлы также как файлы верхнего уровня, если они находятся в пути поиска).

Будущее

Это зависит от потребностей моего проекта, ваших отзывов и вашего вклада :-).

Где его взять

Из GitHub проекта или из репозитория Maven Central , снимки также могут появляться в репозитории снимков Sonatype .

Приложения

A. Зависимости v.0.9.0 (также в основном аналогичны для более поздних версий):

(Примечание: Spring на самом деле не нужен, если у вас нет bean-компонентов JSF, управляемых Spring.)

aopalliance: aopalliance: jar: 1.0: компилировать
commons-beanutils: commons-beanutils: jar: 1.6: компилировать
commons-collection: commons-collection: jar: 2.1: компилировать
commons-digester: commons-digester: jar: 1.5: компилировать
commons- io: commons-io: jar: 1.4: скомпилировать
ведение журнала с помощью общего
доступа: регистрация с использованием общего ресурса: jar: 1.0: скомпилировать javax.faces: jsf-api: jar: 1.1_02: скомпилировать
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: скомпилировать
org.apache.tomcat : jasper -el: jar: 6.0.29: скомпилировать
org.apache.tomcat: jasper-jdt: jar: 6.0.29: compile
org.apache.tomcat: jsp-api: jar: 6.0.29: скомпилировать
org.apache.tomcat: juli: jar: 6.0.29: скомпилировать
org.apache.tomcat: servlet-api: jar: 6.0.29: скомпилировать
org .mockito: mockito-all: jar: 1.8.5: компилировать
org.springframework: spring-beans: jar: 2.5.6: компилировать
org.springframework: spring-context: jar: 2.5.6: компилировать
org.springframework: spring- core: jar: 2.5.6: компилировать
xml-apis: xml-apis: jar: 1.0.b2: компилировать

 

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