Introduction
In this tutorial you will learn how to set up a JSF (Jakarta Faces) 2.3 development environment with the Eclipse IDE, the Maven dependency management system, the WildFly application server, and the H2 database from scratch.
Noted should be this tutorial was originally written by me for the “From Zero to Hello World” chapter of the book
The Definitive Guide to JSF in
Update March 2022: Also noted should be that there's already a newer version of the book available which among others helps setting up Jakarta Faces 4.0 from scratch: The Definitive Guide to Jakarta Faces in
Back to top
Installing Java SE JDK
You probably already know that
Therefore, you need to make sure that you already have a JDK installed as per
Oracle’s instructions. The current
The most important parts are that the PATH
environment variable covers the /bin
folder containing the Java executables (e.g., /path/to/jdk/bin
), and that the JAVA_HOME
environment variable is set to the JDK root folder (e.g., /path/to/jdk
). This is not strictly
required by JSF, but Eclipse and WildFly need this. Eclipse will need the PATH
in order to
find the Java executables. WildFly will need the JAVA_HOME
in order to find the JDK tools.
Back to top
What About Jakarta EE?
First a small bit of history:
JSF itself is part of
There also exist so-called servlet containers which provide basically only the Servlet,
JASPIC, JSP, EL, and WebSocket APIs out of the box, such as Tomcat and Jetty. Therefore,
it would require some work to manually install and configure among others JSF, JSTL,
CDI, EJB, JPA and/or JSON-P on such a servlet container. It is not even trivial in the case of EJB as
it basically requires modifying the servlet container’s internals. That is, by the way, exactly why
TomEE exists. It’s a
Back to top
Installing WildFly
WildFly is an open source
Installing is basically a matter of unzipping the downloaded file and putting it somewhere in your home folder. We’ll leave it there until we have Eclipse up and running, so that we can then integrate WildFly in Eclipse and let Eclipse manage the WildFly application server.
Back to top
Installing Eclipse
Eclipse is an open source IDE written in Java. You can download it from eclipse.org.
It is basically like notepad but then with
thousands if not millions of extra features, such as automatically compiling class files,
building a WAR (Web Application Archive) file with them, and deploying it to an application server without the
need to manually fiddle around with javac
in a command console.
Eclipse is available in a lot of flavors, even for C/C++ and PHP. As we’re going to develop with
Also here, installing is basically a matter of unzipping the downloaded file and putting it somewhere in your home folder.
In Windows and Linux you’ll find the eclipse.ini
configuration file in the unzipped
folder. In Mac OS this configuration file is located in Eclipse.app/Contents/Eclipse
.
Open this file for editing. We want to increase the allocated memory for Eclipse. At the
bottom of eclipse.ini
, you’ll find the following lines:
-Xms256m
-Xmx2048m
This sets, respectively, the initial and maximum memory size pool which Eclipse
may use. This is a bit too low when you want to develop a bit decent
-Xms512m
-Xmx4g
Watch out that you don’t declare more than the available physical memory in the Xmx
setting. When
the actual memory usage exceeds the available physical memory, it will continue into
virtual memory, usually in a swap file on disk. This will greatly decrease performance
and result in major hiccups and slowdowns.
Now you can start Eclipse by executing the eclipse executable in the unzipped folder. You will be asked to select a directory as workspace. This is the directory where Eclipse will save all workspace projects and metadata.
After that, Eclipse will show a welcome screen. This is not interesting for now. You can click the Workbench button on the right top to close the welcome screen. Untick if necessary “Always show Welcome at start up” on the bottom right. After that, you will enter the workbench. By default, it looks like this:
Back to top
Configuring Eclipse
Before we can start writing code, we would like to fine-tune Eclipse a bit so that we don’t eventually end up in trouble or with annoyances. Eclipse has an enormous amount of settings, and some of its default values should not have been the default values. You can verify and configure the settings via Window ➤ Preferences.
- General ➤ Workspace ➤ Text file encoding must be set to
UTF-8 . Particularly in Windows this might otherwise default to the proprietary encoding CP-1252 which supports only 255 different characters and does not support any characters beyond the Latin range. When reading and saving Unicode files with CP-1252, you risk seeing unintelligible sequences of characters. This is also called “Mojibake”. For more background information on character encodings, see my old but still very relevant blog article Unicode - How to get the characters right? - General ➤ Workspace ➤ New text file line delimiter must be set to Unix. It works just fine on Windows as well. This will particularly keep version control systems happy. Otherwise, developers pulling code on different operating systems might face confusing conflicts or diffs caused by different line endings.
- General ➤ Editors ➤ Text editors ➤ Spelling should preferably
be disabled. This will save you from a potentially big annoyance,
because it unnecessarily also spellchecks technical entries in XML configuration files
such as
faces-config.xml
andweb.xml
for nothing, causing confusing warnings in those files such as “*.xhtml is not correctly spelled”. - Java ➤ Installed JREs must be set to the JDK, not to the JRE. This setting will normally also be used to execute the integrated application server which usually requires the JDK.
- Java ➤ Compiler ➤ Compiler compliance level must be set to at least 1.8. This
is the minimum required Java version for
Jakarta EE 8 . You can of course also pick a higher version. For example 11 will work just fine. We’ll use that version in the remainder of this tutorial. It’s also going to be the minimum required Java version of the upcomingJakarta EE 9 .
Back to top
Integrating New Server in Eclipse
We need to familarize Eclipse with any installed application server so that Eclipse can
seamlessly link its
In order to integrate a new application server in Eclipse, first check the bottom section of the workbench with several tabs representing several Views (you can add new ones via Window ➤ Show View). Click the Servers tab to open the servers view.
Click the link which literally says “No servers are available. Click this link to create a new server...”. It’ll show the New Server wizard, displaying a list of available server types. From the list, select Red Hat JBoss Middleware ➤ JBoss AS, WildFly, & EAP Server Tools.
After clicking Next, it will download the
Let it finish downloading and installing the
Once returned into the workspace, click the same link in the Servers view again. You’ll now see a JBoss Community ➤ WildFly option.
Advance to the next step. Just leave this screen default.
Advance to the next step. Here, you should point the Home Directory field to the folder of the WildFly installation, there where you have unzipped it after downloading. And, you should adjust the Execution Environment to the desired JDK version. Pick JavaSE-11.
Complete the remainder of the New Server wizard with default settings. You don’t need to edit any other fields. The newly added server will now appear in the Servers view.
Back to top
Creating New Project in Eclipse
We’re now ready to create a new project for our JSF application in Eclipse. This can be done via the left section of the workbench which by default shows only one tab representing the Project Explorer view (also here, you can add new views via Window ➤ Show View). By default, when there are no projects in your workspace, then it shows a list of options to create a new project. These options are also available via File ➤ New.
Eclipse, being an IDE for many different project tasks, offers a bewildering amount
of different project types from which to choose. For a
The difference is that the first is an Eclipse native project that really only works on Eclipse, while the latter is a universal type of project that can be built by any IDE, as well as easily on the command line and by various CI servers such as Travis and Jenkins. For this reason, the Maven project type is really the only viable choice.
In the next step, make sure that the option Create a simple project (skip archetype selection) is checked.
This will let us start with a really empty Maven project so that we can configure and populate it ourselves. Of course, you could also choose from an archetype, which is basically a template project with several already prepared files and configurations. But we don’t need any for now.
In the next step, we can specify our own Maven coordinates of the project. The
Maven coordinates consist of, among others, Group Id, Artifact Id, and Version, also
known as GAV in the Maven world. The Group Id usually matches the root package name
you’re going to use, such as com.example
. The Artifact Id usually represents the project
name you’re going to use. For simplicity, we’ll use project
. The Version can be kept default at 0.0.1-SNAPSHOT
. Finally the
Packaging must be set to war
(Web Application Archive). This will during the build procude a WAR file instead of a JAR file.
Now click Finish. You don’t need to edit any other fields. Once you’ve finished the wizard, you’ll get to see the project structure in the Project Explorer view.
Unfortunately, the Eclipse-generated pom.xml
, which is the main indicator of the
project being a Maven project and containing its configuration, is less than ideal. It’s not
current any more, even when generated by the latest Eclipse.
You can already see that by the pom.xml
file being marked with an alarming
red cross and an error message in the Markers view. Any project that has at least one
such red cross cannot be built and won’t be deployable.
The error message literally says
“web.xml is missing and <failOnMissingWebXml> is set to true.” In other words, Maven
somehow thinks that it’s still a pom.xml
with
the current standards, we need to open pom.xml
for editing and adjust it to look exactly like follows:
<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>com.example</groupId>
<artifactId>project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>8.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
Once you save this file, Eclipse will automatically sort it out by itself and clear out the alarming red cross. Now that looks much better.
We’ll briefly go through the most important settings here.
- Packaging war — indicates the project is a “web” project, and that the project’s contents will be assembled into a WAR (Web Application Archive) file.
- Encoding
UTF-8 — sets the encoding that the source files are in and with which the (reporting) output files should be generated. This makes the build repeatable, as it otherwise would default to the system default encoding. - Compiler 11 — sets the version of
Java SE used in the.java
source files as well as the byte code output in the.class
files. Without setting this, Maven defaults to the oldest version possible, and sometimes even a lower version than that. - failOnMissingWebXml false — older versions of
Java EE required the/WEB-INF/web.xml
file to be physically present in the WAR project. Even though this has not been required any more sinceJava EE 6 , which was released in 2009, Maven still checks for this file to be present. Setting this tofalse
prevents this unnecessary check. - Dependency jakarta.platform:jakarta.jakartaee-api:8.0.0:provided — this declares a
dependency on the
Jakarta EE 8 API , and makes sure all theJakarta EE types likeFacesServlet
,@Named
and@Entity
are known to the compiler. This dependency is set toprovided
since those types are in our case already provided by the target runtime, which is WildFly 18, aJakarta EE 8 compatible application server. This dependency will then only be used to compile the source code against, and the associated JAR files won’t be included in the generated WAR file. You need to make absolutely sure that any compile time dependency which is already provided by the target runtime is set toprovided
; otherwise it will eventually end up in the generated WAR file and you may run into class loading trouble wherein duplicate different versioned libraries are conflicting with each other. In case you’re actually not targeting a full-fledgedJakarta EE server but a barebones servlet container, such as Tomcat, then you would need to adjust the dependencies as instructed in the installation instructions of Mojarra, one of the available JSF implementations and actually the one used under the covers of WildFly.
Now, in Eclipse’s Markers view, there are two warnings left which say
“The compiler compliance specified is 1.5 but a JRE 14 is used”
and
“Build path specifies execution environment J2SE-1.5. There are no JREs installed in the workspace that are strictly compatible with this environment”.
Well, this basically means that Eclipse recognizes this Maven project as a pom.xml
being set to 11. This is an Eclipse+Maven quirk.
Eclipse clearly knows how to find the configured compiler version in pom.xml
, but unfortunately refuses to automatically adjust its project settings based on that.
In order to tell Eclipse that this is really a pom.xml
).
While at it, we also need to update the Servlet API version and add the
JSF and JPA facets. The Servlet API is represented by the “Dynamic Web Module”
entry. This needs to be set to version 4.0, which matches
As you can see in the yellow warning bar, Eclipse requires further configuration. This concerns the newly selected JSF and JPA facets. Click the link. We get to see the Modify Faceted Project wizard.
The first step of the Modify Faceted Project wizard allows us to configure the JPA facet. We need to make sure that Eclipse is being instructed that the JPA implementation is already provided by the target runtime and thus Eclipse doesn’t need to include any libraries. This can be achieved by choosing the “Disable Library Configuration” option in the JPA implementation field.
As we’re going to use the WildFly-provided Hibernate as
the actual JPA implementation, which supports automatically discovering of @Entity
annotated classes, we’d like to instruct Eclipse to do the same.
So, choose the “Discover annotated classes automatically” option in the Persistent class management field.
Otherwise Eclipse would automatically add entities to the persistence.xml
file when going through the entity code
generation wizards, or it would show warnings when we manually create one and don’t add it to the persistence.xml
.
Note that configuring a database connection (for Eclipse’s
In the next step of the Modify Faceted Project wizard, we can configure the JSF capabilities. Also here, we need to make sure that Eclipse is being instructed that the JSF implementation is already provided by the target runtime and thus Eclipse doesn’t need to include any libraries. This can be achieved by choosing the “Disable Library Configuration” option in the JSF Implementation Library field.
Further, we need to rename the servlet name of the FacesServlet to match the fictive instance
variable name: facesServlet
. Last but not least, we absolutely need to change the URL mapping
pattern from the jurassic /faces/*
to the modern *.xhtml
.
Actually, the entire registration of the FacesServlet
in web.xml
is since JSF 2.2
not strictly necessary any more; you could even uncheck the Configure JSF servlet in
deployment descriptor option and rely on the default auto-registered mappings of
/faces/*
, *.faces
, *.jsf
and *.xhtml
.
However, as this allows endusers and even searchbots to open the very same JSF page by different URLs,
and thus causes confusion among endusers and duplicate content penalties among searchbots, we’d better restrict to only
one explicitly configured URL pattern of *.xhtml
.
For more background information on JSF URL patterns, see also this Stack Overflow post.
Now, finish and apply all the wizards and dialogs.
Unfortunately, the JPA persistence.xml
in the wrong place.
It’s basically not being aware of the project being a Maven-based WAR project and Eclipse’s JPA src/main/java/META-INF
folder as if it were a JAR project. This is wrong. You need to manually move it into
src/main/resources/META-INF
folder conform the rules of a Maven-based WAR project. The workbench must now look like as follows:
Back to top
Adjusting Deployment Descriptors
We only need to verify and adjust all the deployment descriptors to catch up to the
Servlet, JSF, JPA, and CDI versions actually used by the target runtime. This is normally done by editing the root element of
the deployment descriptor XML file to set the desired version
attribute along with version-specific XML schema definitions (XSDs).
You can find all javaee
part may in the future even be renamed. At this landing page, click the “
You can open the deployment descriptor XML file for editing by
double-clicking it in the Project Explorer and then selecting the Source tab in the editor. The correct root element
declarations for
1. src/main/webapp/WEB-INF/web.xml
for Servlet 4.0:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
>
<!-- Servlet configuration here. -->
</web-app>
Make sure that the FacesServlet
as we created in the Modify Faceted Project wizard is indeed there and that it’s indeed mapped on an URL pattern of *.xhtml
.
Unfortunately, the currently available Eclipse version might annoyingly freeze for ~15 seconds during saving the web.xml
file when its xsi:schemalocation
references XSD file of version 4.0.
This is caused by Eclipse Bug 534776.
There is so far no clear work around yet. You could only remove the xsi:schemalocation
attribute to avoid this from happening.
The disadvantage of removing this is that the contents of the web.xml
file cannot
anymore be autocompleted during typing, nor be validated by Eclipse, and thus e.g. typos may slip through.
2. src/main/webapp/WEB-INF/faces-config.xml
for JSF 2.3:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config version="2.3"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
>
<!-- JSF configuration here. -->
</faces-config>
As of now it’s indeed empty.
3. src/main/resources/META-INF/persistence.xml
for JPA 2.2:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd"
>
<!-- JPA configuration here. -->
</persistence>
As of it indeed only contains the default persistence unit. We’ll reconfigure it later on.
4. src/main/webapp/WEB-INF/beans.xml
for CDI 2.0:
For sake of completeness we need to manually create one more deployment descriptor
file, the one for CDI 2.0. This isn’t automatically generated as it’s not required. CDI
is by default always enabled in any <f:websocket>
relies fully
on CDI for tracking the opened web sockets.
Right-click the WEB-INF
folder of the project and choose New ➤ File.
Give it the name beans.xml
. Populate the new file as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans version="2.0" bean-discovery-mode="annotated"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_2_0.xsd"
>
<!-- CDI configuration here. -->
</beans>
Note that the bean-discovery-mode
is set to annotated
. This is the default value. The other value is all
. This is slightly more intrusive and expensive as it will consider any Java class as a potential CDI bean.
Back to top
Configuring JSF
JSF has a lot of settings available which can be configured via <context-param>
entries in web.xml
.
You can find an overview of them in this Stack Overflow post.
The default configuration is okay to start with, but there’s one peculiar setting which we really want to set right from the beginning on.
It’s the setting which instructs JSF whether to interpret empty string submitted values as null
.
Elaborate background information can be found in my another blog article The empty String madness.
This can be done by adding the following section to top of the web.xml
:
<context-param>
<param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
<param-value>true</param-value>
</context-param>
Back to top
Creating the Backing Bean Class
With the project now correctly configured we can finally start with developing the actual MVC
application. The Controller part of MVC is already configured as FacesServlet
in web.xml
.
The Model part of MVC is what we’re going to create now. It’s basically just a simple
Java class which is by JSF convention called a Backing Bean since it “backs” a View.
Right-click the src/main/java
folder of the project and choose New ➤ Class. The
New Java Class wizard will now appear. In this
wizard, set the Package to com.example.project.view
, set the Name to Bean
.
The rest of the fields can be kept default or empty.
The class editor will now open with the newly created backing bean class. We’ll
modify it to add CDI @Named
and @RequestScoped
annotations on the class, so that it becomes a CDI managed bean.
And we need to add two properties, input
and output
, and accompany the input
property with a getter and setter pair, the output
property with only a getter, so that these can be referenced from the view. Finally we’ll add a submit()
action method which prepares the output property based on the input property, so that this can be invoked from the view.
As a hint, in Eclipse after entering the properties, you can right-click anywhere in the class editor and choose Source ➤ Generate Getters and Setters to have the IDE to generate them.
In its entirety, the edited backing bean class should look as follows:
package com.example.project.view;
import javax.enterprise.context.RequestScoped;
import javax.inject.Named;
@Named @RequestScoped
public class Bean {
private String input;
private String output;
public void submit() {
output = "Hello World! You have typed: " + input;
}
public String getInput() {
return input;
}
public void setInput(String input) {
this.input = input;
}
public String getOutput() {
return output;
}
}
We’ll briefly go through the annotations that are used here.
@Named
— gives the bean a name, which is primarily used to reference it via EL in the view. Without any attributes this name defaults to the simple class name with the first letter in lowercase, thus “bean
” here. It will be available by#{bean}
in EL. This can be used in JSF pages.@RequestScoped
— gives the bean a scope, which means that the same instance of the bean is used throughout the given lifespan. In the case of@RequestScoped
that lifespan is the duration of an HTTP request. When the scope ends, then the bean is automatically destroyed. There are more scopes available, such as@ViewScoped
,@SessionScoped
and@ApplicationScoped
. You can read more about scopes in this Stack Overflow post.
Back to top
Creating the Facelets File
Next, we’ll create the View part of MVC. It’s basically just a XHTML file which is by JSF
interpreted as a Facelets file or just “Facelet”. When the FacesServlet
is invoked with an URL matching the path of this Facelets file, then JSF will ultimately parse it and generate the HTML markup that is sent to the browser as a response to the request. With help of EL, it can
reference a bean property and invoke a bean action.
Right-click the webapp folder of the project and choose New ➤ File.
Give it the name test.xhtml
.
The HTML editor will now open with the newly created Facelets file. It’s initially empty. Fill it with the following content:
<!DOCTYPE html>
<html lang="en"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
>
<h:head>
<title>Hello World</title>
</h:head>
<h:body>
<h1>Hello World</h1>
<h:form>
<h:outputLabel for="input" value="Input" />
<h:inputText id="input" value="#{bean.input}" />
<h:commandButton value="Submit" action="#{bean.submit}">
<f:ajax execute="@form" render=":output" />
</h:commandButton>
</h:form>
<h:outputText id="output" value="#{bean.output}" />
</h:body>
</html>
We’ll briefly go through the
<h:head>
— generates the HTML<head>
element. It gives JSF the opportunity to automatically include any necessary JavaScript files, such as the one containing the necessary logic for<f:ajax>
.<h:body>
— generates the HTML<body>
element. You can also use a plain HTML<body>
in this specific Facelet example, but then it doesn’t give any other JSF tag the opportunity to automatically include any necessary JavaScript in the end of the HTML<body>
.<h:form>
— generates the HTML<form>
element. JSF will automatically include the so-called view state in a hidden input field within the form.<h:outputLabel>
— generates the HTML<label>
element. You can also use a plain HTML<label>
in this specific Facelet, but then you’d have to manually take care of figuring out the actual ID of the target input element.<h:inputText>
— generates the HTML<input type="text">
element. JSF will automatically get and set the value in the bean property specified in thevalue
attribute.<h:commandButton>
— generates the HTML<input type="submit">
element. JSF will automatically invoke the bean method specified in theaction
attribute.<f:ajax>
— generates the necessary JavaScript code to enable Ajax behavior of the tag it is being nested in, in this case thus the<h:commandButton>
. You can also do as good without it, but then the form submit won’t be performed asynchronously. Theexecute
attribute of@form
indicates that the entire<h:form>
where it is sitting in must be processed on submit, and therender
attribute of:output
indicates that the tag identified byid="output"
must be automatically updated on complete of the Ajax submit. For more background information on the syntax of theexecute
andrender
attributes, see this Stack Overflow post.<h:outputText>
— generates the HTML<span>
element. This is the one being updated on completion of the Ajax submit. It will merely print the bean property specified in the value attribute.
Those
Noted should be that a HTML5 doctype is indeed explicitly being used “in spite of” that it’s a XHTML file. HTML5 is these days the standard for web pages. For more background information on this, see this Stack Overflow post.
Back to top
Deploying the Project
In the Servers view, right-click the WildFly server entry and choose Add and Remove. It will show the Add and Remove wizard which gives you the opportunity to add and remove WAR projects to the server. Do so for our newly created project and finish the wizard.
Now start the WildFly server. You can do so by selecting it and then clicking the green arrow icon whose tool tip says “Start the server”. You can, of course, also use the bug icon whose tooltip says “Start the server in debug mode”. The Console view will briefly show the server startup log. Wait until the server starts up and has, in the Servers view, gained the status Started.
Now, open a tab in your favorite web browser and enter the web address http://localhost:8080/project-0.0.1-SNAPSHOT/test.xhtml in order to open the newly created JSF page. Play a bit around with it.
Coming back to the URL, the “localhost:8080
” part is by convention the default
domain of any /project-0.0.1-SNAPSHOT
” part is by
default the filename of the Maven-generated WAR file. In case of WildFly, you can find it in its /standalone/deployments
folder.
This part is in Servlet terms called the “context path” and obtainable by
HttpServletRequest#getContextPath()
and in JSF delegated by
ExternalContext#getRequestContextPath()
.
If you have hard time in figuring out the actually used context path, then you can usually find it in the server logs.
The exact logger line is however dependent on the server being used. In case of WildFly, it’ll be the line identified by WFLYUT0021
.
Open the Console view, press Ctrl+F and search for WFLYUT0021
. It’ll show the following line:
INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 42) WFLYUT0021: Registered web context: '/project-0.0.1-SNAPSHOT' for server 'default-server'
The context path part can also be set to “/
”. The deployed web
application will then end up in the domain root. How to do that depends on the server being used.
In case of WildFly, you’ll need to create the JBoss-specific jboss-web.xml
as follows:
src/main/webapp/WEB-INF/jboss-web.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC
"-//JBoss//DTD JBOSS 5.0//EN"
"http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd"
>
<jboss-web>
<context-root>/</context-root>
</jboss-web>
Now it will be deployed to the domain root and after restarting the server you can access the JSF page by http://localhost:8080/test.xhtml.
We can even get a step further by making test.xhtml
the default landing file so that this also doesn’t need to be specified in the URL. This can
be achieved by adding the following entry to the bottom of web.xml
:
<welcome-file-list>
<welcome-file>test.xhtml</welcome-file>
</welcome-file-list>
This will basically instruct the server to use the specified file within the folder as default resource when any folder is requested.
So, if for example /
folder is requested, then it’ll search for a /test.xhtml
and serve it.
Or if for example /foo
folder is requested, then it’ll search for a /foo/test.xhtml
and serve it. Etcetera.
Now, save the web.xml
and restart the server.
Coming back to the web browser, you’ll notice that the JSF page is now also accessible by merely http://localhost:8080.
Back to top
Installing H2
H2 is an <dependencies>
section of the pom.xml
file:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
That’s basically it. Its JDBC (Java Database Connecivity) driver is also provided by this dependency.
Back to top
Configuring DataSource
In order to be able to interact with any SQL database, we need to configure a so-called data source
in the web application project. This can be done by adding the following section to the web.xml
:
<data-source>
<name>java:global/DataSourceName</name>
<class-name>org.h2.jdbcx.JdbcDataSource</class-name>
<url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1</url>
</data-source>
It basically creates a connection pooled data source in the server itself.
The data source <name>
represents the JNDI (Java Naming and Directory Interface)
name. The “java:global/
” part is mandatory. The DataSourceName
is free to your choice.
It’s the one that ultimately needs to be registered in persistence.xml
later on.
The <class-name>
represents the fully qualified name of the javax.sql.DataSource
implementation of the JDBC driver being used.
The <url>
represents the JDBC driver-specific URL format.
The syntax is dependent on the JDBC driver being used. Usually the syntax can be found in the JDBC driver specific documentation.
For an test
,” that’s thus jdbc:h2:mem:test
. The
DB_CLOSE_DELAY=-1
path parameter basically instructs its JDBC driver not to
automatically shut down the database when it hasn’t been accessed for some time, even
though the application server is still running.
After configuring the data source, a concrete instance of the javax.sql.DataSource
can now be injected in any servlet container
managed artifact such as a plain vanilla servlet or filter as follows:
@Resource
private DataSource dataSource;
You could get a SQL connection from it via DataSource#getConnection()
for the
plain old JDBC work. However, as we’re going to use pure
Back to top
Configuring JPA
In order to familiarize JPA with the newly added data source, we need to add a new
persistence unit to the persistence.xml
which uses the data source as a JTA data source.
<persistence-unit name="PersistenceUnitName" transaction-type="JTA">
<jta-data-source>java:global/DataSourceName</jta-data-source>
<properties>
<property
name="javax.persistence.schema-generation.database.action"
value="drop-and-create" />
</properties>
</persistence-unit>
You see, the data source is identified by its JNDI name which we confgured earlier in web.xml
.
You’ll also notice a JPA-specific
javax.persistence.schema-generation.database.action
property with a
value of “drop-and-create
” which basically means that the web application should
automatically drop and create all SQL tables based on JPA entities. This is, of course,
only useful for prototyping purposes, as we’re basically doing in this tutorial.
For real-world applications, you’d better pick either “create
” or “none
”
(which is the default).
The transaction type being set to “JTA
” basically means that the
application server should automatically manage database transactions. This way every
method invocation on an EJB from its client (usually, a JSF backing bean) transparently
starts a new transaction and when the EJB method returns to the client (usually, the
calling backing bean), the transaction is automatically committed and flushed. And, any
runtime exception from an EJB method automatically rolls back the transaction. For more information on usefulness of this, see also this Stack Overflow post.
Back to top
Creating the JPA Entity
Now we’re going to create a JPA entity. Basically, it’s a JavaBean class which represents
a single record of a database table. Each bean property is mapped to a particular
column of the database table. Normally, JPA entities are modeled against existing
database tables. But, as you’ve read in the previous section, “Configuring JPA”, about
the persistence.xml
, it’s also possible to do it the other way round: database tables are
generated based on JPA entities.
This is not recommended for production applications, but it’s useful for prototyping as we’re basically doing in this tutorial.
Right-click the src/main/java
folder of the project and choose New ➤ JPA Entity.
In the wizard, set the Java package to com.example.project.model
and set the Class name to
Message
. The rest of the fields can be kept default or empty.
Modify the new entity class as follows:
package com.example.project.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
import javax.validation.constraints.NotNull;
@Entity
public class Message implements Serializable {
private static final long serialVersionUID = 1L;
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false) @Lob
private @NotNull String text;
// Add/generate getters and setters.
}
As a reminder, you can let Eclipse generate getters and setters by right-clicking anywhere in the class editor and choosing Source ➤ Generate Getters and Setters.
We’ll briefly go through the annotations that are used here.
@Entity
— marks the bean as a JPA entity, so that the JPA implementation will automatically collect database-related metadata based on all its properties.@Id @GeneratedValue(strategy=IDENTITY)
— marks a property to be mapped to a SQL database column of “IDENTITY
” type. In MySQL terms, that’s the equivalent of “AUTO_INCREMENT
”. In PostgreSQL terms, that’s the equivalent of “BIGSERIAL
”.@Column
— marks a property to be mapped to a regular SQL database column. The actual SQL database column type depends on the Java type being used. In case of a JavaString
, without the additional@Lob
annotation, that’s by default aVARCHAR(255)
whose length can be manipulated by @Column(length=n)
. With the@Lob
annotation present, however, the SQL database column type becomes by defaultTEXT
.@Lob
— marks aString
property to be mapped to a SQL database column of typeTEXT
instead of a limitedVARCHAR
.@NotNull
— this is actually not part of JPA but of “Bean Validation”. To the point, it ensures that the bean property is being validated never to benull
when submitting a JSF form and when persisting the JPA entity. Also note that this basically replicates the@Column(nullable=false)
, but that’s only because JPA doesn’t consider any Bean Validation annotations as valid database metadata in order to generate appropriate SQL tables.
Back to top
Creating the EJB Service
Next, we need to create an EJB in order to be able to save an instance of the
aforementioned JPA entity in the database, and to obtain a list of them from the database.
Right-click the src/main/java
folder of the project and choose New ➤ Class. In
the wizard, set the Package to com.example.project.service
and set the Name to
MessageService
. The rest of the fields can be kept default or empty.
Modify the new service class as follows:
package com.example.project.service;
import java.util.List;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import com.example.project.model.Message;
@Stateless
public class MessageService {
@PersistenceContext
private EntityManager entityManager;
public void create(Message message) {
entityManager.persist(message);
}
public List<Message> list() {
return entityManager
.createQuery("FROM Message m", Message.class)
.getResultList();
}
}
That’s basically it. Let’s briefly go through the annotations.
@Stateless
— marks the bean as a stateless EJB service, so that the application server knows whether it should pool them and when to start and stop database transactions. The alternative annotations are@Stateful
and@Singleton
. Note that a@Stateless
does not mean that the container will make sure that the class itself is stateless. You as developer are still responsible to ensure that the class doesn’t contain any shared and mutable instance variables. Otherwise, you’d better mark it as either@Stateful
or@Singleton
, depending on its purpose. See also this Stack Overflow post.@PersistenceContext
— basically injects the JPA entity manager from the persistence unit as configured in the project’spersistence.xml
. The entity manager is, in turn, responsible for mapping all JPA entities against a SQL database. It will, under the covers, do all the hard JDBC work.
Back to top
Adjusting the Backing Bean and Facelet
Now we’re going to adjust the earlier created backing bean in order to save the messages in the database and display all of them in a table.
package com.example.project.view;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import com.example.project.model.Message;
import com.example.project.service.MessageService;
@Named @RequestScoped
public class Bean {
private Message message = new Message();
private List<Message> messages;
@Inject
private MessageService messageService;
@PostConstruct
public void init() {
messages = messageService.list();
}
public void submit() {
messageService.create(message);
messages.add(message);
message = new Message();
}
public Message getMessage() {
return message;
}
public List<Message> getMessages() {
return messages;
}
}
Note that you don’t need setters for the message
and messages
properties. We’re going to use the
getters and setters of the Message
entity itself.
Finally, adjust the <h:body>
of test.xhtml
Facelet as follows:
<h1>Hello World</h1>
<h:form>
<h:outputLabel for="input" value="Input" />
<h:inputText id="input" value="#{bean.message.text}" />
<h:message id="input_m" for="input" />
<h:commandButton value="Submit" action="#{bean.submit}">
<f:ajax execute="@form" render="input input_m :table" />
</h:commandButton>
</h:form>
<h:dataTable id="table" value="#{bean.messages}" var="message">
<h:column>#{message.id}</h:column>
<h:column>#{message.text}</h:column>
</h:dataTable>
Now, restart the server, reload the page in your favorite web browser and create some messages.
As a reminder, the
That's it! Hopefully it's helpful to get started with JSF 2.3 in
Back to top
Copyright - None of this article may be taken over without explicit authorisation.
(C) April 2020, BalusC
32 comments:
i got this error:
The errors below were detected when validating the file "web-app_4_0.xsd" via the file "web.xml". In most cases these errors can be detected by validating "web-app_4_0.xsd" directly. However it is possible that errors will only occur when web-app_4_0.xsd is validated in the context of web.xml.
when i change web.xml file from eclipse generated file http://xmlns.jcp.org/xml/ns/javaee/web-app_2_5.xsd"
version="2.5" to
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0"
Would you mind to help me on this error.
Thanks
Thanks man, helped me a lot!
This is perhaps the clearest tutorial I've seen in years.
Carefully crafted, lots of details that evidently come from knowledge of theory and true real practice.
Thank you so much.
have the repository for this project?
Great tutorial. Thank you for doing this!
I think i spotted a minor mistake in the "Deploying the Project" chapter:
"Now it will be deployed to the domain root and after restarting the server you can access the JSF page by http://localhost:8080/test.xhtml."
The link actually leads to: http://localhost:8080/hello.xhtml
Other than that I could easily follow every step and am very grateful for the additional information provided.
A really great tutorial with clear instructions. Thank you very much!
The bad part is that since the release of the initial version of JSF 2.3, there is a serious bug involving INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL that has not yet been fixed. #4550 A pull request (4704) was sent but not merged. And the last 2.3 relase was in october 2019.
I got redmark when I add this:
java:global/DataSourceName
org.h2.jdbcx.JdbcDataSource
jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
what i have been missing?
Thank you so much. After weeks of trying, this is the first JSF/JPA tutorial that worked for me. You released all of my headaches.
Regarding the data-source tag with redmark in Eclipse, you need to add the follow web.xml header (like BalusC suggested)
...
I know that JSF is not wildly used out there since react and angular are on the market. But the lack of documentations is still shocking!
This is the best tutorial so far! You really saved my mental health!
This is the only JSF tutorial that has worked so far for me.
I have now tried for 3 weeks to get JSF working in my environment, server & client. And nothing before this tut has worked out. NOTHING.
Congratulations to Bauke Scholz (BalusC) !!! THANK YOU !!!
With all the explanations and background information it is exactly what I have been missing so far.
The best JSF expert, thanks balusC!!!
Excelent! Thank's :)
Thanks for your work, it's awesome!
#pasquale
Very good tutorial. All it's perfect. Thanks a lot.
Hi Balusc, thanks for your detailed article. I am facing a strange error. Following your tutorial I arrived to the part where I have to run for first time the JSF project on Wildfly. It fail, with an error like Wildfly is not providing JSF implementation at all. Next is the stacktrace, can you give me some clue, please?
21:45:48,684 ERROR [org.jboss.msc.service.fail] (MSC service thread 1-1) MSC000001: Failed to start service jboss.deployment.unit."project-jsf-ejb-0.0.1-SNAPSHOT.war".undertow-deployment.UndertowDeploymentInfoService: org.jboss.msc.service.StartException in service jboss.deployment.unit."project-jsf-ejb-0.0.1-SNAPSHOT.war".undertow-deployment.UndertowDeploymentInfoService: java.lang.ClassNotFoundException: javax.faces.webapp.FacesServlet from [Module "deployment.project-jsf-ejb-0.0.1-SNAPSHOT.war" from Service Module Loader]
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService.createServletConfig(UndertowDeploymentInfoService.java:1075)
at org.wildfly.extension.undertow@23.0.2.Final//org.wildfly.extension.undertow.deployment.UndertowDeploymentInfoService.start(UndertowDeploymentInfoService.java:276)
at org.jboss.msc@1.4.12.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.startService(ServiceControllerImpl.java:1739)
at org.jboss.msc@1.4.12.Final//org.jboss.msc.service.ServiceControllerImpl$StartTask.execute(ServiceControllerImpl.java:1701)
at org.jboss.msc@1.4.12.Final//org.jboss.msc.service.ServiceControllerImpl$ControllerTask.run(ServiceControllerImpl.java:1559)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1990)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
at org.jboss.threads@2.4.0.Final//org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1363)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.lang.ClassNotFoundException: javax.faces.webapp.FacesServlet from [Module "deployment.project-jsf-ejb-0.0.1-SNAPSHOT.war" from Service Module Loader]
at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:255)
I have been having a lot of issues building maven webapps on Apache Netbeans (since Oracles 8.2) and have had issues with jsf mysql access.
This tutorial worked perfectly on Apache Netbeans 12.4 - just loading Maven WebApp option.
The default configurations with JDK 11 seemed to be a problem but 1.8 worked well.
Good to get a tutorial properly updated to newer releases.
Two Thumbs Up!!
Thanks a LOT!
What about adding maven dependencies to the Deployment Assembly in Eclipse?
DeploymentUnitProcessingException: java.lang.ClassNotFoundException: org.h2.jdbcx.JdbcDataSource from [Module \"deployment.balusc.war\"
Is it possible to have this code on github?
What a wonderful tutorial :). Really helps me
Thanks for this wonderful tutorial!
I didn't find any video tutorial so well explained about this JSF version and its implementation, but your post was very helpful. Thank you very much bro.
Would be nice if you could update this post to show more current information, such as the renaming of javax.* classes to jakarta.*.
Its very helpful, thank you very much.
How to integration Jakarta EE10 Faces, Spring 6 and Hibernate 6 without CDI. use only spring configuration and data jpa.
Clear tutorial.
I get the datasource from JBoss:
In persistence.xml: java:jboss/datasources/ExampleDS
In Jboss standalone.xml:
jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
h2
sa
sa
No need to include it in the web.xml
@Satya Rai: including datasource config in web.xml will make your app immediately portable across all different servers without the need to edit their configuration before you could run your app on it.
You are my hero! Thanks a LOT!
The article, in the "The first call" section, mentions creating a new UIViewRoot instance. To check, I added a breakpoint in the class constructor and loaded the page.
However, execution stopped at the breakpoint during the "Restore view" step, as indicated by the "START PHASE RESTORE_VIEW 1" message on the console. This raises the question: is the new UIViewRoot instance created in the "Restore view" step or the "Render response" step?
@Andre: in that case the restore view phase as confirmed by the log message (and the call stack).
I followed above instructions and created sample project, project runs from eclipse but when i try to deploy ear it gives following error.
Caused by: java.lang.IllegalArgumentException: No persistence unit named 'entityManager' is available in scope RegisterSupportTool. Available persistence units: [] at
Post a Comment