You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@empire-db.apache.org by do...@apache.org on 2024/06/12 12:01:21 UTC

(empire-db) branch master updated: EMPIREDB-428 Jakarta Faces Extensions project

This is an automated email from the ASF dual-hosted git repository.

doebele pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/empire-db.git


The following commit(s) were added to refs/heads/master by this push:
     new 227ac744 EMPIREDB-428 Jakarta Faces Extensions project
227ac744 is described below

commit 227ac74403e050bb2b5eec1dbcdd44b23d0f7be7
Author: Rainer Döbele <do...@apache.org>
AuthorDate: Wed Jun 12 14:01:16 2024 +0200

    EMPIREDB-428
    Jakarta Faces Extensions project
---
 empire-db-dist/pom.xml                             |   10 +
 .../empire-db-example-jakarta-faces/.gitignore     |    8 +
 .../src/main/webapp/WEB-INF/faces-config.pageflow  |    2 +
 .../pom.xml                                        |   36 +-
 .../jakarta/custom/controls/FileInputControl.java  |  165 ++
 .../empire/jakarta/websample/db/SampleDB.java      |  269 +++
 .../jakarta/websample/db/SampleDBwithMeta.java     |   85 +
 .../empire/jakarta/websample/db/SampleTable.java   |   82 +
 .../websample/db/records/DepartmentRecord.java     |   33 +
 .../websample/db/records/EmployeeRecord.java       |   88 +
 .../jakarta/websample/db/records/SampleRecord.java |   89 +
 .../jakarta/websample/web/AppExceptionHandler.java |  228 +++
 .../websample/web/AppExceptionHandlerFactory.java  |   47 +
 .../websample/web/AuthenticationPhaseListener.java |  154 ++
 .../websample/web/SampleAppStartupListener.java    |   60 +
 .../jakarta/websample/web/SampleApplication.java   |  362 ++++
 .../empire/jakarta/websample/web/SampleConfig.java |  195 ++
 .../jakarta/websample/web/SampleContext.java       |   77 +
 .../jakarta/websample/web/SampleSession.java       |   79 +
 .../empire/jakarta/websample/web/SampleUser.java   |   67 +
 .../empire/jakarta/websample/web/SampleUtils.java  |   96 +
 .../web/objects/EmployeeSearchFilter.java          |  102 +
 .../empire/jakarta/websample/web/objects/User.java |   49 +
 .../websample/web/pages/EmployeeDetailPage.java    |  178 ++
 .../websample/web/pages/EmployeeListPage.java      |  267 +++
 .../jakarta/websample/web/pages/ErrorPage.java     |   72 +
 .../jakarta/websample/web/pages/LoginPage.java     |   62 +
 .../jakarta/websample/web/pages/SamplePage.java    |   70 +
 .../jakarta/websample/web/pages/SamplePages.java   |   32 +
 .../src/main/resources/lang/messages_de.properties |  151 ++
 .../src/main/resources/lang/messages_en.properties |  151 ++
 .../src/main/webapp/.gitignore                     |    2 +
 .../src/main/webapp/META-INF/context.xml           |   28 +
 .../src/main/webapp/WEB-INF/config.xml             |  182 ++
 .../webapp/WEB-INF/facelets/templates/header.xhtml |   31 +
 .../webapp/WEB-INF/facelets/templates/layout.xhtml |   75 +
 .../src/main/webapp/WEB-INF/faces-config.xml       |  143 ++
 .../src/main/webapp/WEB-INF/web.xml                |   60 +
 .../src/main/webapp/css/common.css                 |   87 +
 .../src/main/webapp/css/content.css                |  135 ++
 .../src/main/webapp/css/form.css                   |  206 ++
 .../src/main/webapp/css/layout.css                 |   24 +
 .../src/main/webapp/css/root.css                   |   36 +
 .../src/main/webapp/img/boxes.gif                  |  Bin 0 -> 1132 bytes
 .../src/main/webapp/index.html                     |   31 +
 .../src/main/webapp/pages/employeeDetailPage.xhtml |  108 ++
 .../src/main/webapp/pages/employeeListPage.xhtml   |  111 ++
 .../src/main/webapp/pages/loginPage.xhtml          |   62 +
 .../webapp/resources/components/formControl.xhtml  |   41 +
 .../webapp/resources/components/pageButtons.xhtml  |   36 +
 .../src/main/webapp/resources/empire/control.xhtml |   53 +
 .../main/webapp/resources/empire/formGrid.xhtml    |   35 +
 .../src/main/webapp/resources/empire/input.xhtml   |   43 +
 .../src/main/webapp/resources/empire/label.xhtml   |   38 +
 .../src/main/webapp/resources/empire/link.xhtml    |   40 +
 .../src/main/webapp/resources/empire/mitem.xhtml   |   37 +
 .../src/main/webapp/resources/empire/mlist.xhtml   |   37 +
 .../src/main/webapp/resources/empire/record.xhtml  |   35 +
 .../src/main/webapp/resources/empire/select.xhtml  |   39 +
 .../src/main/webapp/resources/empire/tabPage.xhtml |   33 +
 .../src/main/webapp/resources/empire/tabView.xhtml |   37 +
 .../src/main/webapp/resources/empire/title.xhtml   |   37 +
 .../src/main/webapp/resources/empire/value.xhtml   |   41 +
 empire-db-examples/empire-db-example-jsf2/pom.xml  |    6 +-
 empire-db-examples/pom.xml                         |    1 +
 empire-db-jakarta-faces/.gitignore                 |    7 +
 .../pom.xml                                        |   37 +-
 .../apache/empire/jakarta/app/DBELResolver.java    |  193 ++
 .../empire/jakarta/app/FacesConfiguration.java     |  615 ++++++
 .../jakarta/app/FacesRequestPhaseListener.java     |   80 +
 .../org/apache/empire/jakarta/app/FacesUtils.java  |  544 ++++++
 .../apache/empire/jakarta/app/TextResolver.java    |   35 +
 .../empire/jakarta/app/WebAppStartupListener.java  |  175 ++
 .../apache/empire/jakarta/app/WebApplication.java  |  640 +++++++
 .../apache/empire/jakarta/app/WebDBContext.java    |  170 ++
 .../empire/jakarta/components/ControlTag.java      |  820 ++++++++
 .../empire/jakarta/components/FormGridTag.java     |  169 ++
 .../apache/empire/jakarta/components/InputTag.java |  435 +++++
 .../apache/empire/jakarta/components/LabelTag.java |  140 ++
 .../apache/empire/jakarta/components/LinkTag.java  |  418 ++++
 .../empire/jakarta/components/MenuItemTag.java     |  315 +++
 .../empire/jakarta/components/MenuListTag.java     |  306 +++
 .../apache/empire/jakarta/components/NbspTag.java  |   70 +
 .../empire/jakarta/components/RecordTag.java       |   66 +
 .../empire/jakarta/components/SelectTag.java       |  472 +++++
 .../empire/jakarta/components/TabPageTag.java      |  126 ++
 .../empire/jakarta/components/TabViewTag.java      |  467 +++++
 .../apache/empire/jakarta/components/TitleTag.java |  117 ++
 .../apache/empire/jakarta/components/UnitTag.java  |   80 +
 .../apache/empire/jakarta/components/ValueTag.java |  119 ++
 .../jakarta/controls/CheckboxInputControl.java     |  116 ++
 .../controls/InputAttachedObjectsHandler.java      |  103 +
 .../empire/jakarta/controls/InputControl.java      |  881 +++++++++
 .../jakarta/controls/InputControlManager.java      |  184 ++
 .../empire/jakarta/controls/RadioInputControl.java |  330 ++++
 .../jakarta/controls/SelectInputControl.java       |  449 +++++
 .../jakarta/controls/TextAreaInputControl.java     |  154 ++
 .../empire/jakarta/controls/TextInputControl.java  |  689 +++++++
 .../empire/jakarta/impl/FacesImplementation.java   |  121 ++
 .../empire/jakarta/impl/MojarraImplementation.java |  221 +++
 .../empire/jakarta/impl/MyFacesImplementation.java |  194 ++
 .../empire/jakarta/impl/ResourceTextResolver.java  |  124 ++
 .../jakarta/pageelements/BeanListPageElement.java  |  636 ++++++
 .../jakarta/pageelements/ListPageElement.java      |  709 +++++++
 .../jakarta/pageelements/RecordPageElement.java    |  244 +++
 .../pageelements/StaticListPageElement.java        |  122 ++
 .../java/org/apache/empire/jakarta/pages/Page.java |  569 ++++++
 .../empire/jakarta/pages/PageDefinition.java       |  226 +++
 .../empire/jakarta/pages/PageDefinitions.java      |  168 ++
 .../empire/jakarta/pages/PageELResolver.java       |  115 ++
 .../apache/empire/jakarta/pages/PageElement.java   |  163 ++
 .../jakarta/pages/PageNavigationHandler.java       |  164 ++
 .../apache/empire/jakarta/pages/PageOutcome.java   |   64 +
 .../empire/jakarta/pages/PagePhaseListener.java    |  233 +++
 .../empire/jakarta/pages/PagesELResolver.java      |  166 ++
 .../empire/jakarta/utils/ControlRenderInfo.java    |   51 +
 .../empire/jakarta/utils/ListColumnFinder.java     |   26 +
 .../empire/jakarta/utils/ListItemSelection.java    |   31 +
 .../apache/empire/jakarta/utils/ParameterMap.java  |  324 ++++
 .../empire/jakarta/utils/ParameterObject.java      |   34 +
 .../apache/empire/jakarta/utils/SessionMap.java    |   66 +
 .../empire/jakarta/utils/StringResponseWriter.java |  151 ++
 .../empire/jakarta/utils/TagContextInfo.java       |   26 +
 .../empire/jakarta/utils/TagEncodingHelper.java    | 2024 ++++++++++++++++++++
 .../jakarta/utils/TagEncodingHelperFactory.java    |   54 +
 .../apache/empire/jakarta/utils/TagStyleClass.java |  182 ++
 .../jakarta/utils/ValueExpressionUnwrapper.java    |  125 ++
 empire-db-jsf2/pom.xml                             |    5 +-
 pom.xml                                            |   34 +-
 129 files changed, 22113 insertions(+), 57 deletions(-)

diff --git a/empire-db-dist/pom.xml b/empire-db-dist/pom.xml
index 75984f2b..bab6e99f 100644
--- a/empire-db-dist/pom.xml
+++ b/empire-db-dist/pom.xml
@@ -73,6 +73,10 @@
 			<groupId>org.apache.empire-db</groupId>
 			<artifactId>empire-db-jsf2</artifactId>
 		</dependency>
+		<dependency>
+			<groupId>org.apache.empire-db</groupId>
+			<artifactId>empire-db-jakarta-faces</artifactId>
+		</dependency>
 		<!-- javadoc -->
 		<dependency>
 			<groupId>org.apache.empire-db</groupId>
@@ -86,6 +90,12 @@
 			<version>3.1.1-SNAPSHOT</version>
 			<classifier>javadoc</classifier>
 		</dependency>
+		<dependency>
+			<groupId>org.apache.empire-db</groupId>
+			<artifactId>empire-db-jakarta-faces</artifactId>
+			<version>3.1.1-SNAPSHOT</version>
+			<classifier>javadoc</classifier>
+		</dependency>
 	</dependencies>
 
 </project>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/.gitignore b/empire-db-examples/empire-db-example-jakarta-faces/.gitignore
new file mode 100644
index 00000000..22247c22
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/.gitignore
@@ -0,0 +1,8 @@
+/target/
+/.settings/
+/.classpath
+/.project
+/.tomcatplugin
+/.apt_generated/
+/.factorypath
+/.apt_generated_tests/
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/.metadata/src/main/webapp/WEB-INF/faces-config.pageflow b/empire-db-examples/empire-db-example-jakarta-faces/.metadata/src/main/webapp/WEB-INF/faces-config.pageflow
new file mode 100644
index 00000000..0e082772
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/.metadata/src/main/webapp/WEB-INF/faces-config.pageflow
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<pageflow:Pageflow xmlns:pageflow="http://www.sybase.com/suade/pageflow" id="pf17181908672340" configfile="/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/faces-config.xml"/>
diff --git a/empire-db-examples/empire-db-example-jsf2/pom.xml b/empire-db-examples/empire-db-example-jakarta-faces/pom.xml
similarity index 83%
copy from empire-db-examples/empire-db-example-jsf2/pom.xml
copy to empire-db-examples/empire-db-example-jakarta-faces/pom.xml
index 91f9c91c..eedea683 100644
--- a/empire-db-examples/empire-db-example-jsf2/pom.xml
+++ b/empire-db-examples/empire-db-example-jakarta-faces/pom.xml
@@ -21,9 +21,9 @@
 		<groupId>org.apache.empire-db</groupId>
 		<version>3.1.1-SNAPSHOT</version>
 	</parent>
-	<artifactId>empire-db-example-jsf2</artifactId>
+	<artifactId>empire-db-example-jakarta-faces</artifactId>
 	<name>Apache Empire-db JSF2 Example</name>
-	<description>Example showing the use of the empire-db-jsf2 extensions</description>
+	<description>Example showing the use of the empire-db-jakarta-faces extensions</description>
 	<packaging>war</packaging>
 	
 	<properties>
@@ -38,42 +38,48 @@
 
 		<dependency>
 			<groupId>org.apache.empire-db</groupId>
-			<artifactId>empire-db-jsf2</artifactId>
+			<artifactId>empire-db-jakarta-faces</artifactId>
 		</dependency>
 
+		<!-- Servlet API -->
 		<dependency>
-		    <groupId>javax.servlet</groupId>
-		    <artifactId>javax.servlet-api</artifactId>
-			<scope>provided</scope>
+		    <groupId>jakarta.servlet</groupId>
+		    <artifactId>jakarta.servlet-api</artifactId>
+		    <version>5.0.0</version>
+		    <scope>provided</scope>
 		</dependency>
 
-		<!-- Sun Mojarra -->
+		<!-- Mojarra -->
 		<!--
 		<dependency>
-			<groupId>com.sun.faces</groupId>
-			<artifactId>jsf-api</artifactId>
+		    <groupId>jakarta.faces</groupId>
+		    <artifactId>jakarta.faces-api</artifactId>
+		    <version>3.0.0</version>
 		</dependency>
 		<dependency>
-			<groupId>com.sun.faces</groupId>
-			<artifactId>jsf-impl</artifactId>
-		</dependency>
+		    <groupId>org.glassfish</groupId>
+		    <artifactId>jakarta.faces</artifactId>
+		    <version>3.0.5</version>
+		</dependency>		 
 		 -->
 
 		<!-- Apache MyFaces -->
 		<dependency>
 			<groupId>org.apache.myfaces.core</groupId>
 			<artifactId>myfaces-api</artifactId>
+		    <version>3.0.2</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.myfaces.core</groupId>
 			<artifactId>myfaces-impl</artifactId>
+		    <version>3.0.2</version>
 		</dependency>
 
 		<!-- EL --> 
 		<dependency>
-		    <groupId>javax.el</groupId>
-		    <artifactId>javax.el-api</artifactId>
-			<type>jar</type>
+		    <groupId>jakarta.el</groupId>
+		    <artifactId>jakarta.el-api</artifactId>
+		    <version>4.0.0</version>
 			<scope>provided</scope>
 		</dependency>
 
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/custom/controls/FileInputControl.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/custom/controls/FileInputControl.java
new file mode 100644
index 00000000..42286a81
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/custom/controls/FileInputControl.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.custom.controls;
+
+import java.util.List;
+
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.html.HtmlInputText;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.PhaseId;
+
+import org.apache.empire.exceptions.InternalException;
+import org.apache.empire.exceptions.UnexpectedReturnValueException;
+import org.apache.empire.jakarta.controls.InputControl;
+
+public class FileInputControl extends InputControl
+{
+    public static final String NAME = "file";
+
+    private Class<? extends HtmlInputFile> inputComponentClass = null;
+
+    public FileInputControl(Class<? extends HtmlInputFile> inputComponentClass)
+    {
+        super(NAME);
+        this.inputComponentClass = inputComponentClass;
+    }
+
+    public FileInputControl()
+    {
+        this(HtmlInputFile.class);
+    }
+
+    @Override
+    protected void createInputComponents(UIComponent parent, InputInfo ii, FacesContext context, List<UIComponent> compList)
+    {
+        HtmlInputFile input;
+        try
+        {
+            input = inputComponentClass.newInstance();
+            copyAttributes(parent, ii, input);
+        }
+        catch (InstantiationException e1)
+        {
+            throw new InternalException(e1);
+        }
+        catch (IllegalAccessException e2)
+        {
+            throw new InternalException(e2);
+        }
+        compList.add(input);
+        // update
+        updateInputState(compList, ii, context, context.getCurrentPhaseId());
+    }
+
+    @Override
+    protected void updateInputState(List<UIComponent> compList, InputInfo ii, FacesContext context, PhaseId phaseId)
+    {
+        UIComponent comp = compList.get(0);
+        if (!(comp instanceof HtmlInputFile))
+        {
+            throw new UnexpectedReturnValueException(comp.getClass().getName(), "compList.get(0)");
+        }
+        // update state
+        HtmlInputFile input = (HtmlInputFile) comp;
+        input.setDisabled(ii.isDisabled());
+        // set value
+        if (phaseId==PhaseId.RENDER_RESPONSE)
+        {   // set value
+            setInputValue(input, ii);
+        }
+    }
+
+    public class HtmlInputFile extends HtmlInputText
+    {
+        public static final String RENDER_TYPE = "org.apache.empire.jsf2.custom.controls.FileInputRenderer";
+
+        @Override
+        public String getRendererType()
+        {
+            return RENDER_TYPE;
+        }
+    }
+
+    /*
+    public class FileInputRenderer extends TextRenderer
+    {
+
+        // Constants ----------------------------------------------------------------------------------
+
+        private static final String EMPTY_STRING     = "";
+        private final Attribute[]   INPUT_ATTRIBUTES = AttributeManager.getAttributes(AttributeManager.Key.INPUTTEXT);
+
+        // Actions ------------------------------------------------------------------------------------
+
+        @Override
+        protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue)
+            throws IOException
+        {
+            ResponseWriter writer = context.getResponseWriter();
+            writer.startElement("input", component);
+            writeIdAttributeIfNecessary(context, writer, component);
+            writer.writeAttribute("type", "file", null);
+            writer.writeAttribute("name", (component.getClientId(context)), "clientId");
+
+            // Render styleClass, if any.
+            String styleClass = (String) component.getAttributes().get("styleClass");
+            if (styleClass != null)
+            {
+                writer.writeAttribute("class", styleClass, "styleClass");
+            }
+
+            // Render standard HTMLattributes expect of styleClass.
+            RenderKitUtils.renderPassThruAttributes(context, writer, component, INPUT_ATTRIBUTES, getNonOnChangeBehaviors(component));
+            RenderKitUtils.renderXHTMLStyleBooleanAttributes(writer, component);
+            RenderKitUtils.renderOnchange(context, component, false);
+
+            writer.endElement("input");
+        }
+
+        @Override
+        public void decode(FacesContext context, UIComponent component)
+        {
+            rendererParamsNotNull(context, component);
+            if (!shouldDecode(component))
+            {
+                return;
+            }
+            String clientId = decodeBehaviors(context, component);
+            if (clientId == null)
+            {
+                clientId = component.getClientId(context);
+            }
+
+            //            File file = ((MultipartRequest) context.getExternalContext().getRequest()).getFile(clientId);
+            // If no file is specified, set empty String to trigger validators.
+            //            ((UIInput) component).setSubmittedValue((file != null) ? file : EMPTY_STRING);
+        }
+
+        @Override
+        public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue)
+            throws ConverterException
+        {
+            return (submittedValue != EMPTY_STRING) ? submittedValue : null;
+        }
+
+    }
+    */
+
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleDB.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleDB.java
new file mode 100644
index 00000000..eefde568
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleDB.java
@@ -0,0 +1,269 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.db;
+
+import java.sql.SQLException;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBContext;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBSQLScript;
+import org.apache.empire.db.DBTableColumn;
+import org.apache.empire.db.validation.DBModelChecker;
+import org.apache.empire.db.validation.DBModelErrorLogger;
+import org.apache.empire.dbms.postgresql.DBMSHandlerPostgreSQL;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <PRE>
+ * This file contains the definition of the data model in Java.
+ * The SampleDB data model consists of three tables and two foreign key relations.
+ * The tables are defined as nested classes here, but you may put them in separate files if you want to.
+ * 
+ * The class also provided methods to create the database (if it doesn't exist) 
+ * and to check the datamodel i.e. to compare this definition to the database in the DBMS
+ *
+ * PLEASE NOTE THE NAMING CONVENTION:
+ * Since all tables, views and columns are declared as "final" constants we have all put them in upper case.
+ *
+ * You may declare other database tables or views in the same way.
+ * </PRE>
+ */
+public class SampleDB extends DBDatabase
+{
+    // Logger
+    private static final Logger log = LoggerFactory.getLogger(SampleDB.class);
+    
+    /**
+     * Gender enum
+     */
+    public enum Gender
+    {
+        M("Male"),
+        F("Female"),
+        U("Unknown");
+        
+        private final String title;
+        private Gender(String title)
+        {
+            this.title = title;
+        }
+        @Override
+        public String toString()
+        {
+            return title;
+        }
+    }
+
+    /**
+     * This class represents the Departments table.
+     */
+    public static class TDepartments extends SampleTable
+    {
+        public final DBTableColumn ID;
+        public final DBTableColumn NAME;
+        public final DBTableColumn HEAD;
+        public final DBTableColumn BUSINESS_UNIT;
+        public final DBTableColumn UPDATE_TIMESTAMP;
+
+        public TDepartments(SampleDB db)
+        {
+            super("DEPARTMENTS", db);
+            // ID
+            ID              = addIdentity ("ID",               "DEP_ID_SEQUENCE"); // Optional Sequence for some DBMS (e.g. Oracle)
+            NAME            = addColumn   ("NAME",             DataType.VARCHAR,      80, true);
+            HEAD            = addColumn   ("HEAD",             DataType.VARCHAR,      80, false);
+            BUSINESS_UNIT   = addColumn   ("BUSINESS_UNIT",    DataType.VARCHAR,       4, true, "ITTK");
+            UPDATE_TIMESTAMP= addTimestamp("UPDATE_TIMESTAMP");
+
+            // Primary Key (automatically set due to addIdentity()) otherwise use 
+            // setPrimaryKey(...);
+
+            // Set other Indexes
+            addIndex("DEPARTMENT_NAME_IDX", true, new DBColumn[] { NAME });
+            
+            // optional: set entity name for selectQualified() to singular since table is plural
+            setEntityName("DEPARTMENT");
+        }
+    }
+
+    /**
+     * This class represents the Employees table.
+     */
+    public static class TEmployees extends SampleTable
+    {
+        public final DBTableColumn ID;
+        public final DBTableColumn SALUTATION;
+        public final DBTableColumn FIRST_NAME;
+        public final DBTableColumn LAST_NAME;
+        public final DBTableColumn DATE_OF_BIRTH;
+        public final DBTableColumn DEPARTMENT_ID;
+        public final DBTableColumn GENDER;
+        public final DBTableColumn PHONE_NUMBER;
+        public final DBTableColumn EMAIL;
+        public final DBTableColumn SALARY;
+        public final DBTableColumn RETIRED;
+        public final DBTableColumn UPDATE_TIMESTAMP;
+
+        public TEmployees(SampleDB db)
+        {
+            super("EMPLOYEES", db);
+            
+            // ID
+            ID              = addIdentity  ("ID",               "EMPLOYEE_ID_SEQUENCE");  // Optional Sequence name for some DBMS (e.g. Oracle)
+            SALUTATION      = addColumn    ("SALUTATION",       DataType.VARCHAR,      5, false);
+            FIRST_NAME      = addColumn    ("FIRST_NAME",       DataType.VARCHAR,     40, true);
+            LAST_NAME       = addColumn    ("LAST_NAME",        DataType.VARCHAR,     40, true);
+            DATE_OF_BIRTH   = addColumn    ("DATE_OF_BIRTH",    DataType.DATE,         0, false);
+            DEPARTMENT_ID   = addForeignKey("DEPARTMENT_ID",    db.DEPARTMENTS,           true);
+            GENDER          = addColumn    ("GENDER",           DataType.VARCHAR,      1, true, Gender.class);
+            PHONE_NUMBER    = addColumn    ("PHONE_NUMBER",     DataType.VARCHAR,     40, false, "0815-");
+            EMAIL           = addColumn    ("EMAIL",            DataType.VARCHAR,     80, false);
+            SALARY          = addColumn    ("SALARY",           DataType.DECIMAL,   10.2, false);
+            RETIRED         = addColumn    ("RETIRED",          DataType.BOOL,         0, true, false);
+            UPDATE_TIMESTAMP= addTimestamp ("UPDATE_TIMESTAMP");
+            
+            // Primary Key (automatically set due to addIdentity()) otherwise use 
+            // setPrimaryKey(...);
+
+            // Set other Indexes
+            addIndex("EMPLOYEE_NAME_IDX", true, new DBColumn[] { FIRST_NAME, LAST_NAME, DATE_OF_BIRTH });
+
+            // optional: set entity name for selectQualified() to singular since table is plural
+            setEntityName("EMPLOYEE");
+        }
+    }
+
+    /**
+     * This class represents the Payments table.
+     */
+    public static class TPayments extends SampleTable
+    {
+        public final DBTableColumn EMPLOYEE_ID;
+        public final DBTableColumn YEAR;
+        public final DBTableColumn MONTH;
+        public final DBTableColumn AMOUNT;
+
+        public TPayments(SampleDB db)
+        {
+            super("PAYMENTS", db);
+            
+            // ID
+            EMPLOYEE_ID     = addForeignKey("EMPLOYEE_ID",  db.EMPLOYEES,             true);
+            YEAR            = addColumn("YEAR",             DataType.DECIMAL,    4.0, true);
+            MONTH           = addColumn("MONTH",            DataType.DECIMAL,    2.0, true);
+            AMOUNT          = addColumn("AMOUNT",           DataType.DECIMAL,    8.2, true);
+
+            // Primary Key 
+            setPrimaryKey(EMPLOYEE_ID, YEAR, MONTH);
+
+            // optional: set entity name for selectQualified() to singular since table is plural
+            setEntityName("PAYMENT");
+        }
+    }
+    
+    // Declare all Tables and Views here
+    public final TDepartments  DEPARTMENTS = new TDepartments(this);
+    public final TEmployees    EMPLOYEES   = new TEmployees(this);
+    public final TPayments     PAYMENTS    = new TPayments(this);
+
+    /**
+     * Constructor of the SampleDB data model
+     *
+     * Put all foreign key relations here.
+     */
+    public SampleDB()
+    {
+        // Define additional Foreign-Key Relations here
+        // which have not already been defined by addForeignKey()
+        // addRelation( {Source Column}.referenceOn( {Target Column} ));
+        log.info("SampleDB has been created with {} Tables and {} Relations", getTables().size(), getRelations().size());
+    }
+
+    // Needed for the DBELResolver
+    @Override
+    protected void register(String id)
+    {
+        super.register("db");
+    }
+    
+    @Override
+    public void open(DBContext context)
+    {
+        // Enable prepared statements
+        // setPreparedStatementsEnabled(true);
+        // Check exists
+        if (checkExists(context))
+        {   // attach to driver
+            super.open(context);
+            // yes, it exists, then check the model
+            checkDataModel(context);
+        }
+        else
+        {   // PostgreSQL does not support DDL in transaction
+            if(getDbms() instanceof DBMSHandlerPostgreSQL)
+                setAutoCommit(context, true);
+            // create the database
+            createDatabase(context);
+            // PostgreSQL does not support DDL in transaction
+            if(getDbms() instanceof DBMSHandlerPostgreSQL)
+                setAutoCommit(context, false);
+            // attach to driver
+            super.open(context);
+        }
+    }
+
+    private void createDatabase(DBContext context)
+    {
+        // create DDL for Database Definition
+        DBSQLScript script = new DBSQLScript(context);
+        getCreateDDLScript(script);
+        // Show DDL Statement
+        log.info(script.toString());
+        // Execute Script
+        script.executeAll(false);
+        // Commit
+        context.commit();
+    }
+    
+    private void checkDataModel(DBContext context)
+    {   try {
+            DBModelChecker modelChecker = context.getDbms().createModelChecker(this);
+            // Check data model   
+            log.info("Checking DataModel for {} using {}", getClass().getSimpleName(), modelChecker.getClass().getSimpleName());
+            // dbo schema
+            DBModelErrorLogger logger = new DBModelErrorLogger();
+            modelChecker.checkModel(this, context.getConnection(), logger);
+            // show result
+            log.info("Data model check done. Found {} errors and {} warnings.", logger.getErrorCount(), logger.getWarnCount());
+        } catch(Exception e) {
+            log.error("FATAL error when checking data model. Probably not properly implemented by DBMSHandler!");
+        }
+    }
+    
+    private void setAutoCommit(DBContext context, boolean enable)
+    {   try {
+            context.getConnection().setAutoCommit(enable);
+        } catch (SQLException e) {
+            log.error("Unable to set AutoCommit on Connection", e);
+        }
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleDBwithMeta.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleDBwithMeta.java
new file mode 100644
index 00000000..2ee1d848
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleDBwithMeta.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.db;
+
+import org.apache.empire.commons.Options;
+import org.apache.empire.data.Column;
+import org.apache.empire.db.DBContext;
+import org.apache.empire.jakarta.controls.TextInputControl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SampleDBwithMeta extends SampleDB
+{
+    // Logger
+    private static final Logger log = LoggerFactory.getLogger(SampleDBwithMeta.class);
+   
+    @Override
+    public void open(DBContext context)
+    {
+        super.open(context);
+        // Hint about metadata 
+        log.info("Basic metadata such as Title has already been set in SampleTable.addColumn()");
+        // add additional metadata e.g. for JSF-controls
+        addMeta(EMPLOYEES);
+    }
+    
+    private void addMeta(SampleDB.TEmployees T)
+    {
+        log.info("Adding additional Metadata for {}", T.getName());
+        // Set custom style for SALUTATION
+        T.SALUTATION.setAttribute("styleClass", "eInpShort");
+        
+        // Create Options for GENDER column
+        // add the message-keys instead of the enum string value
+        Options genders = new Options();
+        genders.set(Gender.M, "!option.employee.gender.male");
+        genders.set(Gender.F, "!option.employee.gender.female");
+        T.GENDER.setOptions(genders);
+        T.GENDER.setControlType("select");
+        T.GENDER.setAttribute("styleClass", "eInpShort");
+
+        // RETIRED column
+        Options retired = new Options();
+        retired.set(false, "!option.employee.active");
+        retired.set(true,  "!option.employee.retired");
+        T.RETIRED.setOptions(retired)
+                 .setControlType("checkbox")
+                 .setAttribute("wrapperClass", "checkboxWrapper"); /* NEW: Wrapper class for <e:input> and <e:control>! */
+
+        // Hint for DATE_OF_BIRTH
+        T.DATE_OF_BIRTH.setAttribute("format:date", "yyyy-MM-dd");
+        T.DATE_OF_BIRTH.setAttribute("hint", "[yyyy-MM-dd]");
+         
+        // Salary special
+        T.SALARY.setAttribute("styleClass", "eInpDecimal")
+                .setAttribute(TextInputControl.FORMAT_UNIT_ATTRIBUTE, "USD")
+                .setAttribute(Column.COLATTR_NUMBER_TYPE, "Decimal")
+                .setAttribute(Column.COLATTR_FRACTION_DIGITS, 2)
+                .setAttribute(Column.COLATTR_NUMBER_GROUPSEP, true);
+        
+        // Set special control types
+        T.DEPARTMENT_ID.setControlType("select");
+        // T.PHONE_NUMBER .setControlType("phone");
+        
+        // UPDATE_TIMESTAMP
+        T.UPDATE_TIMESTAMP.setAttribute("format:date", "full");
+        // format="date-format:full" readonly="true" rendered="#{page.idParam != null}"         
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleTable.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleTable.java
new file mode 100644
index 00000000..3b211fce
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/SampleTable.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.db;
+
+import java.util.Locale;
+
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBTable;
+import org.apache.empire.db.DBTableColumn;
+
+/**
+ * Base class definition for all database tables Automatically generates a message-key for the field title e.g. for the column
+ * EMPLOYEES.DATE_OF_BIRTH it generates the key "!field.title.employees.dateOfBirth";
+ */
+public class SampleTable extends DBTable
+{
+    // *Deprecated* private static final long serialVersionUID = 1L;
+    
+    public final String       MESSAGE_KEY_PREFIX = "!field.title.";
+
+    public SampleTable(String name, DBDatabase db)
+    {
+        super(name, db);
+    }
+
+    @Override
+    protected void addColumn(DBTableColumn column)
+    {
+        // Set Translation Title
+        String col = column.getBeanPropertyName();
+        String tbl = getName().toLowerCase();
+        String key = MESSAGE_KEY_PREFIX + tbl + "." + col;
+        column.setTitle(key);
+
+        // Set Default Control Type
+        DataType type = column.getDataType();
+        column.setControlType((type == DataType.BOOL) ? "checkbox" : "text");
+
+        // Add Column
+        super.addColumn(column);
+    }
+
+    public enum LanguageIndex {
+        DE(Locale.GERMAN),
+
+        EN(Locale.ENGLISH);
+
+        private final Locale locale;
+
+        private LanguageIndex(Locale locale)
+        {
+            this.locale = locale;
+        }
+
+        public Locale getLocale()
+        {
+            return this.locale;
+        }
+
+        public String getDBLangKey()
+        {
+            return this.name().toUpperCase();
+        }
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/DepartmentRecord.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/DepartmentRecord.java
new file mode 100644
index 00000000..dac3463f
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/DepartmentRecord.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.db.records;
+
+import org.apache.empire.jakarta.websample.db.SampleDB.TDepartments;
+import org.apache.empire.jakarta.websample.web.SampleContext;
+
+public class DepartmentRecord extends SampleRecord<TDepartments>
+{
+    private static final long serialVersionUID = 1L;
+
+    public DepartmentRecord(SampleContext context)
+    {
+        super(context, context.getDatabase().DEPARTMENTS);
+    }
+  
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/EmployeeRecord.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/EmployeeRecord.java
new file mode 100644
index 00000000..b5318a20
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/EmployeeRecord.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.db.records;
+
+import org.apache.empire.commons.Options;
+import org.apache.empire.data.Column;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBCommand;
+import org.apache.empire.jakarta.websample.db.SampleDB;
+import org.apache.empire.jakarta.websample.db.SampleDB.TEmployees;
+import org.apache.empire.jakarta.websample.web.SampleContext;
+
+public class EmployeeRecord extends SampleRecord<TEmployees>
+{
+    private static final long serialVersionUID = 1L;
+
+    public EmployeeRecord(SampleContext context)
+    {
+        super(context, context.getDatabase().EMPLOYEES);
+    }
+    
+	/*
+	 * Add some business logic:
+	 * Make all fields read only if employee is retired (except the retired field itself!) 
+	 */
+    @Override
+    public boolean isFieldVisible(Column column)
+    {
+        if (!super.isFieldVisible(column))
+            return false;
+        // UPDATE_TIMESTAMP
+        if (column==T.UPDATE_TIMESTAMP && isNew())
+            return false; // invisible for new records
+        // default
+        return true; 
+    }
+    
+    @Override
+    public boolean isFieldReadOnly(Column column)
+    {
+    	if (column!=T.RETIRED && getBoolean(T.RETIRED))
+    	{	/* Employee is retired */
+    		return true;
+    	}	
+    	return super.isFieldReadOnly(column);	
+    }
+
+    @Override
+    public Options getFieldOptions(DBColumn column)
+    {
+        if (column.equals(T.DEPARTMENT_ID))
+        {
+            SampleDB db = (SampleDB) getDatabase();
+            DBCommand cmd = context.createCommand();
+            cmd.select(db.DEPARTMENTS.ID);
+            cmd.select(db.DEPARTMENTS.NAME);
+            cmd.orderBy(db.DEPARTMENTS.NAME);
+            return context.getUtils().queryOptionList(cmd);
+        }
+        // base class implementation
+        return super.getFieldOptions(column);
+    }
+
+    // Sample Implementation for Department Record
+    public DepartmentRecord getDepartmentRecord()
+    {
+        DepartmentRecord rec = new DepartmentRecord((SampleContext)getContext());
+        rec.read(this.getInt(T.DEPARTMENT_ID));
+        return rec;
+    }
+
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/SampleRecord.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/SampleRecord.java
new file mode 100644
index 00000000..f114e766
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/db/records/SampleRecord.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.db.records;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import jakarta.faces.context.FacesContext;
+
+import org.apache.empire.commons.ClassUtils;
+import org.apache.empire.db.DBContext;
+import org.apache.empire.db.DBRecord;
+import org.apache.empire.jakarta.app.FacesUtils;
+import org.apache.empire.jakarta.websample.db.SampleTable;
+import org.apache.empire.jakarta.websample.web.pages.SamplePage;
+
+public abstract class SampleRecord<T extends SampleTable> extends DBRecord
+{
+    private static final long   serialVersionUID = 1L;
+
+    /*
+     * Store the table for convenience
+     */
+    protected final transient T T;  /* transient for serialization */
+
+    /**
+     * Custom deserialization for transient T.
+     */
+    private void readObject(ObjectInputStream strm)
+        throws IOException, ClassNotFoundException
+    {   // Restore T
+        @SuppressWarnings("unchecked")
+        T table = (T)super.getRowSet();
+        ClassUtils.setPrivateFieldValue(SampleRecord.class, this, "T", table);
+        // read the rest
+        strm.defaultReadObject();
+    }
+
+    @Override
+    protected void writeContext(ObjectOutputStream strm) throws IOException
+    {
+        // Nothing
+    }
+    
+    @Override
+    protected DBContext readContext(ObjectInputStream strm)  throws IOException, ClassNotFoundException
+    {
+        SamplePage page = FacesUtils.getPage(FacesContext.getCurrentInstance());
+        return page.getSampleContext();
+    }
+
+    /**
+     * Constructor for SampleRecord
+     * @param context
+     * @param table
+     */
+    protected SampleRecord(DBContext context, T table)
+    {
+        super(context, table);
+        this.T = table;
+    }
+
+    /**
+     * Returns the table this record is based upon.
+     * @return The table this record is based upon.
+     */
+    public T getTable()
+    {
+        return T;
+    }
+
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AppExceptionHandler.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AppExceptionHandler.java
new file mode 100644
index 00000000..074bfbbf
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AppExceptionHandler.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import jakarta.faces.FacesException;
+import jakarta.faces.application.FacesMessage;
+import jakarta.faces.context.ExceptionHandler;
+import jakarta.faces.context.ExceptionHandlerWrapper;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.ExceptionQueuedEvent;
+import jakarta.faces.event.ExceptionQueuedEventContext;
+
+import org.apache.empire.exceptions.EmpireException;
+import org.apache.empire.exceptions.InternalException;
+import org.apache.empire.jakarta.app.FacesUtils;
+import org.apache.empire.jakarta.app.TextResolver;
+import org.apache.empire.jakarta.app.WebApplication;
+import org.apache.empire.jakarta.websample.web.pages.SamplePages;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * AppExceptionHandler
+ */
+public class AppExceptionHandler extends ExceptionHandlerWrapper
+{
+    private static final Logger    log = LoggerFactory.getLogger(AppExceptionHandler.class);
+    
+    private final ExceptionHandler delegate;
+
+    public AppExceptionHandler(ExceptionHandler delegate)
+    {
+        // super(delegate);  --- Use this in MyFaces 2.3.x
+        this.delegate = delegate;
+    }
+    
+    @Override
+    public ExceptionHandler getWrapped()
+    {
+        return delegate;
+    }
+
+    @Override
+    public void handle()
+        throws FacesException
+    {
+        // boolean redirectToErrorPage = false;
+        Throwable rootCause = null;
+        Iterator<ExceptionQueuedEvent> events = getUnhandledExceptionQueuedEvents().iterator();
+
+        // log each error
+        while (events.hasNext())
+        {
+            ExceptionQueuedEvent event = events.next();
+            // handle
+            try {
+            
+                ExceptionQueuedEventContext source = (ExceptionQueuedEventContext) event.getSource();
+                FacesContext context = source.getContext();
+                Throwable t = source.getException();
+                
+                // check t
+                if (t==null)
+                {   log.error("Cannot handle exception. Exception not supplied with context!");
+                    setErrorMessage(context, null);
+                    continue;
+                }    
+
+                // find root
+                rootCause = t.getCause();
+                // second option: getRootCause
+                if (rootCause == null)
+                {   // get cause
+                    rootCause = getRootCause(t);
+                }
+                // third option: use t
+                if (rootCause == null)
+                {
+                    rootCause = t;
+                }
+                
+                // Walk up the tree
+                while (true)
+                {   // if its an empire-exception: game over
+                    if (rootCause instanceof EmpireException)
+                        break;
+                    // has root cause
+                    t = rootCause.getCause();
+                    if (t==null)
+                        break; 
+                    // yes, continue search
+                    rootCause = t;
+                }
+                
+                /*
+                if (rootCause instanceof org.icefaces.application.SessionExpiredException)
+                {   // expired
+                    log.info("Handling SessionExpiredException. No error message is set.");
+                    continue;
+                }
+                */
+
+                // set message
+                String msg = "Handling exception of type "+rootCause.getClass().getSimpleName();
+                // log
+                // msg = appendSessionInfo(context, msg);
+                log.error(msg, rootCause);
+                
+                // set message
+                if (!(rootCause instanceof EmpireException))
+                {   // Wrap as internal exception
+                    rootCause = new InternalException(rootCause);
+                }
+                setErrorMessage(context, rootCause);
+                
+            } catch(Throwable t2) {
+                log.error("Failed to handle exception: "+t2.getMessage(), t2);
+            } finally {
+                events.remove();
+            }
+        }
+
+        // if an error has been found
+        /*
+        if (redirectToErrorPage)
+        {
+            HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
+            try
+            {
+                // put error to session map (will be cleared by ErrorPage)
+                if (rootCause != null)
+                {
+                    FacesUtils.getFin2Session().setError(rootCause);
+                }
+                // redirect to error page
+                String errorPage = FacesUtils.getContextPath() + "/pages/error.iface";
+                Fin2ExceptionHandler.log.debug("Redirecting to error page at '" + errorPage + "'...");
+                FacesUtils.redirectDirectly(errorPage);
+            }
+            catch (Exception e)
+            {
+                Fin2ExceptionHandler.log.error("Error during exception handling.", e);
+                throw new FacesException(e);
+            }
+        }
+        */
+        // let next handler deal
+        getWrapped().handle();
+    }
+    
+    /*
+    private String appendSessionInfo(FacesContext context, String msg)
+    {
+        // Provide session info
+        Fin2Session session = FinUtils.getFin2Session(context, false);
+        Fin2User user = (session!=null ? session.getUser() : null);
+        if (user!=null)
+        {   // Add user information
+            String viewId;
+            if (context.getViewRoot()!=null)
+                viewId = context.getViewRoot().getViewId();
+            else {
+                viewId = "[NO VIEW]";
+                if (context.getExternalContext()!=null)
+                {   // External Context 
+                    Object request = context.getExternalContext().getRequest(); 
+                    if (request instanceof HttpServletRequest) 
+                    {   // The HttpServletRequest Servlet Path
+                        String path = ((HttpServletRequest)request).getServletPath();
+                        viewId += " ("+String.valueOf(path)+")";
+                    }    
+                    else if (request!=null)
+                        viewId += " {"+request.getClass().getName()+"}";
+                }
+            }
+            // Message
+            msg += MessageFormat.format(" for User {0} of DUNS {1} on View {2}.", user.getUserId(), user.getDUNS(), viewId);
+        }
+        return msg;
+    }
+    */
+    
+    private void setErrorMessage(FacesContext fContext, Throwable rootCause)
+    {
+        FacesMessage message = new FacesMessage();
+        message.setSeverity(FacesMessage.SEVERITY_ERROR);
+     
+        Map<String, Object> sm = fContext.getExternalContext().getSessionMap();
+        if (sm.containsKey(SampleSession.MANAGED_BEAN_NAME))
+        {
+            TextResolver resolver = WebApplication.getInstance().getTextResolver(fContext);
+            if (rootCause instanceof Exception)
+            {
+                message.setSummary(resolver.getExceptionMessage((Exception)rootCause));
+            }
+            else
+            {
+                message.setSummary(resolver.resolveKey("global_general_error"));
+            }
+            // Add message
+            fContext.addMessage(null, message);
+        }
+        else
+        {
+            log.error("Unable to handle exception, redirecting to StartPage.", rootCause);
+            FacesUtils.redirectDirectly(fContext, SamplePages.LoginPage);
+        }
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AppExceptionHandlerFactory.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AppExceptionHandlerFactory.java
new file mode 100644
index 00000000..693cd344
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AppExceptionHandlerFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import jakarta.faces.context.ExceptionHandler;
+import jakarta.faces.context.ExceptionHandlerFactory;
+
+/**
+ * AppExceptionHandlerFactory
+ */
+public class AppExceptionHandlerFactory extends ExceptionHandlerFactory
+{
+    private final ExceptionHandlerFactory delegate;
+    public AppExceptionHandlerFactory(ExceptionHandlerFactory delegate)
+    {
+        // super(delegate);  --- Use this in MyFaces 2.3.x
+        this.delegate = delegate;
+    }
+    
+    @Override
+    public ExceptionHandlerFactory getWrapped()
+    {
+        return delegate;
+    }
+
+    @Override
+    public ExceptionHandler getExceptionHandler()
+    {
+        return new AppExceptionHandler(getWrapped().getExceptionHandler());
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AuthenticationPhaseListener.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AuthenticationPhaseListener.java
new file mode 100644
index 00000000..ae26fefb
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/AuthenticationPhaseListener.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.PhaseEvent;
+import jakarta.faces.event.PhaseId;
+import jakarta.faces.event.PhaseListener;
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.jakarta.app.FacesUtils;
+import org.apache.empire.jakarta.websample.web.pages.SamplePages;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AuthenticationPhaseListener implements PhaseListener
+{
+    private static final long   serialVersionUID = 1L;
+    private static final Logger log              = LoggerFactory.getLogger(AuthenticationPhaseListener.class);
+
+    private static final String LOGOUT_PARAM     = "logout";    // must be ?logout=true
+//    private static final String LANGUAGE_PARAM   = "prefLanguage";    // must be ?logout=true
+    
+    public AuthenticationPhaseListener()
+    {
+    }
+
+    @Override
+    public PhaseId getPhaseId()
+    {
+        return PhaseId.RESTORE_VIEW;
+    }
+
+    @Override
+    public void beforePhase(PhaseEvent pe)
+    {
+        // Check App
+        SampleApplication app = SampleUtils.getSampleApplication();
+        if (app == null)
+        {
+            log.error("Application not initialized!");
+            return;
+        }
+        try
+        {
+            FacesContext fc = pe.getFacesContext();
+            // Path Info
+            if (log.isDebugEnabled())
+            {
+                String path = fc.getExternalContext().getRequestServletPath();
+                log.debug("Restoring view for request path {}.", path);
+            }    
+            checkAuthetication(fc, app);
+        }
+        catch (Exception e)
+        {
+            log.error("Authetication exception \"{}\" redirecting to error page!", e.getMessage(), e);
+            String errorPage = getPageUri(app.getSampleConfig().getAccessDeniedPage());
+            FacesUtils.redirectDirectly(pe.getFacesContext(), errorPage);
+        }
+    }
+    
+    @Override
+    public void afterPhase(PhaseEvent event)
+    {
+        
+    }
+    
+    /******************************************************************************************/
+
+    
+    private void checkAuthetication(FacesContext fc, SampleApplication app)
+        throws Exception
+    {
+        SampleSession session = SampleUtils.getSampleSession(fc);
+
+        if (!session.isAuthorized())
+        {   
+            HttpServletRequest req = FacesUtils.getHttpRequest(fc);
+            String reqURI = req.getRequestURI();
+            int iSession = reqURI.indexOf(';');
+            if (iSession>0)
+            	reqURI = reqURI.substring(0,iSession);
+            
+            String loginPage = getPageUri(SamplePages.LoginPage.getOutcome().toString());
+            if (reqURI.equalsIgnoreCase(loginPage))
+            {   
+                log.debug("Showing login page {}.", loginPage);
+                return;
+            }
+            FacesUtils.redirectDirectly(fc, SamplePages.LoginPage);
+        }
+        else
+        {   // Check Logout
+            HttpServletRequest req = FacesUtils.getHttpRequest(fc);
+            Object logout = req.getParameter(LOGOUT_PARAM);
+            if (logout!=null && ObjectUtils.getBoolean(logout))
+            {   // Perform logout
+                doLogout(fc);
+                // return to login page
+                String redirectPage = req.getRequestURI();
+                log.info("User logout performed. Returning to start page {}!", redirectPage);
+                FacesUtils.redirectDirectly(fc, redirectPage);
+            }
+        }
+        
+     
+    }
+    
+    /******************************************************************************************/
+
+    private void doLogout(FacesContext fc)
+    {
+        fc.getExternalContext().invalidateSession();
+    }
+    
+   
+    
+    private String getPageUri(String page)
+    {
+        if (StringUtils.isEmpty(page))
+            throw new InvalidArgumentException("page", page);
+        if (page.startsWith("http"))
+            return page;
+        if (page.startsWith("/")==false)
+            page="/"+page;
+        // Detect page uri
+        String pathPrefix = SampleUtils.getContextPath();
+        String pageUri = pathPrefix + page;
+        return pageUri;
+    }
+
+ 
+    
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleAppStartupListener.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleAppStartupListener.java
new file mode 100644
index 00000000..30f18596
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleAppStartupListener.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import jakarta.servlet.ServletContext;
+
+import org.apache.empire.jakarta.app.FacesConfiguration;
+import org.apache.empire.jakarta.app.WebAppStartupListener;
+import org.apache.empire.jakarta.app.WebApplication;
+
+import jakarta.faces.context.FacesContext;
+
+/**
+ * Custom StartupListener
+ * Faces Configuration is done programmatically 
+ * @author rainer
+ */
+public class SampleAppStartupListener extends WebAppStartupListener
+{
+    public final SampleConfig config = new SampleConfig();
+    
+    public SampleAppStartupListener()
+    {
+        super(FacesConfiguration.class);
+    }
+    
+    @Override
+    protected void initFacesConfiguration(FacesContext startupContext)
+    {
+        ServletContext servletContext = (ServletContext)startupContext.getExternalContext().getContext();
+        config.init(servletContext.getRealPath("WEB-INF/config.xml"));
+        // Load Configuration
+        super.initFacesConfiguration(startupContext);
+    }
+    
+    @Override
+    protected void initWebApplication(WebApplication facesApp, FacesContext startupContext)
+    {
+        // Set Configuration
+        ((SampleApplication)facesApp).setConfig(config);
+        // init now
+        super.initWebApplication(facesApp, startupContext);
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleApplication.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleApplication.java
new file mode 100644
index 00000000..de8f119d
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleApplication.java
@@ -0,0 +1,362 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.time.LocalDate;
+import java.util.Locale;
+
+import jakarta.servlet.ServletContext;
+import javax.sql.DataSource;
+
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.db.DBCommand;
+import org.apache.empire.db.DBContext;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBRecord;
+import org.apache.empire.db.context.DBContextStatic;
+import org.apache.empire.db.context.DBRollbackManager;
+import org.apache.empire.db.context.DBRollbackManager.ReleaseAction;
+import org.apache.empire.dbms.DBMSHandler;
+import org.apache.empire.jakarta.app.WebApplication;
+import org.apache.empire.jakarta.controls.InputControlManager;
+import org.apache.empire.jakarta.custom.controls.FileInputControl;
+import org.apache.empire.jakarta.websample.db.SampleDB;
+import org.apache.empire.jakarta.websample.db.SampleDBwithMeta;
+import org.apache.empire.jakarta.websample.web.pages.SamplePages;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SampleApplication extends WebApplication {
+	// Logger
+	private static final Logger log = LoggerFactory
+			.getLogger(SampleApplication.class);
+
+	protected static final String MANAGED_BEAN_NAME = "sampleApplication";
+	protected static final Locale DEFAULT_LOCALE = Locale.ENGLISH;
+	// Non-Static
+	private SampleConfig config = null; // SampleAppStartupListener.config;
+	private SampleDB sampleDB = null;
+
+	private Connection conn = null;
+
+	public static SampleApplication get() {
+		return (SampleApplication)WebApplication.getInstance();
+	}
+
+	public SampleApplication() { 
+		
+		// trace
+		SampleApplication.log.trace("SampleApplication created");
+	}
+	
+	/**
+	 * Sets the Configuration
+	 * Called only from SampleAppStartupListener
+	 */
+	void setConfig(SampleConfig config) 
+	{
+	    this.config = config;
+	}
+
+	@Override
+	public void init(ServletContext servletContext) {
+		// register all controls
+		// InputControlManager.registerControl("myType", new
+		// MyTypeInputControl());
+
+		// Get a JDBC Connection
+		log.info("*** getJDBCConnection() ***");
+		conn = getJDBCConnection(servletContext);
+
+		log.info("*** initDatabase() ***");
+		initDatabase();
+		
+        log.info("*** initControls() ***");
+		initControls();
+
+		log.info("*** initPages() ***");
+		initPages(servletContext);
+
+		// Set Database to Servlet Context
+		servletContext.setAttribute("db", sampleDB);
+
+		// Done
+	}
+	
+    /**
+     * initializes JSF control components
+     */
+    private void initControls()
+    {
+        // register custom control types
+        InputControlManager.registerControl(new FileInputControl());
+	}
+
+    /*
+     * getJDBCConnection
+     */
+    private Connection getJDBCConnection(ServletContext appContext)
+    {
+        // Establish a new database connection
+        Connection conn = null;
+        String jdbcURL = config.getJdbcURL();
+        if (jdbcURL.indexOf("file:") > 0)
+            jdbcURL = StringUtils.replace(jdbcURL, "file:", "file:" + appContext.getRealPath("/"));
+        // Connect
+        log.info("Connecting to Database'" + jdbcURL + "' / User=" + config.getJdbcUser());
+        try
+        { // Connect to the database
+            Class.forName(config.getJdbcClass()).newInstance();
+            conn = DriverManager.getConnection(jdbcURL, config.getJdbcUser(), config.getJdbcPwd());
+            log.info("Connected successfully");
+            // set the AutoCommit to false this session. You must commit
+            // explicitly now
+            conn.setAutoCommit(false);
+            log.info("AutoCommit is " + conn.getAutoCommit());
+
+        }
+        catch (Exception e)
+        {
+            log.error("Failed to connect directly to '" + config.getJdbcURL() + "' / User=" + config.getJdbcUser());
+            log.error(e.toString());
+            throw new RuntimeException(e);
+        }
+        return conn;
+    }
+
+	private void initDatabase() {
+	    // Create Database with Metadata!
+		sampleDB = new SampleDBwithMeta();
+
+		// Open Database (and create if not existing)
+        DBMSHandler dbmsHandler = getDBMSHandler(config.getDatabaseProvider(), conn);
+        log.info("Opening database '{}' using '{}'", sampleDB.getClass().getSimpleName(), dbmsHandler.getClass().getSimpleName());
+		Connection conn = null;
+		DBContext context = null;
+		try {
+			conn = getConnection(sampleDB);
+			context = new DBContextStatic(dbmsHandler, conn);
+			sampleDB.open(context);
+	        // check if database was just created 
+	        DBCommand cmd = context.createCommand();
+	        cmd.select(sampleDB.EMPLOYEES.count());
+	        if (context.getUtils().querySingleInt(cmd)==0)
+	        {   // Populate Database
+	            populateDatabase(context);
+	        }
+		} finally {
+		    context.discard();
+			releaseConnection(conn, true, null);
+		}
+	}
+
+    /**
+     * Creates an Empire-db DatabaseDriver for the given provider and applies dbms specific configuration 
+     */
+    private DBMSHandler getDBMSHandler(String provider, Connection conn)
+    {
+        try
+        {   // Get Driver Class Name
+            String dbmsHandlerClass = config.getDbmsHandlerClass();
+            if (StringUtils.isEmpty(dbmsHandlerClass))
+                throw new RuntimeException("Configuration error: Element 'dbmsHandlerClass' not found in node 'properties-"+provider+"'");
+
+            // Create dbms
+            DBMSHandler dbms = (DBMSHandler) Class.forName(dbmsHandlerClass).newInstance();
+
+            // Configure dbms
+            config.readProperties(dbms, "properties-"+provider, "dbmsHandlerProperties");
+
+            // done
+            return dbms;
+            
+        } catch (Exception e)
+        {   // catch any checked exception and forward it
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+
+	private void initPages(ServletContext sc) {
+		// register Page Beans
+		new SamplePages().registerPageBeans(this.getFacesImplementation());
+	}
+
+	public SampleDB getDatabase() {
+		return sampleDB;
+	}
+
+	/*
+	 * creates a DDL Script for the entire SampleDB Database then checks if the
+	 * department table exists by running "select count(*) from DEPARTMENTS" if
+	 * the department tables does not exist, the entire dll-script is executed
+	 * line by line
+	 */
+	private void populateDatabase(DBContext context) {
+		// Insert Sample Departments
+		insertDepartmentSampleRecord(context, "Procurement", "ITTK");
+		int idDevDep = insertDepartmentSampleRecord(context, "Development", "ITTK");
+		int idSalDep = insertDepartmentSampleRecord(context, "Sales", 		 "ITTK");
+		// Insert Sample Employees
+		insertEmployeeSampleRecord(context, "Mr.", "Eugen", "Miller", "M",		30000, idDevDep);
+		insertEmployeeSampleRecord(context, "Mr.", "Max", "Mc. Callahan", "M",	22500, idDevDep);
+		insertEmployeeSampleRecord(context, "Mrs.", "Anna", "Smith", "F", 		44250, idSalDep);
+		// Commit
+        context.commit();
+	}
+
+	/*
+	 * Insert a department
+	 */
+	private int insertDepartmentSampleRecord(DBContext context, String department_name, String businessUnit) {
+		// Insert a Department
+		DBRecord rec = new DBRecord(context, sampleDB.DEPARTMENTS);
+		rec.create()
+		   .set(sampleDB.DEPARTMENTS.NAME, department_name)
+		   .set(sampleDB.DEPARTMENTS.BUSINESS_UNIT, businessUnit)
+		   .update();
+		// Return Department ID
+		return rec.getInt(sampleDB.DEPARTMENTS.ID);
+	}
+
+	/*
+	 * Insert a person
+	 */
+	private int insertEmployeeSampleRecord(DBContext context, String salutation, String firstName, String lastName, String gender, int salary, int depID) {
+		// Insert an Employee
+	    SampleDB.TEmployees EMP = sampleDB.EMPLOYEES;
+		DBRecord rec = new DBRecord(context, sampleDB.EMPLOYEES);
+		rec.create()
+		   .set(EMP.SALUTATION, salutation)
+		   .set(EMP.FIRST_NAME, firstName)
+		   .set(EMP.LAST_NAME, lastName)
+		   .set(EMP.GENDER, gender)
+		   .set(EMP.DEPARTMENT_ID, depID)
+           .set(EMP.SALARY, salary)
+		   .update();
+		// insert Payments
+		insertPayments(rec);		
+		// Return Employee ID
+		return rec.getInt(sampleDB.EMPLOYEES.ID);
+	}
+
+    /**
+     * <PRE>
+     * Inserts an Payments for a particular Employee
+     * </PRE>
+     */
+    private void insertPayments(DBRecord employee)
+    {
+        if (employee.isNull(sampleDB.EMPLOYEES.SALARY))
+            return; // No salary
+        // monthlySalary
+        BigDecimal monthlySalary = employee.getDecimal(sampleDB.EMPLOYEES.SALARY).divide(new BigDecimal(12), 2, RoundingMode.HALF_UP);
+        // Insert an Employee
+        LocalDate date = LocalDate.now();
+        date = date.minusDays(date.getDayOfMonth()-1); // first day of this month
+        // Add Payment for each month
+        SampleDB.TPayments PAY = sampleDB.PAYMENTS;
+        DBRecord rec = new DBRecord(employee.getContext(), PAY);
+        int months = (int)(Math.random()*6)+11;
+        for (LocalDate month=date.minusMonths(months); !month.isAfter(date); month=month.plusMonths(1))
+        {
+            BigDecimal variation = new BigDecimal((Math.random()*200) - 100.0);
+            variation = variation.setScale(2, RoundingMode.HALF_UP);
+            // insert
+            rec.create(DBRecord.key(employee.getIdentity(), month.getYear(), month.getMonth()));
+            rec.set(PAY.AMOUNT, monthlySalary.add(variation));
+            rec.update();
+        }
+    }
+
+	@Override
+	/**
+	 * returns null as connection pooling is not used
+	 */
+	protected DataSource getAppDataSource(DBDatabase db) {
+		return null;
+	}
+
+    /**
+     * Usually returns a connection from the connection pool
+     * As the sample does not use connection pooling, a static connection is returned
+     * @return a connection for the requested db
+     */
+    @Override
+    protected Connection getConnection(DBDatabase db)
+    {
+    	return conn;
+    }
+
+    /**
+     * Usually releases a connection from the connection pool
+     * As the sample does not use connection pooling, only a commit or rollback is performed
+     */
+    @Override
+    protected void releaseConnection(Connection conn, boolean commit, DBRollbackManager dbrm)
+    {
+        try
+        { // release connection
+            if (conn == null)
+            {
+                return;
+            }
+            log.trace("releasing Connection {}", conn.hashCode());
+            // Commit or rollback connection depending on the exit code
+            if (commit)
+            {   // success: commit all changes
+                if (dbrm!=null)
+                    dbrm.releaseConnection(conn, ReleaseAction.Discard);  // before commit
+                conn.commit();
+                log.debug("REQUEST commited.");
+            }
+            else
+            {   // failure: rollback all changes
+                conn.rollback();
+                if (dbrm!=null)
+                    dbrm.releaseConnection(conn, ReleaseAction.Rollback); // after rollback
+                log.debug("REQUEST rolled back.");
+            }
+            // Release Connection (don't do that here!)
+            // conn.close();
+            // done
+            if (log.isDebugEnabled())
+                log.debug("Connection released but not closed.");
+        }
+        catch (SQLException e)
+        {
+            log.error("Error releasing connection", e);
+            e.printStackTrace();
+        }
+    }
+	
+	protected SampleConfig getSampleConfig() {
+		if (config == null) {
+			SampleApplication.log
+					.error("Application configuration not initialized!");
+		}
+		return config;
+	}
+
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleConfig.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleConfig.java
new file mode 100644
index 00000000..0105c7c3
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleConfig.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import org.apache.empire.xml.XMLConfiguration;
+import org.apache.empire.xml.XMLUtil;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+
+public class SampleConfig extends XMLConfiguration
+{
+    // Logger
+    private static final Logger log = LoggerFactory.getLogger(SampleConfig.class);
+    
+    private String loggingNodeName  = "log4j:configuration";
+
+    private String jndiContext      = "";
+
+    private String databaseProvider = "oracle";
+
+    private String jdbcClass        = "oracle.jdbc.driver.OracleDriver";
+
+    private String jdbcURL          = "jdbc:oracle:thin:@192.168.0.2:1521:ora10";
+
+    private String dataSource       = "java:comp/env/jdbc/sampleDataSource";
+
+    private String jdbcUser         = "DBSAMPLE";
+
+    private String jdbcPwd          = "DBSAMPLE";
+
+    private String dbmsHandlerClass = "";
+    
+    /**
+     * Initialize the configuration
+     * 
+     * @param filename the file to read
+     * 
+     * @return true on success
+     */
+    public boolean init(String filename)
+    {
+        try
+        {   // init
+            super.init(filename, false);
+            // Init Logging
+            if (initLogging() == false)
+                return false;
+            // Read config
+            log.info("*** init Configuration ***");
+            log.info("Config file is '{}'", filename);
+            // Read the properties
+            readProperties(this, "properties");
+            readProperties(this, "properties-" + databaseProvider);
+        } catch (Exception e)
+        {
+            log.error(e.getLocalizedMessage());
+            return false;
+        }
+        return true;
+    }
+
+    public String getDatabaseProvider()
+    {
+        return databaseProvider;
+    }
+
+    public String getJdbcClass()
+    {
+        return jdbcClass;
+    }
+
+    public void setJdbcClass(String jdbcClass)
+    {
+        this.jdbcClass = jdbcClass;
+    }
+
+    public String getJdbcPwd()
+    {
+        return jdbcPwd;
+    }
+
+    public void setJdbcPwd(String jdbcPwd)
+    {
+        this.jdbcPwd = jdbcPwd;
+    }
+
+    public String getJdbcURL()
+    {
+        return jdbcURL;
+    }
+
+    public String getDbmsHandlerClass()
+    {
+        return dbmsHandlerClass;
+    }
+
+    // ------- Setters -------
+
+    public void setDatabaseProvider(String databaseProvider)
+    {
+        this.databaseProvider = databaseProvider;
+    }
+
+    public void setJdbcURL(String jdbcURL)
+    {
+        this.jdbcURL = jdbcURL;
+    }
+
+    public String getJdbcUser()
+    {
+        return jdbcUser;
+    }
+
+    public void setJdbcUser(String jdbcUser)
+    {
+        this.jdbcUser = jdbcUser;
+    }
+
+    public void setDbmsHandlerClass(String dbmsHandlerClass)
+    {
+        this.dbmsHandlerClass = dbmsHandlerClass;
+    }
+
+    /**
+     * Init logging using Log4J's DOMConfigurator
+     * 
+     * @return
+     */
+    private boolean initLogging()
+    {
+        // Get configuration root node
+        Element rootNode = getRootNode();
+        if (rootNode == null)
+        {
+            return false;
+        }
+        // Find log configuration node
+        Element loggingNode = XMLUtil.findFirstChild(rootNode, loggingNodeName);
+        if (loggingNode == null)
+        { // log configuration node not found
+            SampleConfig.log.error("Log configuration node {} has not been found. Logging has not been configured.", loggingNodeName);
+            return false;
+        }
+        // Init Log4J
+        DOMConfigurator.configure(loggingNode);
+        // done
+        SampleConfig.log.info("Logging successfully configured from node {}.", loggingNodeName);
+        return true;
+    }
+
+    public String getDataSourceName()
+    {
+        return dataSource;
+    }
+
+    public void setDataSourceName(String dataSource)
+    {
+        this.dataSource = dataSource;
+    }
+
+
+    public String getJndiContextFactoryName()
+    {
+        return jndiContext;
+    }
+
+    public void setJndiContextFactoryName(String jndiContext)
+    {
+        this.jndiContext = jndiContext;
+    }
+
+    public String getAccessDeniedPage()
+    {
+        return null;
+    }
+
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleContext.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleContext.java
new file mode 100644
index 00000000..95aa92d6
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleContext.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+import jakarta.faces.context.FacesContext;
+
+import org.apache.empire.commons.ClassUtils;
+import org.apache.empire.jakarta.app.WebDBContext;
+import org.apache.empire.jakarta.websample.db.SampleDB;
+
+/**
+ * This is an example for a custom DBContext extension
+ * @author rainer
+ */
+public class SampleContext extends WebDBContext<SampleDB>
+{
+    private static final long serialVersionUID = 1L;
+    
+    private final transient SampleSession session;
+    
+    /**
+     * Custom serialization for transient fields.
+     * 
+     */
+    private void writeObject(ObjectOutputStream strm) throws IOException 
+    {   // write the object
+        strm.defaultWriteObject();
+    }
+    
+    /**
+     * Custom deserialization for transient fields.
+     */
+    private void readObject(ObjectInputStream strm) 
+        throws IOException, ClassNotFoundException
+    {   // Restore Session
+        FacesContext fc = FacesContext.getCurrentInstance();
+        SampleSession session = SampleUtils.getSampleSession(fc);
+        ClassUtils.setPrivateFieldValue(SampleContext.class, this, "session", session);
+        // Read the object
+        strm.defaultReadObject();
+    }
+    
+    /**
+     * Constructs a SampleContext 
+     */
+    public SampleContext(SampleDB db, SampleSession session)
+    {
+        super(db);
+        // the session
+        this.session = session;
+    }
+
+    public SampleUser getUser()
+    {
+        return session.getUser();
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleSession.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleSession.java
new file mode 100644
index 00000000..3725931b
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleSession.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SampleSession // *Deprecated* implements Serializable
+{
+    // *Deprecated* private static final long serialVersionUID = 1L;
+
+    private static final Logger   log               = LoggerFactory.getLogger(SampleSession.class);
+
+    protected static final String MANAGED_BEAN_NAME = "sampleSession";
+
+    private SampleUser            user              = null;
+
+    private String                page              = "loginPage";
+
+    private Throwable             rootCause;
+
+    public SampleSession()
+    {
+        SampleSession.log.info("SESSION: created.");
+    }
+
+    public void setPage(String page)
+    {
+        this.page = page;
+    }
+
+    public String getPage()
+    {
+        return this.page;
+    }
+
+    public void setError(Throwable rootCause)
+    {
+        this.rootCause = rootCause;
+    }
+
+    public Throwable getError()
+    {
+        return this.rootCause;
+    }
+
+    public boolean isAuthorized()
+    {
+        return user != null;
+    }
+    
+    public SampleUser getUser()
+    {
+        return user;
+    }
+
+    public void setUser(SampleUser user)
+    {
+        this.user = user;
+    }
+
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleUser.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleUser.java
new file mode 100644
index 00000000..a9ef8160
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleUser.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import java.util.Locale;
+
+public class SampleUser // *Deprecated* implements Serializable
+{
+    // *Deprecated* private static final long serialVersionUID = 1L;
+
+    protected static final String MANAGED_BEAN_NAME = "sampleUser";
+
+    private String                userName          = "test";
+    private String                password          = "";
+    private Locale                language          = SampleApplication.DEFAULT_LOCALE;
+
+    public SampleUser()
+    {
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public String getUserName()
+    {
+        return userName;
+    }
+
+    public void setUserName(String name)
+    {
+        this.userName = name;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    public Locale getLocale()
+    {
+        return this.language;
+    }
+    
+    public void setLocale(Locale locale)
+    {
+        this.language = locale;
+    }
+
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleUtils.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleUtils.java
new file mode 100644
index 00000000..9991f7b1
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/SampleUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web;
+
+import jakarta.faces.context.FacesContext;
+
+import org.apache.empire.jakarta.app.FacesUtils;
+import org.apache.empire.jakarta.utils.ParameterMap;
+import org.apache.empire.jakarta.websample.db.SampleDB;
+
+public class SampleUtils extends FacesUtils
+{
+    public static SampleApplication getSampleApplication()
+    {
+        return SampleApplication.get();
+    }
+
+    public static SampleDB getDatabase()
+    {
+        return getSampleApplication().getDatabase();
+    }
+
+    public static SampleUser getSampleUser()
+    {
+        FacesContext fc = getContext();
+        return (SampleUser) getManagedBean(fc, SampleUser.MANAGED_BEAN_NAME);
+    }
+
+    public static SampleSession getSampleSession(FacesContext fc)
+    {
+        return (SampleSession) getManagedBean(fc, SampleSession.MANAGED_BEAN_NAME);
+    }
+
+    public static SampleSession getSampleSession()
+    {
+        return getSampleSession(FacesContext.getCurrentInstance());
+    }
+
+    public static ParameterMap getParameterMap()
+    {
+        return getParameterMap(getContext());
+    }
+
+    public static String getContextPath()
+    {
+        return FacesContext.getCurrentInstance().getExternalContext().getRequestContextPath();
+    }
+
+    public static Object getRequestAttribute(final String key)
+    {
+        FacesContext fc = getContext();
+        return getRequestAttribute(fc, key);
+    }
+
+    public static void setRequestAttribute(final String key, Object value)
+    {
+        FacesContext fc = getContext();
+        setRequestAttribute(fc, key, value);
+    }
+
+    public static <T> T getManagedBean(final Class<T> cls)
+    {
+        FacesContext fc = getContext();
+        return getManagedBean(fc, cls);
+    }
+
+    /* file */
+
+    public static String getRealPath(String path)
+    {
+        FacesContext fc = getContext();
+        return getRealPath(fc, path);
+    }
+
+    public static String getFilePath(String path, String file)
+    {
+        FacesContext fc = getContext();
+        return getFilePath(fc, path, file);
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/objects/EmployeeSearchFilter.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/objects/EmployeeSearchFilter.java
new file mode 100644
index 00000000..0ecab457
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/objects/EmployeeSearchFilter.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.objects;
+
+
+public class EmployeeSearchFilter // *Deprecated* implements Serializable
+{
+    // *Deprecated* private static final long serialVersionUID = 1L;
+
+    private String            employeeId;
+    private String            firstName;
+    private String            lastName;
+    private String            departmentId;
+    private String            gender;
+
+    public EmployeeSearchFilter()
+    {
+        resetFilter();
+    }
+
+    public void resetFilter()
+    {
+        employeeId = "";
+        firstName = "";
+        lastName = "";
+        departmentId = "";
+        gender = "";
+    }
+
+    /**
+     * Additional getter for addSearchConstraint(queryCmd, EMP.ID, filter);
+     * @return the employeeId
+     */
+    public String getId()
+    {
+        return employeeId;
+    }
+
+    public String getEmployeeId()
+    {
+        return employeeId;
+    }
+
+    public void setEmployeeId(String employeeId)
+    {
+        this.employeeId = employeeId;
+    }
+
+    public String getDepartmentId()
+    {
+        return departmentId;
+    }
+
+    public void setDepartmentId(String departmentId)
+    {
+        this.departmentId = departmentId;
+    }
+
+    public String getFirstName()
+    {
+        return firstName;
+    }
+
+    public void setFirstName(String firstName)
+    {
+        this.firstName = firstName;
+    }
+
+    public String getLastName()
+    {
+        return lastName;
+    }
+
+    public void setLastName(String lastName)
+    {
+        this.lastName = lastName;
+    }
+
+	public String getGender() {
+		return gender;
+	}
+
+	public void setGender(String gender) {
+		this.gender = gender;
+	}
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/objects/User.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/objects/User.java
new file mode 100644
index 00000000..0c6b3437
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/objects/User.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.objects;
+
+
+public class User // *Deprecated* implements Serializable
+{
+    // *Deprecated* private static final long serialVersionUID = 1L;
+
+    private String            name             = "";
+
+    private String            password         = "";
+
+    public String getName()
+    {
+        return name;
+    }
+
+    public void setName(String name)
+    {
+        this.name = name;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/EmployeeDetailPage.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/EmployeeDetailPage.java
new file mode 100644
index 00000000..eb6df0a6
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/EmployeeDetailPage.java
@@ -0,0 +1,178 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.pages;
+
+import java.util.List;
+
+import jakarta.faces.event.ActionEvent;
+
+import org.apache.empire.commons.ClassUtils;
+import org.apache.empire.data.list.DataListEntry;
+import org.apache.empire.db.DBCommand;
+import org.apache.empire.jakarta.pageelements.RecordPageElement;
+import org.apache.empire.jakarta.pages.PageOutcome;
+import org.apache.empire.jakarta.websample.db.SampleDB;
+import org.apache.empire.jakarta.websample.db.records.EmployeeRecord;
+import org.apache.empire.jakarta.websample.web.SampleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EmployeeDetailPage extends SamplePage
+{
+
+    private static final Logger               log               = LoggerFactory.getLogger(EmployeeDetailPage.class);
+
+    private String                            idParam;
+
+    private RecordPageElement<EmployeeRecord> employee;
+
+    private int                               activeTab         = 0;
+    
+    private List<DataListEntry>               payments;
+    
+    public EmployeeDetailPage()
+    {
+        log.trace("EmployeeDetailPage created");
+
+        EmployeeRecord emplRec = new EmployeeRecord(getSampleContext());
+        employee = new RecordPageElement<EmployeeRecord>(this, emplRec.getTable(), emplRec);
+    }
+
+    public String getIdParam()
+    {
+        return this.idParam;
+    }
+
+    public void setIdParam(String idParam)
+    {
+        log.info("EmployeeDetailPage idParam = {}.", idParam);
+        this.idParam = idParam;
+    }
+
+    public RecordPageElement<EmployeeRecord> getEmployee()
+    {
+        return employee;
+    }
+
+    public EmployeeRecord getEmployeeRecord()
+    {
+        return employee.getRecord();
+    }
+
+    public List<DataListEntry> getPayments()
+    {
+        return payments;
+    }
+
+    public int getActiveTab()
+    {
+        return activeTab;
+    }
+
+    public void setActiveTab(int activeTab)
+    {
+        this.activeTab = activeTab;
+    }
+
+    @Override
+    public void doInit()
+    { // Notify Elements
+        if (!employee.getRecord().isValid())
+        {
+            employee.reloadRecord();
+            // Load payment data
+            loadPaymentData();
+        }
+    }
+
+    public void doLoad()
+    {
+        log.info("EmployeeDetailPage Loading entryId {}.", this.idParam);
+        // load the record
+        this.employee.loadRecord(this.idParam);
+        // Load payment data
+        loadPaymentData();
+    }
+
+    public void doCreate()
+    {
+        // use create(null) to defer primaryKey generation
+        getEmployeeRecord().create(null);
+        doRefresh();
+    }
+    
+    public PageOutcome doSave()
+    {
+        getEmployeeRecord().update();
+        
+        /* test transaction 
+        SampleDB db = this.getDatabase();
+        if (getEmployeeRecord().isNull(db.T_EMPLOYEES.PHONE_NUMBER))
+            throw new MiscellaneousErrorException("Phone number must not be empty!");
+        */  
+        
+        return getParentOutcome(true);
+    }
+    
+    public void doTestSerialization(ActionEvent ae)
+    {
+        /* test serialization */
+        EmployeeRecord before = getEmployeeRecord();
+        EmployeeRecord after  = ClassUtils.testSerialization(EmployeeRecord.class, before);
+        
+        addInfoMessage("!employeeDetail_testMessage");
+        addInfoMessage("Rowset is {0}",  (after.getRowSet() ==before.getRowSet())  ? "Same" : "Different");
+        addInfoMessage("Context is {0}", (after.getContext()==before.getContext()) ? "Same" : "Different");
+        after.read(before.getKey());
+
+        addInfoMessage("!global_status", after.isValid() ? "!global_valid" : "!global_invalid");
+    }
+
+    public PageOutcome doDelete()
+    {
+        getEmployeeRecord().delete();
+        return getParentOutcome(true);
+    }
+    
+    public PageOutcome doCancel()
+    {
+        return getParentOutcome(true);
+    }
+
+    public void onTabChanged(int newPage)
+    {
+        log.debug("onTabChanged " + newPage);
+    }
+    
+    private void loadPaymentData()
+    {
+        SampleContext context = getSampleContext();
+        SampleDB db = this.getDatabase();
+        SampleDB.TPayments PAY = db.PAYMENTS;
+        
+        DBCommand cmd = context.createCommand();
+        cmd.select(PAY.YEAR, PAY.MONTH, PAY.AMOUNT);
+        cmd.where(PAY.EMPLOYEE_ID.is(employee.getRecord().getIdentity()));
+        cmd.orderBy(PAY.YEAR.desc(), PAY.MONTH.desc());
+
+        this.payments = context.getUtils().queryDataList(cmd); 
+        log.info("{} payments have been loaded", this.payments.size());
+    }
+    
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/EmployeeListPage.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/EmployeeListPage.java
new file mode 100644
index 00000000..643c33e1
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/EmployeeListPage.java
@@ -0,0 +1,267 @@
+/*
+
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.pages;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.commons.beanutils.PropertyUtils;
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.commons.Options;
+import org.apache.empire.db.DBColumn;
+import org.apache.empire.db.DBColumnExpr;
+import org.apache.empire.db.DBCommand;
+import org.apache.empire.exceptions.BeanPropertyGetException;
+import org.apache.empire.jakarta.pageelements.BeanListPageElement;
+import org.apache.empire.jakarta.pageelements.ListPageElement;
+import org.apache.empire.jakarta.pageelements.ListPageElement.ParameterizedItem;
+import org.apache.empire.jakarta.websample.db.SampleDB.Gender;
+import org.apache.empire.jakarta.websample.db.SampleDB.TDepartments;
+import org.apache.empire.jakarta.websample.db.SampleDB.TEmployees;
+import org.apache.empire.jakarta.websample.web.SampleUtils;
+import org.apache.empire.jakarta.websample.web.objects.EmployeeSearchFilter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class EmployeeListPage extends SamplePage
+{
+
+	private static final Logger log = LoggerFactory.getLogger(EmployeeListPage.class);
+
+    private BeanListPageElement<EmployeeListEntry> employees;
+    
+    public static class EmployeeListEntry extends ListPageElement.SelectableItem implements ParameterizedItem
+    {
+        // *Deprecated* private static final long serialVersionUID = 1L;
+
+        private int               id; // employeeId;
+        private String            name;
+        private Gender            gender;
+        private Date              dateOfBirth;
+        private String            department;
+        private boolean	 	      retired;
+        private String            idParam;
+
+        /**
+         * Implements ParameterizedItem.
+         * Used to uniquely identify this entry for selection and navigation 
+         */
+        @Override
+        public String getIdParam()
+        {
+            return this.idParam;
+        }
+
+        /**
+         * Implements ParameterizedItem.
+         * This will automatically set the item idParam for navigation 
+         */
+        @Override
+        public void setIdParam(String idParam)
+        {
+            this.idParam = idParam;
+        }
+
+        public int getId()
+        {
+            return id;
+        }
+
+        public void setId(int id)
+        {
+            this.id = id;
+        }
+
+        public String getName()
+        {
+            return name;
+        }
+
+        public void setName(String name)
+        {
+            this.name = name;
+        }
+
+        public Gender getGender()
+        {
+            return gender;
+        }
+
+        public void setGender(Gender gender)
+        {
+            this.gender = gender;
+        }
+
+        public Date getDateOfBirth()
+        {
+            return dateOfBirth;
+        }
+
+        public void setDateOfBirth(Date dateOfBirth)
+        {
+            this.dateOfBirth = dateOfBirth;
+        }
+
+        public String getDepartment()
+        {
+            return department;
+        }
+
+        public void setDepartment(String department)
+        {
+            this.department = department;
+        }
+
+		public boolean isRetired() {
+			return retired;
+		}
+
+		public void setRetired(boolean retired) {
+			this.retired = retired;
+		}
+
+    }
+
+    public EmployeeListPage()
+    {
+        EmployeeListPage.log.trace("EmployeeListPage created");
+        TEmployees EMP = getDatabase().EMPLOYEES;
+
+        // create the Employees List page element
+        employees = new BeanListPageElement<EmployeeListEntry>(this, EmployeeListEntry.class, getSampleContext(), EMP.ID);
+    }
+
+    
+    public EmployeeSearchFilter getSearchFilter()
+    {
+        return SampleUtils.getManagedBean(EmployeeSearchFilter.class);
+    }
+    
+    public ListPageElement<EmployeeListEntry> getEmployees()
+    {
+        return employees;
+    }
+
+    /*** Action Section ***/
+
+    @Override
+    public void doInit()
+    { // Notify Elements
+        super.doInit();
+    }
+    
+    public void doResetSearch()
+    {
+        getSearchFilter().resetFilter();
+        this.employees.clearItems();
+    }
+    
+    public void doSearch()
+    {
+        TDepartments DEP = getDatabase().DEPARTMENTS;
+        TEmployees EMP = getDatabase().EMPLOYEES;
+
+        DBColumnExpr FULL_NAME = EMP.LAST_NAME.append(", ").append(EMP.FIRST_NAME).as("NAME");
+        DBColumnExpr DEPARTMENT = DEP.NAME.as("DEPARTMENT");
+
+        DBCommand queryCmd = createQueryCommand();
+
+        queryCmd.select(EMP.ID, FULL_NAME);
+        queryCmd.select(EMP.GENDER, EMP.DATE_OF_BIRTH, EMP.RETIRED);
+        // queryCmd.select(EMP.RETIRED.decode(true, "X", "-"));
+        queryCmd.select(DEPARTMENT);
+
+        queryCmd.join(DEP.ID, EMP.DEPARTMENT_ID);
+        queryCmd.orderBy(EMP.FIRST_NAME);
+        
+        addAllConstraints(queryCmd);
+
+        employees.initItems(queryCmd);
+    }
+
+
+    public Options getDepartmentOptions()
+    {
+    	TDepartments DEP = getDatabase().DEPARTMENTS;
+
+    	DBCommand queryCmd = createQueryCommand();
+    	queryCmd.select(DEP.ID, DEP.NAME);
+    	
+        return getSampleContext().getUtils().queryOptionList(queryCmd);
+    }
+    
+    
+    protected void addAllConstraints(DBCommand queryCmd)
+    {
+        TEmployees EMP = getDatabase().EMPLOYEES;
+        EmployeeSearchFilter filter = getSearchFilter();
+        
+        addSearchConstraint(queryCmd, EMP.ID, filter);
+        addSearchConstraint(queryCmd, EMP.FIRST_NAME, filter);
+        addSearchConstraint(queryCmd, EMP.LAST_NAME, filter);
+        addSearchConstraint(queryCmd, EMP.GENDER, filter);
+        addSearchConstraint(queryCmd, EMP.DEPARTMENT_ID, filter);
+    }
+    
+    private void addSearchConstraint(DBCommand cmd, DBColumn col, Object bean)
+    {
+        Object value;
+        try
+        {
+            value = PropertyUtils.getProperty(bean, col.getBeanPropertyName());
+            if (ObjectUtils.isEmpty(value))
+                return;
+            // is it an array
+            if (value instanceof Collection<?> || value.getClass().isArray())
+            {
+                cmd.where(col.in(value));
+                return;
+            }
+            // text
+            if (col.getOptions() == null && col.getDataType().isText())
+            {
+                StringBuilder b = new StringBuilder();
+                b.append("%");
+                b.append(((String) value).toUpperCase());
+                b.append("%");
+                cmd.where(col.upper().like(b.toString()));
+                return;
+            }
+            // value
+            cmd.where(col.is(value));
+            return;
+        }
+        catch (IllegalAccessException e)
+        {
+            throw new BeanPropertyGetException(bean, col.getBeanPropertyName(), e);
+        }
+        catch (InvocationTargetException e)
+        {
+            throw new BeanPropertyGetException(bean, col.getBeanPropertyName(), e);
+        }
+        catch (NoSuchMethodException e)
+        {
+            throw new BeanPropertyGetException(bean, col.getBeanPropertyName(), e);
+        }
+    }
+
+    
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/ErrorPage.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/ErrorPage.java
new file mode 100644
index 00000000..ead91891
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/ErrorPage.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.pages;
+
+
+public class ErrorPage extends SamplePage
+{
+    
+    private boolean showDetails = false;
+
+    public boolean isShowDetails()
+    {
+        return this.showDetails;
+    }
+
+    public void setShowDetails(boolean showDetails)
+    {
+        this.showDetails = showDetails;
+    }
+
+    public void toggleShowDetails()
+    {
+        this.showDetails = !this.showDetails;
+    }
+
+//    public String getMessage()
+//    {
+//        if (getSampleSession().getError() != null)
+//        {
+//            return getSampleSession().getError().getMessage();
+//        }
+//        return "NULL";
+//    }
+//
+//    public String getType()
+//    {
+//        if (getSampleSession().getError() != null)
+//        {
+//            return getSampleSession().getError().getClass().getName();
+//        }
+//        return "NULL";
+//    }
+
+//    public List<String> getTrace()
+//    {
+//        List<String> trace = new ArrayList<String>();
+//        if (getSampleSession().getError() != null && getSampleSession().getError().getStackTrace() != null)
+//        {
+//            for (StackTraceElement element : getSampleSession().getError().getStackTrace())
+//            {
+//                trace.add(element.toString());
+//            }
+//        }
+//        return trace;
+//    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/LoginPage.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/LoginPage.java
new file mode 100644
index 00000000..778ec729
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/LoginPage.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.pages;
+
+import java.util.Locale;
+
+import org.apache.empire.commons.Options;
+import org.apache.empire.jakarta.app.FacesUtils;
+import org.apache.empire.jakarta.pages.PageOutcome;
+import org.apache.empire.jakarta.websample.web.SampleSession;
+import org.apache.empire.jakarta.websample.web.SampleUtils;
+
+public class LoginPage extends SamplePage {
+    
+    private static final Options languageOptions;
+    
+    static {
+        languageOptions = new Options();
+        languageOptions.set(Locale.US, "English");
+        languageOptions.set(Locale.GERMAN, "German");
+    }
+    
+    public Options getLanguageOptions()
+    {
+        return languageOptions;
+    }
+
+	public void doLogin() 
+	{
+		SampleSession session =	SampleUtils.getSampleSession();
+		session.setUser(SampleUtils.getSampleUser());
+		// goto list page
+		navigateTo(SamplePages.EmployeeListPage.getRedirect());
+	}
+
+	public void doLogout() 
+	{
+		// Perform logout
+		PageOutcome logout = this.getPageDefinition().getRedirect().addParam("logout", String.valueOf(true));
+		navigateTo( logout );
+		
+        // Invalidate Session
+		FacesUtils.getContext().getExternalContext().invalidateSession();
+	}
+	
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/SamplePage.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/SamplePage.java
new file mode 100644
index 00000000..c2639664
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/SamplePage.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.pages;
+
+import org.apache.empire.db.DBCommand;
+import org.apache.empire.jakarta.pages.Page;
+import org.apache.empire.jakarta.websample.db.SampleDB;
+import org.apache.empire.jakarta.websample.web.SampleApplication;
+import org.apache.empire.jakarta.websample.web.SampleContext;
+import org.apache.empire.jakarta.websample.web.SampleSession;
+import org.apache.empire.jakarta.websample.web.SampleUser;
+import org.apache.empire.jakarta.websample.web.SampleUtils;
+
+public class SamplePage extends Page
+{
+
+	private SampleContext sampleContext = null;
+
+    public synchronized SampleContext getSampleContext()
+    {
+        if (this.sampleContext == null)
+        {
+            SampleApplication app = SampleUtils.getSampleApplication();
+            SampleSession session = SampleUtils.getSampleSession();
+            this.sampleContext = new SampleContext(app.getDatabase(), session);
+        }
+        return this.sampleContext;
+    }
+
+    protected SampleApplication getApplication()
+    {
+        return SampleUtils.getSampleApplication();
+    }
+
+    protected SampleDB getDatabase()
+    {
+        return SampleUtils.getDatabase();
+    }
+
+    public SampleUser getUser()
+    {
+        return SampleUtils.getSampleUser();
+    }
+
+//    public int getLanguageIndex()
+//    {
+//        return SampleUtils.getSampleSession().getUser().getLanguageIndex();
+//    }
+
+    protected DBCommand createQueryCommand()
+    {
+        return getSampleContext().createCommand();
+    }
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/SamplePages.java b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/SamplePages.java
new file mode 100644
index 00000000..cb04fb94
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/java/org/apache/empire/jakarta/websample/web/pages/SamplePages.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.websample.web.pages;
+
+import org.apache.empire.jakarta.pages.PageDefinition;
+import org.apache.empire.jakarta.pages.PageDefinitions;
+
+public final class SamplePages extends PageDefinitions
+{
+
+    public static final PageDefinition LoginPage          = new PageDefinition("/pages/loginPage.xhtml", LoginPage.class);
+
+    public static final PageDefinition EmployeeListPage   = new PageDefinition("/pages/employeeListPage.xhtml", EmployeeListPage.class, SamplePages.LoginPage);
+    public static final PageDefinition EmployeeDetailPage = new PageDefinition("/pages/employeeDetailPage.xhtml", EmployeeDetailPage.class, SamplePages.EmployeeListPage);
+
+}
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/resources/lang/messages_de.properties b/empire-db-examples/empire-db-example-jakarta-faces/src/main/resources/lang/messages_de.properties
new file mode 100644
index 00000000..b28e91a1
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/resources/lang/messages_de.properties
@@ -0,0 +1,151 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#fields
+field.title.employees.id=Mitarbeiter Nr.
+field.title.employees.salutation=Anrede
+field.title.employees.firstName=Vorname
+field.title.employees.lastName=Nachname
+field.title.employees.dateOfBirth=Geburtsdatum
+field.title.employees.departmentId=Abteilung
+field.title.employees.gender=Geschlecht
+field.title.employees.phoneNumber=Telefonnummer
+field.title.employees.email=E-Mail
+field.title.employees.salary=Jahresgehalt
+field.title.employees.retired=Ausgeschieden
+field.title.employees.updateTimestamp=Zuletzt ge�ndert am
+
+field.title.departments.id=Abteilungsnr
+field.title.departments.name=Abteilungsname
+field.title.departments.head=Abteilugnsleiter
+
+field.title.payments.year=Jahr
+field.title.payments.month=Monat
+field.title.payments.amount=Betrag
+
+#options
+option.employee.gender.male=M�nnlich
+option.employee.gender.female=Weiblich
+
+#options
+option.employee.active=-
+option.employee.retired=Ja
+
+#application
+application_title=Jakarta Mitarbeiterverwaltung
+application_todo=Noch zu implementieren
+application_logout=Abmelden
+
+
+#
+global_list_result_empty=Es wurden keine Elemente mit den angegebenen Filterkriterien gefunden.
+global_status=Der Status ist {0}
+global_valid=g�ltig
+global_invalid=ung�ltig
+
+#pages
+login_title=Anmeldung
+login_login=anmelden
+login_field_name=Benutzername
+login_field_name_required=Es muss ein Benutzername angegeben werden.
+login_field_password=Passwort
+login_field_password_required=Es muss ein Passwort eingegeben werden.
+login_field_language=Sprache
+login_hint=Hinweis: Geben Sie einen beliebigen Benutzernamen und Kennwort an.
+
+employeeSearch_title=Mitarbeitersuche
+employeeSearch_search=Suche
+employeeSearch_field_employeeId=Mitarbeiter ID
+employeeSearch_field_firstName=Vorname
+employeeSearch_field_lastName=Nachname
+employeeSearch_field_departmentId= Abteilung
+
+employeeList_title=Mitarbeiter�bersicht
+employeeList_create=Neuen Mitarbeiter erstellen
+employeeList_field_employeeId=ID
+employeeList_field_name=Name
+employeeList_field_gender=Geschlecht
+employeeList_field_dateOfBirth=Geburtsdatum
+employeeList_field_department=Abteilung
+employeeList_search=Suchen
+employeeList_resetSearch=Suche zur�cksetzen
+
+employeeDetail_title=Mitarbeiterdetails
+employeeDetail_tab_basic=Grunddaten
+employeeDetail_tab_extended=Weitere Daten
+employeeDetail_msg_extended=Hier k�nnen weitere Daten angezeigt werden
+employeeDetail_save=Speichern
+employeeDetail_back=Zur�ck
+employeeDetail_delete=L�schen
+employeeDetail_test=Test
+employeeDetail_testMessage=Serialization erfolgreich durchgef�hrt
+employeeDetail_payments=Zahlungen
+
+# Old stuff
+
+#labels
+application.title=Mitarbeiterverwaltung (Empire-db)
+label.employees=Mitarbeiterliste
+label.employees.beanList=Mitarbeiterliste (Bean-Liste)
+label.employees.reader=Mitarbeiterliste (DBReader)
+label.delete=L�schen
+label.edit=Bearbeiten
+label.employee.edit=Eintrag bearbeiten
+label.employee.add=Eintrag hinzuf�gen
+label.name=Name
+label.gender=Geschlecht
+label.age=Alter
+label.department=Abteilung
+label.user.Name=Name
+label.user.Pwd=Passwort
+
+page.label.login=Anmelden
+page.label.logininfo=Sie sind angemeldet als:
+page.label.search=Mitarbeiter suchen
+page.label.list=Mitarbeiter ausw�hlen
+page.label.update=Mitarbeiter bearbeiten
+page.label.add=Mitarbeiter hinzuf�gen
+
+#button labels
+button.label.login=Anmelden
+button.label.search=Suchen...
+button.label.save=Speichern
+button.label.cancel=Abbrechen
+button.label.delete=L�schen
+
+#link labels
+link.label.addemployee=Neuen Eintrag anlegen
+link.label.edit=Bearbeiten
+link.label.delete=L�schen
+link.label.logout=Logout
+link.label.search=Suchen
+
+##-- errors
+errors.prefix=<span style="color:red;font-weight:bold;">
+errors.suffix=</span>
+
+error.invalidpassword=Der eingegebene Benutzername ist ung�ltig (Es ist mindestens ein Zeichen erforderlich!)
+error.web.recordsDontMatch=The form submitted is invalid for the current context.
+error.web.columnNotFound=Die Spalte {0} wurde nicht gefunden!
+error.web.fieldError=Feld {0}: {1}
+error.web.input.integerFormat=Ung�ltiges Zahlenformat
+error.web.input.numberFormat=Ung�ltiges Zahlenformat
+error.web.input.dateFormat=Ung�ltiges Datumsformat
+error.web.input.date.notime=Es wurde keine Zeit angegeben
+error.web.input.required=Das Feld ist eingabepflichtig 
+error.web.input.outOfRange=Der Wert muss zwischen {0} und {1} liegen.
+error.web.input.textTooShort=Der Wert muss mindestens {0} Zeichen enthalten. 
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/resources/lang/messages_en.properties b/empire-db-examples/empire-db-example-jakarta-faces/src/main/resources/lang/messages_en.properties
new file mode 100644
index 00000000..bcf5132d
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/resources/lang/messages_en.properties
@@ -0,0 +1,151 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+#fields
+field.title.employees.id=Employee ID
+field.title.employees.salutation=Salutation
+field.title.employees.firstName=Firstname
+field.title.employees.lastName=Lastname
+field.title.employees.dateOfBirth=Date of Birth
+field.title.employees.departmentId=Department
+field.title.employees.gender=Gender
+field.title.employees.phoneNumber=Phone
+field.title.employees.email=E-Mail
+field.title.employees.salary=Salary p.a.
+field.title.employees.retired=Retired
+field.title.employees.updateTimestamp=Last change
+
+field.title.departments.id=Department ID
+field.title.departments.name=Department
+field.title.departments.head=Head of Dept.
+
+field.title.payments.year=Year
+field.title.payments.month=Month
+field.title.payments.amount=Amount
+
+#options
+option.employee.gender.male=Male
+option.employee.gender.female=Female
+
+#options
+option.employee.active=-
+option.employee.retired=Yes
+
+#application
+application_title=Jakarta Employee Management
+application_todo=To do...
+application_logout=Logout
+
+
+#
+global_list_result_empty=No employee(s) found
+global_status=The status is {0}
+global_valid=valid
+global_invalid=invalid
+
+#pages
+login_title=Login
+login_login=login
+login_field_name=User name
+login_field_name_required=User name is required.
+login_field_password=Password
+login_field_password_required=Password is required.
+login_field_language=Sprache
+login_hint=Hint: enter any non null username and password.
+
+employeeSearch_title=Search employee
+employeeSearch_search=Search
+employeeSearch_field_employeeId=Employee ID
+employeeSearch_field_firstName=First name
+employeeSearch_field_lastName=Last name
+employeeSearch_field_departmentId=Department
+
+employeeList_title=Employee List
+employeeList_create=Create employee
+employeeList_field_employeeId=ID
+employeeList_field_name=Name
+employeeList_field_gender=Gender
+employeeList_field_dateOfBirth=Date of Birth
+employeeList_field_department=Department
+employeeList_search= Search
+employeeList_resetSearch=Search reset
+
+employeeDetail_title=Employee Detail
+employeeDetail_tab_basic=Basic data
+employeeDetail_tab_extended=Other data
+employeeDetail_msg_extended=Please display additional information here
+employeeDetail_save=Save
+employeeDetail_back=Back
+employeeDetail_delete=Delete
+employeeDetail_test=Test
+employeeDetail_testMessage=Serialization successful
+employeeDetail_payments=Payments
+
+# Old stuff
+
+#labels
+application.title=Employee Management Application
+label.employees=List of employees
+label.employees.beanList=Employees (Bean-List)
+label.employees.reader=Employees (DBReader)
+label.delete=Delete
+label.edit=Edit
+label.employee.edit=Employee details
+label.employee.add=Add employee
+label.department=Department
+label.name=Name
+label.gender=Gender
+label.age=Age
+label.user.Pwd=Password
+
+page.label.login=Login
+page.label.loginHint=Hint: enter any non null username and password.
+page.label.logininfo=You are logged in as:
+page.label.search=Search employee
+page.label.list=Select employee
+page.label.update=Edit employee details
+page.label.add=Add new employee
+
+#button labels
+button.label.login=Login
+button.label.search=Search
+button.label.save=Save
+button.label.cancel=Cancel
+button.label.delete=Delete
+
+#link labels
+link.label.addemployee=Add new employee
+link.label.edit=Edit
+link.label.delete=Delete
+link.label.logout=Logout
+link.label.search=Search
+
+##-- errors
+errors.prefix=<span style="color:red;font-weight:bold;">
+errors.suffix=</span>
+
+error.invalidpassword=The username is invalid (At least one character is required!)
+error.web.recordsDontMatch=The form submitted is invalid for the current context.
+error.web.columnNotFound=The column {0} has not been found!
+error.web.fieldError=Field {0}: {1}
+error.web.input.integerFormat=Invalid Integer format
+error.web.input.numberFormat=Invalid number format
+error.web.input.dateFormat=Invalid date format
+error.web.input.date.notime=No time specified.
+error.web.input.required=This field is required 
+error.web.input.outOfRange=The value must be in the range of {0} and {1}.
+error.web.input.textTooShort=The value must contain at least {0} characters. 
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/.gitignore b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/.gitignore
new file mode 100644
index 00000000..8db7f2e8
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/.gitignore
@@ -0,0 +1,2 @@
+/.#webclasspath
+/hsqldb/
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/META-INF/context.xml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/META-INF/context.xml
new file mode 100644
index 00000000..1bd17625
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/META-INF/context.xml
@@ -0,0 +1,28 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ -->
+<!-- This file is read by Tomcat 6 and creates the datasources required just by being in the META-INF directory -->
+<Context path="/empjsf" debug="5" reloadable="true" crossContext="true">
+
+	<!-- Connection pool definition - not used in sample 
+	<Resource name="jdbc/sampleDataSource" auth="Container" type="javax.sql.DataSource"
+		maxActive="20" maxIdle="10" maxWait="-1" driverClassName="oracle.jdbc.driver.OracleDriver"
+		url="jdbc:oracle:thin:@devserver:1521:ora19" username="DBSAMPLE" password="DBSAMPLE" />
+	-->
+
+</Context>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/config.xml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/config.xml
new file mode 100644
index 00000000..b6cc3adb
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/config.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+  
+   http://www.apache.org/licenses/LICENSE-2.0
+  
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+-->
+<config>
+
+	<properties>
+		<!-- provider name must match the property-section containing the connection data -->
+		<databaseProvider>hsqldb</databaseProvider>
+	</properties>
+
+	<properties-hsqldb>
+		<!-- JDBC properties for a HSQLDB Database connection -->
+		<!-- Required jar file: hsqldb.jar -->
+		<jdbcClass>org.hsqldb.jdbc.JDBCDriver</jdbcClass>
+		<jdbcURL>jdbc:hsqldb:file:hsqldb/sample;shutdown=true</jdbcURL>
+		<jdbcUser>sa</jdbcUser>
+		<jdbcPwd></jdbcPwd>
+		<!-- Empire-db driver configuration -->
+		<dbmsHandlerClass>org.apache.empire.dbms.hsql.DBMSHandlerHSql</dbmsHandlerClass>
+		<dbmsHandlerProperties property="false">
+			<!-- no properties -->
+		</dbmsHandlerProperties>
+	</properties-hsqldb>
+
+	<properties-mysql>
+		<!-- JDBC properties for a MySQL Database connection -->
+		<!-- Required jar file: mysql-connector-java-5.1.6-bin.jar -->
+		<jdbcClass>com.mysql.jdbc.Driver</jdbcClass>
+		<jdbcURL>jdbc:mysql://localhost</jdbcURL>
+		<jdbcUser>root</jdbcUser>
+		<jdbcPwd>esteam</jdbcPwd>
+		<!-- Empire-db driver configuration -->
+		<dbmsHandlerClass>org.apache.empire.dbms.mysql.DBMSHandlerMySQL</dbmsHandlerClass>
+		<dbmsHandlerProperties property="false">
+			<databaseName>DBSAMPLE</databaseName>
+		</dbmsHandlerProperties>
+	</properties-mysql>
+	
+	<properties-postgresql>
+		<!-- JDBC properties for a PostreSQL Database connection -->
+		<!-- Required jar file: postgresql-8.3-603.jdbc4.jar -->
+		<jdbcClass>org.postgresql.Driver</jdbcClass>
+		<jdbcURL>jdbc:postgresql://localhost/dbsample</jdbcURL>
+		<jdbcUser>dbsample</jdbcUser>
+		<jdbcPwd>DBSAMPLE</jdbcPwd>
+		<!-- Empire-db driver configuration -->
+		<dbmsHandlerClass>org.apache.empire.dbms.postgresql.DBMSHandlerPostgreSQL</dbmsHandlerClass>
+		<dbmsHandlerProperties property="false">
+			<!-- no properties -->
+		</dbmsHandlerProperties>
+	</properties-postgresql>
+	
+	<properties-h2>
+		<!-- JDBC properties for a H2 Database connection -->
+		<!-- Required jar file: h2-1.1.107.jar -->
+		<jdbcClass>org.h2.Driver</jdbcClass>
+		<jdbcURL>jdbc:h2:h2/DBSAMPLE</jdbcURL>
+		<jdbcUser>sa</jdbcUser>
+		<jdbcPwd></jdbcPwd>
+		<!-- Empire-db driver configuration -->
+		<dbmsHandlerClass>org.apache.empire.dbms.h2.DBMSHandlerH2</dbmsHandlerClass>
+		<dbmsHandlerProperties property="false">
+			<databaseName>DBSAMPLE</databaseName>
+		</dbmsHandlerProperties>
+	</properties-h2>
+	
+	<properties-derby>
+		<!-- JDBC properties for a Derby Database connection -->
+		<!-- Required jar file: derby-10.4.2.0.jar -->
+		<jdbcClass>org.apache.derby.jdbc.EmbeddedDriver</jdbcClass>
+		<jdbcURL>jdbc:derby:derby/DBSAMPLE;create=true</jdbcURL>
+		<jdbcUser></jdbcUser>
+		<jdbcPwd></jdbcPwd>
+		<!-- Empire-db driver configuration -->
+		<dbmsHandlerClass>org.apache.empire.dbms.derby.DBMSHandlerDerby</dbmsHandlerClass>
+		<dbmsHandlerProperties property="false">
+			<databaseName>DBSAMPLE</databaseName>
+		</dbmsHandlerProperties>
+	</properties-derby>
+
+	<properties-oracle>
+		<!-- JDBC properties for an Oracle Database connection -->
+		<!-- Required jar file: ojdbc14.jar -->
+		<jdbcClass>oracle.jdbc.driver.OracleDriver</jdbcClass>
+		<jdbcURL>jdbc:oracle:thin:@devserver:1521:ora19</jdbcURL>
+		<jdbcUser>DBSAMPLE</jdbcUser>
+		<jdbcPwd>DBSAMPLE</jdbcPwd>
+		<!-- Empire-db driver configuration -->
+		<dbmsHandlerClass>org.apache.empire.dbms.oracle.DBMSHandlerOracle</dbmsHandlerClass>
+		<dbmsHandlerProperties property="false">
+			<!-- no properties -->
+		</dbmsHandlerProperties>
+	</properties-oracle>
+
+	<properties-sqlserver>
+		<!-- JDBC properties for a Microsoft SQL-Server Database connection -->
+		<jdbcClass>com.microsoft.sqlserver.jdbc.SQLServerDriver</jdbcClass>
+		<jdbcURL>jdbc:sqlserver://devserver</jdbcURL>
+		<jdbcUser>jdbcUser</jdbcUser>
+		<jdbcPwd>jdbcPwd</jdbcPwd>
+		<!-- Empire-db driver configuration -->
+		<dbmsHandlerClass>org.apache.empire.dbms.sqlserver.DBMSHandlerMSSQL</dbmsHandlerClass>
+		<dbmsHandlerProperties property="false">
+			<databaseName>DBSAMPLE</databaseName>
+			<useSequenceTable>false</useSequenceTable>
+			<!-- useDateTime2: set to false for databases using DATETIME for timestamps instead of DATETIME2 -->
+			<useDateTime2>true</useDateTime2>
+		</dbmsHandlerProperties>
+	</properties-sqlserver>
+
+	<!-- log4j configuration node -->
+	<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+		<!-- Default Appender -->
+		<appender name="default" class="org.apache.log4j.ConsoleAppender">
+			<layout class="org.apache.log4j.PatternLayout">
+				<param name="ConversionPattern" value="%-5p : %m		at %l %n"/>
+			</layout>
+		</appender>
+	
+		<!-- Appender for DBUtils (SQL-Statements) -->
+		<appender name="utils" class="org.apache.log4j.ConsoleAppender">
+			<layout class="org.apache.log4j.PatternLayout">
+				<param name="ConversionPattern" value="--------------------------------%n%-5p : %m%n"/>
+			</layout>
+		</appender>
+	
+		<!-- Appender for SampleApp -->
+		<appender name="sample" class="org.apache.log4j.ConsoleAppender">
+			<layout class="org.apache.log4j.PatternLayout">
+				<param name="ConversionPattern" value="%-5p : *** %m *** %n"/>
+			</layout>
+		</appender>
+	
+		<!-- log detail configuration -->
+		<logger name="org.apache.empire.db" additivity="false">
+			<level value="warn"/>
+			<appender-ref ref="default"/>
+		</logger>
+
+		<!-- Show Config-file settings -->
+		<logger name="org.apache.empire.xml" additivity="false">
+			<level value="info"/>
+			<appender-ref ref="default"/>
+		</logger>
+
+		<!-- Set this level to "debug" to log all SQL-Statements or to "info" to log DML-Statements only -->		
+		<logger name="org.apache.empire.db.DBUtils" additivity="false">
+			<level value="debug"/>
+			<appender-ref ref="utils"/>
+		</logger>
+
+		<!-- This is the log output of the SampleApp -->		
+		<logger name="org.apache.empire.jsf2.websample" additivity="false">
+			<level value="info"/>
+			<appender-ref ref="sample"/>
+		</logger>
+
+		<root>
+			<priority value="info"/>
+			<appender-ref ref="default"/>
+		</root>
+
+	</log4j:configuration>
+
+</config>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/facelets/templates/header.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/facelets/templates/header.xhtml
new file mode 100644
index 00000000..eb7f3f12
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/facelets/templates/header.xhtml
@@ -0,0 +1,31 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<h:panelGroup id="header" xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core">
+
+	<!-- application title -->
+	<h:panelGroup layout="block" styleClass="titleDiv">
+		<h:outputText value="#{msg.application_title}" />	
+	</h:panelGroup>
+		
+	<!-- page box -->
+	<h1><h:outputText value="#{pageTitle}" /></h1>
+
+</h:panelGroup>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/facelets/templates/layout.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/facelets/templates/layout.xhtml
new file mode 100644
index 00000000..6daae079
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/facelets/templates/layout.xhtml
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:f="http://java.sun.com/jsf/core">
+
+<!-- internationalization -->
+<f:view locale="#{sampleUser.locale}" />
+<ui:param name="webRoot" value=".." />
+
+<f:metadata>
+	<ui:insert name="metadata" />
+</f:metadata>
+
+<h:head>
+
+	<!-- dynamic page title -->
+	<title>#{msg.application_title} - #{pageTitle}</title>
+
+	<!-- meta information -->
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
+	<meta http-equiv="Content-Language" content="en" />
+	<meta http-equiv="imagetoolbar" content="no" />
+	<meta name="keywords" content="" />
+	<meta name="description" content="" />
+
+	<!-- global stylesheets -->
+	<link href="#{webRoot}/css/layout.css" rel="stylesheet" type="text/css" />
+
+	<!-- custom page stylesheets -->
+	<ui:insert name="pageCss" />
+
+	<!-- javascript -->
+
+	<!-- custom page script -->
+	<ui:insert name="pageScript" />
+</h:head>
+<h:body>
+	<f:view>
+
+		<!-- header -->
+		<ui:include src="header.xhtml" />
+
+		<!-- (error) messages -->
+		<h:panelGroup id="messages" layout="block">
+			<h:messages />
+		</h:panelGroup>
+
+		<!-- content -->
+		<h:panelGroup id="content" layout="block">
+			<ui:insert name="content" />
+		</h:panelGroup>
+
+	</f:view>
+</h:body>
+</html>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/faces-config.xml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/faces-config.xml
new file mode 100644
index 00000000..5ebfe1ac
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/faces-config.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+	license agreements. See the NOTICE file distributed with this work for additional 
+	information regarding copyright ownership. The ASF licenses this file to 
+	you under the Apache License, Version 2.0 (the "License"); you may not use 
+	this file except in compliance with the License. You may obtain a copy of 
+	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
+	by applicable law or agreed to in writing, software distributed under the 
+	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
+	OF ANY KIND, either express or implied. See the License for the specific 
+	language governing permissions and limitations under the License. -->
+<faces-config 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-facesconfig_2_0.xsd"
+	version="2.0">
+
+	<factory>
+		<exception-handler-factory>org.apache.empire.jakarta.websample.web.AppExceptionHandlerFactory</exception-handler-factory>
+	</factory>
+
+	<application>
+	    <!-- System event listener --> 
+	    <system-event-listener>
+	    	<!-- (StartupListener for manual configuration)
+			<system-event-listener-class>org.apache.empire.jakarta.app.WebAppStartupListener</system-event-listener-class>
+			-->
+	    	<!-- (StartupListener for programmatic configuration) -->
+			<system-event-listener-class>org.apache.empire.jakarta.websample.web.SampleAppStartupListener</system-event-listener-class>
+			<!-- Faces Event -->
+			<system-event-class>jakarta.faces.event.PostConstructApplicationEvent</system-event-class>
+	    </system-event-listener>
+	    <!-- locales -->
+		<locale-config>
+			<default-locale>en</default-locale>
+			<supported-locale>en</supported-locale>
+			<supported-locale>de</supported-locale>
+		</locale-config>
+		<message-bundle>lang.messages</message-bundle>
+		<!-- messages -->
+		<message-bundle>lang.messages</message-bundle>
+		<resource-bundle>
+			<base-name>lang.messages</base-name>
+			<var>msg</var>
+		</resource-bundle>
+		<!-- EL-Resolvers (Cannot be added programmatically in Mojarra!) -->
+		<el-resolver>org.apache.empire.jakarta.app.DBELResolver</el-resolver>
+		<el-resolver>org.apache.empire.jakarta.pages.PagesELResolver</el-resolver>
+		<!-- Navigation handler (added programmatically)
+		<navigation-handler>org.apache.empire.jakarta.pages.PageNavigationHandler</navigation-handler>
+		 -->
+	</application>
+
+	<!-- Lifecycle -->
+	<lifecycle>
+		<!-- (added programmatically)
+		<phase-listener>org.apache.empire.jakarta.app.FacesRequestPhaseListener</phase-listener>
+		<phase-listener>org.apache.empire.jakarta.pages.PagePhaseListener</phase-listener>
+		 -->
+		<phase-listener>org.apache.empire.jakarta.websample.web.AuthenticationPhaseListener</phase-listener>
+	</lifecycle>
+
+	<!-- Application bean (MUST be named 'webApplication' for WebAppStartupListener!) -->
+	<managed-bean>
+		<managed-bean-name>webApplication</managed-bean-name>
+		<managed-bean-class>org.apache.empire.jakarta.websample.web.SampleApplication</managed-bean-class>
+		<managed-bean-scope>application</managed-bean-scope>
+	</managed-bean>
+
+	<!-- Session beans -->
+	<managed-bean>
+		<managed-bean-name>sampleSession</managed-bean-name>
+		<managed-bean-class>org.apache.empire.jakarta.websample.web.SampleSession</managed-bean-class>
+		<managed-bean-scope>session</managed-bean-scope>
+	</managed-bean>
+
+	<managed-bean>
+		<managed-bean-name>employeeSearchFilter</managed-bean-name>
+		<managed-bean-class>org.apache.empire.jakarta.websample.web.objects.EmployeeSearchFilter</managed-bean-class>
+		<managed-bean-scope>session</managed-bean-scope>
+	</managed-bean>
+
+	<managed-bean>
+		<managed-bean-name>sampleUser</managed-bean-name>
+		<managed-bean-class>org.apache.empire.jakarta.websample.web.SampleUser</managed-bean-class>
+		<managed-bean-scope>session</managed-bean-scope>
+	</managed-bean>
+
+	<!-- Empire-JSF components (added programmatically)
+	<component>
+		<component-type>org.apache.empire.component.RecordTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.RecordTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.ControlTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.ControlTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.InputTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.InputTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.FormGridTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.FormGridTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.LabelTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.LabelTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.LinkTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.LinkTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.SelectTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.SelectTag</component-class>
+	</component>	
+	<component>
+		<component-type>org.apache.empire.component.TitleTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.TitleTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.ValueTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.ValueTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.MenuListTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.MenuListTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.MenuItemTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.MenuItemTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.TabViewTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.TabViewTag</component-class>
+	</component>
+	<component>
+		<component-type>org.apache.empire.component.TabPageTag</component-type>
+		<component-class>org.apache.empire.jakarta.components.TabPageTag</component-class>
+	</component>
+	 -->
+
+</faces-config>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/web.xml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/web.xml
new file mode 100644
index 00000000..5152f879
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor 
+	license agreements. See the NOTICE file distributed with this work for additional 
+	information regarding copyright ownership. The ASF licenses this file to 
+	you under the Apache License, Version 2.0 (the "License"); you may not use 
+	this file except in compliance with the License. You may obtain a copy of 
+	the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required 
+	by applicable law or agreed to in writing, software distributed under the 
+	License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS 
+	OF ANY KIND, either express or implied. See the License for the specific 
+	language governing permissions and limitations under the License. -->
+<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+	id="JSF2Sample" version="3.0">
+	<display-name>Empire DBWebSample</display-name>
+
+	<welcome-file-list>
+		<welcome-file>index.html</welcome-file>
+	</welcome-file-list>
+
+	<servlet>
+		<servlet-name>Faces Servlet</servlet-name>
+		<servlet-class>jakarta.faces.webapp.FacesServlet</servlet-class>
+	</servlet>
+
+	<servlet-mapping>
+		<servlet-name>Faces Servlet</servlet-name>
+		<url-pattern>/pages/</url-pattern>
+		<url-pattern>*.xhtml</url-pattern>
+		<url-pattern>*.jsf</url-pattern>
+	</servlet-mapping>
+	
+	
+	<!-- Taglib 
+	<context-param>
+		<param-name>jakarta.faces.FACELETS_LIBRARIES</param-name>
+		<param-value>/WEB-INF/facelets/taglib.xml</param-value>
+	</context-param>
+	--> 
+	
+	<context-param>
+		<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
+		<param-name>jakarta.faces.STATE_SAVING_METHOD</param-name>
+		<param-value>server</param-value>
+	</context-param>
+	<context-param>
+		<param-name>jakarta.faces.PROJECT_STAGE</param-name>
+		<param-value>Development</param-value>
+	</context-param>
+	<context-param>
+		<param-name>jakarta.faces.FACELETS_SKIP_COMMENTS</param-name>
+		<param-value>true</param-value>
+	</context-param>
+	<context-param>
+		<param-name>jakarta.faces.PARTIAL_STATE_SAVING</param-name>
+		<param-value>false</param-value>
+	</context-param>
+	
+</web-app>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/common.css b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/common.css
new file mode 100644
index 00000000..1c65dc7c
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/common.css
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* common */
+* {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+*:before,
+*:after {
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+html, body  {
+	min-width: 400px;
+	max-width: 1200px;
+    margin-left: 10px;
+    margin-right: auto;
+    margin-bottom: 5px;
+    padding-right: 20px;
+    color: black;
+    background-color: white;
+    font-family: Verdana, Arial, sans-serif;
+    font-size:12px;
+}
+.titleDiv {
+    background-color: #EFFBEF;
+    font-weight:bold;
+    font-size:18px;
+    text-align:left;
+    padding-left:10px;
+    padding-top:10px;
+    padding-bottom:10px;
+    border:2px solid #8F99EF;
+}
+h1 { font-weight:bold; color: brown; font-size:15px; text-align:left;}
+
+td { font-size:12px; padding-right:10px; }
+th { text-align:left; font-weight:bold; font-size:13px; padding-right:10px; }
+
+A:link { text-decoration:none;}
+A:visited { text-decoration:none;}
+A:hover { text-decoration:underline;}
+
+.even { background-color: #EFEFFB; 	text-align:left;}
+.odd { background-color:  #FFFFFF; 	text-align:left; }
+
+.nowrap { white-space:nowrap; }
+
+
+#messages ul {
+    background-color: #FBF0F0;
+    border:2px solid #8F99EF;
+	list-style-type: square;
+	padding: 8px;
+	padding-left: 24px;
+}
+#messages ul > li {
+	padding: 4px 0;
+/*
+	padding-left:18px;
+    padding-top:2px;
+    padding-bottom:10px;
+    padding-right:5px;
+*/    
+	color: #922727;
+    font-weight: bold;
+}
+
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/content.css b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/content.css
new file mode 100644
index 00000000..ef0806ba
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/content.css
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+ 
+.margin-above {
+	margin-top: 10px;
+}
+ 
+/* form panel */
+.formPanel {
+	border: 1px #8F99EF solid;
+	background-color: var(--form-background-color);
+	padding: 8px 4px;
+}
+.formPanel > .buttonBar {
+	padding: var(--control-v-pad) 0;
+	padding-left: var(--label-width);
+	margin-top: 10px;
+}
+
+.buttonBar a,
+.buttonBar a:visited {
+	display: inline-block;
+	border: var(--button-border-width) var(--button-border-color) solid;
+	padding: var(--input-v-pad) 12px;
+	color: black;
+	text-decoration: none;
+	margin-right: 16px;
+	background-color: white;
+	font-weight: bold;
+}
+.buttonBar a:hover {
+	background-color: #EFFBEF;
+}
+
+/* result table */
+div.searchResult,
+span.searchResult {
+	display: block;
+	margin: 12px 0;
+	padding: 4px 0;
+}
+.searchResult table {
+	width: 100%;
+	border: 1px #8F99EF solid;
+	border-collapse: collapse;
+}
+.searchResult table tr th,
+.searchResult table tr td {
+	padding: var(--table-cell-v-pad) var(--table-cell-h-pad);
+}
+.searchResult table tr th {
+	background-color: #EFFBEF;
+	border-bottom: 1px #8F99EF solid;
+}
+.searchResult table a.eLink,
+.searchResult table a.eLink:visited {
+	color: brown;
+	font-weight: bold;
+}
+
+/* tab view */
+div.eTabView {
+}
+div.eTabBar {
+	display: flex;
+}
+table.eTabBar {
+	width: 100%;
+	border-spacing: 0;
+	border-collapse: collapse;
+	table-layout: auto;
+	padding: 0;
+}
+div.eTabBar .eTabLabel,
+table.eTabBar tr td.eTabLabel {
+	border: 1px #8F99EF solid;
+	padding: 8px;	
+	white-space: nowrap;
+}
+div.eTabBar .eTabLabel.eTabActive,
+table.eTabBar tr td.eTabLabel.eTabActive {
+	border-bottom: 1px #F8F8F8 solid;
+	background-color: #F8F8F8;	
+}
+table.eTabBar tr td.eTabLabel.eTabDisabled {
+	color: gray;
+	font-style: italic;
+}
+.eTabBar .eTabBarEmpty {
+	width: 99%;
+	border-bottom: 1px #8F99EF solid;	
+}
+table.eTabPanel {
+	width: 100%;
+	border-spacing: 0;
+	border-collapse: collapse;
+}
+div.eTabPanel {
+	display: grid;
+}
+.eTabPanel {
+	border: 1px #8F99EF solid;
+	border-top: 0px;	
+}
+.eTabPanel .eTabPage {
+	padding: 8px;
+	background-color: #F8F8F8;
+	vertical-align: top;
+}
+.eTabPanel .eTabPage .formPanel {
+	border: 0;
+}
+div.info-msg {
+	padding: 12px;
+	background-color: #FFFFCA;
+}
+.paymentData {
+	border: 2px gray solid;
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/form.css b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/form.css
new file mode 100644
index 00000000..eb4f8f78
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/form.css
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+label {
+	font-weight: bold; 
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+label.eLabel.eInpReq:after {
+	content: "*";
+	color: #D00000;
+}
+select, 
+input[type=text],
+input[type=password] {
+	padding: var(--input-v-pad) var(--input-h-pad);
+	border:  var(--input-border-width) var(--input-border-color) solid;
+}
+select.eInpReq, 
+input[type=text].eInpReq {
+	background-color: #FFFFE0;
+}
+select.eInpDis, 
+textarea.eInpDis,
+input[type=text].eInpDis {
+	border: 1px #C0C0C0 solid;
+	color: #808080;
+}
+select.eInput.eInpShort,
+input.eInput.eInpShort,
+input.eInput.eTypeDate {
+	width: 100px;
+}
+input.eInput.eInpDecimal {
+	width: 100px;
+	text-align: right;
+}
+
+/* general */
+.eCtlInput > .eInput {
+	width: 100%;
+}
+.eCtlInput > .eInput.eTypeBool {
+	width: auto;
+}
+.eCtlInput > span {
+	display: inline-block;
+	padding: var(--input-v-pad) 0;
+	border: var(--input-border-width) transparent solid;
+}
+.eCtlInput .eUnit {
+	padding-left: var(--input-h-pad);
+	font-weight: bold;
+}
+.eInputHint {
+	padding-left: 4px;
+	font-style: italic;
+}
+.eTypeBoolFalse {
+	width: 12px;
+	height: 12px;
+	background-image:url('../img/boxes.gif');
+	background-repeat: no-repeat;
+	background-position: 0 0;
+}
+.eTypeBoolTrue {
+	width: 12px;
+	height: 12px;
+	background-image:url('../img/boxes.gif');
+	background-repeat: no-repeat;
+	background-position: -12px 0;
+}
+.eControl > .eCtlLabel {
+	overflow: hidden;
+	text-overflow: ellipsis;
+}
+
+/* wrapper class styles */
+.eWrapInp {
+	/* general wrapper style */
+	display: flex;
+	padding: var(--input-v-pad) var(--input-h-pad);
+}
+.eWrapVal {
+	/* general wrapper style */
+	display: inline-block;
+}
+.checkboxWrapper {
+	/* special checkbox wrapper style */
+	/* background-color: #E8E8E8; */
+}
+
+/* formGrid in Grid mode */
+div.eFormGrid {
+	display: grid;
+	grid-template-columns: 1fr;
+	grid-gap: var(--grid-v-space) var(--grid-h-space);
+/*	background-color: white; */
+}
+
+/* grid-top-align */ 
+div.eFormGrid > .eControl {
+	display: flex;
+/*	background-color: #F8F8F8; */
+	min-height: var(--control-min-height);
+}
+div.eFormGrid > .eControl > .eCtlLabel {
+	width: var(--label-width);
+	padding: calc(var(--control-v-pad) + var(--input-v-pad) + 1px) var(--control-h-pad) var(--control-v-pad) var(--control-h-pad);
+}
+div.eFormGrid > .eControl > .eCtlInput {
+	width: calc(100% - var(--label-width));
+	padding: var(--control-v-pad) var(--control-h-pad);
+	padding-left: 0;
+}
+
+/* grid-v-align */ 
+div.eFormGrid.grid-v-align {
+}
+div.eFormGrid.grid-v-align > .eControl {
+	display: table;
+}
+div.eFormGrid.grid-v-align > .eControl > .eCtlLabel {
+	display: table-cell; 
+	vertical-align: middle;
+	padding: 0 var(--control-h-pad);
+	max-width: var(--label-width);
+}
+div.eFormGrid.grid-v-align > .eControl > .eCtlInput {
+	display: table-cell; 
+	vertical-align: middle;
+}
+
+/* extentions */
+@media (min-width: 1024px) {
+	div.eFormGrid.grid-columns-2 {
+    	grid-template-columns: repeat(2, 1fr);
+	}	
+	div.eFormGrid > .eControl.grid-full-row {
+	    grid-column: 1 / -1;
+	}
+	div.eFormGrid > .eControl.grid-new-row {
+	    grid-column: 1 / 2;
+	}
+}
+
+/* formGrid in table mode */
+table.eFormGrid {
+	width: 100%;
+/*	max-width: 800px; */
+	table-layout: fixed;
+    border-spacing: 0 var(--grid-v-space);
+    border-collapse: separate;
+}
+table.eFormGrid td.eCtlLabel {
+	vertical-align: middle;
+	padding: 0 var(--control-h-pad);
+	width: var(--label-width);
+}
+table.eFormGrid td.eCtlInput {
+	vertical-align: middle;
+	padding: var(--control-v-pad) var(--control-h-pad);
+	padding-left: 0;
+}
+
+/* grid-white-gap */
+.grid-white-gap {
+	background-color: white;
+}
+.grid-white-gap .eCtlLabel {
+	/* optional */
+	background-color: var(--form-background-color);
+}
+.grid-white-gap .eCtlInput {
+	/* optional */
+	background-color: var(--form-background-color);
+}
+
+/* misc */
+.formSection {
+    grid-column: 1 / -1;
+    background-color: lightgray;
+	padding: var(--control-v-pad) 0;
+    text-align: center;
+    font-weight: bold;
+}
+.xSalaryWrapper {
+	background-color: yellow;
+}
+
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/layout.css b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/layout.css
new file mode 100644
index 00000000..839f9ce7
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/layout.css
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+@charset "UTF-8";
+
+@import url("root.css");
+@import url("common.css");
+@import url("content.css");
+@import url("form.css");
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/root.css b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/root.css
new file mode 100644
index 00000000..8a6a5f35
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/css/root.css
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+:root {
+	--label-width: 150px;
+	--button-border-width: 2px;
+	--button-border-color: #8F99EF;
+	--input-border-width: 1px;
+	--input-border-color: #8F99EF;
+	--input-v-pad: 7px;
+	--input-h-pad: 7px;
+	--control-v-pad: 5px;
+	--control-h-pad: 5px;
+	--control-min-height: 20px;
+	--grid-v-space: 2px;
+	--grid-h-space: 20px;
+	--table-cell-v-pad: 10px;
+	--table-cell-h-pad: 5px;
+	--form-background-color: #F8F8F8;
+}
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/img/boxes.gif b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/img/boxes.gif
new file mode 100644
index 00000000..57bf32d6
Binary files /dev/null and b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/img/boxes.gif differ
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/index.html b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/index.html
new file mode 100644
index 00000000..2e100884
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/index.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ -->
+<html>
+<head>
+	<title>Redirecting</title>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+	<META HTTP-EQUIV="Refresh" CONTENT="0; URL=pages/loginPage.xhtml">
+</head>
+<body>
+	Redirecting to 	<a href="pages/loginPage.xhtml">Start...</a>
+</body>
+
+
+</html>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/employeeDetailPage.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/employeeDetailPage.xhtml
new file mode 100644
index 00000000..f811e116
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/employeeDetailPage.xhtml
@@ -0,0 +1,108 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ -->
+<ui:composition template="/WEB-INF/facelets/templates/layout.xhtml"
+	xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:e="http://java.sun.com/jsf/composite/empire"
+	xmlns:sample="http://java.sun.com/jsf/composite/components"
+	xmlns:h="http://java.sun.com/jsf/html">
+	<f:metadata>
+		<f:viewParam name="action" value="#{page.action}" />
+		<f:viewParam name="id" value="#{page.idParam}" />
+	</f:metadata>
+
+	<!-- parameters used in layout.xhtml -->
+	<ui:param name="pageTitle" value="#{msg.employeeDetail_title}" />
+	<ui:param name="contentStyle" value="unstyled" />
+
+	<!-- content -->
+	<ui:define name="content">
+
+		<h:form id="employeeDetail">
+
+		<e:tabView minHeight="400px" activeIndex="#{page.activeTab}" tabChangedListener="#{page.onTabChanged}">
+			<!-- first page -->
+			<e:tabPage id="tabBasic" title="#{msg.employeeDetail_tab_basic}">
+				<e:record value="#{page.employeeRecord}">
+				<!-- formPanel -->
+				<div class="formPanel">
+					<e:formGrid mode="grid" styleClass="grid-columns-2"><!--  grid-v-align grid-white-gap -->
+						<e:control column="#{db.EMPLOYEES.SALUTATION}"/>
+						<e:control column="#{db.EMPLOYEES.FIRST_NAME}" controlClass="grid-new-row"/>
+						<e:control column="#{db.EMPLOYEES.LAST_NAME}"/>
+						<e:control column="#{db.EMPLOYEES.GENDER}"/>
+						<e:control column="#{db.EMPLOYEES.DATE_OF_BIRTH}"/>
+						<e:control column="#{db.EMPLOYEES.DEPARTMENT_ID}"/>
+						<e:control column="#{db.EMPLOYEES.PHONE_NUMBER}" controlClass="grid-new-row"/>
+						<e:control column="#{db.EMPLOYEES.EMAIL}" controlClass="grid-full-row"/>
+						<e:control column="#{db.EMPLOYEES.SALARY}"/>
+						<e:control column="#{db.EMPLOYEES.RETIRED}"/>
+						<e:control column="#{db.EMPLOYEES.UPDATE_TIMESTAMP}"/> 
+					</e:formGrid>
+				</div>
+				<!-- end formPanel -->
+				</e:record>
+			</e:tabPage>
+			
+			<!-- second page -->
+			<e:tabPage id="tabExtended" title="#{msg.employeeDetail_tab_extended}" disabled="#{not page.employeeRecord.exists}">
+				<h3><h:outputText value="#{msg.employeeDetail_payments}"/></h3>
+				<!-- Payment data -->
+				<h:dataTable id="paymentData" value="#{page.payments}" var="item" rowClasses="odd,even">
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.PAYMENTS.YEAR}" />
+						</f:facet>
+						<e:value record="#{item}" column="#{db.PAYMENTS.YEAR}"/>
+					</h:column>
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.PAYMENTS.MONTH}" />
+						</f:facet>
+						<e:value record="#{item}" column="#{db.PAYMENTS.MONTH}"/>
+					</h:column>
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.PAYMENTS.AMOUNT}" />
+						</f:facet>
+						<e:value record="#{item}" column="#{db.PAYMENTS.AMOUNT}"/>
+					</h:column>
+				</h:dataTable>
+				<!-- info message -->
+				<h:panelGroup layout="block" styleClass="info-msg">
+					<h:outputText value="#{msg.employeeDetail_msg_extended}"/>
+				</h:panelGroup>
+			</e:tabPage>
+		</e:tabView>	
+		
+		<!-- button bar -->
+		<sample:pageButtons>
+			<h:commandLink value="#{msg.employeeDetail_back}" action="#{page.doCancel}" immediate="true" />
+			<h:commandLink value="#{msg.employeeDetail_delete}" action="#{page.doDelete}" immediate="true" rendered="#{page.employeeRecord.exists}" />
+			<h:commandLink value="#{msg.employeeDetail_save}" action="#{page.doSave}" />
+			<!-- 
+			<h:commandLink value="#{msg.employeeDetail_test}" actionListener="#{page.doTestSerialization}" />
+			 -->
+		</sample:pageButtons>
+
+		</h:form>
+
+	</ui:define>
+</ui:composition>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/employeeListPage.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/employeeListPage.xhtml
new file mode 100644
index 00000000..3cef89fc
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/employeeListPage.xhtml
@@ -0,0 +1,111 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ -->
+<ui:composition template="/WEB-INF/facelets/templates/layout.xhtml"
+	xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:e="http://java.sun.com/jsf/composite/empire"
+	xmlns:sample="http://java.sun.com/jsf/composite/components"
+	xmlns:h="http://java.sun.com/jsf/html">
+	<f:metadata>
+		<f:viewParam name="action" value="#{page.action}" />
+	</f:metadata>
+
+	<!-- parameters used in layout.xhtml -->
+	<ui:param name="pageTitle" value="#{msg.employeeList_title}" />
+	<ui:param name="contentStyle" value="unstyled" />
+
+	<!-- content -->
+	<ui:define name="content">
+		<h:form id="employeeListFilter">
+		
+			<!-- search settings -->
+			<div class="formPanel">
+				<!-- search criteria -->
+				<e:formGrid mode="grid" styleClass="grid-columns-2">
+					<e:control column="#{db.EMPLOYEES.FIRST_NAME}" value="#{page.searchFilter.firstName}" />
+					<e:control column="#{db.EMPLOYEES.LAST_NAME}" value="#{page.searchFilter.lastName}" />
+					<e:control column="#{db.EMPLOYEES.DEPARTMENT_ID}" value="#{page.searchFilter.departmentId}"
+						       options="#{page.departmentOptions}" />
+					<e:control column="#{db.EMPLOYEES.GENDER}" value="#{page.searchFilter.gender}" />
+				</e:formGrid>
+				<!-- button bar -->
+				<div class="buttonBar">
+					<h:commandLink action="#{page.doResetSearch}" value="#{msg.employeeList_resetSearch}" immediate="true" />
+					<h:commandLink action="#{page.doSearch}" value="#{msg.employeeList_search}" />
+				</div>
+			</div>
+		</h:form>
+
+		<h:form id="employeeList">
+			<h:panelGroup rendered="#{page.employees.resultEmpty}" styleClass="searchResult" layout="block">
+				<h:outputText value="#{msg.global_list_result_empty}" />
+			</h:panelGroup>
+			<!-- list -->
+			<h:panelGroup styleClass="searchResult" layout="block" rendered="#{page.employees.valid}">
+				<h:dataTable id="employeeTable" value="#{page.employees.items}" var="item" rowClasses="odd,even">
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.EMPLOYEES.ID}" value="ID" />
+						</f:facet>
+						<e:value record="#{item}" column="#{db.EMPLOYEES.ID}"/>
+					</h:column>
+					<h:column styleClass="item">
+						<f:facet name="header">
+							<h:outputText value="#{msg.employeeList_field_name}" />
+						</f:facet>
+						<e:link page="#{pages.EmployeeDetailPage.doLoad}" idparam="#{item.idParam}" value="#{item.name}" />
+					</h:column>
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.EMPLOYEES.DEPARTMENT_ID}" />
+						</f:facet>
+						<h:outputText value="#{item.department}" />
+					</h:column>
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.EMPLOYEES.GENDER}" />
+						</f:facet>
+						<e:value record="#{item}" column="#{db.EMPLOYEES.GENDER}" />
+					</h:column>
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.EMPLOYEES.DATE_OF_BIRTH}" />
+						</f:facet>
+						<e:value record="#{item}" column="#{db.EMPLOYEES.DATE_OF_BIRTH}" />
+					</h:column>
+					<h:column>
+						<f:facet name="header">
+							<e:title column="#{db.EMPLOYEES.RETIRED}" />
+						</f:facet>
+						<e:value record="#{item}" column="#{db.EMPLOYEES.RETIRED}" />
+					</h:column>
+				</h:dataTable>
+			</h:panelGroup>
+		</h:form>
+		
+		<!-- button bar -->
+		<sample:pageButtons>
+			<e:link page="#{pages.LoginPage.doLogout}" value="#{msg.application_logout}"/>
+			<e:link page="#{pages.EmployeeDetailPage.doCreate}" value="#{msg.employeeList_create}" />
+		</sample:pageButtons>
+		
+	</ui:define>
+	
+</ui:composition>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/loginPage.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/loginPage.xhtml
new file mode 100644
index 00000000..a34248a3
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/pages/loginPage.xhtml
@@ -0,0 +1,62 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<ui:composition
+	template="/WEB-INF/facelets/templates/layout.xhtml"
+	xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:ui="http://java.sun.com/jsf/facelets"
+	xmlns:f="http://java.sun.com/jsf/core"
+	xmlns:e="http://java.sun.com/jsf/composite/empire"
+	xmlns:sample="http://java.sun.com/jsf/composite/components"
+	xmlns:h="http://java.sun.com/jsf/html">
+	<f:metadata>
+		<f:viewParam name="action" value="#{page.action}" />
+	</f:metadata>
+
+	<!-- parameters used in layout.xhtml -->
+	<ui:param name="pageTitle" value="#{msg.login_title}" />
+	<ui:param name="contentFooter" value="true" />
+	<ui:param name="contentStyle" value="unstyled" />
+
+	<!-- content -->
+	<ui:define name="content">
+		<h:form id="loginForm" method="post">
+			<p><h:outputText value="#{msg.login_hint}"/></p>
+			<!-- input form -->
+			<div class="formPanel">
+				<!-- search criteria -->
+				<e:formGrid mode="grid">
+					<sample:formControl for="name" label="#{msg.login_field_name}">
+						<h:inputText id="name" value="#{sampleUser.userName}" required="true" requiredMessage="#{msg.login_field_name_required}" />
+					</sample:formControl>
+					<sample:formControl for="password" label="#{msg.login_field_password}">
+						<h:inputSecret id="password" value="#{sampleUser.password}" required="true" requiredMessage="#{msg.login_field_password_required}" />
+					</sample:formControl>
+					<sample:formControl for="password" label="#{msg.login_field_language}">
+						<e:select id="language" value="#{sampleUser.locale}" options="#{page.languageOptions}" />
+					</sample:formControl>
+				</e:formGrid>
+				<!-- button bar -->
+				<div class="buttonBar">
+					<h:commandLink action="#{page.doLogin}" value="#{msg.login_title}" />
+				</div>
+			</div>
+		</h:form>
+	</ui:define>
+	
+</ui:composition>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/components/formControl.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/components/formControl.xhtml
new file mode 100644
index 00000000..9f40011e
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/components/formControl.xhtml
@@ -0,0 +1,41 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml"
+  	xmlns:cc="http://java.sun.com/jsf/composite"
+	xmlns:h="http://java.sun.com/jsf/html">
+
+	<!-- Interface -->
+	<cc:interface>
+		<cc:attribute name="label" />
+		<cc:attribute name="for" targets="outputLabel"/>
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+		<div class="eControl">
+			<div class="eCtlLabel">
+				<h:outputLabel id="outputLabel" value="#{cc.attrs.label}" />:
+			</div>
+			<div class="eCtlInput">
+				<cc:insertChildren/>
+			</div>
+		</div>
+	</cc:implementation>
+	
+</html>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/components/pageButtons.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/components/pageButtons.xhtml
new file mode 100644
index 00000000..d22870bf
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/components/pageButtons.xhtml
@@ -0,0 +1,36 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ -->
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:ice="http://www.icesoft.com/icefaces/component"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+  	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface>
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+		<h:panelGroup styleClass="buttonBar margin-above" layout="block">
+			<cc:insertChildren />
+		</h:panelGroup>
+	</cc:implementation>
+	
+</html>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/control.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/control.xhtml
new file mode 100644
index 00000000..cd3a917c
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/control.xhtml
@@ -0,0 +1,53 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.ControlTag">
+		<cc:attribute name="column" required="true" />	
+		<cc:attribute name="record" />
+		<cc:attribute name="custom" />	
+		<cc:attribute name="value" />
+		<cc:attribute name="options" />
+		<cc:attribute name="mandatory"/>
+		<cc:attribute name="readonly" />	
+		<cc:attribute name="disabled"/>
+		<cc:attribute name="tabindex"/>
+		<cc:attribute name="format"/> 
+		<cc:attribute name="minValue"/> 
+		<cc:attribute name="maxValue"/>
+		<cc:attribute name="styleClass" />
+		<cc:attribute name="labelClass" /> 
+		<cc:attribute name="inputClass" /> 
+		<cc:attribute name="label" />
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+		<c:choose>
+			<c:when test="#{cc.attrs.custom == true}">
+				<cc:insertChildren/>
+			</c:when>
+		</c:choose>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/formGrid.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/formGrid.xhtml
new file mode 100644
index 00000000..eb07beb4
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/formGrid.xhtml
@@ -0,0 +1,35 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.FormGridTag">
+		<cc:attribute name="mode" />
+		<cc:attribute name="renderAutoId" />	
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/input.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/input.xhtml
new file mode 100644
index 00000000..eb7d35b1
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/input.xhtml
@@ -0,0 +1,43 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.InputTag">
+		<cc:attribute name="column" required="true" />
+		<cc:attribute name="record" />
+		<cc:attribute name="value" />
+		<cc:attribute name="options" />
+		<cc:attribute name="mandatory"/>
+		<cc:attribute name="readonly" />	
+		<cc:attribute name="disabled"/>
+		<cc:attribute name="tabindex"/>
+		<cc:attribute name="format"/> 
+		<cc:attribute name="minValue"/> 
+		<cc:attribute name="maxValue"/>
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/label.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/label.xhtml
new file mode 100644
index 00000000..54c4fe9c
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/label.xhtml
@@ -0,0 +1,38 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.LabelTag">
+		<cc:attribute name="column" required="true" />	
+		<cc:attribute name="format" />
+		<cc:attribute name="styleClass" />
+		<cc:attribute name="style" />
+		<cc:attribute name="colon" type="java.lang.Boolean"/>
+		<cc:attribute name="required" type="java.lang.Boolean"/>
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+	</cc:implementation>
+	
+</html>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/link.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/link.xhtml
new file mode 100644
index 00000000..3e65ba3d
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/link.xhtml
@@ -0,0 +1,40 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.LinkTag">
+		<cc:attribute name="styleClass" />
+		<cc:attribute name="page" />
+		<cc:attribute name="idparam" />
+		<cc:attribute name="title" />
+		<cc:attribute name="action" method-signature="void method()" />
+		<cc:attribute name="actionListener" method-signature="void method(jakarta.faces.event.ActionEvent)" />
+		<cc:attribute name="disabled" />
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/mitem.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/mitem.xhtml
new file mode 100644
index 00000000..4752e852
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/mitem.xhtml
@@ -0,0 +1,37 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.MenuItemTag">
+		<cc:attribute name="menuId" required="true"/>
+		<cc:attribute name="page" required="true" />
+		<cc:attribute name="idparam"/>
+		<cc:attribute name="currentOnly" type="java.lang.Boolean" default="false" />
+	</cc:interface>
+
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/mlist.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/mlist.xhtml
new file mode 100644
index 00000000..60d5930d
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/mlist.xhtml
@@ -0,0 +1,37 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.MenuListTag">
+		<cc:attribute name="currentId" />
+		<cc:attribute name="currentClass"/>
+		<cc:attribute name="expandedClass"/>
+		<cc:attribute name="disabledClass"/>
+	</cc:interface>
+
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/record.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/record.xhtml
new file mode 100644
index 00000000..d3222683
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/record.xhtml
@@ -0,0 +1,35 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.RecordTag">
+		<cc:attribute name="value" />
+		<cc:attribute name="readonly" />	
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/select.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/select.xhtml
new file mode 100644
index 00000000..d644d35c
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/select.xhtml
@@ -0,0 +1,39 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.SelectTag">
+		<cc:attribute name="options" required="true" />
+		<cc:attribute name="allowNull"/>	
+		<cc:attribute name="nullText"/>	
+		<cc:attribute name="disabled" />
+		<cc:attribute name="update"/>	
+		<cc:attribute name="listener" type="jakarta.el.MethodExpression"/>
+		<cc:clientBehavior name="change" event="change" targets="#{cc.clientId}:select" default="true"/>
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/tabPage.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/tabPage.xhtml
new file mode 100644
index 00000000..f47fd512
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/tabPage.xhtml
@@ -0,0 +1,33 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.TabPageTag">
+	</cc:interface>
+
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/tabView.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/tabView.xhtml
new file mode 100644
index 00000000..060f7d79
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/tabView.xhtml
@@ -0,0 +1,37 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.TabViewTag">
+		<cc:attribute name="mode" />
+		<cc:attribute name="minHeight" />
+		<cc:attribute name="activeIndex" type="java.lang.Integer" />
+		<cc:attribute name="tabChangedListener" method-signature="void method(java.lang.Integer)"/>
+	</cc:interface>
+
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/title.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/title.xhtml
new file mode 100644
index 00000000..99532b2e
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/title.xhtml
@@ -0,0 +1,37 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.TitleTag">
+		<cc:attribute name="column" required="true" />	
+		<cc:attribute name="format" />
+		<cc:attribute name="tag" />
+		<cc:attribute name="styleClass" />
+		<cc:attribute name="style" />
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+	</cc:implementation>
+	
+</html>
diff --git a/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/value.xhtml b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/value.xhtml
new file mode 100644
index 00000000..332dd020
--- /dev/null
+++ b/empire-db-examples/empire-db-example-jakarta-faces/src/main/webapp/resources/empire/value.xhtml
@@ -0,0 +1,41 @@
+<!-- 
+  Licensed to the Apache Software Foundation (ASF) under one
+  or more contributor license agreements.  See the NOTICE file
+  distributed with this work for additional information
+  regarding copyright ownership.  The ASF licenses this file
+  to you under the Apache License, Version 2.0 (the
+  "License"); you may not use this file except in compliance
+  with the License.  You may obtain a copy of the License at
+
+   http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing,
+  software distributed under the License is distributed on an
+  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  KIND, either express or implied.  See the License for the
+  specific language governing permissions and limitations
+  under the License.
+ --> 
+<html xmlns="http://www.w3.org/1999/xhtml"
+	xmlns:h="http://java.sun.com/jsf/html"
+	xmlns:c="http://java.sun.com/jsp/jstl/core"
+	xmlns:cc="http://java.sun.com/jsf/composite">
+
+	<!-- Interface -->
+	<cc:interface componentType="org.apache.empire.component.ValueTag">
+		<cc:attribute name="column" required="true" />	
+		<cc:attribute name="record" />
+		<cc:attribute name="format" />
+		<cc:attribute name="title" />
+		<cc:attribute name="tag" />
+		<cc:attribute name="styleClass" />
+		<cc:attribute name="style" />
+		<cc:attribute name="options" />
+	</cc:interface>
+	
+	<!-- Implementation -->
+	<cc:implementation>
+		<cc:insertChildren/>
+	</cc:implementation>
+	
+</html>
\ No newline at end of file
diff --git a/empire-db-examples/empire-db-example-jsf2/pom.xml b/empire-db-examples/empire-db-example-jsf2/pom.xml
index 91f9c91c..f413f5da 100644
--- a/empire-db-examples/empire-db-example-jsf2/pom.xml
+++ b/empire-db-examples/empire-db-example-jsf2/pom.xml
@@ -52,10 +52,12 @@
 		<dependency>
 			<groupId>com.sun.faces</groupId>
 			<artifactId>jsf-api</artifactId>
+			<version>2.2.20</version>
 		</dependency>
 		<dependency>
 			<groupId>com.sun.faces</groupId>
 			<artifactId>jsf-impl</artifactId>
+			<version>2.2.20</version>
 		</dependency>
 		 -->
 
@@ -63,17 +65,19 @@
 		<dependency>
 			<groupId>org.apache.myfaces.core</groupId>
 			<artifactId>myfaces-api</artifactId>
+			<version>2.3.10</version>
 		</dependency>
 		<dependency>
 			<groupId>org.apache.myfaces.core</groupId>
 			<artifactId>myfaces-impl</artifactId>
+			<version>2.3.10</version>
 		</dependency>
 
 		<!-- EL --> 
 		<dependency>
 		    <groupId>javax.el</groupId>
 		    <artifactId>javax.el-api</artifactId>
-			<type>jar</type>
+		    <version>3.0.0</version>
 			<scope>provided</scope>
 		</dependency>
 
diff --git a/empire-db-examples/pom.xml b/empire-db-examples/pom.xml
index 3dbc8e2f..0a55a437 100644
--- a/empire-db-examples/pom.xml
+++ b/empire-db-examples/pom.xml
@@ -36,6 +36,7 @@
 		<module>empire-db-example-advanced</module>
 		<module>empire-db-example-codegen</module>
 		<module>empire-db-example-jsf2</module>
+		<module>empire-db-example-jakarta-faces</module>
 		<module>empire-db-example-vue</module>
 		<module>empire-db-example-spring-boot</module>
 	</modules>
diff --git a/empire-db-jakarta-faces/.gitignore b/empire-db-jakarta-faces/.gitignore
new file mode 100644
index 00000000..c49244bd
--- /dev/null
+++ b/empire-db-jakarta-faces/.gitignore
@@ -0,0 +1,7 @@
+/target/
+/.settings/
+/.classpath
+/.project
+/.apt_generated/
+/.factorypath
+/.apt_generated_tests/
diff --git a/empire-db-jsf2/pom.xml b/empire-db-jakarta-faces/pom.xml
similarity index 72%
copy from empire-db-jsf2/pom.xml
copy to empire-db-jakarta-faces/pom.xml
index 78e1e234..c03644bc 100644
--- a/empire-db-jsf2/pom.xml
+++ b/empire-db-jakarta-faces/pom.xml
@@ -22,9 +22,9 @@
 		<groupId>org.apache.empire-db</groupId>
 		<version>3.1.1-SNAPSHOT</version>
 	</parent>
-	<artifactId>empire-db-jsf2</artifactId>
+	<artifactId>empire-db-jakarta-faces</artifactId>
 	<packaging>jar</packaging>
-	<name>Apache Empire-db Java Server Faces 2.x extensions</name>
+	<name>Apache Empire-db Jakarta Faces extensions</name>
 	
 	<properties>
 	</properties>
@@ -36,31 +36,42 @@
 		</dependency> 
 		<!-- Servlet API -->
 		<dependency>
-		    <groupId>javax.servlet</groupId>
-		    <artifactId>javax.servlet-api</artifactId>
+		    <groupId>jakarta.servlet</groupId>
+		    <artifactId>jakarta.servlet-api</artifactId>
+		    <version>5.0.0</version>
+		    <scope>provided</scope>
 		</dependency>
+		<!-- jakarta.faces -->
 		<dependency>
-			<groupId>com.sun.faces</groupId>
-			<artifactId>jsf-api</artifactId>
+		    <groupId>jakarta.faces</groupId>
+		    <artifactId>jakarta.faces-api</artifactId>
+		    <version>3.0.0</version>
 			<scope>provided</scope>
 		</dependency>
-		<!-- Mojarra --> 
 		<dependency>
-			<groupId>com.sun.faces</groupId>
-			<artifactId>jsf-impl</artifactId>
+		    <groupId>org.glassfish</groupId>
+		    <artifactId>jakarta.faces</artifactId>
+		    <version>3.0.5</version>
+			<scope>provided</scope>
+		</dependency>		 
+		<!-- MyFaces -->
+		<dependency>
+		    <groupId>org.apache.myfaces.core</groupId>
+		    <artifactId>myfaces-api</artifactId>
+		    <version>3.0.2</version>
 			<scope>provided</scope>
 		</dependency>
-		<!-- MyFaces --> 
 		<dependency>
 			<groupId>org.apache.myfaces.core</groupId>
 			<artifactId>myfaces-impl</artifactId>
+			<version>3.0.2</version>
 			<scope>provided</scope>
 		</dependency>
 		<!-- EL --> 
 		<dependency>
-		    <groupId>javax.el</groupId>
-		    <artifactId>javax.el-api</artifactId>
-			<type>jar</type>
+		    <groupId>jakarta.el</groupId>
+		    <artifactId>jakarta.el-api</artifactId>
+		    <version>4.0.0</version>
 			<scope>provided</scope>
 		</dependency>
 	</dependencies>
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/DBELResolver.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/DBELResolver.java
new file mode 100644
index 00000000..148283c9
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/DBELResolver.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import java.beans.FeatureDescriptor;
+import java.lang.reflect.Field;
+import java.util.Iterator;
+
+import jakarta.el.ELContext;
+import jakarta.el.ELResolver;
+import jakarta.faces.context.FacesContext;
+
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.data.RecordData;
+import org.apache.empire.db.DBColumnExpr;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.DBRowSet;
+import org.apache.empire.exceptions.NotSupportedException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DBELResolver extends ELResolver
+{
+    private static final Logger log = LoggerFactory.getLogger(DBELResolver.class);
+    
+    @Override
+    public Class<?> getCommonPropertyType(ELContext context, Object base)
+    {
+        if (base==null)
+            log.warn("DBELResolver:getCommonPropertyType is not implemented!");
+        return null;
+    }
+
+    @Override
+    public Class<?> getType(ELContext context, Object base, Object property)
+    {
+        if ((base instanceof DBRowSet) ||
+            (base instanceof DBDatabase) ||
+            (base==null && property.equals("db")))
+            log.warn("DBELResolver:getType is not implemented!");
+        return null;
+    }
+
+    @Override
+    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext arg0, Object arg1)
+    {
+        log.warn("DBELResolver:getFeatureDescriptors is not implemented!");
+        return null;
+    }
+
+    @Override
+    public Object getValue(ELContext context, Object base, Object property)
+    {
+        // Resolve database, table/view or column
+        if (base instanceof DBRowSet)
+        {   // Find matching column
+            String   name = StringUtils.toString(property);
+            DBColumnExpr column = ((DBRowSet)base).getColumn(name);
+            if (column==null)
+                column = findExpressionField(base, name); 
+            if (column!=null)
+                context.setPropertyResolved(true); 
+            else
+                log.error("ELResolver error: Column '{}' cannot be resolved for table/view '{}'.", name.toUpperCase(), ((DBRowSet)base).getName());
+            // done
+            return column;
+        }
+        else if (base instanceof DBDatabase)
+        {   // Lookup RowSet
+            String   name = StringUtils.toString(property);
+            DBRowSet rset = ((DBDatabase)base).getRowSet(name);
+            if (rset!=null)
+                context.setPropertyResolved(true);
+            else
+                log.error("ELResolver error: Table/View '{}' cannot be resolved.", name.toUpperCase());
+            // done
+            return rset;
+        }
+        else if (base instanceof RecordData)
+        {   // Lookup RowSet           
+            String field= StringUtils.toString(property);
+            // field must start with a capital Letter
+            if (field==null || field.length()==0 || field.charAt(0)<'A' || field.charAt(0)>'Z')
+                return null;
+            // try to find field
+            int index = ((RecordData)base).getFieldIndex(field);
+            if (index<0)
+            {   // not a field, it may be a property
+                log.warn("ELResolver warning: field '{}' not found in record .", field);
+                // not resolved, continue search
+                return null; 
+            }
+            // Found! Return field value.
+            context.setPropertyResolved(true);
+            return ((RecordData)base).getValue(index);
+        }
+        else if (base==null)
+        {   // LookupDatabase
+            String name = StringUtils.toString(property);
+            DBDatabase db = DBDatabase.findByIdentifier(name);
+            if (db!=null)
+                context.setPropertyResolved(true);
+            // done
+            return db;
+        }
+        return null;
+    }
+    
+    public static DBColumnExpr findExpressionField(Object rowset, String property)
+    {
+        Class<?> c = rowset.getClass();
+        try
+        {   // Find a matching field name
+            Field f = c.getField(property);
+            if (f==null)
+                return null;
+            Object v = f.get(rowset);
+            if (v==null)
+            {   // invalid data type 
+                log.error("ELResolver error: Field '{}.{}' is null.", c.getSimpleName(), property);
+                return null;
+            }    
+            if (!(v instanceof DBColumnExpr))
+            {   // invalid data type 
+                log.error("ELResolver error: Field '{}.{}' is not a DBColumnExpr.", c.getSimpleName(), property);
+                return null;
+            }    
+            return ((DBColumnExpr)v);
+        }
+        catch (SecurityException e)
+        {
+            log.error("ELResolver error: Unable to access field "+c.getSimpleName()+"."+property, e);
+            return null;
+        }
+        catch (NoSuchFieldException e)
+        {
+            return null;
+        }
+        catch (IllegalArgumentException e)
+        {
+            log.error("ELResolver error: Unable to access field "+c.getSimpleName()+"."+property, e);
+            return null;
+        }
+        catch (IllegalAccessException e)
+        {
+            log.error("ELResolver error: Unable to access field "+c.getSimpleName()+"."+property, e);
+            return null;
+        }
+    }
+
+    @Override
+    public boolean isReadOnly(ELContext context, Object base, Object property)
+    {
+        // is it our's?
+        if ((base instanceof DBRowSet) ||
+            (base instanceof DBDatabase) ||
+            (base==null && property.equals("db")))
+        {   // read only
+            log.info("ELResolver: property {} is read only.", property);
+            return true;
+        }
+        // not our business
+        return false;
+    }
+
+    @Override
+    public void setValue(ELContext context, Object base, Object property, Object value)
+    {
+        if (isReadOnly(context, base, property))
+        {
+            RuntimeException e = new NotSupportedException(this, "setValue");
+            FacesContext.getCurrentInstance().getExternalContext().log(e.getMessage(), e);
+            throw e;
+        }    
+    }
+
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesConfiguration.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesConfiguration.java
new file mode 100644
index 00000000..69dc62c5
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesConfiguration.java
@@ -0,0 +1,615 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import jakarta.el.ELResolver;
+import jakarta.faces.FactoryFinder;
+import jakarta.faces.application.Application;
+import jakarta.faces.application.NavigationHandler;
+import jakarta.faces.application.ProjectStage;
+import jakarta.faces.application.ViewHandler;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.convert.Converter;
+import jakarta.faces.event.PhaseListener;
+import jakarta.faces.lifecycle.Lifecycle;
+import jakarta.faces.lifecycle.LifecycleFactory;
+import jakarta.faces.render.RenderKit;
+import jakarta.faces.render.RenderKitFactory;
+import jakarta.faces.render.Renderer;
+
+import org.apache.empire.commons.ClassUtils;
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.exceptions.InvalidOperationException;
+import org.apache.empire.exceptions.ItemExistsException;
+import org.apache.empire.exceptions.ItemNotFoundException;
+import org.apache.empire.exceptions.ObjectNotValidException;
+import org.apache.empire.jakarta.impl.FacesImplementation;
+import org.apache.empire.jakarta.impl.FacesImplementation.BeanStorageProvider;
+import org.apache.empire.jakarta.pages.PageNavigationHandler;
+import org.apache.empire.jakarta.pages.PagePhaseListener;
+import org.apache.empire.jakarta.pages.PagesELResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * FacesConfiguration
+ * Allows programmatic Faces configuration as an alternative to the faces-config.xml
+ * In order to provide custom configuration you must override the class WebAppStartupListener
+ * and put it in the faces-config.xml
+ * <pre> 
+ *   <system-event-listener-class>de.volkswagen.jsf.FacesStartupListener</system-event-listener-class>
+ * </pre>
+ * Further you must call the super constructor with a FacesConfiguration class like this:
+ * <pre>
+ *   public FacesStartupListener()
+ *   {
+ *      super(FacesExtensions.class);
+ *   }
+ * </pre> 
+ */
+public class FacesConfiguration
+{
+    protected static final Logger log = LoggerFactory.getLogger(FacesConfiguration.class);
+
+    public final static String EMPIRE_COMPONENT_FAMILY = "org.apache.empire.component";
+    
+    /*
+     * Initialized
+     */
+    private static boolean initialized = false;
+    public static boolean isInitialized()
+    {
+        return initialized; 
+    }
+
+    /*
+     * Project Stage
+     * see org.apache.myfaces.application.ApplicationImpl
+     * How to set:
+     *   set JVM-Parameter:     "org.apache.myfaces.PROJECT_STAGE"
+     *   set JNDI-Parameter:    "java:comp/env/jsf/ProjectStage"
+     *   set in web.inf:        
+     *      <context-param>
+     *          <param-name>jakarta.faces.PROJECT_STAGE</param-name>
+     *          <param-value>Development</param-value>
+     *      </context-param>
+     */
+    private static ProjectStage projectStage;
+    public static ProjectStage getProjectStage()
+    {
+        if (projectStage==null)
+            throw new ObjectNotValidException(FacesConfiguration.class, "Not Initialized");
+        return projectStage;
+    }
+    
+    /**
+     * Static Initializer
+     * @param clazz the configuration class
+     * @param startupContext the startupContext
+     */
+    public static <T extends FacesConfiguration> void initialize(Class<T> configClass, FacesContext startupContext, FacesImplementation facesImpl)
+    {
+        // crate and initialize
+        FacesConfiguration fConfig = ClassUtils.newInstance(configClass, FacesImplementation.class, facesImpl);
+        fConfig.initialize(startupContext);
+    }
+
+    /*
+     * Inject
+     */
+    protected final FacesImplementation facesImpl;
+    /*
+     * Temp Variables
+     */
+    protected ExternalContext externalContext;
+    protected Application application;
+    /*
+     * Lazy
+     */
+    private BeanStorageProvider beanStorage; // call getBeanStorageProvider() 
+
+    public FacesConfiguration(FacesImplementation facesImpl)
+    {
+        this.facesImpl = facesImpl;
+    }
+
+    public final void initialize(FacesContext startupContext)
+    {
+        if (initialized)
+            throw new InvalidOperationException("FacesConfiguration already initialized!"); 
+        try
+        {   // Set temporary variables
+            this.externalContext = startupContext.getExternalContext();
+            this.application = startupContext.getApplication();
+            this.beanStorage = null;
+
+            projectStage = application.getProjectStage();
+            log.info("Initializing Faces Configuration for ProjectStage {}", projectStage.name());
+            
+            // Init everything
+            initAll(startupContext);
+
+            // done
+            log.info("Faces Configuration complete.");
+        }
+        finally
+        {   // cleanup
+            this.beanStorage = null;
+            this.application = null;
+            this.externalContext = null;
+            this.facesImpl.configComplete();
+            // set initialized (even if Exception has been thrown)
+            initialized = true;
+        }
+    }
+    
+    /*
+     * Overrideable methods
+     */
+    
+    protected void initAll(FacesContext context)
+    {
+        log.debug("Init FacesParams...");
+        initFacesParams();
+        
+        log.debug("Init Factories...");
+        initFactories();
+
+        log.debug("Init NavigationHandler...");
+        initNavigationHandler();
+        
+        log.debug("Init ResourceHandler...");
+        initResourceHandler();
+
+        log.debug("Registrating Converters...");
+        initConverters();
+
+        log.debug("Registrating EL-Resolvers...");
+        initElResolvers();
+        
+        log.debug("Registrating Lifecycle...");
+        initLifecycle(new LifecycleUpdater());
+
+        log.debug("Registrating Search Expression Resolvers...");
+        initSearchExpressionResolvers();
+
+        log.debug("Registrating Components...");
+        initComponents();
+
+        log.debug("Registrating Renderers...");
+        initRenderers(new RenderKitUpdater(getApplicationRenderKit(context)));
+
+        log.debug("Registrating Managed Beans...");
+        initManagedBeans();            
+
+        log.debug("Registrating Controls...");
+        initControls();
+    }
+
+    protected void initFacesParams()
+    {   // set params
+        setFacesInitParam(ViewHandler.FACELETS_SKIP_COMMENTS_PARAM_NAME, true);
+    }
+
+    protected void initFactories()
+    {
+        // Noting yet
+        /* example
+         *  log.info("Setting Factory {}", FactoryFinder.TAG_HANDLER_DELEGATE_FACTORY);
+            FactoryFinder.setFactory(FactoryFinder.TAG_HANDLER_DELEGATE_FACTORY, CustomTagHandlerDelegateFactory.class.getName());
+            // (check)
+            // TagHandlerDelegateFactory tagHandlerFactory = (TagHandlerDelegateFactory)FactoryFinder.getFactory(FactoryFinder.TAG_HANDLER_DELEGATE_FACTORY);
+            // log.info(tagHandlerFactory.getClass().getName());
+         */
+    }
+    
+    protected void initNavigationHandler()
+    {
+        NavigationHandler wrapped = application.getNavigationHandler();
+        if (wrapped instanceof PageNavigationHandler)
+            return; // Already set
+        // replace
+        log.info("Setting NavigationHandler to {}", PageNavigationHandler.class.getName());
+        application.setNavigationHandler(new PageNavigationHandler(wrapped));
+    }
+    
+    protected void initResourceHandler()
+    {
+        // Not implemented
+        // application.setResourceHandler(new MyResourceHandler(wrapped));
+    }
+
+    protected void initConverters()
+    {
+        // Noting
+    }
+
+    protected void initElResolvers()
+    {
+        // add
+        addELResolver(DBELResolver.class);
+        addELResolver(PagesELResolver.class);
+    }
+    
+    protected void initLifecycle(LifecycleUpdater lcu)
+    {
+        lcu.addPhaseListener(FacesRequestPhaseListener.class);
+        lcu.addPhaseListener(PagePhaseListener.class);
+    }
+
+    protected void initSearchExpressionResolvers()
+    {
+        // Nothing
+        // SearchExpressionResolverFactory.registerResolver("@fragment", new FragmentExpressionResolver());
+    }
+
+    protected void initComponents()
+    {
+        // Empire Components
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.ControlTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.InputTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.FormGridTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.LabelTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.LinkTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.MenuItemTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.MenuListTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.RecordTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.SelectTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.TabPageTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.TabViewTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.TitleTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.UnitTag.class);
+        addComponent(EMPIRE_COMPONENT_FAMILY, org.apache.empire.jakarta.components.ValueTag.class);
+    }
+
+    protected void initRenderers(RenderKitUpdater rku)
+    {
+        // Noting
+        // rku.replace("jakarta.faces.Input", "jakarta.faces.Text", FacesTextInputRenderer.class);
+    }
+    
+    protected void initManagedBeans()
+    {
+        // Nothing
+        // addManagedBean(ConfirmPopup.MANAGED_BEAN_NAME, ConfirmPopup.class, ConfirmPopup.MANAGED_BEAN_SCOPE);
+    }
+
+    protected void initControls()
+    {
+        // Not implemented
+        // InputControlManager.registerControl(new CustomCheckboxInputControl());
+    }
+
+    /*
+     *  Helpers 
+     */
+    
+    protected BeanStorageProvider getBeanStorageProvider()
+    {
+        if (this.beanStorage==null)
+            this.beanStorage = facesImpl.getBeanStorageProvider(externalContext);
+        return this.beanStorage; 
+    }
+    
+    protected RenderKit getApplicationRenderKit(FacesContext context)
+    {
+        String renderKitId = StringUtils.coalesce(application.getDefaultRenderKitId(), RenderKitFactory.HTML_BASIC_RENDER_KIT); 
+        return ((RenderKitFactory)FactoryFinder.getFactory(FactoryFinder.RENDER_KIT_FACTORY)).getRenderKit(context, renderKitId);
+    }
+    
+    protected void setFacesInitParam(String paramName, Object paramValue, boolean overwriteExisting)
+    {
+        // special case
+        if (ProjectStage.PROJECT_STAGE_PARAM_NAME.equals(paramName))
+            throw new InvalidOperationException(ProjectStage.PROJECT_STAGE_PARAM_NAME+" cannot be changed!");
+        // get map
+        String paramVal = StringUtils.toString(paramValue);
+        String orgValue = this.externalContext.getInitParameter(paramName);
+        if (ObjectUtils.compareEqual(paramVal, orgValue))
+            return; // No change
+        if (ObjectUtils.isNotEmpty(orgValue) && !overwriteExisting)
+        {   // Ingnore
+            log.info("Ignoring FacesParam \"{}\" (\"{}\"). Keeping current value of \"{}\"", paramName, paramVal, orgValue);
+            return;
+        }
+        if (orgValue!=null)
+            log.info("Setting FacesParam \"{}\" to \"{}\". Original value was \"{}\"", paramName, paramVal, orgValue);
+        else
+            log.info("Setting FacesParam \"{}\" to \"{}\".", paramName, paramVal);
+        // add to map
+        @SuppressWarnings("unchecked")
+        Map<String,String> paramMap = this.externalContext.getInitParameterMap();
+        paramMap.put(paramName, paramVal);
+    }
+
+    protected void setFacesInitParam(String paramName, Object paramValue)
+    {
+        setFacesInitParam(paramName, paramValue, false);
+    }
+    
+    protected void setFacesInitParam(Enum<?> paramName, Object paramValue)
+    {
+        setFacesInitParam(paramName.toString(), paramValue, false);
+    }
+    
+    protected void addConverter(Class<?> targetClass, Class<? extends Converter> converterClass)
+    {
+        log.info("Adding Type-Converter for type \"{}\" using {}", targetClass.getName(), converterClass.getName());
+        application.addConverter(targetClass, converterClass.getName());
+    }
+
+    protected void addComponent(String componentFamily, Class<? extends UIComponent> clazz)
+    {
+        String type = StringUtils.concat(componentFamily, ".", clazz.getSimpleName());
+        log.info("Adding component type \"{}\" using {}", type, clazz.getName());
+        application.addComponent(type, clazz.getName());
+    }
+    
+    protected void addManagedBean(String beanName, Class<?> beanClass, String scope)
+    {
+        facesImpl.registerManagedBean(beanName, beanClass.getName(), scope);
+    }
+
+    protected void replaceComponent(String componentType, Class<? extends UIComponent> overrideComponent)
+    {
+        if (StringUtils.isEmpty(componentType))
+            throw new InvalidArgumentException("componentType", componentType);
+        // check
+        checkComponentTypeExists(componentType);
+        log.info("Replacing component type \"{}\" with {}", componentType, overrideComponent.getName());
+        application.addComponent(componentType, overrideComponent.getName());
+    }
+
+    protected void replaceComponent(Class<? extends UIComponent> componentClassWithType, Class<? extends UIComponent> overrideComponent)
+    {
+        String componentType = (String) ClassUtils.getFieldValue(componentClassWithType, null, "COMPONENT_TYPE", true);
+        replaceComponent(componentType, overrideComponent);
+    }
+
+    protected void replaceComponent(String componentFamily, Class<? extends UIComponent> componentClassToReplace, Class<? extends UIComponent> overrideComponent)
+    {
+        String componentType = StringUtils.concat(componentFamily, ".", componentClassToReplace.getSimpleName());
+        replaceComponent(componentType, overrideComponent);
+    }
+    
+    protected void checkComponentTypeExists(String componentType)
+    {
+        Iterator<String> types = application.getComponentTypes();
+        while (types.hasNext())
+        {
+            String type = types.next();
+            if (componentType.equals(type))
+                return; // found;
+        }
+        throw new ItemNotFoundException("Component-Type: "+componentType);
+    }
+    
+    protected void addELResolver(Class<? extends ELResolver> resolverClass)
+    {
+        boolean added = facesImpl.registerElResolver(resolverClass);
+        if (added)
+            log.info("Adding FacesConfigElResolver {}", resolverClass.getName());
+    }
+    
+    /*
+     * list
+     */
+    protected void listCompoennts()
+    {
+        ConfigTypeList list = new ConfigTypeList("Component-Types");
+        Iterator<String> types = application.getComponentTypes();
+        while (types.hasNext())
+        {
+            String componentType = types.next();
+            // log.info("Renderer-Family: {} Type {}", componentFamily, rendererType);
+            list.addItem(componentType);
+        }
+        log.info(list.toString());
+    }
+    
+    /**
+     * RenderKitReplacer
+     * @author doebele
+     */
+    protected class RenderKitUpdater
+    {
+        private final RenderKit renderKit;
+        public RenderKitUpdater(RenderKit renderKit)
+        {
+            this.renderKit = renderKit;            
+        }
+        
+        public RenderKit getRenderKit()
+        {
+            return renderKit;
+        }
+
+        public void listAll()
+        {   // list all
+            ConfigTypeList list = new ConfigTypeList("Renderer-Types");
+            Iterator<String> families = renderKit.getComponentFamilies();
+            while (families.hasNext())
+            {
+                String componentFamily = families.next();
+                Iterator<String> types = renderKit.getRendererTypes(componentFamily);
+                while (types.hasNext())
+                {
+                    String rendererType = types.next();
+                    // log.info("Renderer-Family: {} Type {}", componentFamily, rendererType);
+                    list.addItem(componentFamily, rendererType);
+                }
+            }
+            log.info(list.toString());
+        }
+
+        public void add(String componentFamily, String rendererType, Class<? extends Renderer> rendererClass)
+        {
+            Renderer check = findRenderer(componentFamily, rendererType);
+            if (check!=null)
+            {   if (check.getClass().equals(rendererClass))
+                    return; // already there
+                // Another renderer exists
+                throw new ItemExistsException(StringUtils.concat(componentFamily, " / ", rendererType));
+            }
+            // add
+            log.info("Adding Renderer type \"{}\" using {}", rendererType, rendererClass.getName());
+            renderKit.addRenderer(componentFamily, rendererType, ClassUtils.newInstance(rendererClass));
+        }
+
+        public void replace(String componentFamily, String rendererType, Class<? extends Renderer> replaceClass)
+        {
+            // checkRenderTypeExists(componentFamily, rendererType);
+            Renderer check = findRenderer(componentFamily, rendererType);
+            if (check==null)
+                throw new ItemNotFoundException(StringUtils.concat(componentFamily, " / ", rendererType));
+            if (check.getClass().equals(replaceClass))
+                return; // Already replaced
+            // replace
+            log.info("Replacing Renderer type \"{}\" with class {}", rendererType, replaceClass.getName());
+            renderKit.addRenderer(componentFamily, rendererType, ClassUtils.newInstance(replaceClass));
+        }
+        
+        /*
+        public void replace(Class<? extends CoreRenderer> orgClass, Class<? extends CoreRenderer> replaceClass)
+        {
+            String name = orgClass.getName();
+            int sep = name.lastIndexOf('.');
+            replace(name.substring(0, sep), name.substring(sep+1), replaceClass);
+        }
+        */
+        
+        private Renderer findRenderer(String componentFamily, String rendererType)
+        {
+            // list all
+            Iterator<String> families = renderKit.getComponentFamilies();
+            while (families.hasNext())
+            {
+                String family = families.next();
+                if (componentFamily.equals(family)) {
+                    Iterator<String> types = renderKit.getRendererTypes(family);
+                    while (types.hasNext())
+                    {
+                        String type = types.next();
+                        if (rendererType.equals(type))
+                            return renderKit.getRenderer(componentFamily, rendererType); // found
+                    }
+                }
+            }
+            return null;
+        }
+        
+    }
+    
+    /**
+    * LifecycleUpdater
+    * @author doebele
+    */
+    protected class LifecycleUpdater
+    {
+        private final Lifecycle lifecycle;
+        private PhaseListener[] phaseListeners;
+
+        public LifecycleUpdater()
+        {
+           // The DEFAULT Lifecycle
+           LifecycleFactory lifecycleFactory = (LifecycleFactory) FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); 
+           this.lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+           this.phaseListeners = lifecycle.getPhaseListeners();
+        }
+
+        public void listAll()
+        {
+            ConfigTypeList list = new ConfigTypeList("Phase-Listeners");
+            this.phaseListeners = lifecycle.getPhaseListeners();
+            for (PhaseListener pl : phaseListeners)
+            {
+                list.addItem(pl.getClass(), pl.getPhaseId());
+            }
+            log.info(list.toString());
+        }
+
+        public void addPhaseListener(Class<? extends PhaseListener> phaseListenerClass)
+        {
+            for (PhaseListener pl : phaseListeners)
+            {
+                if (pl.getClass().equals(phaseListenerClass))
+                    return; // already there
+            }
+            // Not found: Create and Append
+            log.info("Adding Lifecycle PhaseListener {}", phaseListenerClass.getName());
+            PhaseListener listener = ClassUtils.newInstance(phaseListenerClass);
+            // Add to bean storage
+            getBeanStorageProvider().injectBean(listener);
+            // Add to lifecycle
+            lifecycle.addPhaseListener(listener);
+            // refresh
+            // this.phaseListeners = lifecycle.getPhaseListeners();
+        }
+    }
+
+    /**
+     * ConfigTypeList
+     * @author doebele
+     */
+    protected static class ConfigTypeList
+    {
+        private static final String CRLF = "\r\n";
+        private static final String TAB = "\t";
+        private final StringBuilder b;
+        public ConfigTypeList(String listName)
+        {
+            this.b = new StringBuilder(200);
+            b.append(listName);
+            b.append(":");
+            b.append(CRLF);
+        }
+        public void addItem(Object item, Object... more)
+        {
+            b.append(TAB);
+            b.append(toString(item));
+            for (int i=0; i<more.length; i++)
+            {
+                b.append(TAB);
+                b.append(toString(more[i]));
+            }
+            b.append(CRLF);
+        }
+        protected String toString(Object o)
+        {
+            if (o instanceof Class<?>)
+                return ((Class<?>)o).getName();
+            if (o instanceof Enum<?>)
+                return ((Enum<?>)o).name();
+            return String.valueOf(o);
+        }
+        @Override
+        public String toString()
+        {
+           return this.b.toString();
+        }
+    }
+
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesRequestPhaseListener.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesRequestPhaseListener.java
new file mode 100644
index 00000000..27a48a87
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesRequestPhaseListener.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.PhaseEvent;
+import jakarta.faces.event.PhaseId;
+import jakarta.faces.event.PhaseListener;
+import jakarta.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FacesRequestPhaseListener implements PhaseListener
+{
+    private static final long   serialVersionUID = 1L;
+    final Logger                log              = LoggerFactory.getLogger(FacesRequestPhaseListener.class);
+
+    public FacesRequestPhaseListener()
+    {
+        // foo
+    }
+
+    @Override
+    public PhaseId getPhaseId()
+    {
+        return PhaseId.ANY_PHASE;
+    }
+
+    @Override
+    public void beforePhase(PhaseEvent pe)
+    {
+        // Only when rendering the response
+        if (pe.getPhaseId() != PhaseId.RENDER_RESPONSE)
+            return;
+        // Add header information    
+        FacesContext facesContext = pe.getFacesContext();
+        HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
+        response.addHeader("Pragma", "no-cache");
+        response.addHeader("Cache-Control", "no-cache");
+        response.addHeader("Cache-Control", "no-store");
+        response.addHeader("Cache-Control", "must-revalidate");
+    }
+
+    /** 
+     * VERY VERY IMPORTANT FUNCTION: Release Connection on End of request! 
+     **/
+    @Override
+    public void afterPhase(PhaseEvent pe)
+    {
+        // Check Response complete
+        FacesContext ctx = pe.getFacesContext();
+        if (pe.getPhaseId() == PhaseId.RENDER_RESPONSE || ctx.getResponseComplete())
+        {   // Cleanup when response is complete
+            WebApplication app = WebApplication.getInstance();
+            if (app!=null)
+                app.onRequestComplete(ctx);
+            else
+                log.warn("No WebApplication available to complete and cleanup request. Please create a managed bean of name "+WebApplication.APPLICATION_BEAN_NAME);
+        }
+            
+    }
+
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesUtils.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesUtils.java
new file mode 100644
index 00000000..edbd5601
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/FacesUtils.java
@@ -0,0 +1,544 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import jakarta.el.ValueExpression;
+import jakarta.faces.application.FacesMessage;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIInput;
+import jakarta.faces.component.UIViewRoot;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.ActionEvent;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
+
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.data.Column;
+import org.apache.empire.exceptions.EmpireException;
+import org.apache.empire.exceptions.InternalException;
+import org.apache.empire.exceptions.ItemNotFoundException;
+import org.apache.empire.jakarta.impl.FacesImplementation;
+import org.apache.empire.jakarta.pages.Page;
+import org.apache.empire.jakarta.pages.PageDefinition;
+import org.apache.empire.jakarta.pages.PageOutcome;
+import org.apache.empire.jakarta.utils.ParameterMap;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FacesUtils
+{
+
+    private static final Logger log = LoggerFactory.getLogger(FacesUtils.class);
+    
+    public static final String SKIP_INPUT_VALIDATION_PARAM = "empire.jsf.input.skipValidation";
+
+    /* App */
+
+    public static WebApplication getWebApplication()
+    {
+        return WebApplication.getInstance();
+    }
+    
+    public static FacesImplementation getFacesImplementation()
+    {
+        return getWebApplication().getFacesImplementation();    
+    }
+    
+    public static FacesContext getContext()
+    {
+        return FacesContext.getCurrentInstance();
+    }
+
+    /* Session */
+    
+    public static Map<String, Object> getSessionMap(final FacesContext fc)
+    {
+        return fc.getExternalContext().getSessionMap();
+    }
+    
+    /* Request */
+    
+    public static HttpServletRequest getHttpRequest(final FacesContext fc)
+    {
+        return (HttpServletRequest) fc.getExternalContext().getRequest();
+    }
+    
+    public static String getRequestUri(FacesContext context)
+    {
+        ExternalContext ec = context.getExternalContext();
+        Object req = (ec!=null ? ec.getRequest() : null);
+        if (req instanceof HttpServletRequest) {
+            HttpServletRequest hr = (HttpServletRequest)req;
+            return hr.getRequestURI(); 
+        }
+        return "{No HttpServletRequest}";
+    }
+    
+    public static String getRequestUserAgent(FacesContext context)
+    {
+        ExternalContext ec = context.getExternalContext();
+        Object req = (ec!=null ? ec.getRequest() : null);
+        if (req instanceof HttpServletRequest)
+        {   // get User-Agent
+            String agent = ((HttpServletRequest)req).getHeader("user-agent");
+            return agent;
+        }
+        return "{No HttpServletRequest}";
+    }
+
+    public static String getRequestContextPath()
+    {
+        ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
+        return (ec!=null ? ec.getRequestContextPath() : null);
+    }
+
+    public static Object getRequestAttribute(final String key)
+    {
+        FacesContext fc = getContext();
+        return getRequestAttribute(fc, key);
+    }
+
+    public static Object getRequestAttribute(final FacesContext fc, final String key)
+    {
+        return getHttpRequest(fc).getAttribute(key);
+    }
+
+    public static <T> T getRequestAttribute(final FacesContext fc, final String key, Class<T> clazz)
+    {
+        Object requestValue = getRequestAttribute(fc, key);
+        return (requestValue != null ? ObjectUtils.convert(clazz, requestValue) : null);
+    }
+
+    public static void setRequestAttribute(final String key, Object value)
+    {
+        FacesContext fc = getContext();
+        setRequestAttribute(fc, key, value);
+    }
+
+    public static void setRequestAttribute(final FacesContext fc, final String key, Object value)
+    {
+        if (value!=null)
+            getHttpRequest(fc).setAttribute(key, value);
+        else
+            getHttpRequest(fc).removeAttribute(key);
+    }
+
+    public static String getRequestParam(final FacesContext context, final String param)
+    {
+        return context.getExternalContext().getRequestParameterMap().get(param);
+    }
+
+    public static String getRequestParam(final String key)
+    {
+        FacesContext fc = getContext();
+        return getRequestParam(fc, key);
+    }
+
+    public static boolean isSkipInputValidation(final FacesContext fc)
+    {
+        // Skip validate
+        String val = FacesUtils.getRequestParam(fc, SKIP_INPUT_VALIDATION_PARAM);
+        return (val!=null && ObjectUtils.getBoolean(val));
+    }
+    
+    public static boolean isSkipInputValidation()
+    {
+        FacesContext fc = getContext();
+        return isSkipInputValidation(fc); 
+    }
+
+    public static <T> T getActionEventAttribute(ActionEvent ae, String name, Class<T> clazz)
+    {
+        Object item = ae.getComponent().getAttributes().get(name);
+        return ObjectUtils.convert(clazz, item);
+    }
+
+    /* Navigation */
+    
+    public static void redirectDirectly(final FacesContext fc, final String url)
+    {
+        try
+        {   // log
+            if (log.isDebugEnabled())
+                log.debug("Redirecting directly to {}.", url);
+            if (fc.getResponseComplete())
+                log.warn("Redirecting although response is already complete!");
+            // redirectDirectly
+            fc.getExternalContext().redirect(url);
+            fc.responseComplete();
+        }
+        catch (IOException e)
+        {
+            log.error("Unable to redirect to " + url);
+        }
+    }
+
+    public static void redirectDirectly(final FacesContext fc, final PageOutcome outcome)
+    {
+        String ctxPath = fc.getExternalContext().getRequestContextPath();
+        String pageURI = ctxPath + outcome.toString();
+        FacesUtils.redirectDirectly(fc, pageURI);
+    }
+
+    public static void redirectDirectly(final FacesContext fc, final PageDefinition page)
+    {
+        FacesUtils.redirectDirectly(fc, page.getOutcome());
+    }
+    
+    /* Connection 
+    
+    public DBContext getDBContext(final FacesContext fc, DBDatabase db) 
+    {
+        return getWebApplication().getDBContextForRequest(fc, db);
+    }
+    
+    public void releaseDBContext(final FacesContext fc, DBDatabase db, boolean commit)
+    {
+        getWebApplication().releaseDBContext(fc, db, commit);
+    }
+    
+    public void releaseAllDBContexts(final FacesContext fc, boolean commit)
+    {
+        getWebApplication().releaseAllDBContexts(fc, commit);
+    }
+    */
+    
+    /* Pages */
+
+    @SuppressWarnings("unchecked")
+    public static <T extends Page> T getPage(final FacesContext fc)
+    {
+        UIViewRoot vr = fc.getViewRoot();
+        if (vr==null)
+            throw new ItemNotFoundException("ViewRoot");
+        // find page
+        Page page = (Page)vr.getViewMap().get("page");
+        if (page==null)
+            throw new ItemNotFoundException("page");
+        // ok
+        return (T)page; 
+    }
+
+    /* Parameter-map */
+    public static final String PARAMETER_MAP_ATTRIBUTE = "PARAMETER_MAP";
+
+    public static final String PARAMETER_MAP_CLASS_ATTRIBUTE = "PARAMETER_MAP_CLASS";
+
+    public static void setParameterMapClass(final FacesContext fc, Class<? extends ParameterMap>clazz)
+    {
+        Map<String, Object> am = fc.getExternalContext().getApplicationMap();
+        am.put(PARAMETER_MAP_CLASS_ATTRIBUTE, clazz);
+    }
+    
+    public static ParameterMap getParameterMap(final FacesContext fc)
+    {
+        Map<String, Object> sm = fc.getExternalContext().getSessionMap();
+        ParameterMap pm = (ParameterMap)sm.get(PARAMETER_MAP_ATTRIBUTE);
+        if (pm==null)
+        {   try
+            {   // Create Paramter Map
+                Map<String, Object> am = fc.getExternalContext().getApplicationMap();
+                Object pmClass = am.get(PARAMETER_MAP_CLASS_ATTRIBUTE);
+                if (pmClass instanceof Class<?>) {
+                    // ParamterMapClass provided as Object
+                    pm = (ParameterMap)((Class<?>)pmClass).newInstance(); 
+                } 
+                else if (pmClass instanceof String) {
+                    // ParamterMapClass provided as String
+                    pm = (ParameterMap)Class.forName((String)pmClass).newInstance(); 
+                }
+                else {
+                    // not provided, use default
+                    pm = new ParameterMap();
+                }
+            }
+            catch (ClassNotFoundException e)
+            {
+                throw new InternalException(e);
+            }
+            catch (InstantiationException e)
+            {
+                throw new InternalException(e);
+            }
+            catch (IllegalAccessException e)
+            {
+                throw new InternalException(e);
+            }
+            // put on session
+            sm.put(PARAMETER_MAP_ATTRIBUTE, pm);
+        }
+        return pm;
+    }
+    
+    public static ParameterMap getParameterMap()
+    {
+        return getParameterMap(getContext());        
+    }
+
+    /* PageResource-map */
+    public static final String PAGE_RESOURCE_MAP_ATTRIBUTE = "pageResources"; /** use el-expression: #{pageResources.xxx} **/
+    
+    /**
+     * returns the page resource map
+     * @param fc the faces context
+     * @return the ressource map
+     */
+    @SuppressWarnings("unchecked")
+    public static Map<String, Object> getPageResourceMap(final FacesContext fc)
+    {
+        Map<String, Object> sm = fc.getExternalContext().getSessionMap();
+        Map<String, Object> rm = (Map<String, Object>)sm.get(PAGE_RESOURCE_MAP_ATTRIBUTE);
+        if (rm==null)
+        {   rm = new HashMap<String, Object>();
+            sm.put(PAGE_RESOURCE_MAP_ATTRIBUTE, rm);
+        }
+        return rm;
+    }
+    
+    public static Map<String, Object> getPageResourceMap()
+    {
+        return getPageResourceMap(getContext());
+    }
+    
+    /* Managed Beans */
+    
+    public static Object getManagedBean(final FacesContext fc, final String name)
+    {
+        if (fc == null)
+        {
+            throw new NullPointerException("context must not be null");
+        }
+        if (name == null)
+        {
+            throw new NullPointerException("name must not be null");
+        }
+        /*
+        final ELContext elcontext = fc.getELContext();
+        final Application application = fc.getApplication();
+        return application.getELResolver().getValue(elcontext, null, name);
+        */
+        return getWebApplication().getFacesImplementation().getManagedBean(name, fc);
+    }
+    
+    public static <T> T getManagedBean(final FacesContext fc, final Class<T> cls, final String name)
+    {
+        @SuppressWarnings("unchecked")
+        T bean = (T)getManagedBean(fc, name);
+        if (bean==null)
+        {   log.warn("Managed Bean {} ist not available.", name);
+            throw new ItemNotFoundException(name);
+        }    
+        return bean;
+    }
+    
+    public static <T> T getManagedBean(final FacesContext fc, final Class<T> cls)
+    {
+        String name = cls.getName();
+        int i = name.lastIndexOf('.')+1;
+        name  = name.substring(i, i+1).toLowerCase()+name.substring(i+1);
+        return getManagedBean(fc, cls, name);
+    }
+    
+    public static <T> T getManagedBean(final Class<T> cls)
+    {
+        FacesContext fc = getContext();
+        return getManagedBean(fc, cls); 
+    }
+
+    /*
+    @SuppressWarnings("unchecked") 
+    public static <T> T findBean(String beanName) { 
+        FacesContext context = FacesContext.getCurrentInstance(); 
+        return (T) context.getApplication().evaluateExpressionGet(context, "#{" + beanName + "}", Object.class); 
+    } 
+    */
+    
+    /* file */
+    
+    public static String getRealPath(final FacesContext fc, String path)
+    {
+        return (((ServletContext)fc.getExternalContext().getContext())).getRealPath(path);
+    }
+
+    public static String getFilePath(final FacesContext fc, String path, String file)
+    {
+        String realPath = getRealPath(fc, path);
+        return realPath.endsWith(File.separator) ? realPath+file : realPath + File.separator + file ;
+    }
+    
+    /* Messages */
+    
+    public static TextResolver getTextResolver(final FacesContext fc)
+    {
+        return getWebApplication().getTextResolver(fc);
+    }
+    
+    public static String getMessage(final FacesContext fc, String key)
+    {
+        return getTextResolver(fc).resolveKey(key);
+    }
+    
+    public static String getMessage(String messageKey)
+    {
+        return getMessage(getContext(), messageKey);
+    }
+
+    public static String formatMessage(String msgKey, Object... params)
+    {
+        TextResolver tr = getTextResolver(FacesContext.getCurrentInstance());
+        String pattern = tr.resolveKey(msgKey);
+        return MessageFormat.format(pattern, params);
+    }
+
+    /*
+    public static void addInfoMessage(FacesContext fc, String clientId, String msg)
+    {
+        fc.addMessage(clientId, new FacesMessage(FacesMessage.SEVERITY_INFO, msg, msg));
+    }
+    
+    public static void addInfoMessage(FacesContext fc, String msg)
+    {
+        addInfoMessage(fc, null, msg);
+    }
+
+    public static void addWarnMessage(FacesContext fc, String clientId, String msg)
+    {
+        fc.addMessage(clientId, new FacesMessage(FacesMessage.SEVERITY_WARN, msg, msg));
+    }
+    
+    public static void addWarnMessage(FacesContext fc, String msg)
+    {
+        addWarnMessage(fc, null, msg);
+    }
+
+    public static void addErrorMessage(FacesContext fc, String clientId, String msg)
+    {
+        fc.addMessage(clientId, new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg));
+    }
+
+    public static void addErrorMessage(FacesContext fc, String msg)
+    {
+        addErrorMessage(fc, null, msg);
+    }
+    */
+
+    /*
+     * indicates whether submitted values in InputControl should be cleared or preserved.
+     * Default is true.   
+     * @param fc the faces context
+     * @return true if the submitted values should be cleared or false if they shold be preserved
+    public static boolean isClearSubmittedValues(FacesContext fc)
+    {
+        Object validate = fc.getExternalContext().getRequestMap().get("CLEAR_SUBMITTED_VALUES");
+        return (validate!=null ? ObjectUtils.getBoolean(validate) : false);
+    }
+
+    public static void setClearSubmittedValues(FacesContext fc, boolean validate)
+    {
+        fc.getExternalContext().getRequestMap().put("CLEAR_SUBMITTED_VALUES", validate);
+    }
+    */
+    
+    public static FacesMessage getFacesErrorMessage(FacesContext fc, Throwable t)
+    {
+        if (!(t instanceof EmpireException))
+            t = new InternalException(t);
+        // Get Message
+        TextResolver tr = getWebApplication().getTextResolver(fc);
+        String msg = tr.getExceptionMessage((EmpireException)t);
+        // create Faces Message
+        return new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, null);
+    }
+    
+    public static void redirectFromError(Page page, Throwable t)
+    {
+        PageOutcome pageTarget = page.getPageDefinition().getOutcome();
+        // throw new InternalException(e);
+        FacesContext fc = FacesUtils.getContext();
+        boolean committed = fc.getExternalContext().isResponseCommitted();
+        if (committed)
+        {   log.warn("Cannot redirect to {} from an already committed response! Error is {}.", pageTarget, t.getMessage());
+            return;
+        }
+        // redirect to target page
+        FacesMessage facesMsg = getFacesErrorMessage(fc, t);
+        ExternalContext ec = fc.getExternalContext();
+        ec.getSessionMap().put(Page.SESSION_MESSAGE, facesMsg);
+        // redirect
+        FacesUtils.redirectDirectly(fc, pageTarget);
+    }
+    
+    
+    /* Component search */
+    public static UIInput findInputComponent(UIComponent parent, Column column)
+    {
+        // Get Children
+        Iterator<UIComponent> children = parent.getFacetsAndChildren();
+        while (children.hasNext())
+        {   // Iterate through all children 
+            UIComponent nextChild = children.next();
+            log.info("Checking child {}", nextChild.getClass().getName());
+            if (nextChild instanceof UIInput)
+            {   // Check Column Attribute
+                Object col = nextChild.getAttributes().get("column");
+                if (col==null)
+                {   // Check ID
+                    ValueExpression ve = nextChild.getValueExpression("column");
+                    if (ve!=null)
+                        log.warn("TODO: evaluate Value Expression!");
+                }
+                else if (column.equals(col))
+                {   // check form
+                    return (UIInput)nextChild;
+                }    
+            }
+            else
+            {   // recurse children and facets    
+                UIInput input = findInputComponent(nextChild, column); 
+                if (input!=null)
+                    return input;
+            }
+        }
+        // Not found
+        return null;
+    }
+
+    public static UIInput findInputComponent(FacesContext fc, String formId, Column column)
+    {
+        UIComponent parent = fc.getViewRoot();
+        if (StringUtils.isNotEmpty(formId))
+        {
+            parent = WebApplication.findChildComponent(parent, formId);
+            if (parent==null)
+                return null; // not found
+        }
+        // find
+        return findInputComponent(parent, column);
+    }
+    
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/TextResolver.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/TextResolver.java
new file mode 100644
index 00000000..3b3a60ac
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/TextResolver.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import java.util.Locale;
+
+public interface TextResolver
+{
+    public static String MSG_KEY_INDICATOR = "!";
+    
+    Locale getLocale();
+
+    String resolveKey(String key);
+    
+    String resolveText(String text);
+    
+    String getExceptionMessage(Exception e);
+    
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebAppStartupListener.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebAppStartupListener.java
new file mode 100644
index 00000000..fccaa425
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebAppStartupListener.java
@@ -0,0 +1,175 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import jakarta.faces.application.Application;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.event.AbortProcessingException;
+import jakarta.faces.event.PostConstructApplicationEvent;
+import jakarta.faces.event.PreDestroyApplicationEvent;
+import jakarta.faces.event.SystemEvent;
+import jakarta.faces.event.SystemEventListener;
+import jakarta.servlet.ServletContext;
+
+import org.apache.empire.jakarta.impl.FacesImplementation;
+import org.apache.empire.jakarta.impl.MojarraImplementation;
+import org.apache.empire.jakarta.impl.MyFacesImplementation;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class WebAppStartupListener implements SystemEventListener
+{
+    private static final Logger log = LoggerFactory.getLogger(WebAppStartupListener.class);
+    
+    private final Class<? extends FacesConfiguration> facesConfigClass;
+
+    private FacesImplementation facesImplementation;
+    
+    /**
+     * Default Constructor with no initialization
+     */
+    public WebAppStartupListener()
+    {
+        this.facesConfigClass = null;
+    }
+
+    /**
+     * Default Constructor with additional configuration
+     */
+    public WebAppStartupListener(Class<? extends FacesConfiguration> facesConfigClass)
+    {
+        this.facesConfigClass = facesConfigClass;
+    }
+
+    public FacesImplementation getFacesImplementation()
+    {
+        return facesImplementation;
+    }
+    
+    protected ServletContext getServletContext(FacesContext startupContext)
+    {
+        return (ServletContext)startupContext.getExternalContext().getContext();        
+    }
+
+    @Override
+    public boolean isListenerForSource(Object source)
+    {
+        return (source instanceof Application);
+    }
+
+    @Override
+    public void processEvent(SystemEvent event)
+        throws AbortProcessingException
+    {
+        log.info("ApplicationStartupListener:processEvent");
+        if (event instanceof PostConstructApplicationEvent)
+        {
+            FacesContext startupContext = FacesContext.getCurrentInstance();
+            ExternalContext externalContext = startupContext.getExternalContext();
+            // Detect implementation
+            this.facesImplementation = detectFacesImplementation(externalContext);
+            // Init Configuration
+            initFacesConfiguration(startupContext);
+            // Create and init application
+            WebApplication facesApp = createWebApplication(startupContext);
+            initWebApplication(facesApp, startupContext);
+            // Set Servlet Attribute
+            ServletContext servletContext = (ServletContext) externalContext.getContext();
+            if (servletContext.getAttribute(WebApplication.APPLICATION_BEAN_NAME)!=facesApp)
+            {
+                log.warn("WARNING: Ambiguous application definition. An object of name '{}' already exists on application scope!", WebApplication.APPLICATION_BEAN_NAME);
+            }
+        }
+        else if (event instanceof PreDestroyApplicationEvent)
+        {
+            log.info("Processing PreDestroyApplicationEvent");
+        }
+
+    }
+
+    /**
+     * Detects the JSF Implementation and returns an implmentation handler class.
+     * Supported Implementations are Sun Mojarra (2.2.x) and Apache MyFaces (2.2.x) 
+     * @return the faces implementation
+     */
+    protected FacesImplementation detectFacesImplementation(ExternalContext externalContext)
+    {
+        log.debug("Detecting JSF-Implementation...");
+        // Test for Apache MyFaces
+        try {
+            Class.forName("org.apache.myfaces.application.ApplicationFactoryImpl");
+            return new MyFacesImplementation(externalContext);
+        } catch (ClassNotFoundException e) {
+            // It's not MyFaces
+        }
+        // Test for Sun Mojarra
+        try {
+            Class.forName("com.sun.faces.application.ApplicationFactoryImpl");
+            return new MojarraImplementation(externalContext);
+        } catch (ClassNotFoundException e) {
+            // It's not Mojarra
+        }
+        // Not found
+        log.error("JSF-Implementation missing or unknown. Please make sure either Apache MyFaces or Sun Mojarra implementation is provided");
+        throw new UnsupportedOperationException(); 
+    }
+
+    /**
+     * Allows to programmatically extend the faces configuration
+     * @param facesImplementation the Faces Implementation
+     * @param startupContext the Startup Context
+     */
+    protected void initFacesConfiguration(FacesContext startupContext)
+    {
+        // Init FacesExtentions
+        if (facesConfigClass!=null) {
+            log.info("Initializing FacesExtentions");
+            FacesConfiguration.initialize(facesConfigClass, startupContext, getFacesImplementation());
+        }
+    }
+
+    /**
+     * Creates a WebApplication instance
+     * @param facesImplementation the Faces Implementation
+     * @param startupContext the Startup Context
+     * @return the WebApplication instance
+     */
+    protected WebApplication createWebApplication(FacesContext startupContext)
+    {
+        // Create Application
+        Object app = getFacesImplementation().getManagedBean(WebApplication.APPLICATION_BEAN_NAME, startupContext);
+        if (!(app instanceof WebApplication))
+            throw new AbortProcessingException("Error: Application is not a "+WebApplication.class.getName()+" instance. Please create an ApplicationFactory!");
+        // done
+        return (WebApplication)app;
+    }
+    
+    /**
+     * Initializes a WebApplication instance
+     * @param facesApp the WebApplication instance
+     * @param facesImplementation the Faces Implementation
+     * @param startupContext the Startup Context
+     */
+    protected void initWebApplication(WebApplication facesApp, FacesContext startupContext)
+    {
+        facesApp.init(getFacesImplementation(), startupContext);
+    }
+    
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebApplication.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebApplication.java
new file mode 100644
index 00000000..c1065eb4
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebApplication.java
@@ -0,0 +1,640 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import jakarta.faces.application.Application;
+import jakarta.faces.application.FacesMessage;
+import jakarta.faces.application.FacesMessage.Severity;
+import jakarta.faces.application.ViewExpiredException;
+import jakarta.faces.component.NamingContainer;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIViewRoot;
+import jakarta.faces.context.ExternalContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.servlet.ServletContext;
+import jakarta.servlet.http.HttpServletRequest;
+import javax.sql.DataSource;
+
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.data.DataType;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.context.DBRollbackManager;
+import org.apache.empire.db.context.DBRollbackManager.ReleaseAction;
+import org.apache.empire.db.exceptions.UserLevelException;
+import org.apache.empire.exceptions.InternalException;
+import org.apache.empire.exceptions.InvalidArgumentException;
+import org.apache.empire.exceptions.NotSupportedException;
+import org.apache.empire.exceptions.UnexpectedReturnValueException;
+import org.apache.empire.jakarta.controls.TextAreaInputControl;
+import org.apache.empire.jakarta.controls.TextInputControl;
+import org.apache.empire.jakarta.impl.FacesImplementation;
+import org.apache.empire.jakarta.impl.ResourceTextResolver;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class WebApplication
+{
+    private static final Logger log                   = LoggerFactory.getLogger(WebApplication.class);
+    
+    private static final String REQUEST_CONNECTION_MAP = "requestConnectionMap";
+
+    private static final String CONN_ROLLBACK_MANAGER = "connRollbackManager";
+    
+    public static String        APPLICATION_BEAN_NAME = "webApplication";
+
+    protected TextResolver[]    textResolvers         = null;
+
+    private String              webRoot               = null;
+    
+    private FacesImplementation facesImpl			  = null;
+    
+    private static WebApplication appInstance         = null;
+    
+    /**
+     * Returns the one and only instance of the WebApplication (Singleton)
+     * @param <T> the application type
+     * @return the WebApplication instance
+     */
+    @SuppressWarnings("unchecked")
+    public static <T extends WebApplication> T getInstance()
+    {
+        if (appInstance==null)
+            log.warn("No WebApplication instance available. Please add a PostConstructApplicationEvent using WebAppStartupListener in your faces-config.xml to create the WebApplication object.");
+        // return instance
+        return (T)appInstance;
+    }
+
+    protected abstract void init(ServletContext servletContext);
+    
+    protected abstract DataSource getAppDataSource(DBDatabase db);
+
+    protected WebApplication()
+    {   // subscribe
+        log.info("WebApplication {} created", getClass().getName());
+        // Must be a singleton
+        if (appInstance!=null) {
+            throw new RuntimeException("An attempt was made to create second instance of WebApplication. WebApplication must be a Singleton!");
+        }
+        // set Instance
+        appInstance = this;
+    }
+
+    /**
+     * Init the Application
+     * @param facesImpl the faces implementation
+     * @param startupContext the startup context
+     */
+    public final void init(FacesImplementation facesImpl, FacesContext startupContext)
+    {
+        // Only call once!
+        if (this.facesImpl!=null || this.webRoot!=null) 
+        {   // already initialized
+            log.warn("WARNING: WebApplication has already been initialized! Continuing without init...");
+            return;
+        }
+        // set imppl
+        this.facesImpl = facesImpl;
+        // webRoot
+        ServletContext servletContext = (ServletContext) startupContext.getExternalContext().getContext();
+        webRoot = servletContext.getContextPath();
+        servletContext.setAttribute("webRoot", webRoot);
+        servletContext.setAttribute("app", this);
+        // Init
+        init(servletContext);
+        // post init
+        if (this.textResolvers==null)
+        {   // text resolvers
+            log.info("*** initTextResolvers() ***");
+            initTextResolvers(startupContext.getApplication());
+        }
+        // Log info
+        log.info("*** WebApplication initialization complete ***");
+        log.info("JSF-Implementation is '{}'", facesImpl.getClass().getName());
+        log.info("WebRoot is '{}'", webRoot);
+    }
+
+    public void destroy()
+    {
+    	// Override if needed
+    }
+    
+    /* Context handling */
+    
+    /**
+     * handle request cleanup
+     * @param ctx the faces context
+     */
+    public void onRequestComplete(final FacesContext ctx)
+    {
+        releaseAllConnections(ctx);
+    }
+
+    /**
+     * handle view not found
+     * @param fc the faces context
+     * @param req the request
+     */
+    public void onViewNotFound(final FacesContext fc, final HttpServletRequest req)
+    {   // View not Found Error
+        log.warn("No view found for request to '{}'. Use FacesUtils.redirectDirectly() to redirect to valid view.", req.getRequestURI());
+    }
+
+    /**
+     * handle view change
+     * @param fc the faces context
+     * @param viewId the view id
+     */
+    public void onChangeView(final FacesContext fc, String viewId)
+    {   // allow custom view change logic
+
+        // clear page resources
+        Map<String, Object> sm = FacesUtils.getSessionMap(fc);
+        if (sm!=null)
+            sm.remove(FacesUtils.PAGE_RESOURCE_MAP_ATTRIBUTE);
+    }
+
+    /**
+     * adds a Javascript call to the request 
+     * @param fc the faces context
+     * @param function the javascript command
+     */
+    public void addJavascriptCall(final FacesContext fc, String function)
+    {
+        throw new NotSupportedException(this, "addJavascriptCall");
+    }
+
+    /**
+     * return the interface for Implementation specific features 
+     * that are specific for Mojarra or MyFaces
+     * @return the faces implementation
+     */
+    public FacesImplementation getFacesImplementation() 
+    {
+		return facesImpl;
+	}
+    
+    /**
+     * returns the web context path as returned from ServletContext.getContextPath()
+     * @return the web root
+     */
+    public String getWebRoot()
+    {
+        return webRoot;
+    }
+
+    /**
+     * returns the active locale for a given FacesContext
+     * @param ctx the faces context
+     * @return the context locale
+     */
+    public Locale getContextLocale(final FacesContext ctx)
+    {
+        UIViewRoot root;
+        // Start out with the default locale
+        Locale locale;
+        Locale defaultLocale = Locale.getDefault();
+        locale = defaultLocale;
+        // See if this FacesContext has a ViewRoot
+        if (null != (root = ctx.getViewRoot()))
+        {
+            // If so, ask it for its Locale
+            if (null == (locale = root.getLocale()))
+            {
+                // If the ViewRoot has no Locale, fall back to the default.
+                locale = defaultLocale;
+            }
+        }
+        return locale;
+    }
+
+    public TextResolver getTextResolver(Locale locale)
+    {
+        // No text Resolvers provided
+        if (textResolvers == null || textResolvers.length == 0)
+        {
+            throw new NotSupportedException(this, "getTextResolver");
+        }
+        // Lookup resolver for locale
+        for (int i = 0; i < textResolvers.length; i++)
+            if (locale.equals(textResolvers[i].getLocale()))
+                return textResolvers[i];
+        // locale not found: return default
+        return textResolvers[0];
+    }
+
+    public TextResolver getTextResolver(FacesContext ctx)
+    {
+        return getTextResolver(getContextLocale(ctx));
+    }
+
+    /**
+     * checks if the current context contains an error
+     * @param fc the FacesContext
+     * @return true if the context has an error set or false otherwise
+     */
+    public boolean hasError(final FacesContext fc)
+    {
+        Iterator<FacesMessage> msgIterator = fc.getMessages();
+        if (msgIterator != null)
+        { // Check Messages
+            while (msgIterator.hasNext())
+            { // Check Severity
+                Severity fms = msgIterator.next().getSeverity();
+                if (fms == FacesMessage.SEVERITY_ERROR || fms == FacesMessage.SEVERITY_FATAL)
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Logs an exception as Error with additional information 
+     * @param message the message to log
+     * @param e the underlying exception
+     */
+    public void logException(Logger logger, String message, Throwable e)
+    {
+        if (logger==null)
+            logger=log;
+        // View Exceptions
+        if ((e instanceof ViewExpiredException)) // || (e instanceof ViewNotFoundException)) -- MyFaces only
+        {   // Session expired
+            logger.warn(message+": "+e.getMessage());
+            return;
+        }
+        // Get Session Info
+        String addlInfo;
+        try {
+            FacesContext fc = FacesContext.getCurrentInstance();
+            ExternalContext ec = (fc!=null ? fc.getExternalContext() : null);
+            String sessionInfo = (ec!=null ? getExceptionSessionInfo(ec.getSessionMap()) : "[NoSession]");
+            addlInfo = StringUtils.coalesce(sessionInfo, "[NoSessionInfo]");
+        } catch(Exception ex) {
+            addlInfo = "[Error:"+ex.getMessage()+"] ";
+        }
+        // Log now
+        if (e instanceof UserLevelException)
+            logger.info(StringUtils.concat(addlInfo, e.getClass().getSimpleName(), ": ", e.getMessage()));
+        else
+            logger.error(StringUtils.concat(addlInfo, message), e);                
+    }
+    
+    /**
+     * Override this to provide additonal session info for an exception
+     * @param sessionMap
+     * @return the additional session info
+     */
+    protected String getExceptionSessionInfo(Map<String, Object> sessionMap)
+    {
+        return null;
+    }
+    
+    /**
+     * returns true if a form input element has been partially submitted
+     * @param fc the Faces Context
+     * @return the componentId or null if no partial submit was been performed
+     */
+    public boolean isPartialSubmit(final FacesContext fc)
+    {
+        // Detect Partial Submit
+        Map<String,String> parameterMap = fc.getExternalContext().getRequestParameterMap();
+        return ObjectUtils.getBoolean(parameterMap.get("jakarta.faces.partial.ajax"));
+    }
+
+    /**
+     * returns the componentId for which a partial submit has been performed.
+     * @param fc the Faces Context
+     * @return the componentId or null if no partial submit was been performed
+     */
+    public String getPartialSubmitComponentId(final FacesContext fc)
+    {
+        Map<String,String> parameterMap = fc.getExternalContext().getRequestParameterMap();
+        return parameterMap.get("jakarta.faces.source");  // or jakarta.faces.partial.execute ?
+    }
+
+    /**
+     * finds the component with the given id that is located in the same NamingContainer as a given component 
+     * @param fc the FacesContext
+     * @param componentId the component id
+     * @param nearComponent a component within the same naming container from which to start the search (optional)
+     * @return the component or null if no component was found
+     */
+    public UIComponent findComponent(FacesContext fc, String componentId, UIComponent nearComponent)
+    {
+        if (StringUtils.isEmpty(componentId))
+            throw new InvalidArgumentException("componentId", componentId);
+        // Begin search near given component (if any)
+        UIComponent component = null;
+        if (nearComponent != null)
+        {   // Search below the nearest naming container  
+            component = nearComponent.findComponent(componentId);
+            if (component == null)
+            {   // Recurse upwards
+                UIComponent nextParent = nearComponent;
+                while (true)
+                {
+                    nextParent = nextParent.getParent();
+                    // search NamingContainers only
+                    while (nextParent != null && !(nextParent instanceof NamingContainer))
+                    {
+                        nextParent = nextParent.getParent();
+                    }
+                    if (nextParent == null)
+                    {
+                        break;
+                    }
+                    else
+                    {
+                        component = nextParent.findComponent(componentId);
+                    }
+                    if (component != null)
+                    {
+                        break;
+                    }
+                }
+            }
+        }
+        // Not found. Search the entire tree 
+        if (component == null)
+            component = findChildComponent(fc.getViewRoot(), componentId);
+        // done
+        return component;
+    }
+
+    /**
+     * finds a child component with the given id that is located below the given parent component 
+     * @param parent the parent
+     * @param componentId the component id
+     * @return the component or null if no component was found
+     */
+    public static UIComponent findChildComponent(UIComponent parent, String componentId)
+    {
+        UIComponent component = null;
+        if (parent.getChildCount() == 0)
+            return null;
+        Iterator<UIComponent> children = parent.getChildren().iterator();
+        while (children.hasNext())
+        {
+            UIComponent nextChild = children.next();
+            if (nextChild instanceof NamingContainer)
+            {
+                component = nextChild.findComponent(componentId);
+            }
+            if (component == null)
+            {
+                component = findChildComponent(nextChild, componentId);
+            }
+            if (component != null)
+            {
+                break;
+            }
+        }
+        return component;
+    }
+
+    /**
+     * returns the default input control type for a given data Type
+     * @see org.apache.empire.jakarta.controls.InputControlManager
+     * @param dataType
+     * @return an Input Cnotrol type
+     */
+    public String getDefaultControlType(DataType dataType)
+    {
+        switch (dataType)
+        {
+            case CLOB:
+                return TextAreaInputControl.NAME;
+            default:
+                return TextInputControl.NAME;
+        }
+    }
+
+    /* Message handling */
+
+    protected void initTextResolvers(Application app)
+    {
+        int count = 0;
+        Iterator<Locale> locales = app.getSupportedLocales();
+        for (count = 0; locales.hasNext(); count++)
+        {
+            locales.next();
+        }
+
+        // get message bundles
+        String messageBundle = app.getMessageBundle();
+        textResolvers = new TextResolver[count];
+        locales = app.getSupportedLocales();
+        for (int i = 0; locales.hasNext(); i++)
+        {
+            Locale locale = locales.next();
+            textResolvers[i] = new ResourceTextResolver(ResourceBundle.getBundle(messageBundle, locale));
+            log.info("added TextResolver for {} bundle='{}'", locale.getLanguage(), messageBundle);
+        }
+    }
+
+    /**
+     * Obtains a JDBC-Connection from the connection pool
+     * @param db the database for which to obtain a connection
+     * @return the connection
+     */
+    protected Connection getConnection(DBDatabase db)
+    {
+        // Get From Pool
+        try
+        { // Obtain a connection
+            Connection conn = getAppDataSource(db).getConnection();
+            conn.setAutoCommit(false);
+            log.trace("Connection {} obtained from pool", conn.hashCode());
+            return conn;
+        }
+        catch (SQLException e)
+        {
+            log.error("Failed to get connection from pool.", e);
+            throw new InternalException(e);
+        }
+    }
+
+    /**
+     * Releases a JDBC-Connection from the connection pool
+     * @param conn the connection to release
+     * @param commit flag whether to commit changes
+     * @param dbrm the rollback manager
+     */
+    protected void releaseConnection(Connection conn, boolean commit, DBRollbackManager dbrm)
+    {
+        try
+        {   // check
+            if (conn == null)
+                return;
+            // release connection
+            log.trace("releasing Connection {}", conn.hashCode());
+            // Commit or rollback connection depending on the exit code
+            if (commit)
+            {   // success: commit all changes
+                if (dbrm!=null)
+                    dbrm.releaseConnection(conn, ReleaseAction.Discard);  // before commit
+                conn.commit();
+                log.debug("REQUEST commited.");
+            }
+            else
+            {   // failure: rollback all changes
+                conn.rollback();
+                if (dbrm!=null)
+                    dbrm.releaseConnection(conn, ReleaseAction.Rollback); // after rollback
+                log.debug("REQUEST rolled back.");
+            }
+            // Release Connection
+            conn.close();
+            // done
+            if (log.isDebugEnabled())
+                log.debug("REQUEST returned connection to pool.");
+        }
+        catch (SQLException e)
+        {
+            log.error("Error releasing connection", e);
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Obtains a DBRollbackManager for the current request
+     * @param fc the FacesContext
+     * @param create if true a DBRollbackManager will be created if not already present
+     * @return the rollback manager
+     */
+    public DBRollbackManager getRollbackManagerForRequest(FacesContext fc, boolean create)
+    {
+        DBRollbackManager dbrm = (DBRollbackManager)FacesUtils.getRequestAttribute(fc, CONN_ROLLBACK_MANAGER);
+        if (dbrm==null && create)
+        {   dbrm = new DBRollbackManager(1, 8);
+            FacesUtils.setRequestAttribute(fc, CONN_ROLLBACK_MANAGER, dbrm);
+        }
+        return dbrm;
+    }
+    
+    /**
+     * Obtains a JDBC-Connection for the current request
+     * @param fc the FacesContext
+     * @param db the DBDatabase for which to obtain a connection
+     * @param create if true a Connection will be created if not already present
+     * @return the connection
+     */
+    public Connection getConnectionForRequest(FacesContext fc, DBDatabase db, boolean create)
+    {
+        if (fc == null)
+            throw new InvalidArgumentException("FacesContext", fc);
+        if (db == null)
+            throw new InvalidArgumentException("DBDatabase", db);
+        // Get the ConnectionContextInfo map
+        @SuppressWarnings("unchecked")
+        Map<DBDatabase, Connection> connMap = (Map<DBDatabase, Connection>) FacesUtils.getRequestAttribute(fc, REQUEST_CONNECTION_MAP);
+        if (connMap== null && create)
+        {   connMap = new HashMap<DBDatabase, Connection>(1);
+            FacesUtils.setRequestAttribute(fc, REQUEST_CONNECTION_MAP, connMap);
+        }
+        else if (connMap==null)
+        {   // Nothing to do
+            return null; 
+        }
+        Connection conn = connMap.get(db); 
+        if (conn==null && create)
+        {   // Get Pooled Connection
+            conn = getConnection(db);
+            if (conn== null)
+                throw new UnexpectedReturnValueException(this, "getConnection"); 
+            // Add to map
+            connMap.put(db, conn);
+        }
+        // done
+        return conn;
+    }
+
+    /**
+     * Releases all connections attached to the current request
+     * @param fc the FacesContext
+     * @param commit when true changes are committed otherwise they are rolled back
+     */
+    public void releaseAllConnections(final FacesContext fc, boolean commit)
+    {
+        @SuppressWarnings("unchecked")
+        Map<DBDatabase, Connection> connMap = (Map<DBDatabase, Connection>) FacesUtils.getRequestAttribute(fc, REQUEST_CONNECTION_MAP);
+        if (connMap == null)
+            return; // Nothing to do
+        // Walk the connection map
+        DBRollbackManager dbrm = getRollbackManagerForRequest(fc, false);
+        for (Connection conn : connMap.values())
+        {
+            releaseConnection(conn, commit, dbrm);
+        }
+        // remove from request map
+        FacesUtils.setRequestAttribute(fc, REQUEST_CONNECTION_MAP, null);
+    }
+
+    /**
+     * Releases all connections attached to the current request
+     * If an error is detected in the faces message list, a rollback will automatically be performed insteamd of a commmit
+     * @param fc the FacesContext
+     */
+    public void releaseAllConnections(final FacesContext fc)
+    {
+        releaseAllConnections(fc, !hasError(fc));
+    }
+
+    /**
+     * Releases the connection associated with a database from the request
+     * @param fc the FacesContext
+     * @param db the DBDatabase for which to release the connection
+     * @param commit when true changes are committed otherwise they are rolled back
+     */
+    public void releaseConnection(final FacesContext fc, DBDatabase db, boolean commit)
+    {
+        @SuppressWarnings("unchecked")
+        Map<DBDatabase, Connection> connMap = (Map<DBDatabase, Connection>) FacesUtils.getRequestAttribute(fc, REQUEST_CONNECTION_MAP);
+        if (connMap == null || !connMap.containsKey(db))
+            return; // Nothing to do;
+        // Get RollbackManager   
+        DBRollbackManager dbrm = getRollbackManagerForRequest(fc, false);
+        // Release Connection   
+        Connection conn = connMap.get(db);
+        releaseConnection(conn, commit, dbrm);
+        // Remove from map
+        connMap.remove(db);
+    }
+
+    /**
+     * Releases the connection associated with a database from the request
+     * If an error is detected in the faces message list, a rollback will automatically be performed insteamd of a commmit
+     * @param fc the FacesContext
+     * @param db the DBDatabase for which to release the connection
+     */
+    public void releaseConnection(final FacesContext fc, DBDatabase db)
+    {
+        releaseConnection(fc, db, !hasError(fc));
+    }
+    
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebDBContext.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebDBContext.java
new file mode 100644
index 00000000..6a3b2bdf
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/app/WebDBContext.java
@@ -0,0 +1,170 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.app;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.sql.Connection;
+
+import jakarta.faces.context.FacesContext;
+
+import org.apache.empire.commons.ClassUtils;
+import org.apache.empire.db.DBDatabase;
+import org.apache.empire.db.context.DBContextBase;
+import org.apache.empire.db.context.DBRollbackManager;
+import org.apache.empire.dbms.DBMSHandler;
+import org.apache.empire.exceptions.ItemNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is the basic implementation of a DBContext for a web application
+ * Important: The connection is obtained on HttpRequest scope and hot held by the context
+ * The connection is automatically released via the FacesRequestPhaseListener
+ * @author rainer
+ */
+public class WebDBContext<DB extends DBDatabase> extends DBContextBase implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private static final Logger    log = LoggerFactory.getLogger(WebDBContext.class);
+
+    protected final transient WebApplication app;
+    protected final transient DB             database;
+    protected final transient DBMSHandler    dbms;
+    
+    private final boolean rollbackHandlingEnabled;
+
+    /**
+     * Custom serialization for transient fields.
+     * @param strm the output stream
+     * @throws IOException from strm.defaultWriteObject
+     */ 
+    private void writeObject(ObjectOutputStream strm) throws IOException 
+    {   // Database
+        strm.writeObject((database!=null ? database.getIdentifier() : ""));
+        // write the rest
+        strm.defaultWriteObject();
+    }
+    
+    /**
+     * Custom deserialization for transient fields.
+     * @param strm the input stream
+     * @throws IOException from strm.defaultReadObject
+     * @throws ClassNotFoundException from strm.defaultReadObject
+     */ 
+    private void readObject(ObjectInputStream strm) 
+        throws IOException, ClassNotFoundException
+    {   // WebApplication
+        WebApplication app = WebApplication.getInstance();
+        ClassUtils.setPrivateFieldValue(WebDBContext.class, this, "app", app);
+        // Database
+        String dbid = String.valueOf(strm.readObject());
+        DBDatabase database = DBDatabase.findByIdentifier(dbid);
+        if (database==null)
+            throw new ItemNotFoundException(dbid);
+        ClassUtils.setPrivateFieldValue(WebDBContext.class, this, "database", database);
+        ClassUtils.setPrivateFieldValue(WebDBContext.class, this, "dbms",     database.getDbms());
+        // read the rest
+        strm.defaultReadObject();
+    }
+    
+    /**
+     * Constructor
+     * @param db the database
+     * @param rollbackHandlingEnabled flag whether to enable rollback handling
+     */
+    public WebDBContext(DB db, boolean rollbackHandlingEnabled)
+    {
+        this.app  = WebApplication.getInstance();
+        this.dbms = db.getDbms();
+        this.database = db;
+        // more
+        this.rollbackHandlingEnabled = rollbackHandlingEnabled;
+        // check dbms
+        if (db.getDbms() == null)
+            log.warn("Database {} has no dbms attached.", db.getClass().getSimpleName());
+    }
+    
+    /**
+     * Constructor
+     * @param db the database
+     */
+    public WebDBContext(DB db)
+    {
+        this(db, true);
+    }
+
+    public DB getDatabase()
+    {
+        return database;
+    }
+
+    @Override
+    public DBMSHandler getDbms()
+    {
+        return dbms;
+    }
+    
+    @Override
+    public boolean isPreparedStatementsEnabled()
+    {
+        return database.isPreparedStatementsEnabled();
+    }
+
+    @Override
+    public boolean isRollbackHandlingEnabled()
+    {
+        return rollbackHandlingEnabled;
+    }
+
+    /**
+     * Unclear weather this is any useful here
+     */
+    @Override
+    public void discard()
+    {
+        /*
+        FacesContext fc = FacesContext.getCurrentInstance();
+        this.app.releaseConnection(fc, database);
+        // discard
+        super.discard();
+        */
+    }
+
+    @Override
+    protected Connection getConnection(boolean create)
+    {
+        return app.getConnectionForRequest(getFacesContext(), database, create);
+    }
+
+    @Override
+    protected DBRollbackManager getRollbackManager(boolean create)
+    {
+        return app.getRollbackManagerForRequest(getFacesContext(), create);
+    }
+    
+    public FacesContext getFacesContext()
+    {
+        return FacesContext.getCurrentInstance();
+    }
+    
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/ControlTag.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/ControlTag.java
new file mode 100644
index 00000000..db4cbcf1
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/ControlTag.java
@@ -0,0 +1,820 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.components;
+
+import java.io.IOException;
+import java.util.List;
+
+import jakarta.faces.component.NamingContainer;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIComponentBase;
+import jakarta.faces.component.UIInput;
+import jakarta.faces.component.UINamingContainer;
+import jakarta.faces.component.html.HtmlOutputLabel;
+import jakarta.faces.component.visit.VisitCallback;
+import jakarta.faces.component.visit.VisitContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.context.ResponseWriter;
+import jakarta.faces.convert.ConverterException;
+
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.data.Column;
+import org.apache.empire.db.exceptions.FieldIllegalValueException;
+import org.apache.empire.exceptions.EmpireException;
+import org.apache.empire.jakarta.controls.InputControl;
+import org.apache.empire.jakarta.utils.ControlRenderInfo;
+import org.apache.empire.jakarta.utils.TagEncodingHelper;
+import org.apache.empire.jakarta.utils.TagEncodingHelperFactory;
+import org.apache.empire.jakarta.utils.TagStyleClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ControlTag extends UIInput implements NamingContainer
+{
+    /**
+     * ControlSeparatorComponent
+     */
+    public static abstract class ControlSeparatorComponent extends jakarta.faces.component.UIComponentBase
+    {
+        private ControlTag control = null;
+        
+        protected String tagName = null;
+
+        protected abstract String getTagName(ControlRenderInfo renderInfo);
+        
+        protected abstract void writeAttributes(ResponseWriter writer, TagEncodingHelper helper)
+            throws IOException;
+
+        @Override
+        public String getFamily()
+        {
+            return UINamingContainer.COMPONENT_FAMILY;
+        }
+
+        protected final ControlTag getControl()
+        {
+            return control;
+        }
+
+        @Override
+        public void encodeBegin(FacesContext context)
+            throws IOException
+        {
+            super.encodeBegin(context);
+
+            UIComponent parent = getParent();
+            if (!(parent instanceof ControlTag))
+                parent = parent.getParent();
+            if (!(parent instanceof ControlTag))
+            {   log.error("Invalid parent component for " + getClass().getName());
+                return;
+            }
+
+            // Get Control and TagName
+            this.control = (ControlTag) parent;
+            this.tagName = (this.control.controlVisible ? getTagName(control.renderInfo) : null);
+
+            // Start
+            if (tagName!=null && tagName.length()>0) 
+            {   // render tag
+                ResponseWriter writer = context.getResponseWriter();
+                writer.startElement(tagName, this);
+                writeAttributes(writer, this.control.helper);
+            }
+        }
+        
+        @Override
+        public void encodeEnd(FacesContext context)
+            throws IOException
+        {
+            if (tagName!=null && tagName.length()>0) 
+            {   // render tag
+                ResponseWriter writer = context.getResponseWriter();
+                writer.endElement(tagName);
+            }
+            // call default
+            super.encodeEnd(context);
+        }
+        
+        @Override
+        public boolean getRendersChildren()
+        {
+            // return super.getRendersChildren();
+            return control.helper.isVisible();
+        }
+    }
+
+    public static class LabelSeparatorComponent extends ControlSeparatorComponent
+    {
+        @Override
+        protected String getTagName(ControlRenderInfo renderInfo)
+        {
+            return renderInfo.LABEL_WRAPPER_TAG;
+        }
+        
+        @Override
+        protected void writeAttributes(ResponseWriter writer, TagEncodingHelper helper)
+            throws IOException
+        {
+            // style Class
+            String labelClass = helper.getTagAttributeStringEx("labelClass");
+            helper.writeStyleClass(writer, TagStyleClass.CONTROL_LABEL.get(), labelClass);
+        }
+
+        @Override
+        public void encodeChildren(FacesContext context)
+            throws IOException
+        {
+            // encode label component
+            getControl().encodeLabel(context, this);
+            // now render
+            super.encodeChildren(context);
+        }
+    }
+
+    public static class InputSeparatorComponent extends ControlSeparatorComponent
+    {
+        @Override
+        protected String getTagName(ControlRenderInfo renderInfo)
+        {
+            return renderInfo.INPUT_WRAPPER_TAG;
+        }
+
+        @Override
+        protected void writeAttributes(ResponseWriter writer, TagEncodingHelper helper)
+            throws IOException
+        {
+            // style Class
+            String inputClass = helper.getTagAttributeStringEx("inputClass");
+            // String inputState =(helper.isRenderValueComponent() ? TagStyleClass.INPUT_DIS.get() : null); 
+            helper.writeStyleClass(writer, TagStyleClass.CONTROL_INPUT.get(), inputClass); // , inputState
+            // colspan
+            String colSpan = tagName.equalsIgnoreCase(InputControl.HTML_TAG_TD) ? helper.getTagAttributeStringEx("colspan") : null;            
+            if (colSpan!=null)
+                writer.writeAttribute("colspan", colSpan, null);
+        }
+
+        @Override
+        public void encodeChildren(FacesContext context)
+            throws IOException
+        {
+            // encode input components
+            getControl().encodeInput(context, this);
+            // don't call super.encodeChildren()!
+        }
+    }
+
+    public static class ValueOutputComponent extends jakarta.faces.component.UIComponentBase
+    {
+        private final String tagName = "span";
+
+        /*
+        public ValueOutputComponent()
+        {
+            if (log.isTraceEnabled())
+                log.trace("ValueOutputComponent created.");
+        } 
+        */
+
+        @Override
+        public String getFamily()
+        {
+            return UINamingContainer.COMPONENT_FAMILY;
+        }
+
+        @Override
+        public void encodeBegin(FacesContext context)
+            throws IOException
+        {
+            super.encodeBegin(context);
+
+            UIComponent parent = getParent();
+            if (!(parent instanceof ControlTag))
+                parent = parent.getParent();
+            if (!(parent instanceof ControlTag))
+                parent = parent.getParent();
+            if (!(parent instanceof ControlTag))
+            {   log.error("Invalid parent component for " + getClass().getName());
+                return;
+            }
+
+            ControlTag controlTag = (ControlTag) parent;
+            if (!controlTag.controlVisible)
+                return; // Not visible!
+            
+            InputControl control = controlTag.control;
+            InputControl.ValueInfo valInfo = controlTag.inpInfo;
+
+            TagEncodingHelper helper = controlTag.helper;
+            if (control == null)
+                control = helper.getInputControl(); // Oops, should not come here 
+            if (valInfo == null)
+                valInfo = helper.getValueInfo(context); // Oops, should not come here 
+
+            String styleClass = helper.getTagStyleClass(TagStyleClass.INPUT_DIS.get());
+            String tooltip = helper.getValueTooltip(helper.getTagAttributeValue("title"));
+
+            // render components
+            control.renderValue(this, this.tagName, styleClass, tooltip, valInfo, context);
+        }
+    }
+
+    // Logger
+    private static final Logger       log                  = LoggerFactory.getLogger(ControlTag.class);
+
+    protected static final String     readOnlyState        = "readOnlyState";
+
+    protected final TagEncodingHelper helper               = TagEncodingHelperFactory.create(this, TagStyleClass.INPUT.get()); // Must be "INPUT" not "CONTROL"!
+
+    protected InputControl            control              = null;
+    protected InputControl.InputInfo  inpInfo              = null;
+    protected ControlRenderInfo       renderInfo           = null;
+    protected boolean                 encodeLabel          = true;
+    private boolean                   creatingComponents   = false;
+    protected boolean                 valueValidated       = false;
+    protected boolean                 controlVisible       = true;
+
+    public ControlTag()
+    {
+        super();
+    }
+
+    @Override
+    public String getFamily()
+    {
+        return "jakarta.faces.NamingContainer";
+    }
+
+    protected void saveState()
+    {
+        // getStateHelper().put(inpControlPropName, control);
+        // getStateHelper().put(inputInfoPropName, inpInfo);
+        getStateHelper().put(ControlTag.readOnlyState, (this.inpInfo == null));
+    }
+
+    protected boolean initState(FacesContext context)
+    {
+        // Check read-Only
+        Boolean ros = (Boolean) getStateHelper().get(ControlTag.readOnlyState);
+        if (ros != null && ros.booleanValue())
+            return false;
+        // Must have children        
+        if (getChildCount() == 0)
+        {   log.warn("ControlTag '{}' has no children. encodeBegin may not have been called yet.", getClientId());
+            return false;
+        }
+        // control = ;
+        helper.prepareData();
+        this.control = helper.getInputControl();
+        this.inpInfo = helper.getInputInfo(context);
+        return (this.control != null && this.inpInfo != null);
+    }
+
+    @Override
+    public String getId()
+    {
+        String compId = super.getId();
+        // Mojarra-Patch since Id might have been set to "null"
+        if ("null".equals(compId))
+            compId =  helper.completeInputTagId(null);
+        // done
+        return compId;
+    }
+    
+    @Override
+    public void setId(String id) 
+    {   // complete
+        id = helper.completeInputTagId(id); 
+        // set
+        super.setId(id);
+    }
+
+    @Override
+    public void setParent(UIComponent parent)
+    {
+        super.setParent(parent);
+        // check
+        if (helper.hasComponentId())
+            return;
+        if (this.renderInfo==null) {
+            /*
+             * Attention: Only works if FormGrid is a direct parent of the Control.
+             * Does not work, if other components are between the Control and the FormGrid.
+             */
+            this.renderInfo=helper.getControlRenderInfo();
+            if (this.renderInfo!=null && this.renderInfo.AUTO_CONTROL_ID!=null) {
+                // Auto set component Id
+                setId(this.renderInfo.AUTO_CONTROL_ID.toString());
+                log.warn("Auto-Setting compontent id for control to {}", this.getId());
+            }
+        }
+    }
+
+    /**
+     * remember original clientId
+     * necessary only inside UIData
+     */
+    protected String treeClientId = null;
+    
+    @Override
+    public boolean visitTree(VisitContext visitContext, VisitCallback callback) 
+    {
+        FacesContext context = visitContext.getFacesContext();
+        treeClientId = this.getClientId(context);
+        return super.visitTree(visitContext, callback);
+    }
+
+    @Override
+    public String getClientId(FacesContext context)
+    {
+        // Check if dynamic components are being created
+        if (this.treeClientId!=null && (this.creatingComponents || this.control!=null && this.control.isCreatingComponents()))
+        {   // return the original tree client id
+            return treeClientId; 
+        }
+        // default behavior
+        return super.getClientId(context);
+    }
+
+    @Override
+    public boolean getRendersChildren()
+    {
+        // render children ourselves using 
+        //     encodeChildren(FacesContext context)
+        return true;
+    }
+
+    /*
+     * Add label and input components when the view is loaded for the first time
+     */
+    @Override
+    public void encodeBegin(FacesContext context)
+        throws IOException
+    {
+        super.encodeBegin(context);
+
+        // init
+        helper.encodeBegin();
+
+        // set InputControl
+        if (this.control==null)
+            this.control = helper.getInputControl();
+        
+        // renderInfo
+        if (this.renderInfo==null) {
+            this.renderInfo=helper.getControlRenderInfo();
+            if (this.renderInfo==null)
+                this.renderInfo=ControlRenderInfo.DEFAULT_CONTROL_RENDER_INFO;  // No FormGrid found. Use Default!
+        }
+
+        // Check visiblity
+        this.controlVisible = helper.isVisible();
+
+        // Render
+        if (this.controlVisible && renderInfo.CONTROL_TAG!=null)
+        {   // control wrapper tag
+            ResponseWriter writer = context.getResponseWriter();
+            writer.startElement(renderInfo.CONTROL_TAG, this);
+            // render id
+            helper.writeComponentId(writer, false);
+            // style class
+            String controlClass = helper.getTagAttributeStringEx("controlClass");
+            String styleClass   = helper.getTagAttributeString(InputControl.CSS_STYLE_CLASS);
+            String contextClass = helper.getContextStyleClass(); 
+            helper.writeStyleClass(writer, TagStyleClass.CONTROL.get(), controlClass, styleClass, contextClass);
+        }
+        
+        // Encode LabelSeparatorComponent
+        if (this.encodeLabel)
+        {   // Create Label Separator Tag
+            ControlSeparatorComponent labelSepTag = null;
+            if (getChildCount() > 0)
+                labelSepTag = (ControlSeparatorComponent) getChildren().get(0);
+            if (labelSepTag == null)
+            {   try {
+                    creatingComponents = true;
+                    labelSepTag = new LabelSeparatorComponent();
+                    getChildren().add(labelSepTag);
+                    helper.resetComponentId(labelSepTag);
+                } finally {
+                    creatingComponents = false;
+                }
+            }
+            // encode
+            labelSepTag.setRendered(this.controlVisible);
+            if (this.controlVisible)
+                labelSepTag.encodeAll(context);
+        }
+        
+        // Encode InputSeparatorComponent
+        boolean isCustomInput = isCustomInput();
+        if (!isCustomInput)
+        {   // Create Input Separator Tag
+            ControlSeparatorComponent inputSepTag = null;
+            if (getChildCount() > 1)
+                inputSepTag = (ControlSeparatorComponent) getChildren().get(1);
+            if (inputSepTag == null)
+            {   try {
+                    creatingComponents = true;
+                    inputSepTag = new InputSeparatorComponent();
+                    getChildren().add(inputSepTag);
+                    helper.resetComponentId(inputSepTag);
+                } finally {
+                    creatingComponents = false;
+                }
+            }
+            // encode
+            inputSepTag.setRendered(this.controlVisible);
+            if (this.controlVisible)
+                inputSepTag.encodeAll(context);
+        }
+        // done
+        saveState();
+    }
+
+    @Override
+    public void encodeChildren(FacesContext context)
+        throws IOException
+    {
+        /*
+         * Composite component renderer must render children for custom components
+         *   <cc:implementation>
+         *       <c:choose>
+         *           <c:when test="#{cc.attrs.custom == true}">
+         *               <cc:insertChildren/>
+         *           </c:when>
+         *       </c:choose>
+         *   </cc:implementation>
+         */
+        if (this.controlVisible && isCustomInput())
+        {   // Custom input
+            ResponseWriter writer = null;
+            String tagName  = renderInfo.INPUT_WRAPPER_TAG;
+            if (tagName!=null && tagName.length()>0)
+            {   // render tag
+                writer = context.getResponseWriter();
+                writer.startElement(tagName, this);
+                // style Class
+                String inpClass = helper.getTagAttributeStringEx("inputClass");
+                helper.writeStyleClass(writer, TagStyleClass.CONTROL_INPUT.get(), inpClass);
+                // write more
+                String colSpan = tagName.equalsIgnoreCase(InputControl.HTML_TAG_TD) ? helper.getTagAttributeStringEx("colspan") : null;
+                if (colSpan!=null)
+                    writer.writeAttribute("colspan", colSpan, null);
+            }
+            // encode children
+            super.encodeChildren(context);
+            // end of element
+            if (writer!=null)
+            {   // render tag
+                writer.endElement(tagName);
+            }
+        }
+    }
+
+    @Override
+    public void encodeEnd(FacesContext context)
+        throws IOException
+    {
+        // encodeEnd
+        if (this.controlVisible && renderInfo.CONTROL_TAG!=null)
+        {   // control wrapper tag
+            ResponseWriter writer = context.getResponseWriter();
+            writer.endElement(renderInfo.CONTROL_TAG);
+        }
+        // call base
+        super.encodeEnd(context);
+    }
+
+    public boolean isCustomInput()
+    {
+        Object custom = getAttributes().get("custom");
+        if (custom != null)
+            return ObjectUtils.getBoolean(custom);
+        return false;
+    }
+    
+    /**
+     * called from LabelSeparatorComponent
+     * @param context the faces context
+     * @param parent the LabelSeparatorComponent
+     * @throws IOException from ResponseWriter
+     */
+    protected void encodeLabel(FacesContext context, UIComponentBase parent)
+        throws IOException
+    {
+        UIComponent labelFacet = getFacet("label");
+        if (labelFacet!=null)
+        {   // label facet
+            labelFacet.encodeAll(context);
+        }
+        else
+        {   // render components
+            try {
+                creatingComponents = true;
+                HtmlOutputLabel labelComponent = null;
+                if (parent.getChildCount() > 0)
+                {
+                    labelComponent = (HtmlOutputLabel) parent.getChildren().get(0);
+                    // update
+                    helper.updateLabelComponent(context, labelComponent, null);
+                }
+                if (labelComponent == null)
+                {
+                    String forInput = isCustomInput() ? helper.getTagAttributeString("for") : "*";
+                    // createLabelComponent 
+                    labelComponent = helper.createLabelComponent(context, forInput, "eLabel", null, getColon());
+                    parent.getChildren().add(0, labelComponent);
+                    helper.resetComponentId(labelComponent);
+                }
+            } finally {
+                creatingComponents = false;
+            }
+        }
+    }
+
+    /**
+     * called from InputSeparatorComponent
+     * @param context the faces context
+     * @param parent the InputSeparatorComponent
+     * @throws IOException from ResponseWriter
+     */
+    protected void encodeInput(FacesContext context, UIComponent parent)
+        throws IOException
+    {
+        // render components
+        try {
+            creatingComponents = true;
+            // check children
+            int count = parent.getChildCount();
+            UIComponent valueComp = (count>0 ? parent.getChildren().get(count-1) : null);
+            boolean resetChildId = (count==0);
+            // continue
+            this.inpInfo = helper.getInputInfo(context);
+            // set required
+            super.setRequired(helper.isValueRequired());
+	        // create Input Controls
+            // boolean recordReadOnly = helper.isRecordReadOnly();
+            if (count==0)
+            {   // Create components
+                control.createInput(parent, inpInfo, context);
+                // create Value Component
+                if (valueComp == null)
+                {   // create ValueOutputComponent
+                    valueComp = new ValueOutputComponent();
+                    parent.getChildren().add(valueComp);
+                }
+            }
+            else
+            {   // Update
+                control.updateInputState(parent, inpInfo, context, context.getCurrentPhaseId());
+            }
+            // set rendered
+            boolean renderValue = helper.isRenderValueComponent();
+            List<UIComponent> children = parent.getChildren();
+            for (UIComponent child : children)
+            {   // reset child id
+                if (resetChildId && child.getId()!=null)
+                    child.setId(child.getId());
+                // set rendered
+                boolean valueOutput = (child instanceof ValueOutputComponent);
+                child.setRendered((valueOutput ? renderValue : !renderValue));
+            }
+            // wrapperTag
+            String wrapperTag = helper.writeWrapperTag(context, false, renderValue); 
+            // render
+            control.renderInput(parent, inpInfo, context);
+            // wrapperTagEnd
+            if (wrapperTag!=null)
+            {   // control wrapper tag
+                ResponseWriter writer = context.getResponseWriter();
+                writer.endElement(wrapperTag);
+            }
+        } finally {
+            creatingComponents = false;
+        }
+    }
+
+    @Override
+    public Object getValue()
+    {
+        // check for record
+        if (helper.getRecord() != null)
+            return helper.getDataValue(true);
+        // default
+        Object value = super.getValue();
+        return value;
+    }
+    
+    @Override
+    public Object getSubmittedValue()
+    { // Check state
+        if (this.control == null || this.inpInfo == null || helper.isReadOnly())
+            return null;
+        // Get Input Tag
+        if (getChildCount() <= 1)
+            return null;
+        // get Input Value
+        ControlSeparatorComponent inputSepTag = (ControlSeparatorComponent) getChildren().get(1);
+        return this.control.getInputValue(inputSepTag, this.inpInfo, true);
+    }
+    
+    @Override
+    protected Object getConvertedValue(FacesContext context, Object newSubmittedValue)
+        throws ConverterException
+    { // Check state
+        if (this.control == null || this.inpInfo == null || helper.isReadOnly())
+            return null;
+        // Get Input Tag
+        if (getChildCount() <= 1)
+            return null;
+        // get Input Value
+        ControlSeparatorComponent inputSepTag = (ControlSeparatorComponent) getChildren().get(1);
+        return this.control.getConvertedValue(inputSepTag, this.inpInfo, newSubmittedValue);
+    }
+
+    @Override
+    public void processDecodes(FacesContext context) 
+    {
+        // check UI-Data
+        if (helper.isInsideUIData() && getChildCount()>0)
+        {   // update input state
+            updateControlInputState(context);
+            // Set Rendered for children (changed 2024-04-05)
+            // boolean readOnly = helper.isRecordReadOnly();
+            boolean renderValue = helper.isRenderValueComponent();
+            setRenderInput(!renderValue);
+        }
+        // default
+        super.processDecodes(context);
+    }
+
+    @Override
+    public void processValidators(FacesContext context)
+    {
+        // check UI-Data
+        if (helper.isInsideUIData() && getChildCount()>0)
+        {   // update input state
+            updateControlInputState(context);
+            // No need to processValidators for children
+            setRenderInput(false);
+        }
+        // process all validators (including children)
+        super.processValidators(context);
+    }
+
+    @Override
+    public void validate(FacesContext context)
+    {   // init state
+        if (initState(context) == false)
+            return;
+        // get submitted value and validate
+        if (log.isDebugEnabled())
+            log.debug("Validating input for {}.", this.inpInfo.getColumn().getName());
+
+        // Will internally call getSubmittedValue() and validateValue()
+        this.valueValidated = false;
+        super.validate(context);
+        // post check
+        if (!this.valueValidated && !helper.isReadOnly())
+        {   // New since 2024-04-04
+            // Validate Record value
+            Object value = helper.getDataValue(true);
+            log.warn("No Submitted value for {}. Validating Record value of \"{}\" instead.", helper.getColumnName(), value);
+            if (value!=null)
+                validateValue(context, value);
+        }
+    }
+
+    @Override
+    public void validateValue(FacesContext context, Object value)
+    {   // Validate value
+        try
+        {   // Check state
+            if (this.inpInfo == null || !isValid())
+                return;
+            // validateValue
+            if (helper.beginValidateValue(context, value))
+                inpInfo.validate(value);
+            setValid(true);
+            // don't call base class!
+            // super.validateValue(context, value);
+
+        } catch (Exception e) {
+            // Value is not valid
+            if (!(e instanceof EmpireException))
+                e = new FieldIllegalValueException(helper.getColumn(), "", e);
+            // Add error message
+            helper.addErrorMessage(context, e);
+            setValid(false);
+
+        } finally {
+            this.valueValidated = true;
+        }
+    }
+
+    @Override
+    public void updateModel(FacesContext context)
+    {
+        if (initState(context) == false)
+            return;
+        // No Action
+        if (!isValid() || !isLocalValueSet())
+            return;
+        // updateModel
+        Object value = getLocalValue();
+        if (helper.beginUpdateModel(context, value)) 
+        {   // don't call base class!
+            // super.updateModel(context);
+            log.debug("Updating model input for {}.", this.inpInfo.getColumn().getName());
+            this.inpInfo.setValue(value);
+        }
+        setValue(null);
+        setLocalValueSet(false);
+        // Post update
+        ControlSeparatorComponent inputSepTag = (ControlSeparatorComponent) getChildren().get(1);
+        this.control.postUpdateModel(inputSepTag, this.inpInfo, context);
+    }
+
+    public InputControl getInputControl()
+    {
+        return this.control;
+    }
+
+    public Column getInputColumn()
+    {
+        return helper.getColumn();
+    }
+
+    public boolean isInputReadOnly()
+    {
+        return helper.isRecordReadOnly();
+    }
+
+    public boolean isInputRequired()
+    {
+        return helper.isValueRequired();
+    }
+    
+    protected void updateControlInputState(FacesContext context)
+    {
+        // get control
+        helper.prepareData();
+        if (control==null)
+            control = helper.getInputControl();
+        if (inpInfo==null)
+            inpInfo = helper.getInputInfo(context);
+        // update control
+        ControlSeparatorComponent inputSepTag = (ControlSeparatorComponent) getChildren().get(1);
+        control.updateInputState(inputSepTag, inpInfo, context, context.getCurrentPhaseId());
+    }
+
+    protected void setRenderInput(boolean renderInput)
+    {
+        if (getChildCount()>1 && (getChildren().get(1) instanceof InputSeparatorComponent))
+        {   // Make sure all inputs are rendered
+            boolean hasChanged = false;
+            InputSeparatorComponent parent = (InputSeparatorComponent)getChildren().get(1);
+            // set rendered of children
+            for (UIComponent child : parent.getChildren())
+            {   // set rendered 
+                boolean rendered = (child instanceof ValueOutputComponent) ? renderInput : !renderInput;
+                if (child.isRendered()!=rendered)
+                {   child.setRendered(rendered);
+                    hasChanged = true;
+                }    
+            }
+            // give control chance to update
+            if (hasChanged && log.isDebugEnabled())
+                log.debug("Changing UIInput readOnly state for {} to {}", helper.getColumnName(), renderInput);
+        }
+        else
+        {   // Must have at least two children, for label and input
+            log.warn("Control-Tag does not have separate Label and Input components");
+        }
+    }
+    
+    protected boolean getColon()
+    {
+        Object colon = getAttributes().get("colon");
+        if (colon!=null)
+            return ObjectUtils.getBoolean(colon);
+        return true;
+    }
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/FormGridTag.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/FormGridTag.java
new file mode 100644
index 00000000..3248cbd5
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/FormGridTag.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.components;
+
+import java.io.IOException;
+
+import jakarta.faces.component.UINamingContainer;
+import jakarta.faces.component.UIOutput;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.context.ResponseWriter;
+
+import org.apache.empire.commons.StringUtils;
+import org.apache.empire.jakarta.controls.InputControl;
+import org.apache.empire.jakarta.utils.ControlRenderInfo;
+import org.apache.empire.jakarta.utils.TagEncodingHelper;
+import org.apache.empire.jakarta.utils.TagEncodingHelperFactory;
+import org.apache.empire.jakarta.utils.TagStyleClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FormGridTag extends UIOutput // implements NamingContainer
+{
+    // Logger
+    private static final Logger log = LoggerFactory.getLogger(FormGridTag.class);
+    
+    /*
+     * FormGridMode
+     */
+    private enum FormGridMode 
+    {
+        LEGACY(InputControl.HTML_TAG_DIV, null, InputControl.HTML_TAG_TD, InputControl.HTML_TAG_TD),
+        TABLE (InputControl.HTML_TAG_TABLE, InputControl.HTML_TAG_TR, InputControl.HTML_TAG_TD, InputControl.HTML_TAG_TD),
+        GRID  (InputControl.HTML_TAG_DIV, InputControl.HTML_TAG_DIV, InputControl.HTML_TAG_DIV, InputControl.HTML_TAG_DIV),
+        FLAT  (InputControl.HTML_TAG_DIV, InputControl.HTML_TAG_DIV, null, null);
+        
+        public final String GRID_TAG;
+        public final String CONTROL_TAG;
+        public final String LABEL_TAG;
+        public final String INPUT_TAG;
+        
+        private FormGridMode(String gridTag, String controlTag, String labelTag, String inputTag)
+        {
+            this.GRID_TAG = gridTag;
+            this.CONTROL_TAG = controlTag;
+            this.LABEL_TAG = labelTag;
+            this.INPUT_TAG = inputTag;
+        }
+        
+        public static FormGridMode detect(String mode)
+        {
+            if (mode==null || mode.length()==0)
+                return LEGACY;
+            // find
+            FormGridMode[] values = values();
+            for (int i=0; i<values.length; i++)
+            {
+                if (values[i].name().equalsIgnoreCase(mode))
+                    return values[i]; 
+            }
+            // not found
+            log.warn("FormGridMode \"{}\" not found. Using default!", mode);
+            return LEGACY;
+        }
+        
+        @Override
+        public String toString()
+        {
+            return name();
+        }
+    }
+    
+    protected final TagEncodingHelper helper = TagEncodingHelperFactory.create(this, TagStyleClass.FORM_GRID.get());
+    
+    protected ControlRenderInfo controlRenderInfo = null;
+    
+    private FormGridMode mode;
+
+    @Override
+    public String getFamily()
+    {
+        return UINamingContainer.COMPONENT_FAMILY; 
+    }
+        
+    @Override
+    public void encodeBegin(FacesContext context)
+        throws IOException
+    {
+        // call base
+        super.encodeBegin(context);
+        
+        // tagName
+        this.mode = FormGridMode.detect(helper.getTagAttributeString("mode", FormGridMode.GRID.name())); 
+        
+        // render components
+        ResponseWriter writer = context.getResponseWriter();
+        writer.startElement(mode.GRID_TAG, this);
+        // id
+        helper.writeComponentId(writer);
+        // style class
+        helper.writeStyleClass(writer);
+        helper.writeAttribute(writer, InputControl.HTML_ATTR_STYLE, helper.getTagAttributeString("style"));
+    }
+
+    @Override
+    public boolean getRendersChildren()
+    {
+        return super.getRendersChildren();
+    }
+    
+    @Override
+    public void encodeChildren(FacesContext context)
+        throws IOException
+    {
+        super.encodeChildren(context);
+    }
+
+    @Override
+    public void encodeEnd(FacesContext context)
+        throws IOException
+    {
+        // call base
+        super.encodeEnd(context);
+        // close
+        ResponseWriter writer = context.getResponseWriter();
+        writer.endElement(mode.GRID_TAG);
+    }
+    
+    public ControlRenderInfo getControlRenderInfo()
+    {
+        // already set?
+        if (controlRenderInfo!=null)
+            return controlRenderInfo;
+        // check mode
+        if (this.mode==null)
+            this.mode = FormGridMode.detect(helper.getTagAttributeString("mode", FormGridMode.GRID.name())); 
+        // autoControlId
+        Character autoControlId = null;
+        Object autoId = helper.getTagAttributeString("autoControlId");
+        if (autoId!=null)
+        {   // check
+            String id = autoId.toString();
+            if ("true".equalsIgnoreCase(id))
+                autoControlId = TagEncodingHelper.PH_COLUMN_SMART;
+            else if (id.length()==1 && StringUtils.indexOfAny(id, TagEncodingHelper.ALLOWED_COLUMN_PH)>=0)
+                autoControlId = id.charAt(0);
+            else if (!"false".equalsIgnoreCase(id))
+                log.warn("FormGridTag: Invalid value \"{}\" for attribute \"autoControlId\". Allowed values are *|@|&", id);
+        }
+        // create control info
+        this.controlRenderInfo = new ControlRenderInfo(mode.CONTROL_TAG, mode.LABEL_TAG, mode.INPUT_TAG, autoControlId);
+        return controlRenderInfo;
+    }
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/InputTag.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/InputTag.java
new file mode 100644
index 00000000..5c2c8d78
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/InputTag.java
@@ -0,0 +1,435 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.components;
+
+import java.io.IOException;
+import java.util.List;
+
+import jakarta.faces.component.NamingContainer;
+import jakarta.faces.component.UIComponent;
+import jakarta.faces.component.UIInput;
+import jakarta.faces.component.visit.VisitCallback;
+import jakarta.faces.component.visit.VisitContext;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.context.ResponseWriter;
+import jakarta.faces.convert.ConverterException;
+import jakarta.faces.view.AttachedObjectHandler;
+
+import org.apache.empire.data.Column;
+import org.apache.empire.db.exceptions.FieldIllegalValueException;
+import org.apache.empire.exceptions.EmpireException;
+import org.apache.empire.jakarta.controls.InputControl;
+import org.apache.empire.jakarta.utils.TagEncodingHelper;
+import org.apache.empire.jakarta.utils.TagEncodingHelperFactory;
+import org.apache.empire.jakarta.utils.TagStyleClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InputTag extends UIInput implements NamingContainer
+{
+    // Logger
+    private static final Logger       log                  = LoggerFactory.getLogger(InputTag.class);
+
+    // private static final String inpControlPropName = InputControl.class.getSimpleName();
+    // private static final String inputInfoPropName = InputControl.InputInfo.class.getSimpleName();
+    protected static final String     readOnlyState        = "readOnlyState";
+
+    protected final TagEncodingHelper helper               = TagEncodingHelperFactory.create(this, TagStyleClass.INPUT.get());
+
+    protected InputControl            control              = null;
+    protected InputControl.InputInfo  inpInfo              = null;
+    protected boolean                 valueValidated       = false;
+
+    /*
+    private static int itemIdSeq = 0;
+    private final int itemId;
+    */
+
+    public InputTag()
+    {
+        super();
+        // Debug stuff
+        /*
+        itemId = ++itemIdSeq;
+        if (log.isDebugEnabled())
+            log.debug("InputTag {} created", itemId);
+        */
+    }
+
+    @Override
+    public String getFamily()
+    {
+        return "jakarta.faces.NamingContainer";
+    }
+
+    protected void saveState()
+    {
+        // getStateHelper().put(inpControlPropName, control);
+        // getStateHelper().put(inputInfoPropName, inpInfo);
+        getStateHelper().put(readOnlyState, (inpInfo == null));
+    }
+
+    protected boolean initState(FacesContext context)
+    {
+        // Check visibility
+        helper.prepareData();
+        if (helper.isVisible() == false)
+            return false; // not visible
+        // Read only State
+        Boolean ros = (Boolean) getStateHelper().get(readOnlyState);
+        if (ros != null && ros.booleanValue())
+            return false;
+        // Must have children
+        if (getChildCount() == 0)
+        {   log.warn("InputTag '{}' has no children. encodeBegin may not have been called yet.", getClientId());
+            return false;
+        }
+        // Init Control and inputInfo;
+        control = helper.getInputControl();
+        inpInfo = helper.getInputInfo(context);
+        return (control != null && inpInfo != null);
+    }
+
+    /**
+     * remember original clientId
+     * necessary only inside UIData
+     */
+    private String treeClientId = null;
+
+    @Override
+    public boolean visitTree(VisitContext visitContext, VisitCallback callback)
+    {
+        FacesContext context = visitContext.getFacesContext();
+        treeClientId = getClientId(context);
+        return super.visitTree(visitContext, callback);
+    }
+
+    @Override
+    public String getClientId(FacesContext context)
+    {
+        // Check if dynamic components are being created
+        if (this.treeClientId != null && control != null && control.isCreatingComponents())
+        { // return the original tree client id
+            return treeClientId;
+        }
+        // default behavior
+        return super.getClientId(context);
+    }
+
+    @Override
+    public boolean getRendersChildren()
+    {
+        // render children ourselves using 
+        //     encodeChildren(FacesContext context)
+        return true;
+    }
+
+    @Override
+    public void encodeBegin(FacesContext context)
+        throws IOException
+    {
+        super.encodeBegin(context);
+
+        // get Control (before checking visible)
+        helper.encodeBegin();
+        
+        // create child components
+        this.control = helper.getInputControl();
+        this.inpInfo = helper.getInputInfo(context);
+        // set required
+        super.setRequired(helper.isValueRequired());
+        // create input
+        if (this.getChildCount()==0)
+        {   // create input
+            control.createInput(this, inpInfo, context);
+            attachEvents(context);
+        }
+        else
+        {   // update state
+            control.updateInputState(this, inpInfo, context, context.getCurrentPhaseId());
+        }
+        
+        // Set Render Input
+        boolean controlVisible = helper.isVisible();
+        boolean renderValue = helper.isRenderValueComponent();
+        setRenderInput(!renderValue && controlVisible);
+        // Render when visible
+        if (controlVisible)
+        {   // Render now
+            String wrapperTag = helper.writeWrapperTag(context, true, renderValue);
+            // render components
+            if (renderValue)
+            {   // render value
+                String tagName = "span";
+                String styleClass = helper.getTagStyleClass(TagStyleClass.INPUT_DIS.get());
+                String tooltip = helper.getValueTooltip(helper.getTagAttributeValue("title"));
+                control.renderValue(this, tagName, styleClass, tooltip, inpInfo, context);
+            }
+            else
+            {   // render input
+                control.renderInput(this, inpInfo, context);
+            }
+            // wrapperTagEnd
+            if (wrapperTag!=null)
+            {   // control wrapper tag
+                ResponseWriter writer = context.getResponseWriter();
+                writer.endElement(wrapperTag);
+            }
+        }
+        saveState();
+    }
+
+    @Override
+    public void encodeChildren(FacesContext context)
+        throws IOException
+    {
+        // Ignore this
+        // super.encodeChildren(context);
+        log.debug("InputTag:encodeChildren is ignored");
+    }
+
+    @Override
+    public void encodeEnd(FacesContext context)
+        throws IOException
+    {
+        // call base
+        super.encodeEnd(context);
+    }
+
+    @Override
+    public String getId()
+    {
+        String compId = super.getId();
+        // Mojarra-Patch since Id might have been set to "null"
+        if ("null".equals(compId))
+            compId =  helper.completeInputTagId(null);
+        // done
+        return compId;
+    }
+
+    @Override
+    public void setId(String id)
+    {   // complete
+        id = helper.completeInputTagId(id); 
+        // setId
+        super.setId(id);
+    }
+
+    @Override
+    public Object getValue()
+    {
+        // check for record
+        if (helper.getRecord() != null)
+            return helper.getDataValue(true);
+        // default
+        Object value = super.getValue();
+        return value;
+    }
+
+    @Override
+    public Object getSubmittedValue()
+    { // Check state
+        if (control == null || inpInfo == null || helper.isReadOnly())
+            return null;
+        // get Input Value
+        return control.getInputValue(this, inpInfo, true);
+    }
+
+    @Override
+    protected Object getConvertedValue(FacesContext context, Object newSubmittedValue)
+        throws ConverterException
+    {   // Check state
+        if (control == null || inpInfo == null || helper.isReadOnly())
+            return null;
+        // parse and convert value
+        return this.control.getConvertedValue(this, this.inpInfo, newSubmittedValue);
+    }
+
+    @Override
+    public void processDecodes(FacesContext context) 
+    {
+        // check UI-Data
+        if (helper.isInsideUIData() && getChildCount()>0)
+        {   // update input state
+            updateControlInputState(context);
+            // Set Rendered for children (changed 2024-04-05)
+            // boolean readOnly = helper.isRecordReadOnly();
+            boolean renderValue = helper.isRenderValueComponent();
+            setRenderInput(!renderValue);
+        }
+        // default
+        super.processDecodes(context);
+    }
+
+    @Override
+    public void processValidators(FacesContext context)
+    {
+        // check UI-Data
+        if (helper.isInsideUIData() && getChildCount()>0)
+        {   // update input state
+            updateControlInputState(context);
+            // No need to processValidators for children
+            setRenderInput(false);
+        }
+        // process all validators (including children)
+        super.processValidators(context);
+    }
+
+    @Override
+    public void validate(FacesContext context)
+    {   // init state
+        if (initState(context) == false)
+            return;
+        // get submitted value and validate
+        if (log.isDebugEnabled())
+            log.debug("Validating input for {}.", inpInfo.getColumn().getName());
+
+        // Will internally call getSubmittedValue() and validateValue()
+        this.valueValidated = false;
+        super.validate(context);
+        // post check
+        if (!this.valueValidated && !helper.isReadOnly())
+        {   // New since 2024-04-04
+            // Validate Record value
+            Object value = helper.getDataValue(true);
+            log.warn("No Submitted value for {}. Validating Record value of \"{}\" instead.", helper.getColumnName(), value);
+            if (value!=null)
+                validateValue(context, value);
+        }
+    }
+    
+    @Override
+    public void validateValue(FacesContext context, Object value)
+    {   // Validate value
+        try
+        {   // Check state
+            if (inpInfo == null)
+                return;
+            // validateValue
+            if (helper.beginValidateValue(context, value))
+                inpInfo.validate(value);
+            setValid(true);
+            // don't call base class!
+            // super.validateValue(context, value);
+
+        } catch (Exception e) {
+            // Value is not valid
+            if (!(e instanceof EmpireException))
+                e = new FieldIllegalValueException(helper.getColumn(), "", e);
+            // Add error message
+            helper.addErrorMessage(context, e);
+            setValid(false);
+
+        } finally {
+            this.valueValidated = true;
+        }
+    }
+
+    @Override
+    public void updateModel(FacesContext context)
+    {
+        if (initState(context) == false)
+            return;
+        // No Action
+        if (!isValid() || !isLocalValueSet())
+            return;
+        // updateModel
+        Object value = getLocalValue();
+        if (helper.beginUpdateModel(context, value)) 
+        {   // don't call base class!
+            // super.updateModel(context);
+            log.debug("Updating model input for {}.", this.inpInfo.getColumn().getName());
+            this.inpInfo.setValue(value);
+        }
+        setValue(null);
+        setLocalValueSet(false);
+        // Post update
+        control.postUpdateModel(this, inpInfo, context);
+    }
+
+    public Column getInputColumn()
+    {
+        return helper.getColumn();
+    }
+
+    public boolean isInputReadOnly()
+    {
+        return helper.isRecordReadOnly();
+    }
+
+    public boolean isInputRequired()
+    {
+        return helper.isValueRequired();
+    }
+    
+    protected void updateControlInputState(FacesContext context)
+    {
+        // get control
+        helper.prepareData();
+        if (control==null)
+            control = helper.getInputControl();
+        if (inpInfo==null)
+            inpInfo = helper.getInputInfo(context);
+        // update control
+        control.updateInputState(this, inpInfo, context, context.getCurrentPhaseId());
+    }
+
+    protected void setRenderInput(boolean renderInput)
+    {
+        for (UIComponent child : getChildren())
+        {
+            if (child.isRendered()!=renderInput)
+            {
+                if (log.isDebugEnabled())
+                    log.debug("Changing UIInput rendered state for {} to {}", helper.getColumnName(), renderInput);
+                child.setRendered(renderInput);
+            }    
+        }
+    }
+    
+    protected void attachEvents(FacesContext context)
+    {
+        // Events available?
+        @SuppressWarnings("unchecked")
+        List<AttachedObjectHandler> result = (List<AttachedObjectHandler>) getAttributes().get("jakarta.faces.RetargetableHandlers");
+        if (result == null)
+        {
+            return;
+        }
+        UIInput inputComponent = null;
+        for (UIComponent c : getChildren())
+        {
+            if (c instanceof UIInput)
+            {   // found
+                inputComponent = (UIInput)c;
+                break;
+            }
+        }
+        if (inputComponent == null)
+            return;
+        // Attach Events
+        for (AttachedObjectHandler aoh : result)
+        {
+            aoh.applyAttachedObject(context, inputComponent);
+        }
+        // remove
+        result.clear();
+        getAttributes().remove("jakarta.faces.RetargetableHandlers");
+    }
+}
diff --git a/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/LabelTag.java b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/LabelTag.java
new file mode 100644
index 00000000..0fea3402
--- /dev/null
+++ b/empire-db-jakarta-faces/src/main/java/org/apache/empire/jakarta/components/LabelTag.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.empire.jakarta.components;
+
+import java.io.IOException;
+
+import jakarta.faces.component.UIOutput;
+import jakarta.faces.component.html.HtmlOutputLabel;
+import jakarta.faces.component.visit.VisitCallback;
+import jakarta.faces.component.visit.VisitContext;
+import jakarta.faces.context.FacesContext;
+
+import org.apache.empire.commons.ObjectUtils;
+import org.apache.empire.jakarta.utils.TagEncodingHelper;
+import org.apache.empire.jakarta.utils.TagEncodingHelperFactory;
+import org.apache.empire.jakarta.utils.TagStyleClass;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class LabelTag extends UIOutput // implements NamingContainer
+{
+    // Logger
+    private static final Logger log = LoggerFactory.getLogger(LabelTag.class);
+    
+    protected final TagEncodingHelper helper = TagEncodingHelperFactory.create(this, TagStyleClass.LABEL.get());
+
+    private boolean creatingComponents = false;
+    
+    public LabelTag()
+    {
+        log.trace("component LabelTag created");
+    }
+
+    @Override
+    public String getFamily()
+    {
+        return "jakarta.faces.NamingContainer";
+    }
+    
+    /**
+     * remember original clientId
+     * necessary only inside UIData
+     */
+    private String treeClientId = null;
+    
+    @Override
... 13254 lines suppressed ...