Getting started with JSF 4.0 on WildFly 27

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 5 which contains support for 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
  • The new ClientWindowScope available
  • How to upload single or multiple files using JSF 4.0

Before getting started, it is worth to remind you that since Jakarta EE 9 you have to update the namespace of your packages and application resources from “javax” to “jakarta“.

In terms of application resources, make sure your HTML pages include the following schemas:

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

Next, to run on Jakarta Faces 4.0 must include the following schema declaration in faces-config.xml:

<faces-config
    xmlns="https://jakarta.ee/xml/ns/jakartaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
        https://jakarta.ee/xml/ns/jakartaee/web-facesconfig_4_0.xsd"
    version="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);
        }
    }
}

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 Jakarta Faces 4 application the dependency to include is jakarta.faces-api :

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

On the other hand, you can also build the application by adding the full Jakarta EE 10 dependency:

<dependency>
      <groupId>jakarta.platform</groupId>
      <artifactId>jakarta.jakartaee-api</artifactId>
      <version>10.0.0</version>
      <scope>provided</scope>
</dependency>

Then, build your application and deploy it on WildFly:

mvn install wildfly:deploy

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

Using ClientWindowScoped in your Beans

Jakarta Faces 4. introduces a new scope for managed beans: jakarta.faces.lifecycle.ClientWindowScoped. This is not totally new for JSF developers as it is essentially bound to the lifetime of the existing jakarta.faces.lifecycle.ClientWindow object.

@Named
@ClientWindowScoped()
public class SignupBean implements Serializable {
   //
}

Besides the annotation, you also need to enable CLIENT_WINDOW_MODE in your web.xml otherwise you will get a ContextNotActiveException at runtime:

<context-param>
    <param-name>jakarta.faces.CLIENT_WINDOW_MODE</param-name>
    <param-value>url</param-value>
</context-param>

When you reference a @ClientWindowScoped bean in your page, the request parameter “jfwid” will be created and added to the URL, as you navigate through the pages of your application.

jakarta faces 40

The lifecycle of a @ClientWindowScoped bean will terminate as soon as you open a new browser window or tab on the same URL. It will also terminate if you try to manipulate the value fo the “jfwid” parameter during the navigation. In either case, the JSF engine will create a new @ClientWindowScoped bean.
There is, however, a maximum number of ClientWindow instances per HTTP session, which defaults to ten per session in the Mojarra JSF implementation. You can change the default value through the following context parameter in the web.xml file:

<context-param>
    <param-name>jakarta.faces.NUMBER_OF_CLIENT_WINDOWS</param-name>
    <param-value>50</param-value>
</context-param>

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>

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

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" />

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

}

Conclusion

This article is a walk through some Jakarta Faces 4.0 goodies. We have discussed how to create programmatically Facelets, the new ClientWindowScope available and how to upload single or multiple files using JSF 4.0.

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

Found the article helpful? if so please follow us on Socials