0. Introduction
Maybe, JSF has not been designed to create elements programmatically, or perhaps, I have not understood JSF philosophy at all. But applications containing many beans can be rather cumbersome if you do no automatise some tasks. Model-driven development system can be a good strategy.
Frameworks like
OpenXava are interesting examples of this approach. The leader of this project is
Javier Paniza a person who I admire.
Java owns great resources as Reflection that enables us to manage classes that can be unknown at development time.
1. Updating our pom.xml with el-api dependency
In our system with Eclipse Neon 3 and Tomcat 9, we need to add the
el-api dependency to our pom.xml.
1
2
3
4
5
6
7
8
9
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
| <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.ximodante.jsf</groupId>
<artifactId>JSFv01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>JSFv01</name>
<description>JSF 2.2 & CDI</description>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<!-- Servlet 3.1 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- To solve Tomcat problem : java.lang.ClassNotFoundException: javax.servlet.jsp.jstl.core.Config -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- JSF 2.2 API -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.2.14</version>
</dependency>
<!-- JSF 2.2 Implementation -->
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.2.14</version>
</dependency>
<!-- Primefaces -->
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>6.1</version>
</dependency>
<!-- Primefaces Themes -->
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>all-themes</artifactId>
<version>1.0.8</version>
<type>pom</type>
</dependency>
<!-- Weld CDI for Tomcat (does not fulfill all capabilities !!!) -->
<dependency>
<groupId>org.jboss.weld.servlet</groupId>
<artifactId>weld-servlet-shaded</artifactId>
<version>3.0.0.Final</version>
</dependency>
<!-- Validation API Optional -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.0.CR3</version>
</dependency>
<!-- Hibernate Bean Validator Optional -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<!-- Lombok for setters and getters -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</dependency>
<!-- JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<!-- Apache Commons Utils -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<!-- Apache Commons Text -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.1</version>
</dependency>
<!-- JPA Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.10.Final</version>
</dependency>
<!-- PostgreSQL -->
<!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4.1212</version>
</dependency>
<!-- Primeface Extensions -->
<!-- https://mvnrepository.com/artifact/org.primefaces.extensions/primefaces-extensions -->
<dependency>
<groupId>org.primefaces.extensions</groupId>
<artifactId>primefaces-extensions</artifactId>
<version>6.1.1</version>
</dependency>
<!-- for evaluating EL expressions -->
<!-- https://mvnrepository.com/artifact/javax.el/javax.el-api 16/9/2017-->
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.1-b04</version>
</dependency>
</dependencies>
</project>
|
2. Creating JSF Value and Action Expressions.
This simple facelets code:
1
| <h:outputText value="#{personBean.surname}" />
|
cannot be accomplished in Java code as:
1
| myOutputText.setValue("#{personBean.surname}")
|
In the first case (Facelets), the value of the attribute "surname" is displayed. In the second sample,
"
#{personBean.surname}" is interpreted literally as a string.
David Pisano, explains briefly how to achieve this goal. To accomplish this, these 2 steps should be performed:
- Convert a string to a ValueExpression object
- Use component method setValueExpression in this way:
1
| myOutputText.setValueExpression(String property, ValueExpression expression)
|
where
property, is the name of the property to be assigned, in this case, "
name" and
expression is the object obtained from converting "
#{personBean.surname}" to ValueExpression,
But the same thing happens when setting actions to buttons like components. We need an
ActionExpression object. It is explained by
John Yeary.
In this case, the 2 steps are
- Convert a string to an MethodExpression object
- Use component method setActionExpression in this way:
1
| myButton.setActionExpression(MethodExpression expression)
|
A Java utility class with static methods is included to get ActionExpressions and ValueExpressions:
1
2
3
4
5
6
7
8
9
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
| package org.ximodante.utils.jsf;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.MethodExpression;
import javax.el.ValueExpression;
public class JSFUtils {
/**
* Creates a {@link ValueExpression} that wraps an object instance. This
* method can be used to pass any object as a {@link ValueExpression}. The
* wrapper {@link ValueExpression} is read only, and returns the wrapped
* object via its {@code getValue()} method, optionally coerced.
*
* @param expression The expression to be parsed.
* @param expectedType The type the result of the expression will be coerced
* to after evaluation.
* @return The parsed expression.
* @see http://javaevangelist.blogspot.com.es/2012/10/jsf-2x-tip-of-day-programmatically.html
*/
public static ValueExpression createValueExpression(String expression, Class<?> expectedType) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getExpressionFactory()
.createValueExpression(context.getELContext(), expression, expectedType);
}
/**
*
* @param expression
* @return
* @see http://jannotation.blogspot.com.es/2012/10/jsf-setting-el-expression.html
*/
public static ValueExpression createValueExpression(String expression) {
FacesContext facesContext = FacesContext.getCurrentInstance();
Application app = facesContext.getApplication();
ExpressionFactory elFactory = app.getExpressionFactory();
ELContext elContext = facesContext.getELContext();
ValueExpression valueExp =
elFactory.createValueExpression(elContext,
expression,
Object.class);
return valueExp;
}
/**
* This is a convenience method that parses an expression into a
* {@link MethodExpression} for later evaluation. Use this method for
* expressions that refer to methods. If the expression is a {@code String}
* literal, a {@link MethodExpression} is created, which when invoked,
* returns the {@code String} literal, coerced to expectedReturnType. An
* {@link ELException} is thrown if expectedReturnType is {@code void} or if
* the coercion of the {@code String} literal to the expectedReturnType
* yields an error. This method should perform syntactic validation of the
* expression. If in doing so it detects errors, it should raise an
* {@link ELException}.
*
* @param methodExpression The expression to parse.
* @param expectedReturnType The expected return type for the method to be
* found. After evaluating the expression, the {@link MethodExpression} must
* check that the return type of the actual method matches this type.
* Passing in a value of {@code null} indicates the caller does not care
* what the return type is, and the check is disabled.
* @param expectedParamTypes The expected parameter types for the method to
* be found. Must be an array with no elements if there are no parameters
* expected. It is illegal to pass {@code null}, unless the method is
* specified with arguments in the EL expression, in which case these
* arguments are used for method selection, and this parameter is ignored.
* @return The parsed expression.
* @see http://javaevangelist.blogspot.com.es/2012/10/jsf-2x-tip-of-day-programmatically_20.html
*/
public static MethodExpression createMethodExpression(String methodExpression, Class<?> expectedReturnType, Class<?>[] expectedParamTypes) {
FacesContext context = FacesContext.getCurrentInstance();
return context.getApplication().getExpressionFactory()
.createMethodExpression(context.getELContext(), methodExpression, expectedReturnType, expectedParamTypes);
}
|