8

Getting started with JSF 4.0 on WildFly 27

 2 years ago
source link: http://www.mastertheboss.com/java-ee/jsf/getting-started-with-jsf-4-0-on-wildfly-27/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Getting started with JSF 4.0 on WildFly 27

15 July 202214 July 2022 by F.Marchioni

This article contains a preview of Jakarta Faces 4.0 which is an MVC framework for building user interface with Jakarta EE 10.

To get started with Jakarta EE 10, you need to download the latest release of WildFly 27 which you can use to preview Jakarta EE 10 features. At the time of writing, the latest release is WildFly 27 Alpha 2 which however contains support for most of the Jakarta EE 10 bundles.

Firstly, download WildFly 27 from https://www.wildfly.org/downloads/.

Next, unpack the application server in a folder of your likes.

We are ready to go!

A First sip of Jakarta Faces 4.0

If you are new to JSF technology, we recommend checking some tutorials available in the JSF category.

Looking at the release notes, you can find the list of enhancements added in many of its components or attributes. In this article, we will test the following enhancements:

  • How to create programmatically Facelets
  • How to upload single or multiple files using JSF 4.0

Creating Facelets programmatically

Facelets is a page declaration language used to create JavaServer Faces (JSF) views with XHTML. Basically, it is a visualisation technology, responsible for the appearance of the web page. In addition to the Facelets tag libraries, JavaServer Faces and JSTL, Facelets also supports Expression Language (EL).

You can find a getting started article about Facelets here: Facelets tutorial: Using Templates

Before JSF 4.0 the only option to use Facelets was to add the single page views in your project. You can now create your views programmatically by extending the Class jakarta.faces.view.facelets.Facelet.

Here is a sample View which creates an HTML form with some basic components:

import static jakarta.faces.application.StateManager.IS_BUILDING_INITIAL_STATE;
import java.io.IOException;
import java.util.List;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.annotation.View;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIOutput;
import jakarta.faces.component.html.*;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.facelets.Facelet;
@View("/demo.xhtml")
@ApplicationScoped
public class DemoFacelet extends Facelet {
@Override
public void apply(FacesContext facesContext, UIComponent root) throws IOException {
if (!facesContext.getAttributes().containsKey(IS_BUILDING_INITIAL_STATE)) {
return;
ComponentBuilder components = new ComponentBuilder(facesContext);
List<UIComponent> rootChildren = root.getChildren();
UIOutput output = new UIOutput();
output.setValue("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
rootChildren.add(output);
HtmlBody body = components.create(HtmlBody.COMPONENT_TYPE);
rootChildren.add(body);
HtmlForm form = components.create(HtmlForm.COMPONENT_TYPE);
form.setId("form");
body.getChildren().add(form);
HtmlOutputLabel label = components.create(HtmlOutputLabel.COMPONENT_TYPE);
label.setValue("Enter your name");
form.getChildren().add(label);
HtmlInputText message = components.create(HtmlInputText.COMPONENT_TYPE);
message.setId("message");
form.getChildren().add(message);
HtmlOutputText text = components.create(HtmlOutputText.COMPONENT_TYPE);
form.getChildren().add(text);
HtmlCommandButton actionButton = components.create(HtmlCommandButton.COMPONENT_TYPE);
actionButton.setId("button");
actionButton.addActionListener(e -> text.setValue("Hi " + message.getValue()));
actionButton.setValue("Do action");
form.getChildren().add(actionButton);
output = new UIOutput();
output.setValue("</html>");
rootChildren.add(output);
private static class ComponentBuilder {
FacesContext facesContext;
ComponentBuilder(FacesContext facesContext) {
this.facesContext = facesContext;
@SuppressWarnings("unchecked")
<T> T create(String componentType) {
return (T) facesContext.getApplication().createComponent(facesContext, componentType, null);
import static jakarta.faces.application.StateManager.IS_BUILDING_INITIAL_STATE;

import java.io.IOException;
import java.util.List;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.faces.annotation.View;
import jakarta.faces.component.UIComponent;
import jakarta.faces.component.UIOutput;
import jakarta.faces.component.html.*;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.facelets.Facelet;


@View("/demo.xhtml")
@ApplicationScoped
public class DemoFacelet extends Facelet {

    @Override
    public void apply(FacesContext facesContext, UIComponent root) throws IOException {
        if (!facesContext.getAttributes().containsKey(IS_BUILDING_INITIAL_STATE)) {
            return;
        }

        ComponentBuilder components = new ComponentBuilder(facesContext);
        List<UIComponent> rootChildren = root.getChildren();

        UIOutput output = new UIOutput();

        output.setValue("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
        rootChildren.add(output);

        HtmlBody body = components.create(HtmlBody.COMPONENT_TYPE);
        rootChildren.add(body);

        HtmlForm form = components.create(HtmlForm.COMPONENT_TYPE);
        form.setId("form");
        body.getChildren().add(form);

        HtmlOutputLabel label = components.create(HtmlOutputLabel.COMPONENT_TYPE);
        label.setValue("Enter your name");
        form.getChildren().add(label);

        HtmlInputText message = components.create(HtmlInputText.COMPONENT_TYPE);
        message.setId("message");
        form.getChildren().add(message);

        HtmlOutputText text = components.create(HtmlOutputText.COMPONENT_TYPE);
        form.getChildren().add(text);

        HtmlCommandButton actionButton = components.create(HtmlCommandButton.COMPONENT_TYPE);
        actionButton.setId("button");
        actionButton.addActionListener(e -> text.setValue("Hi " + message.getValue()));
        actionButton.setValue("Do action");
        form.getChildren().add(actionButton);

        output = new UIOutput();
        output.setValue("</html>");
        rootChildren.add(output);
    }

    private static class ComponentBuilder {
        FacesContext facesContext;

        ComponentBuilder(FacesContext facesContext) {
            this.facesContext = facesContext;
        }

        @SuppressWarnings("unchecked")
        <T> T create(String componentType) {
            return (T) facesContext.getApplication().createComponent(facesContext, componentType, null);
        }
    }
}

As you can see, every HTML Component has a matching Class in the jakarta.faces.component.html package. They can be created through the ComponentBuilder factory or in the standard Java way (“new Class()”). Then, you need to add the Component to the HtmlForm or directly to the HtmlBody.

The most interesting aspect, is that you can add actionlisteners to HTML components (such as press button) directly in Java without any Javascript coding.

Testing the template

To build the example, until the Jakarta EE 10 bundle is out, we will compile with the Jakarta EE 9 dependency and override the jakarta.faces dependency to the version 4.0.0:

<dependencies>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<version>9.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.faces</groupId>
<artifactId>jakarta.faces-api</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
  <dependencies>
    <dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>9.0.0</version>
      <scope>provided</scope>
    </dependency>

    <dependency>
      <groupId>jakarta.faces</groupId>
      <artifactId>jakarta.faces-api</artifactId>
      <version>4.0.0</version>
    </dependency>
  </dependencies>

When run, request the page demo.xhtml which will render our Html form:

Screenshot-from-2022-07-14-18-37-13.png

Uploading Files with JSF 4.0

Uploading files is a built-in features of JSF 2.x. To upload single or multiple files, you can use the <h:inputFile /> component and bind the File name with a CDI Bean. When using JSF 4.0 there is an additional attribute “accept” which specifies the file format which you can use for upload. Before that, you had to include a validator to include or exclude some file formats.

Let’s create a sample index.html page with an inputFile in it:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
xmlns:faces="jakarta.faces"
xmlns:ui="jakarta.faces.facelets"
xmlns:f="jakarta.faces.core"
xmlns:h="jakarta.faces.html"
xmlns:pt="jakarta.faces.passthrough"
xmlns:cc="jakarta.faces.composite"
xmlns:my="jakarta.faces.component"
xmlns:c="jakarta.tags.core"
xmlns:fn="jakarta.tags.functions"
<h:body>
<h2>JSF Basic demo</h2>
<h:form id="formpanel" enctype="multipart/form-data">
<h:panelGrid columns="2" styleClass="default">
<h:inputFile id="inputfile" value="#{uploadBean.part}" accept="image/jpeg,image/png,image/gif" />
<h:commandButton value="Upload"
action="#{uploadBean.uploadFile}"/>
</h:panelGrid>
</h:form>
</h:body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
		"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
		xmlns:faces="jakarta.faces"
		xmlns:ui="jakarta.faces.facelets"
		xmlns:f="jakarta.faces.core"
		xmlns:h="jakarta.faces.html"
		xmlns:pt="jakarta.faces.passthrough"
		xmlns:cc="jakarta.faces.composite"
		xmlns:my="jakarta.faces.component"
		xmlns:c="jakarta.tags.core"
		xmlns:fn="jakarta.tags.functions"
>
<h:body>
	<h2>JSF Basic demo</h2>
	<h:form id="formpanel" enctype="multipart/form-data">
		<h:panelGrid columns="2" styleClass="default">
			<h:inputFile  id="inputfile"   value="#{uploadBean.part}" accept="image/jpeg,image/png,image/gif" />
			<h:commandButton value="Upload"
							 action="#{uploadBean.uploadFile}"/>
		</h:panelGrid>
	</h:form>
</h:body>
</html>

Firstly, notice the html tag which contains the new jakarta namespaces to reference the JSF components.

Next, check the inputFile component which includes the accept attribute with a set of file formats.

Finally, the Form includes a commandButton which starts the File upload.

For the sake of completeness, we will include here also the UploadBean which is referenced by the view:

import jakarta.enterprise.inject.Model;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.servlet.http.Part;
import java.io.*;
@Model
public class UploadBean {
private Part part;
public Part getPart() {
return part;
public void setPart(Part part) {
this.part = part;
public String uploadFile() throws IOException {
// Extract file name from content-disposition header of file part
String fileName = getFileName(part);
String basePath = "/tmp/";
File outputFilePath = new File(basePath + fileName);
// Copy uploaded file to destination path
InputStream inputStream = null;
OutputStream outputStream = null;
inputStream = part.getInputStream();
outputStream = new FileOutputStream(outputFilePath);
int read = 0;
final byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
printMessage("Success! File upload completed!");
} catch (IOException e) {
e.printStackTrace();
printMessage("Error! File upload error!");
} finally {
if (outputStream != null) {
outputStream.close();
if (inputStream != null) {
inputStream.close();
return null;
private void printMessage(String message) {
FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, message, null);
FacesContext.getCurrentInstance().addMessage(null, facesMsg);
private String getFileName(Part part) {
final String partHeader = part.getHeader("content-disposition");
for (String content : part.getHeader("content-disposition").split(";")) {
if (content.trim().startsWith("filename")) {
return content.substring(content.indexOf('=') + 1).trim()
.replace("\"", "");
return null;
import jakarta.enterprise.inject.Model;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.servlet.http.Part;

import java.io.*;

@Model
public class UploadBean   {
    private Part part;

    public Part getPart() {
        return part;
    }

    public void setPart(Part part) {
        this.part = part;
    }
 
 
    public String uploadFile() throws IOException {
        // Extract file name from content-disposition header of file part
        String fileName = getFileName(part);
        String basePath = "/tmp/";
        File outputFilePath = new File(basePath + fileName);
        // Copy uploaded file to destination path
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = part.getInputStream();
            outputStream = new FileOutputStream(outputFilePath);

            int read = 0;
            final byte[] bytes = new byte[1024];
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
            printMessage("Success! File upload completed!");
        } catch (IOException e) {
            e.printStackTrace();
            printMessage("Error! File upload error!");
        } finally {
            if (outputStream != null) {
                outputStream.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        }
        return null;
    }

    private void printMessage(String message) {
        FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_INFO, message, null);
        FacesContext.getCurrentInstance().addMessage(null, facesMsg);
    }

    private String getFileName(Part part) {
        final String partHeader = part.getHeader("content-disposition");
        for (String content : part.getHeader("content-disposition").split(";")) {
            if (content.trim().startsWith("filename")) {
                return content.substring(content.indexOf('=') + 1).trim()
                        .replace("\"", "");
            }
        }
        return null;
    }
}

The Upload bean performs the Upload through the mediation of the jakarta.servlet.http.Part, which contains a reference to the File name.

Here is our JSF File upload in action:

Screenshot-from-2022-07-14-18-48-44.png

Uploading Multiple Files

In order to upload multiple files, one option consists in adding a set of <h:inputFile /> components and bind each one with a Servlet Part. A simpler approach could be to use the multiple=true attribute which is available in JSF 4.0. When this attribute is set to true, you will be able to select multiple files in one shot from the Browse button.

Here is an example:

<h:inputFile value="#{uploadBeanMultiple.files}" accept="image/jpeg,image/png,image/gif" multiple="true" />
<h:inputFile     value="#{uploadBeanMultiple.files}" accept="image/jpeg,image/png,image/gif" multiple="true" />

Then, in the backing Bean, we will reference a List of Part objects and loop through them in the upload method:

@Model
public class UploadBeanMultiple {
private List<Part> files;
public List<Part> getFiles() {
return files;
public void setFiles(List<Part> files) {
this.files = files;
public String uploadFile() throws IOException {
for (Part part : files) {
String fileName = Paths.get(part.getSubmittedFileName()).getFileName().toString();
// Same code here
return null;
@Model
public class UploadBeanMultiple {
    private List<Part> files;

    public List<Part> getFiles() {
        return files;
    }

    public void setFiles(List<Part> files) {
        this.files = files;
    }

    public String uploadFile() throws IOException {

        for (Part part : files) {
            String fileName = Paths.get(part.getSubmittedFileName()).getFileName().toString();
            // Same code here   
        }
        return null;
    }

}

Conclusion

This article is a walk through some Jakarta Faces 4.0 goodies. We will keep updating the article as the application server progresses to the final version which fully supports Jakarta EE 10

References: https://balusc.omnifaces.org/2021/11/whats-new-in-faces-40.html

Post Views: 6

Categories JSF Post navigation


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK