Статьи

Реализация пользовательского компонента JSF 2.0 с помощью Maven

Некоторое время назад я написал свой собственный JSF-компонент. Но на тот момент JSF 1.0 все еще был в курсе, и проект не использовал maven в качестве системы сборки. Таким образом, я всегда хотел написать собственный компонент JSF2 с maven. Итак, начнем:

Прежде всего мы настраиваем проект maven с двумя модулями. Вот файл pom.xml родительского проекта:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
    <modelVersion>4.0.0</modelVersion>
    <groupId>martins-developer-world</groupId>
    <artifactId>jsf-component</artifactId>
    <packaging>pom</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>jsf-component Maven Webapp</name>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>jsf-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.sun.faces</groupId>
            <artifactId>jsf-impl</artifactId>
            <version>2.2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <build>
        <finalName>jsf-component</finalName>
    </build>
    <modules>
        <module>jsf-component-webapp</module>
        <module>jsf-component-impl</module>
    </modules>
</project>

Как видите, мы добавили зависимости JSF в верхний уровень pom.xml, чтобы мы наследовали их в дочерних модулях. Поскольку мы будем использовать сервер приложений JBoss для тестирования нашего веб-приложения, мы должны установить область действия предоставляемых maven-зависимостей, чтобы наш war-файл и jar-компонент их не развертывали. Реализация нашего компонента будет находиться в jsf-component-impl, поэтому мы выбрали jar в качестве типа упаковки для этого модуля:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<?xml version="1.0"?>
<project
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>martins-developer-world</groupId>
        <artifactId>jsf-component</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>jsf-component-impl</artifactId>
    <name>jsf-component-impl</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
    </dependencies>
</project>

Теперь давайте реализуем класс Java, который расширяет UIOutput. Я выбрал UIOutput, потому что в качестве первого шага я просто хочу реализовать простой тег helloWorld, который печатает имя и фамилию, заданную в качестве атрибута внутри элемента span. Поскольку этот компонент не получает никакого ввода, UIOutput это уместно:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package martins.developer.world.jsf.component.impl;
 
import java.io.IOException;
 
import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.FacesComponent;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
 
@ResourceDependencies({ @ResourceDependency(name = "css/jsf-component.css", target = "head") })
@FacesComponent("HelloWorld")
public class HelloWorldComponent extends UIOutput {
    private static final String COMPONENT_FAMILY = "martins.developer.world.jsf.component.helloWorld";
 
    private enum PropertyKeys {
        firstName, lastName
    };
 
    @Override
    public String getFamily() {
        return COMPONENT_FAMILY;
    }
 
    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        ResponseWriter writer = context.getResponseWriter();
        writer.startElement("span", this);
        writer.writeAttribute("class", "helloWorldClass", "");
        writer.writeText(String.format("Hello %s %s!", getFirstName(), getLastName()), "");
        writer.endElement("span");
    }
 
    public String getFirstName() {
        return (String) getStateHelper().eval(PropertyKeys.firstName, "???firstName???");
    }
 
    public void setFirstName(String firstName) {
        getStateHelper().put(PropertyKeys.firstName, firstName);
    }
 
    public String getLastName() {
        return (String) getStateHelper().eval(PropertyKeys.lastName, "???lastName???");
    }
 
    public void setLastName(String lastName) {
        getStateHelper().put(PropertyKeys.lastName, lastName);
    }
}

Метод getFamily () является единственным методом, который мы вынуждены реализовать. Интересен здесь метод encodeBegin (). Это место, где мы реализуем наш тег span. Поскольку он должен иметь атрибут класса CSS, мы добавляем его с помощью метода writeAttribute () модуля Writer. Два атрибута результирующего тега JSF моделируются как простые свойства с помощью методов получения и установки. Реализация этих методов получения и установки использует StateHelper, доступный в JSF 2.0. В encodeBegin () мы используем методы получения, чтобы получить значение, данное пользователем.

Интересна также аннотация @ResourceDependencies. С помощью этой аннотации мы можем сообщить инфраструктуре JSF, что у нас есть некоторые файлы, от которых мы зависим. В данном случае это файл CSS, который находится в папке src / main / resources / META-INF / resources / css.
Аннотация @FacesComponent регистрирует этот компонент в процессе загрузки в инфраструктуре JSF. Данное имя используется в файле taglib для ссылки на этот класс:

01
02
03
04
05
06
07
08
09
10
<?xml version="1.0"?>
<facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee">
    <namespace>http://martinsdeveloperworld.wordpress.com</namespace>
    <tag>
        <tag-name>helloWorld</tag-name>
        <component>
            <component-type>HelloWorld</component-type>
        </component>
    </tag>
</facelet-taglib>

В этом файле taglib в каталоге src / main / resources / META-INF мы определяем доступные компоненты, здесь только наш тег helloWorld. Атрибуты тега являются производными от свойств класса Java.

Наконец, мы хотим протестировать наш недавно созданный компонент. Чтобы сделать это, мы настроили простой проект веб-приложения JSF2 и добавили следующий фрагмент в файл web.xml, чтобы объявить, что мы хотим использовать наш пользовательский компонент:

1
2
3
4
<context-param>
        <param-name>facelets.FACELETS_LIBRARIES</param-name>
        <param-value>/META-INF/jsf-component.taglib.xml</param-value>
    </context-param>

Теперь мы можем написать простую страницу JSF, которая ссылается на наш новый тег:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
<h:head>
<title>Hello JSF 2!</title>
</h:head>
<h:body>
    <h2>Hello World!</h2>
    <mdw:helloWorld firstName="Martin" lastName="Developer"/>
</h:body>
</html>

Когда мы развернем это приложение на сервере приложений JBoss и вызовем соответствующий URL, мы получим следующий вывод HTML:

01
02
03
04
05
06
07
08
09
10
11
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<head>
    <title>Hello JSF 2!</title>
    <link type="text/css" rel="stylesheet" href="/jsf-component-webapp/faces/javax.faces.resource/css/jsf-component.css" />
</head>
<body>
    <h2>Hello World!</h2>
    <span class="helloWorldClass">Hello Martin Developer!</span>
</body>
</html>

Ясно, что мы можем видеть тег span с классом CSS и выводом. Ссылка на файл CSS указана в заголовке документа HTML.