Web applications - component style

Content:

  1. Introduction
  2. ASP.NET Web Forms
    1. C#
    2. VB.NET
  3. ASP.NET Core Blazor
  4. JSF
    1. JSP view
    2. Facelet view
    3. Inverted facelet view
  5. Tapestry
    1. with servlet container
    2. with Spring Boot
  6. Wicket

Introduction:

ASP.NET Web Forms:

ASP.NET Web Forms is probably the most well-known component style web application framework.

Name and creator ASP.NET Web Forms / Microsoft
History Version 1.0 in 2002
Version 2.0 in 2005
Programming language C# or VB.NET
View technology ASPX
Deployment IIS
Other technical characteristics
Status Somewhat obsolete with the introduction of ASP.NET MVC in 2009
Truly obsolete when not included in .NET 5.0 in 2020

An ASP.NET Web Forms application consist of an ASPX file with markup annd a code behind file in either C# or VB.NET.

The code behind get compiled normally. The ASPX get translated into C#/VB.NET code that inherits from the code behind class and then that get compiled.

C#:

index.aspx:

<%@ Page Language="C#" Inherits="Demo.Test" %>
<html>
<head>
<title>ASP.NET Web Forms</title>
</head>
<body>
<h1>ASP.NET Web Forms</h1>
<form runat="server">
<h2>Show data (ListView):</h2>
<asp:ListView id="Data1" runat="server">
    <LayoutTemplate>
        <table border="1">
            <tr>
                <th>F1</th>
                <th>F2</th>
                <th>F3</th>
            </tr>
            <tr runat="server" id="itemPlaceholder"/>
        </table>
    </LayoutTemplate>
    <ItemTemplate>
        <tr runat="server">
            <td><%# Eval("F1") %></td>
            <td><%# Eval("F2") %></td>
            <td><%# Eval("F3") %></td>
        </tr>
    </ItemTemplate>
</asp:ListView>
<h2>Show data (Repeater):</h2>
<table border='1'>
    <tr>
        <th>F1</th>
        <th>F2</th>
        <th>F3</th>
    </tr>
    <asp:Repeater id="Data2" runat="server">
        <ItemTemplate>
            <tr runat="server">
                <td><%# Eval("F1") %></td>
                <td><%# Eval("F2") %></td>
                <td><%# Eval("F3") %></td>
            </tr>
        </ItemTemplate>
    </asp:Repeater>
</table>
<h2>Add data:</h2>
F1: <asp:TextBox id="F1" runat="server"/>
<br/>
F2: <asp:TextBox id="F2" runat="server"/>
<br/>
F3: <asp:DropDownList id="F3" runat="server"/>
<br/>
<asp:Button id="Save" text="Add" onclick="Save_Click" runat="server"/>
</form>
</body>
</html>

index.cs (code behind):

using System;
using System.Collections.Generic;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Demo
{
    public class Test : Page
    {
        // components
        protected ListView Data1;
        protected Repeater Data2;
        protected TextBox F1;
        protected TextBox F2;
        protected DropDownList F3;
        protected Button Save;
        // bind all data for show
        private void BindShowData()
        {
            Data1.DataSource = DB.GetAll();
            Data1.DataBind();
            Data2.DataSource = DB.GetAll();
            Data2.DataBind();
        }
        // setup page
        public void Page_Load(Object src, EventArgs e)
        {
            if (!IsPostBack)
            {
                F3.DataSource = new string[] { "", T1.VER, T1.NOTVER };
                F3.DataBind();
            }
            BindShowData();
        }
        // form submit
        public void Save_Click(object sender, EventArgs e)
        {
            DB.SaveOne(new T1 { F1 = int.Parse(F1.Text), F2 = F2.Text, F3 = F3.Text });
            BindShowData();
        }
    }
}

DB.cs (simulated database):

using System;
using System.Collections.Generic;

namespace Demo
{
    // data class (row in database)
    public class T1
    {
        public const String VER = "Verified";
        public const String NOTVER = "Not verified";
        public int F1 { get; set; }
        public string F2 { get; set; }
        public string F3 { get; set; }
    }
    // simulated database
    public class DB
    {
        private static IList<T1> db;
        static DB()
        {
            db  = new List<T1>();
            db.Add(new T1 { F1 = 1, F2 = "A", F3 = T1.VER });
            db.Add(new T1 { F1 = 2, F2 = "BB", F3 = T1.VER });
            db.Add(new T1 { F1 = 3, F2 = "CCC", F3 = T1.VER });
            db.Add(new T1 { F1 = 4, F2 = "DDDD", F3 = T1.VER });
            db.Add(new T1 { F1 = 5, F2 = "EEEEE", F3 = T1.NOTVER });
        }
        public static IList<T1> GetAll()
        {
            return db;
        }
        public static void SaveOne(T1 o)
        {
            db.Add(o);
        }
    }
}

For the .cs files either the source should be placed in App_Code dir or a build assembly should be placed in bin dir.

Nothing is needed in web.config for ASP.NET Web Forms. But for convenience this example use:

    <system.webServer>  
        <defaultDocument enabled="true">  
            <files>  
                <add value="index.aspx" />  
             </files>  
        </defaultDocument>   
    </system.webServer>  

The result looks like:

ASP.NET Web Forms screen

VB.NET:

index.aspx:

<%@ Page Language="VB" Inherits="Demo.Test" %>
<html>
<head>
<title>ASP.NET Web Forms (VB.NET)</title>
</head>
<body>
<h1>ASP.NET Web Forms (VB.NET)</h1>
<form runat="server">
<h2>Show data (ListView):</h2>
<asp:ListView id="Data1" runat="server">
    <LayoutTemplate>
        <table border="1">
            <tr>
                <th>F1</th>
                <th>F2</th>
                <th>F3</th>
            </tr>
            <tr runat="server" id="itemPlaceholder"/>
        </table>
    </LayoutTemplate>
    <ItemTemplate>
        <tr runat="server">
            <td><%# Eval("F1") %></td>
            <td><%# Eval("F2") %></td>
            <td><%# Eval("F3") %></td>
        </tr>
    </ItemTemplate>
</asp:ListView>
<h2>Show data (Repeater):</h2>
<table border='1'>
    <tr>
        <th>F1</th>
        <th>F2</th>
        <th>F3</th>
    </tr>
    <asp:Repeater id="Data2" runat="server">
        <ItemTemplate>
            <tr runat="server">
                <td><%# Eval("F1") %></td>
                <td><%# Eval("F2") %></td>
                <td><%# Eval("F3") %></td>
            </tr>
        </ItemTemplate>
    </asp:Repeater>
</table>
<h2>Add data:</h2>
F1: <asp:TextBox id="F1" runat="server"/>
<br/>
F2: <asp:TextBox id="F2" runat="server"/>
<br/>
F3: <asp:DropDownList id="F3" runat="server"/>
<br/>
<asp:Button id="Save" text="Add" onclick="Save_Click" runat="server"/>
</form>
</body>
</html>

index.vb (code behind):

Imports System
Imports System.Collections.Generic
Imports System.Web.UI
Imports System.Web.UI.WebControls

Namespace Demo
    Public Class Test
        Inherits Page
        ' components
        Protected Data1 As ListView
        Protected Data2 As Repeater
        Protected F1 As TextBox
        Protected F2 As TextBox
        Protected F3 As DropDownList
        Protected Save As Button
        ' bind all data for show
        Private Sub BindShowData()
            Data1.DataSource = DB.GetAll()
            Data1.DataBind()
            Data2.DataSource = DB.GetAll()
            Data2.DataBind()
        End Sub
        ' setup page
        Public Sub Page_Load(src As [Object], e As EventArgs)
            If Not IsPostBack Then
                F3.DataSource = New String() {"", T1.VER, T1.NOTVER}
                F3.DataBind()
            End If
            BindShowData()
        End Sub
        ' form submit
        Public Sub Save_Click(sender As Object, e As EventArgs)
            DB.SaveOne(New T1() With { _
                .F1 = Integer.Parse(F1.Text), _
                .F2 = F2.Text, _
                .F3 = F3.Text _
            })
            BindShowData()
        End Sub
    End Class
End Namespace

DB.vb (simulated database):

Imports System
Imports System.Collections.Generic

Namespace Demo
    ' data class (row in database)
    Public Class T1
        Public Const VER As String = "Verified"
        Public Const NOTVER As String = "Not verified"
        Public Property F1() As Integer
        Public Property F2() As String
        Public Property F3() As String
    End Class
    ' simulated database
    Public Class DB
        Private Shared db As IList(Of T1)
        Shared Sub New()
            db = New List(Of T1)()
            db.Add(New T1() With { _
                .F1 = 1, _
                .F2 = "A", _
                .F3 = T1.VER _
            })
            db.Add(New T1() With { _
                .F1 = 2, _
                .F2 = "BB", _
                .F3 = T1.VER _
            })
            db.Add(New T1() With { _
                .F1 = 3, _
                .F2 = "CCC", _
                .F3 = T1.VER _
            })
            db.Add(New T1() With { _
                .F1 = 4, _
                .F2 = "DDDD", _
                .F3 = T1.VER _
            })
            db.Add(New T1() With { _
                .F1 = 5, _
                .F2 = "EEEEE", _
                .F3 = T1.NOTVER _
            })
        End Sub
        Public Shared Function GetAll() As IList(Of T1)
            Return db
        End Function
        Public Shared Sub SaveOne(o As T1)
            db.Add(o)
        End Sub
    End Class
End Namespace

For the .vb files either the source should be placed in App_Code dir or a build assembly should be placed in bin dir.

Nothing is needed in web.config for ASP.NET Web Forms. But for convenience this example use:

    <system.webServer>  
        <defaultDocument enabled="true">  
            <files>  
                <add value="index.aspx" />  
             </files>  
        </defaultDocument>   
    </system.webServer>  

The result looks like:

ASP.NET Web Forms VB.NET screen

ASP.NET Core Blazor:

Blazor is a new Microsoft web technology.

Actually Blazor exist in several flavors including Blazor Server and Blazor WebAssembly.

Here we will focus on Blazor Server.

Name and creator ASP.NET Core Blazor Server / Microsoft
History First released with .NET Core 3.0 in 2019
Programming language C#
View technology Razor
Deployment ASP.NET Core Kestrel engine
Other technical characteristics Blazor automatically use CSS and JavaScript so look and feel is not "plain HTML"
Status New technology and not widely used yet

In Blazor everything is in the Razor file both HTML markup and C#.

index.razor:

@page "/"

<html>
<head>
<title>Blazor</title>
</head>
<body>
<h1>Blazor</h1>
<h2>Show data:</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
@foreach (var o in data)
{
    <tr>
    <td>@o.F1</td>
    <td>@o.F2</td>
    <td>@o.F3</td>
    </tr>
}
</table>
<h2>Add data:</h2>
<EditForm Model="@o" OnSubmit="@Save">
<label for="f1">F1:</label><InputText id="f1" @bind-Value="o.F1"/>
<br/>
<label for="f2">F2:</label><InputText id="f2" @bind-Value="o.F2"/>
<br/>
<label for="f3">F3:</label><select id="f3" @bind="o.F3">
@foreach(var opt in options)
{
    <option value="@opt">@opt</option>
}
</select>
<br/>
<button type="submit">Add</button>
</EditForm>
</body>
</html>

@code
{
    // class for add data
    private class FormData
    {
        public string F1 { get; set; }
        public string F2 { get; set; }
        public string F3 { get; set; }
    }
    // options
    private String[] options = { "", T1.VER, T1.NOTVER };
    // data to show
    private IList<T1> data = DB.GetAll();
    // data to add
    private FormData o = new FormData { F1 = "", F2 = "", F3 = "" };
    // form submit
    private void Save()
    {
        DB.SaveOne(new T1 { F1 = int.Parse(o.F1), F2 = o.F2, F3 = o.F3 });
    }
}

DB.cs (simulated database):

using System;
using System.Collections.Generic;

namespace Demo
{
    // data class (row in database)
    public class T1
    {
        public const String VER = "Verified";
        public const String NOTVER = "Not verified";
        public int F1 { get; set; }
        public string F2 { get; set; }
        public string F3 { get; set; }
    }
    // simulated database
    public class DB
    {
        private static IList<T1> db;
        static DB()
        {
            db  = new List<T1>();
            db.Add(new T1 { F1 = 1, F2 = "A", F3 = T1.VER });
            db.Add(new T1 { F1 = 2, F2 = "BB", F3 = T1.VER });
            db.Add(new T1 { F1 = 3, F2 = "CCC", F3 = T1.VER });
            db.Add(new T1 { F1 = 4, F2 = "DDDD", F3 = T1.VER });
            db.Add(new T1 { F1 = 5, F2 = "EEEEE", F3 = T1.NOTVER });
        }
        public static IList<T1> GetAll()
        {
            return db;
        }
        public static void SaveOne(T1 o)
        {
            db.Add(o);
        }
    }
}

program.cs (the main program with the embedded web server):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;

namespace Demo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // start Kestrel with Startup config
            Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(b =>
            {
                b.UseStartup<Startup>();
            }).Build().Run();
        }
    }
    // define razor and server size blazor
    public class Startup
    {
        public IConfiguration Configuration { get; }
        public Startup(IConfiguration cfg)
        {
            Configuration = cfg;
        }
        public void ConfigureServices(IServiceCollection svc)
        {
            svc.AddRazorPages();
            svc.AddServerSideBlazor();
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseStaticFiles();
            app.UseRouting();
            app.UseEndpoints(ep =>
            {
                ep.MapBlazorHub();
                ep.MapFallbackToPage("/_Host");
            });
        }
    }
}

An ASP.NET Core Blazor Server project can be created command line with:

dotnet new blazorserver

The result looks like:

ASP.NET Core Blazor screen

JSF:

JSF (Java Server Faces) is the Java worlds version of component style web application framnework.

JSF is really a standard and not a product. There are multiple JSF implementations.

Implementations include Mojarra and Apache MyFaces.

There is a complete article dedicated to JSF here.

Name and creator JSF / Java EE standard
History Version 1.0 in 2004
Version 1.2 in 2006
Version 2.0 in 2010
Version 3.0 in 2020 rename to JSF (Jakarta Server Faces)
Version 4.0 in 2022 rename to JF (Jakarta Faces)
Programming language Java
View technology JSP
Facelet
Deployment Servlet container
Other technical characteristics
Status Actively maintained but limited usage today

JSF is a complicated technology.

The code is in Java classes called backing beans or managed beans. And the view is separate. But there is no simple relationship between view and backing bean. Backing beans just exist in the web application.

Furthermore lots of configuration required (web application, JSF, CDI etc.).

And there are different choices for view technology including:

JSP
traditional JSP, deprecated in version 2.0, dropped in version 4.0
Facelet
XHTML based, preferred technology
Inverted facelet
Facelet that is viewable as standalone XHTML

But first the non-view part that is common for all views.

T1.java (data class):

package demo;

import java.io.Serializable;

// data class (row in database)
public class T1 implements Serializable {
    public static final String VER = "Verified";
    public static final String NOTVER = "Not verified";
    private int f1;
    private String f2;
    private String f3;
    public T1() {
        this(0, "", "");
    }
    public T1(int f1, String f2, String f3) {
        this.f1 = f1;
        this.f2 = f2;
        this.f3 = f3;
    }
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
    public String getF3() {
        return f3;
    }
    public void setF3(String f3) {
        this.f3 = f3;
    }
}

DB.java (simulated database):

package demo;

import java.util.ArrayList;
import java.util.List;

// simulated database
public class DB {
    private static List<T1> db;
    static {
        db = new ArrayList<T1>();
        db.add(new T1(1,"A", T1.VER));
        db.add(new T1(2,"BB", T1.VER));
        db.add(new T1(3,"CCC", T1.VER));
        db.add(new T1(4,"DDDD", T1.VER));
        db.add(new T1(5,"EEEEE", T1.NOTVER));
    }
    public static List<T1> getAll() {
        return db;
    }
    public static void saveOne(T1 o) {
        db.add(o);
    }
}

Test.java (backing bean):

package demo;

import java.util.ArrayList;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

@ManagedBean
@RequestScoped
public class Test {
    // form fields
    private Integer f1;
    private String f2;
    private String f3;
    public Integer getF1() {
        return f1;
    }
    public void setF1(Integer f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
    public String getF3() {
        return f3;
    }
    public void setF3(String f3) {
        this.f3 = f3;
    }
    // options list
    public List<String> getOptions() {
        List<String> res = new ArrayList<String>();
        res.add("");
        res.add(T1.VER);
        res.add(T1.NOTVER);
        return res;
    }
    // show data
    public List<T1> getData() {
        return DB.getAll();
    }
    // form submit
    public void save() {
        DB.saveOne(new T1(f1, f2, f3));
    }
}

web.xml (configuration of web application):

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <!-- setup JSF -->
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!--  use .jsf extension (foobar.jsf will use foobar.jsp or foobar.xhtml as view) -->
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.jsf</url-pattern>
    </servlet-mapping>
    <!-- instruct JSF to store state server side not client side -->
    <context-param>
        <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
        <param-value>server</param-value>
    </context-param>
    <!-- enable dependency injection (necessary Tomcat, not needed JBoss) -->
    <resource-env-ref>
        <resource-env-ref-name>BeanManager</resource-env-ref-name>
        <resource-env-ref-type>javax.enterprise.inject.spi.BeanManager</resource-env-ref-type>
    </resource-env-ref>
    <!-- make index.jsf default -->
    <welcome-file-list>  
        <welcome-file>index.jsf</welcome-file>  
    </welcome-file-list>  
</web-app>

faces-config.xml (configuration of JSF):

<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
              xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
              version="2.0">
</faces-config>

Note: this configuration file is empty, but the file must be there. And it can be used for advanced configuration.

Context.xml (adds support for CDI in Tomcat):

<Context>
    <Resource name="BeanManager" 
              auth="Container"
              type="javax.enterprise.inject.spi.BeanManager"
              factory="org.jboss.weld.resources.ManagerObjectFactory" />
</Context>

beans.xml (required for CDI):

<?xml version="1.0" encoding="UTF-8"?>
<beans 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_1_1.xsd"
       bean-discovery-mode="annotated">
</beans>

JSP view:

JSP is the Java standard web page technology.

JSP is covered in detail in Java EE tricks.

index.jsp:

<%@ page language="java" %>
<%@ page contentType="text/html" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>
<html>
<head>
<title>JSF with JSP</title>
</head>
<body>
<f:view>
<h1>JSF with JSP</h1>
<h2>Show data (dataTable):</h2>
<h:dataTable value="#{test.data}" var="o" border="1">
<h:column><f:facet name="header"><h:outputText value="F1"/></f:facet><h:outputText value="#{o.f1}"/></h:column>
<h:column><f:facet name="header"><h:outputText value="F2"/></f:facet><h:outputText value="#{o.f2}"/></h:column>
<h:column><f:facet name="header"><h:outputText value="F3"/></f:facet><h:outputText value="#{o.f3}"/></h:column>
</h:dataTable>
<h2>Show data (forEach):</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<c:forEach items="#{test.data}" var="o">
<tr>
<td><h:outputText value="#{o.f1}"/></td>
<td><h:outputText value="#{o.f2}"/></td>
<td><h:outputText value="#{o.f3}"/></td>
</tr>
</c:forEach>
</table>
<h2>Add data:</h2>
<h:form>
F1: <h:inputText value="#{test.f1}"/>
<br>
F2: <h:inputText value="#{test.f2}"/>
<br>
F3: <h:selectOneMenu value="#{test.f3}"><f:selectItems value="#{test.options}"/></h:selectOneMenu>
<br>
<h:commandButton value="Add" action="#{test.save}"/>
</h:form>
</f:view>
</body>
</html>

Packaging of war file:

     0 Thu Dec 01 13:49:40 EST 2022 META-INF/
   109 Thu Dec 01 13:49:38 EST 2022 META-INF/MANIFEST.MF
     0 Thu Dec 01 13:49:40 EST 2022 WEB-INF/
  1375 Mon Dec 27 19:30:16 EST 2021 WEB-INF/web.xml
     0 Thu Dec 01 13:49:40 EST 2022 WEB-INF/classes/
     0 Thu Dec 01 13:49:40 EST 2022 WEB-INF/classes/demo/
   762 Thu Dec 01 13:49:40 EST 2022 WEB-INF/classes/demo/DB.class
   746 Thu Dec 01 13:49:40 EST 2022 WEB-INF/classes/demo/T1.class
  1289 Thu Dec 01 13:49:40 EST 2022 WEB-INF/classes/demo/Test.class
  1420 Wed Nov 23 19:46:08 EST 2022 index.jsp
   317 Mon Jul 01 22:09:18 EDT 2019 WEB-INF/beans.xml
   300 Mon Dec 27 19:12:44 EST 2021 WEB-INF/faces-config.xml
   226 Tue Jul 02 20:02:18 EDT 2019 META-INF/Context.xml

The result looks like:

JSF JSP view screen

Facelet view:

Facelets are XHTML with special JSF tags.

index.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>JSF with facelet</title>
</h:head>
<h:body>
<f:view>
<h1>JSF with facelet</h1>
<h2>Show data (dataTable):</h2>
<h:dataTable value="#{test.data}" var="o" border="1">
<h:column><f:facet name="header">F1</f:facet>#{o.f1}</h:column>
<h:column><f:facet name="header">F2</f:facet>#{o.f2}</h:column>
<h:column><f:facet name="header">F3</f:facet>#{o.f3}</h:column>
</h:dataTable>
<h2>Show data (repeat):</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<ui:repeat value="#{test.data}" var="o">
<tr>
<td><h:outputText value="#{o.f1}"/></td>
<td><h:outputText value="#{o.f2}"/></td>
<td><h:outputText value="#{o.f3}"/></td>
</tr>
</ui:repeat>
</table>
<h2>Add data:</h2>
<h:form>
F1: <h:inputText value="#{test.f1}"/>
<br/>
F2: <h:inputText value="#{test.f2}"/>
<br/>
F3: <h:selectOneMenu value="#{test.f3}"><f:selectItems value="#{test.options}"/></h:selectOneMenu>
<br/>
<h:commandButton value="Add" action="#{test.save}"/>
</h:form>
</f:view>
</h:body>
</html>

Packaging of war file:

     0 Thu Dec 01 13:59:40 EST 2022 META-INF/
   109 Thu Dec 01 13:59:38 EST 2022 META-INF/MANIFEST.MF
     0 Thu Dec 01 13:59:40 EST 2022 WEB-INF/
  1375 Mon Dec 27 19:30:16 EST 2021 WEB-INF/web.xml
     0 Thu Dec 01 13:59:40 EST 2022 WEB-INF/classes/
     0 Thu Dec 01 13:59:40 EST 2022 WEB-INF/classes/demo/
   762 Thu Dec 01 13:59:40 EST 2022 WEB-INF/classes/demo/DB.class
   746 Thu Dec 01 13:59:40 EST 2022 WEB-INF/classes/demo/T1.class
  1289 Thu Dec 01 13:59:40 EST 2022 WEB-INF/classes/demo/Test.class
  1220 Wed Nov 23 19:46:46 EST 2022 index.xhtml
   317 Mon Jul 01 22:09:18 EDT 2019 WEB-INF/beans.xml
   300 Mon Dec 27 19:12:44 EST 2021 WEB-INF/faces-config.xml
   226 Tue Jul 02 20:02:18 EDT 2019 META-INF/Context.xml

The result looks like:

JSF facelet view screen

Inverted facelet view:

Facelets can be written so they are more like plain XHTML by replacing JSF tags with jsfc attributes on XHTML tags.

In theory that makes it possible for HTML designers to work with the pages offline.

index.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<head>
<title>JSF with inverted facelet</title>
</head>
<body>
<h1>JSF with inverted facelet</h1>
<h2>Show data (dataTable):</h2>
<table jsfc="h:dataTable" value="#{test.data}" var="o" border="1">
<tr>
<th jsfc="h:column"><span jsfc="f:facet" name="header">F1</span>#{o.f1}</th>
<th jsfc="h:column"><span jsfc="f:facet" name="header">F2</span>#{o.f2}</th>
<th jsfc="h:column"><span jsfc="f:facet" name="header">F3</span>#{o.f3}</th>
</tr>
<tr jsfc="ui:remove">
<td>1</td>
<td>A</td>
<td>X</td>
</tr>
<tr jsfc="ui:remove">
<td>2</td>
<td>B</td>
<td>X</td>
</tr>
</table>
<h2>Show data (repeat):</h2>
<table border="1">
<tr>
<th>F1</th>
<th>F2</th>
<th>F3</th>
</tr>
<tr jsfc="ui:repeat" value="#{test.data}" var="o">
<td><span jsfc="h:outputText" value="#{o.f1}"/></td>
<td><span jsfc="h:outputText" value="#{o.f2}"/></td>
<td><span jsfc="h:outputText" value="#{o.f3}"/></td>
</tr>
<tr jsfc="ui:remove">
<td>1</td>
<td>A</td>
<td>X</td>
</tr>
<tr jsfc="ui:remove">
<td>2</td>
<td>B</td>
<td>X</td>
</tr>
</table>
<h2>Add data:</h2>
<form jsfc="h:form">
F1: <input type="text" jsfc="h:inputText" value="#{test.f1}"/>
<br/>
F2: <input type="text" jsfc="h:inputText" value="#{test.f2}"/>
<br/>
F3: <select jsfc="h:selectOneMenu" value="#{test.f3}"><span jsfc="f:selectItems" value="#{test.options}"></span></select>
<br/>
<h:commandButton value="Add" action="#{test.save}"/>
</form>
</body>
</html>

Packaging of war file:

     0 Thu Dec 01 14:03:16 EST 2022 META-INF/
   109 Thu Dec 01 14:03:14 EST 2022 META-INF/MANIFEST.MF
     0 Thu Dec 01 14:03:16 EST 2022 WEB-INF/
  1375 Mon Dec 27 19:30:16 EST 2021 WEB-INF/web.xml
     0 Thu Dec 01 14:03:16 EST 2022 WEB-INF/classes/
     0 Thu Dec 01 14:03:14 EST 2022 WEB-INF/classes/demo/
   762 Thu Dec 01 14:03:14 EST 2022 WEB-INF/classes/demo/DB.class
   746 Thu Dec 01 14:03:14 EST 2022 WEB-INF/classes/demo/T1.class
  1289 Thu Dec 01 14:03:14 EST 2022 WEB-INF/classes/demo/Test.class
  1641 Wed Nov 23 19:49:52 EST 2022 index.xhtml
   317 Mon Jul 01 22:09:18 EDT 2019 WEB-INF/beans.xml
   300 Mon Dec 27 19:12:44 EST 2021 WEB-INF/faces-config.xml
   226 Tue Jul 02 20:02:18 EDT 2019 META-INF/Context.xml

The result looks like:

JSF inverted facelet view screen

Tapestry:

Tapestry is an old but not very well-known component style web application framework for Java.

Name and creator Apache Tapestry / open source
History Version 2.0 in 2002(!)
Moved to Apache in 2003 with version 3.0
Version 5.0 in 2008
Programming language Java
View technology TML
Deployment Servlet container
Spring Boot
Other technical characteristics Tapestry automatically use CSS and JavaScript so look and feel is not "plain HTML"
Status Actively maintained, not widely used

Index.tml (the view):

<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_4.xsd" xmlns:p="tapestry:parameter">
<head>
<title>Tapestry</title>
</head>
<body>
<h1>Tapestry</h1>
<h2>Show data:</h2>
<t:grid source="data">
</t:grid>
<h2>Add data:</h2>
<t:form t:id="addForm">
<t:label for="f1">F1:</t:label><t:textfield t:id="f1"/>
<t:label for="f2">F2:</t:label><t:textfield t:id="f2"/>
<t:label for="f3">F3:</t:label><t:select t:id="f3" model="options"/>
<t:submit value="Add"/>
</t:form>
</body>
</html>

Note how short it is.

Index.java (Java code behind the TML view):

package demo.pages;

import java.util.List;
import java.util.ArrayList;

import org.apache.tapestry5.annotations.*;
import org.apache.tapestry5.corelib.components.Form;

import demo.T1;
import demo.DB;

public class Index {
    // form fields
    @Persist
    @Property
    private String f1;
    @Persist
    @Property
    private String f2;
    @Persist
    @Property
    private String f3;
    @Component
    private Form addForm;
    @Property
    private List<String> options;
    // define form
    public Index() {
        options = new ArrayList<String>();
        options.add(T1.VER);
        options.add(T1.NOTVER);
    }
    // show data
    public List<T1> getData() {
        return DB.getAll();
    }
    // form submit
    public void onValidateFromAddForm() {
        DB.saveOne(new T1(Integer.parseInt(f1), f2, f3));
    }
}

TestModule.java:

package demo.services;

public class TestModule {
}

T1.java (data class):

package demo;

import java.io.Serializable;

// data class (row in database)
public class T1 implements Serializable {
    public static final String VER = "Verified";
    public static final String NOTVER = "Not verified";
    private int f1;
    private String f2;
    private String f3;
    public T1() {
        this(0, "", "");
    }
    public T1(int f1, String f2, String f3) {
        this.f1 = f1;
        this.f2 = f2;
        this.f3 = f3;
    }
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
    public String getF3() {
        return f3;
    }
    public void setF3(String f3) {
        this.f3 = f3;
    }
}

DB.java (simulated database):

package demo;

import java.util.ArrayList;
import java.util.List;

// simulated database
public class DB {
    private static List<T1> db;
    static {
        db = new ArrayList<T1>();
        db.add(new T1(1,"A", T1.VER));
        db.add(new T1(2,"BB", T1.VER));
        db.add(new T1(3,"CCC", T1.VER));
        db.add(new T1(4,"DDDD", T1.VER));
        db.add(new T1(5,"EEEEE", T1.NOTVER));
    }
    public static List<T1> getAll() {
        return db;
    }
    public static void saveOne(T1 o) {
        db.add(o);
    }
}

with servlet container:

web.xml (configuring the web application):

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <!-- setup Tapestry -->
    <context-param>
        <param-name>tapestry.app-package</param-name>
        <param-value>demo</param-value>
    </context-param>
    <filter>
        <filter-name>Test</filter-name>
        <filter-class>org.apache.tapestry5.TapestryFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Test</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Packaging of war file:

     0 Thu Dec 01 19:31:10 EST 2022 META-INF/
   109 Thu Dec 01 19:31:08 EST 2022 META-INF/MANIFEST.MF
     0 Thu Dec 01 19:31:10 EST 2022 WEB-INF/
   705 Wed Dec 29 20:28:40 EST 2021 WEB-INF/web.xml
     0 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/
     0 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/demo/
   762 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/demo/DB.class
   746 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/demo/T1.class
     0 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/demo/pages/
  1185 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/demo/pages/Index.class
     0 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/demo/services/
   139 Thu Dec 01 19:31:10 EST 2022 WEB-INF/classes/demo/services/TestModule.class
     0 Thu Dec 01 19:31:10 EST 2022 WEB-INF/lib/
163650 Wed Dec 29 20:23:58 EST 2021 WEB-INF/lib/antlr-runtime-3.3.jar
133776 Wed Dec 29 20:23:32 EST 2021 WEB-INF/lib/beanmodel-5.7.3.jar
116058 Wed Dec 29 20:23:34 EST 2021 WEB-INF/lib/commons-5.7.3.jar
 73098 Wed Dec 29 20:23:58 EST 2021 WEB-INF/lib/commons-codec-1.5.jar
556550 Wed Dec 29 20:23:36 EST 2021 WEB-INF/lib/plastic-5.7.3.jar
 41139 Wed Jun 19 21:59:36 EDT 2019 WEB-INF/lib/slf4j-api-1.7.26.jar
  8476 Wed Jun 19 21:59:36 EDT 2019 WEB-INF/lib/slf4j-jdk14-1.7.26.jar
3003207 Wed Dec 29 20:23:42 EST 2021 WEB-INF/lib/tapestry-core-5.7.3.jar
 65642 Wed Dec 29 20:23:48 EST 2021 WEB-INF/lib/tapestry-func-5.7.3.jar
 64920 Wed Dec 29 20:23:48 EST 2021 WEB-INF/lib/tapestry-http-5.7.3.jar
437959 Wed Dec 29 20:23:50 EST 2021 WEB-INF/lib/tapestry-ioc-5.7.3.jar
 40232 Wed Dec 29 20:23:52 EST 2021 WEB-INF/lib/tapestry-json-5.7.3.jar
 28313 Wed Dec 29 20:23:56 EST 2021 WEB-INF/lib/tapestry5-annotations-5.7.3.jar
   504 Wed Nov 23 19:58:52 EST 2022 WEB-INF/classes/demo/pages/Index.tml

The result looks like:

Tapestry view screen

with Spring Boot:

App.java (main program):

package demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;

@SpringBootApplication
public class App extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(AppConfiguration.class);
    }
    public static void main(String[] args) throws Exception {
        SpringApplication application = new SpringApplication(App.class);
        application.setApplicationContextClass(AnnotationConfigWebApplicationContext.class);
        SpringApplication.run(App.class, args);
    }
}

AppConfiguration.java (configuration):

package demo;

import org.apache.tapestry5.TapestryFilter;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

import javax.servlet.DispatcherType;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.SessionTrackingMode;
import java.util.EnumSet;

@Configuration
@ComponentScan({ "demo" })
public class AppConfiguration
{
    @Bean
    public ServletContextInitializer initializer() {
        return new ServletContextInitializer() {
            @Override
            public void onStartup(ServletContext servletContext) throws ServletException {
                servletContext.setInitParameter("tapestry.app-package", "demo");
                servletContext.setInitParameter("tapestry.development-modules", "demo.services.TestModule");
                //servletContext.setInitParameter("tapestry.use-external-spring-context", "true");
                servletContext.addFilter("app", TapestryFilter.class).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR), false, "/*");
                //servletContext.addFilter("app", TapestrySpringFilter.class).addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST, DispatcherType.ERROR), false, "/*");
                servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
            }
        };
    }
    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/error404"));
        return factory;
    }
}

For Spring Boot deployment just use Maven for directory structure and build the jar file.

Maven generated jar file:

     0 Thu Dec 01 19:38:24 EST 2022 META-INF/
   492 Thu Dec 01 19:38:24 EST 2022 META-INF/MANIFEST.MF
     0 Thu Dec 01 19:38:24 EST 2022 org/
     0 Thu Dec 01 19:38:24 EST 2022 org/springframework/
     0 Thu Dec 01 19:38:24 EST 2022 org/springframework/boot/
     0 Thu Dec 01 19:38:24 EST 2022 org/springframework/boot/loader/
     0 Thu Dec 01 19:38:24 EST 2022 org/springframework/boot/loader/data/
  2688 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/data/RandomAccessDataFile$DataInputStream.class
     0 Thu Dec 01 19:38:24 EST 2022 org/springframework/boot/loader/jar/
  5267 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/CentralDirectoryFileHeader.class
  3263 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/data/RandomAccessDataFile$FileAccess.class
     0 Thu Dec 01 19:38:24 EST 2022 org/springframework/boot/loader/archive/
  1487 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/ExplodedArchive$FileEntryIterator$EntryComparator.class
  3837 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/ExplodedArchive$FileEntryIterator.class
   282 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/data/RandomAccessDataFile$1.class
  3116 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/CentralDirectoryEndRecord.class
 11548 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/Handler.class
  5243 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/ExplodedArchive.class
  4015 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/data/RandomAccessDataFile.class
  4624 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/CentralDirectoryParser.class
  1813 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/ZipInflaterInputStream.class
   302 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/Archive$Entry.class
   273 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/ExplodedArchive$1.class
   485 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/data/RandomAccessData.class
   437 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/Archive$EntryFilter.class
  7336 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/JarFileArchive.class
  1953 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/PropertiesLauncher$PrefixMatchingArchiveFilter.class
  1484 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/PropertiesLauncher$ArchiveEntryFilter.class
   266 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/PropertiesLauncher$1.class
 19737 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/PropertiesLauncher.class
  4684 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/Launcher.class
  1502 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/MainMethodRunner.class
  3608 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/ExecutableArchiveLauncher.class
  1721 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/WarLauncher.class
  1585 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/JarLauncher.class
     0 Thu Dec 01 19:38:24 EST 2022 org/springframework/boot/loader/util/
  5203 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/util/SystemPropertyUtils.class
  1535 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/LaunchedURLClassLoader$UseFastConnectionExceptionsEnumeration.class
  5699 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/LaunchedURLClassLoader.class
   616 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/Bytes.class
   702 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarURLConnection$1.class
  1779 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/JarFileArchive$EntryIterator.class
  4306 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarURLConnection$JarEntryName.class
  1081 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/JarFileArchive$JarFileEntry.class
  9854 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarURLConnection.class
   945 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/Archive.class
  1233 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarFile$2.class
  2062 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarFile$1.class
  1374 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarFile$JarFileType.class
 15076 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarFile.class
  4976 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/AsciiBytes.class
  1593 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarFileEntries$1.class
  2046 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarFileEntries$EntryIterator.class
   540 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/CentralDirectoryVisitor.class
   299 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarEntryFilter.class
  3619 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarEntry.class
   345 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/FileHeader.class
  3650 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/StringSequence.class
 14087 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/jar/JarFileEntries.class
  1102 Fri Feb 15 10:26:10 EST 2019 org/springframework/boot/loader/archive/ExplodedArchive$FileEntry.class
     0 Thu Dec 01 19:38:24 EST 2022 BOOT-INF/
     0 Thu Dec 01 19:38:24 EST 2022 BOOT-INF/classes/
     0 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/
     0 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/components/
     0 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/pages/
     0 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/services/
     0 Thu Dec 01 19:38:18 EST 2022 META-INF/maven/
     0 Thu Dec 01 19:38:18 EST 2022 META-INF/maven/demo/
     0 Thu Dec 01 19:38:18 EST 2022 META-INF/maven/demo/Test/
    27 Thu Dec 01 19:38:14 EST 2022 BOOT-INF/classes/application.yml
   283 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/services/TestModule.class
  1238 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/T1.class
  1560 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/App.class
    76 Wed Nov 23 20:12:14 EST 2022 META-INF/maven/demo/Test/pom.properties
  1489 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/AppConfiguration.class
  2219 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/components/Layout.tml
  1009 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/DB.class
  8413 Sat Jan 01 17:52:54 EST 2022 META-INF/maven/demo/Test/pom.xml
  1952 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/AppConfiguration$1.class
  1400 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/pages/Index.class
   504 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/demo/pages/Index.tml
  2176 Wed Nov 23 20:12:12 EST 2022 BOOT-INF/classes/log4j2.yml
     0 Thu Dec 01 19:38:24 EST 2022 BOOT-INF/lib/
   398 Fri Feb 15 10:41:22 EST 2019 BOOT-INF/lib/spring-boot-starter-2.1.3.RELEASE.jar
948706 Fri Feb 15 10:08:36 EST 2019 BOOT-INF/lib/spring-boot-2.1.3.RELEASE.jar
1099682 Wed Feb 13 05:32:30 EST 2019 BOOT-INF/lib/spring-context-5.1.5.RELEASE.jar
1257845 Fri Feb 15 10:18:16 EST 2019 BOOT-INF/lib/spring-boot-autoconfigure-2.1.3.RELEASE.jar
 26586 Wed Feb 21 15:54:16 EST 2018 BOOT-INF/lib/javax.annotation-api-1.3.2.jar
1293377 Wed Feb 13 05:32:02 EST 2019 BOOT-INF/lib/spring-core-5.1.5.RELEASE.jar
 23725 Wed Feb 13 05:31:54 EST 2019 BOOT-INF/lib/spring-jcl-5.1.5.RELEASE.jar
301298 Mon Aug 27 16:23:36 EDT 2018 BOOT-INF/lib/snakeyaml-1.23.jar
   406 Fri Feb 15 10:41:44 EST 2019 BOOT-INF/lib/spring-boot-starter-web-2.1.3.RELEASE.jar
   405 Fri Feb 15 10:41:44 EST 2019 BOOT-INF/lib/spring-boot-starter-json-2.1.3.RELEASE.jar
 33391 Sat Dec 15 23:06:14 EST 2018 BOOT-INF/lib/jackson-datatype-jdk8-2.9.8.jar
100674 Sat Dec 15 23:06:24 EST 2018 BOOT-INF/lib/jackson-datatype-jsr310-2.9.8.jar
  8642 Sat Dec 15 23:06:04 EST 2018 BOOT-INF/lib/jackson-module-parameter-names-2.9.8.jar
   406 Fri Feb 15 10:41:44 EST 2019 BOOT-INF/lib/spring-boot-starter-tomcat-2.1.3.RELEASE.jar
3287907 Mon Feb 04 16:31:00 EST 2019 BOOT-INF/lib/tomcat-embed-core-9.0.16.jar
250080 Mon Feb 04 16:31:02 EST 2019 BOOT-INF/lib/tomcat-embed-el-9.0.16.jar
265364 Mon Feb 04 16:31:02 EST 2019 BOOT-INF/lib/tomcat-embed-websocket-9.0.16.jar
1155701 Fri Jan 04 14:52:48 EST 2019 BOOT-INF/lib/hibernate-validator-6.0.14.Final.jar
 93107 Tue Dec 19 16:23:28 EST 2017 BOOT-INF/lib/validation-api-2.0.1.Final.jar
 66469 Wed Feb 14 13:23:28 EST 2018 BOOT-INF/lib/jboss-logging-3.3.2.Final.jar
 66540 Tue Mar 27 18:35:34 EDT 2018 BOOT-INF/lib/classmate-1.4.0.jar
1381453 Wed Feb 13 05:32:56 EST 2019 BOOT-INF/lib/spring-web-5.1.5.RELEASE.jar
672558 Wed Feb 13 05:32:08 EST 2019 BOOT-INF/lib/spring-beans-5.1.5.RELEASE.jar
800464 Wed Feb 13 05:33:32 EST 2019 BOOT-INF/lib/spring-webmvc-5.1.5.RELEASE.jar
368947 Wed Feb 13 05:32:22 EST 2019 BOOT-INF/lib/spring-aop-5.1.5.RELEASE.jar
280409 Wed Feb 13 05:32:24 EST 2019 BOOT-INF/lib/spring-expression-5.1.5.RELEASE.jar
   408 Fri Feb 15 10:41:54 EST 2019 BOOT-INF/lib/spring-boot-starter-log4j2-2.1.3.RELEASE.jar
1629585 Tue Feb 05 18:12:30 EST 2019 BOOT-INF/lib/log4j-core-2.11.2.jar
 23998 Tue Feb 05 18:19:04 EST 2019 BOOT-INF/lib/log4j-jul-2.11.2.jar
  4596 Thu Mar 16 17:37:48 EDT 2017 BOOT-INF/lib/jul-to-slf4j-1.7.25.jar
3003207 Mon Aug 09 16:12:42 EDT 2021 BOOT-INF/lib/tapestry-core-5.7.3.jar
437959 Mon Aug 09 16:12:36 EDT 2021 BOOT-INF/lib/tapestry-ioc-5.7.3.jar
 65642 Mon Aug 09 16:12:24 EDT 2021 BOOT-INF/lib/tapestry-func-5.7.3.jar
 28313 Mon Aug 09 16:12:24 EDT 2021 BOOT-INF/lib/tapestry5-annotations-5.7.3.jar
556550 Mon Aug 09 16:12:22 EDT 2021 BOOT-INF/lib/plastic-5.7.3.jar
  2497 Tue Oct 13 16:07:04 EDT 2009 BOOT-INF/lib/javax.inject-1.jar
 40232 Mon Aug 09 16:12:38 EDT 2021 BOOT-INF/lib/tapestry-json-5.7.3.jar
133776 Mon Aug 09 16:12:26 EDT 2021 BOOT-INF/lib/beanmodel-5.7.3.jar
116058 Mon Aug 09 16:12:24 EDT 2021 BOOT-INF/lib/commons-5.7.3.jar
167761 Tue Mar 25 07:04:24 EDT 2014 BOOT-INF/lib/antlr-runtime-3.5.2.jar
 64920 Mon Aug 09 16:12:38 EDT 2021 BOOT-INF/lib/tapestry-http-5.7.3.jar
 25150 Tue Dec 11 12:04:00 EST 2018 BOOT-INF/lib/jakarta.annotation-api-1.3.4.jar
115498 Thu Dec 27 15:30:30 EST 2018 BOOT-INF/lib/jakarta.xml.bind-api-2.3.2.jar
 44399 Tue Nov 20 22:13:52 EST 2018 BOOT-INF/lib/jakarta.activation-api-1.2.1.jar
1093432 Wed Sep 12 12:13:52 EDT 2018 BOOT-INF/lib/jaxb-runtime-2.3.1.jar
128076 Wed Sep 12 06:28:46 EDT 2018 BOOT-INF/lib/jaxb-api-2.3.1.jar
 70288 Wed Sep 12 12:12:20 EDT 2018 BOOT-INF/lib/txw2-2.3.1.jar
 25523 Wed Aug 29 05:23:54 EDT 2018 BOOT-INF/lib/istack-commons-runtime-3.0.7.jar
 36073 Tue Apr 24 02:44:02 EDT 2018 BOOT-INF/lib/stax-ex-1.8.jar
311876 Wed Aug 29 07:39:00 EDT 2018 BOOT-INF/lib/FastInfoset-1.2.15.jar
 56674 Wed Sep 06 16:13:14 EDT 2017 BOOT-INF/lib/javax.activation-api-1.2.0.jar
2463818 Fri Jan 18 15:24:58 EST 2019 BOOT-INF/lib/jaxws-rt-2.3.2.jar
 57877 Thu Dec 27 15:51:06 EST 2018 BOOT-INF/lib/jakarta.xml.ws-api-2.3.2.jar
 36161 Thu Dec 27 15:41:42 EST 2018 BOOT-INF/lib/jakarta.xml.soap-api-1.4.1.jar
 15860 Thu Dec 27 15:46:16 EST 2018 BOOT-INF/lib/jakarta.jws-api-1.1.1.jar
181165 Fri Dec 28 14:32:46 EST 2018 BOOT-INF/lib/policy-2.7.6.jar
204972 Fri Dec 28 13:31:16 EST 2018 BOOT-INF/lib/gmbal-4.0.0.jar
 45451 Mon Dec 17 12:16:18 EST 2018 BOOT-INF/lib/management-api-3.2.1.jar
197485 Fri Dec 28 13:23:26 EST 2018 BOOT-INF/lib/pfl-basic-4.0.1.jar
 93886 Fri Dec 28 13:24:32 EST 2018 BOOT-INF/lib/pfl-tf-4.0.1.jar
357914 Fri Dec 28 13:24:10 EST 2018 BOOT-INF/lib/pfl-asm-4.0.1.jar
343539 Fri Dec 28 13:24:20 EST 2018 BOOT-INF/lib/pfl-dynamic-4.0.1.jar
 70799 Fri Dec 28 13:24:06 EST 2018 BOOT-INF/lib/pfl-basic-tools-4.0.1.jar
 49125 Fri Dec 28 13:24:40 EST 2018 BOOT-INF/lib/pfl-tf-tools-4.0.1.jar
 73918 Fri Dec 28 14:33:00 EST 2018 BOOT-INF/lib/streambuffer-1.5.7.jar
 67125 Fri Dec 28 12:46:32 EST 2018 BOOT-INF/lib/mimepull-1.9.11.jar
 36399 Wed Jan 16 03:51:28 EST 2019 BOOT-INF/lib/ha-api-3.1.12.jar
498818 Tue Jun 12 07:08:22 EDT 2018 BOOT-INF/lib/saaj-impl-1.5.0.jar
513602 Sat Mar 31 01:34:20 EDT 2018 BOOT-INF/lib/woodstox-core-5.1.0.jar
169541 Wed Mar 28 03:40:18 EDT 2018 BOOT-INF/lib/stax2-api-4.1.jar
 56615 Wed Sep 12 12:25:44 EDT 2018 BOOT-INF/lib/jaxws-api-2.3.1.jar
 46111 Tue Jun 13 16:36:16 EDT 2017 BOOT-INF/lib/javax.xml.soap-api-1.4.0.jar
335042 Tue Oct 17 08:53:20 EDT 2017 BOOT-INF/lib/commons-codec-1.11.jar
 23239 Tue Feb 05 18:13:54 EST 2019 BOOT-INF/lib/log4j-slf4j-impl-2.11.2.jar
 41203 Thu Mar 16 17:36:32 EDT 2017 BOOT-INF/lib/slf4j-api-1.7.25.jar
266283 Tue Feb 05 18:11:30 EST 2019 BOOT-INF/lib/log4j-api-2.11.2.jar
296324 Thu Nov 09 03:48:28 EST 2017 BOOT-INF/lib/yasson-1.0.1.jar
 23665 Mon Jun 19 08:58:02 EDT 2017 BOOT-INF/lib/javax.json.bind-api-1.0.jar
 31095 Tue Nov 06 17:05:26 EST 2018 BOOT-INF/lib/javax.json-api-1.1.4.jar
113874 Tue May 09 12:04:46 EDT 2017 BOOT-INF/lib/cdi-api-2.0.jar
 73063 Thu Apr 25 15:52:38 EDT 2013 BOOT-INF/lib/javax.el-api-3.0.0.jar
 24404 Fri Apr 26 16:10:40 EDT 2013 BOOT-INF/lib/javax.interceptor-api-1.2.jar
101089 Thu Nov 02 18:57:44 EDT 2017 BOOT-INF/lib/javax.json-1.1.2.jar
1349339 Tue Jun 12 01:14:32 EDT 2018 BOOT-INF/lib/jackson-databind-2.9.6.jar
 66519 Sat Jul 29 20:53:26 EDT 2017 BOOT-INF/lib/jackson-annotations-2.9.0.jar
325619 Sat Dec 15 13:19:12 EST 2018 BOOT-INF/lib/jackson-core-2.9.8.jar
 41291 Tue Jun 12 04:59:46 EDT 2018 BOOT-INF/lib/jackson-dataformat-yaml-2.9.6.jar
 85353 Tue Jul 12 13:10:04 EDT 2011 BOOT-INF/lib/javax.servlet-api-3.0.1.jar

The result looks like:

Tapestry view screen

Wicket:

Wicket really makes web applications look like a desktop application.

Name and creator Apache Wicket / open source
History Version 1.0 in 2005
Moved to Apache in 2007 with version 1.3
Version 6.0 in 2012
Version 9.0 in 2020
Programming language Java
View technology XHTML
Deployment Servlet container
Other technical characteristics
Status Actively maintained, small but enthusiastic user base

TestPage.html:

<html xmlns:wicket="http://wicket.apache.org">
<head>
<title>Wicket</title>
</head>
<body>
<h1>Wicket</h1>
<h2>Show data:</h2>
<table border="1">
<tr wicket:id="data">
<td wicket:id="f1"></td>
<td wicket:id="f2"></td>
<td wicket:id="f3"></td>
</tr>
</table>
<h2>Add data:</h2>
<form wicket:id = "addForm">
F1: <input type="text" name="f1" wicket:id="f1"/>
<br/>
F2: <input type="text" name="f2" wicket:id="f2"/>
<br/>
F3: <select name="f3" wicket:id="f3"/>
<br/>
<input type="submit" value="Add"/>
</form>
</body>
</html>

TestPage.java (code behind TestPage.html):

package demo;

import java.util.List;
import java.util.ArrayList;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.PropertyListView;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.util.value.ValueMap;

public class TestPage extends WebPage {
    // setup page
    public static class AddForm extends Form<ValueMap> {
        // form options list
        public List<String> getOptions() {
            List<String> res = new ArrayList<String>();
            res.add(T1.VER);
            res.add(T1.NOTVER);
            return res;
        }
        // define form
        public AddForm(String name) {
            super(name, new CompoundPropertyModel<ValueMap>(new ValueMap()));
            add(new TextField<String>("f1"));
            add(new TextField<String>("f2"));
            add(new DropDownChoice<String>("f3", getOptions()));
        }
        // form submit
        public void onSubmit() {
            ValueMap values = getModelObject();
            int f1 = Integer.parseInt((String)values.get("f1"));
            String f2 = (String)values.get("f2");
            String f3 = (String)values.get("f3");
            DB.saveOne(new T1(f1, f2, f3));
        }
    }
    public TestPage() {
        // define table
        add(new PropertyListView<T1>("data", DB.getAll()) {
            @Override
            protected void populateItem(ListItem<T1> item) {
                item.add(new Label("f1"));
                item.add(new Label("f2"));
                item.add(new Label("f3"));
            }
        });
        add(new AddForm("addForm"));
    }
}

Test.java (configure Wicket):

package demo;

import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.markup.html.WebPage;

public class Test extends WebApplication
{
    // define start page
    @Override
    public Class<? extends WebPage> getHomePage()
    {
        return TestPage.class;
    }
}

T1.java (data class):

package demo;

import java.io.Serializable;

// data class (row in database)
public class T1 implements Serializable {
    public static final String VER = "Verified";
    public static final String NOTVER = "Not verified";
    private int f1;
    private String f2;
    private String f3;
    public T1() {
        this(0, "", "");
    }
    public T1(int f1, String f2, String f3) {
        this.f1 = f1;
        this.f2 = f2;
        this.f3 = f3;
    }
    public int getF1() {
        return f1;
    }
    public void setF1(int f1) {
        this.f1 = f1;
    }
    public String getF2() {
        return f2;
    }
    public void setF2(String f2) {
        this.f2 = f2;
    }
    public String getF3() {
        return f3;
    }
    public void setF3(String f3) {
        this.f3 = f3;
    }
}

DB.java (simulated database):

package demo;

import java.util.ArrayList;
import java.util.List;

// simulated database
public class DB {
    private static List<T1> db;
    static {
        db = new ArrayList<T1>();
        db.add(new T1(1,"A", T1.VER));
        db.add(new T1(2,"BB", T1.VER));
        db.add(new T1(3,"CCC", T1.VER));
        db.add(new T1(4,"DDDD", T1.VER));
        db.add(new T1(5,"EEEEE", T1.NOTVER));
    }
    public static List<T1> getAll() {
        return db;
    }
    public static void saveOne(T1 o) {
        db.add(o);
    }
}

web.xml (configuring web application):

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
    <!-- setup Wicket -->
    <filter>
        <filter-name>Test</filter-name>
        <filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
        <init-param>
          <param-name>applicationClassName</param-name>
          <param-value>demo.Test</param-value>
        </init-param>
        <init-param>
            <param-name>configuration</param-name>
            <param-value>deployment</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>Test</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
</web-app>

Packaging of war file:

     0 Thu Dec 01 20:02:42 EST 2022 META-INF/
   109 Thu Dec 01 20:02:40 EST 2022 META-INF/MANIFEST.MF
     0 Thu Dec 01 20:02:42 EST 2022 WEB-INF/
   871 Wed Dec 29 19:28:08 EST 2021 WEB-INF/web.xml
     0 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/
     0 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/demo/
   762 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/demo/DB.class
   746 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/demo/T1.class
   322 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/demo/Test.class
  1000 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/demo/TestPage$1.class
  1619 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/demo/TestPage$AddForm.class
   612 Thu Dec 01 20:02:42 EST 2022 WEB-INF/classes/demo/TestPage.class
     0 Thu Dec 01 20:02:42 EST 2022 WEB-INF/lib/
 72446 Wed Dec 29 13:57:42 EST 2021 WEB-INF/lib/commons-fileupload-1.4.jar
327135 Tue Dec 28 20:29:46 EST 2021 WEB-INF/lib/commons-io-2.11.0.jar
 41139 Wed Jun 19 21:59:36 EDT 2019 WEB-INF/lib/slf4j-api-1.7.26.jar
  8476 Wed Jun 19 21:59:36 EDT 2019 WEB-INF/lib/slf4j-jdk14-1.7.26.jar
2345165 Tue Dec 28 16:41:28 EST 2021 WEB-INF/lib/wicket-core-8.13.0.jar
 83670 Tue Dec 28 16:41:32 EST 2021 WEB-INF/lib/wicket-request-8.13.0.jar
382868 Tue Dec 28 16:41:32 EST 2021 WEB-INF/lib/wicket-util-8.13.0.jar
   546 Wed Nov 23 19:52:42 EST 2022 WEB-INF/classes/demo/TestPage.html

The result looks like:

Wicket view screen

Article history:

Version Date Description
1.0 December 3rd 2022 Initial version
1.1 February 19th 2023 Add VB.NET for Win Forms

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj