1 / 20
COIT20259 — Week 10

MVC, FacesServlet,
Navigation, Converters
& Web Security

How Jakarta Faces controls the entire request lifecycle — and how to guard your application from unauthorised access.
Enterprise Computing  ·  Press → to begin  ·  Press T for contents
2 / 20

This WeekLearning Objectives

By the end of this week you will be able to:

Building on Weeks 8 & 9
You can now build pages and forms. This week we go deeper — understanding exactly how JF orchestrates every request, how to enforce data rules in Java, and how to stop unauthorised users from reaching pages they shouldn't see.
3 / 20
Section 1

The MVC Design Pattern

MVC is the architectural blueprint behind Jakarta Faces. Understanding it explains why the framework is designed the way it is — and why this separation of concerns makes applications far easier to maintain.
4 / 20

Section 1.1What Problem Does MVC Solve?

Early web applications mixed everything together — database calls, business rules, and HTML all in the same file. The result was code nobody could maintain.

The "Spaghetti" Problem
Imagine a recipe written as: "While cutting onions, also pre-heat the oven, and by the way here is how to plate the dish." Nobody can follow that — and if the oven changes, you have to rewrite the whole recipe.

MVC fixes this by giving every concern its own place:

  • Data lives in the Model — and nowhere else
  • Display lives in the View — and nowhere else
  • Logic lives in the Controller — and nowhere else
MVC stands for
Model — View — Controller

A software design pattern that separates an application into three distinct responsibilities so each can be built, tested, and changed independently.
Real-World Analogy
A TV news broadcast:
Model = the news research & facts
View = the teleprompter & camera
Controller = the director cueing each segment
5 / 20

Section 1.2MVC Mapped to Jakarta Faces

Model
Your Data & Business Logic
  • Java entity classes (e.g. Product.java)
  • EJB session beans
  • JPA — database access layer
Knows nothing about the View
Controller
Request Orchestrator
  • FacesServlet — single entry point
  • Managed Beans — action methods
  • Manages the 6-phase lifecycle
The "traffic controller"
View
What the User Sees
  • Facelets pages (.xhtml)
  • JF components (h:, f:, ui:)
  • Facelets templates (shared layout)
Knows nothing about the Model
The Golden Rule of MVC
The Model and View are never allowed to talk directly. All communication passes through the Controller. This is why your Facelets page never contains a SQL query, and your entity class never contains HTML.
6 / 20

Section 1.3Why MVC Matters in Practice

Without MVCWith MVC (Jakarta Faces)
Changing the page design requires editing Java code A designer edits only the .xhtml View — no Java needed
Adding a new field means touching HTML, Java, and SQL in one file Add the field to the Model, expose it in the Bean, reference it in the View — cleanly separated
Unit testing the business logic requires a running browser The Model and Managed Beans are plain Java — testable without a browser
Two developers editing the same file creates conflicts Front-end and back-end developers work in separate files simultaneously
Moving from web to mobile requires rewriting everything Replace just the View layer — the Model and Controller stay the same
Analogy
A car has three separate systems: the engine (Model), the dashboard instruments (View), and the steering/pedals (Controller). You can redesign the dashboard without rebuilding the engine.
7 / 20
Section 2

The FacesServlet Controller

FacesServlet is the heartbeat of every Jakarta Faces application. Every single user interaction passes through it. Understanding its 6-phase lifecycle explains every behaviour you will ever see in a JF app.
8 / 20

Section 2.1FacesServlet — The Central Controller

What FacesServlet is
jakarta.faces.webapp.FacesServlet is a standard Java servlet registered in web.xml. It intercepts every *.xhtml HTTP request and takes full control of processing it.

It acts as the single front controller — one entry point for all requests. Rather than having separate servlets for every page, you have one intelligent servlet that manages everything.

What FacesServlet does for every request:

  1. Receives the raw HTTP request from the browser
  2. Creates a FacesContext — a central object holding everything about the current request
  3. Runs the request through the 6-phase JSF lifecycle
  4. Writes the HTML response back to the browser
  5. Disposes the FacesContext when done
Browser Request FacesServlet Front Controller FacesContext 6-Phase Lifecycle HTML Response
9 / 20

Section 2.2The 6-Phase Lifecycle — In Depth

1
Restore View
2
Apply Request Values
3
Process Validations
4
Update Model
5
Invoke Application
6
Render Response
PhasePlain EnglishWho does the workShort-circuits if…
1. Restore ViewRebuild the page's component tree from stateFacesServlet— (always runs)
2. Apply Request ValuesCapture submitted form data from the HTTP requestComponents store raw valuesImmediate=true flag set
3. Process ValidationsRun all validators and converters on the captured dataf: validators + convertersAny validation fails → jump to Phase 6
4. Update ModelWrite validated data into the Managed Bean propertiesEL bindings (#{bean.x})Only if Phase 3 passed
5. Invoke ApplicationCall your action method (e.g., save())Your Java methodOnly if Phase 4 succeeded
6. Render ResponseGenerate and send the HTML page back to the browserFacelets renderer— (always runs)
Critical insight
Your save() method is called in Phase 5 — only if Phases 2, 3, and 4 all succeeded. This is why invalid form data can never reach your business logic.
10 / 20

Knowledge CheckSections 1 & 2 — MVC & FacesServlet

Q1: A junior developer puts a database query directly inside a Facelets .xhtml page. Which MVC principle does this violate?
MVC's core rule is separation of concerns. The View (Facelets) should only display data it receives via EL expressions. Database logic belongs in the Model (JPA entities, EJBs). Mixing them creates unmaintainable "spaghetti" code.
Q2: A user submits a form. The FacesServlet processes Phase 3 and finds that a required field is empty. What happens to Phases 4 and 5?
When Phase 3 (Process Validations) fails, FacesServlet short-circuits the lifecycle. It skips Update Model (Phase 4) and Invoke Application (Phase 5) entirely and goes straight to Render Response (Phase 6) to redisplay the form with error messages. Your business logic is never touched.
11 / 20
Section 3

Page Navigation Technologies

After an action completes, Jakarta Faces must decide which page to show next. There are several ways to control this — from fully automatic to precisely hand-crafted.
12 / 20

Section 3.1Three Navigation Approaches

ApproachHow it worksWhen to use it
Implicit Navigation Return the filename of the target page (without .xhtml) from the action method. JF finds the file automatically. Simple apps, most cases — no extra config needed
Explicit Navigation (faces-config.xml) Define navigation rules in faces-config.xml — map outcomes like "success" and "failure" to specific pages. Complex apps with many conditional routes, or when you want navigation logic separate from Java code
h:link / h:button Non-submitting navigation. Renders as an HTML anchor tag. No form submission, no lifecycle phases. Navigation-only links (menus, cancel buttons, breadcrumbs)

Implicit — return a filename

// Returns to "dashboard.xhtml"
public String save() {
    productService.persist(product);
    return "dashboard";
}

// Returns to "error.xhtml"
public String save() {
    return "error";
}

Explicit — faces-config.xml

<navigation-rule>
  <from-view-id>/login.xhtml</from-view-id>
  <navigation-case>
    <from-outcome>success</from-outcome>
    <to-view-id>/home.xhtml</to-view-id>
  </navigation-case>
  <navigation-case>
    <from-outcome>failure</from-outcome>
    <to-view-id>/login.xhtml</to-view-id>
  </navigation-case>
</navigation-rule>
13 / 20

Section 3.2Forward vs Redirect — A Critical Distinction

Server-Side Forward (default)

If the user presses Refresh, the form is re-submitted. This can create duplicate records.

Redirect (best practice for POST actions)

// Add ?faces-redirect=true to trigger redirect
return "products?faces-redirect=true";
Post-Redirect-Get Pattern
After any form submission that modifies data (save, delete, update), always append ?faces-redirect=true. This prevents duplicate submissions when the user hits the browser's Back or Refresh button.
14 / 20
Section 4

Converters & Validators

Built-in converters and validators handle common cases. But every application has unique business rules — and Jakarta Faces lets you write your own in pure Java.
15 / 20

Section 4.1Built-in vs Custom — When Do You Need Each?

Built-in converters (f:)

Handle common data type conversions automatically:

f:convertNumber f:convertDateTime

Built-in validators (f:)

Enforce common range and length rules:

f:validateLength f:validateLongRange f:validateDoubleRange f:validateRequired f:validateRegex

When you need a custom solution

  • Custom Converter: when the data type is unique — e.g., your application has a special "ProductCode" format (PRD-####)
  • Custom Validator: when the rule cannot be expressed as a range — e.g., "the username must not already exist in the database"
Analogy
Built-in validators are like standard plug sockets — they handle 90% of cases. A custom validator is an adaptor you build yourself for the special appliance nothing else fits.
16 / 20

Section 4.2Building a Custom Converter

What a Converter does
A Converter translates a raw String (from the browser) into a Java object your code understands — and vice versa. You implement the jakarta.faces.convert.Converter interface.
@FacesConverter("productCodeConverter")
public class ProductCodeConverter
         implements Converter<String> {

  /**
   * Called during Phase 2-3:
   * Browser sends "prd-0042" →
   * we return clean "PRD-0042"
   */
  @Override
  public String getAsObject(
        FacesContext ctx,
        UIComponent comp,
        String value) {

    if (value == null || value.isEmpty())
      return null;

    // Uppercase and validate format
    String upper = value.toUpperCase().trim();
    if (!upper.matches("PRD-\\d{4}")) {
      throw new ConverterException(
        new FacesMessage(
          "Format must be PRD-####"));
    }
    return upper;
  }

  /**
   * Called during Phase 6:
   * Java → display string for browser
   */
  @Override
  public String getAsString(
        FacesContext ctx,
        UIComponent comp,
        String value) {
    return (value != null) ? value : "";
  }
}

Use it in your Facelet

<h:inputText
  value="#{bean.productCode}">
  <f:converter
    converterId="productCodeConverter"/>
</h:inputText>
<h:message for="code"/>
Two Methods — Two Directions
getAsObject: String → Java (happens during form submission, Phase 2–3)

getAsString: Java → String (happens when rendering the page, Phase 6)

Both directions must be implemented even if one just returns the value unchanged.
17 / 20

Section 4.3Building a Custom Validator

What a Validator does
A Validator checks a converted value against a business rule. It runs in Phase 3. If the rule fails, it throws a ValidatorException with a user-friendly message. Your bean method is never called.
@FacesValidator("uniqueUsernameValidator")
public class UniqueUsernameValidator
       implements Validator<String> {

  @Override
  public void validate(
        FacesContext context,
        UIComponent component,
        String username) {

    // Simulate checking the database
    if (usernameAlreadyExists(username)) {
      FacesMessage msg = new FacesMessage(
        FacesMessage.SEVERITY_ERROR,
        "Username already taken.",
        "Please choose another.");
      throw new ValidatorException(msg);
    }
  }

  private boolean usernameAlreadyExists(
        String u) {
    // Check against UserService / DB
    return List.of("admin","root")
               .contains(u);
  }
}

Use it in your Facelet

<h:inputText id="uname"
  value="#{regBean.username}">
  <f:validator
    validatorId="uniqueUsernameValidator"/>
</h:inputText>
<h:message for="uname"
            styleClass="error"/>
Converter vs Validator
ConverterTransforms the data type ("prd-0042" → "PRD-0042")
ValidatorChecks a rule; does not change the value
OrderConversion happens first, then validation
FailureBoth throw a FacesMessage to display to the user
18 / 20
Section 5

Web Security Using HTTP Filters

Validation protects your data. Security protects your application. HTTP Filters are the Jakarta EE mechanism for intercepting every request and enforcing access rules before any page or servlet can run.
19 / 20

Section 5.1HTTP Filters — The Security Gatekeeper

What an HTTP Filter is
A Jakarta EE Filter is a class annotated with @WebFilter that intercepts HTTP requests before they reach FacesServlet. It can inspect, modify, allow, or reject each request.

Think of it as a security checkpoint at the airport. Before you can board (reach the page), you must pass through the gate (the filter). The filter can:

  • Check whether the user is logged in (session check)
  • Redirect unauthenticated users to the login page
  • Log all incoming requests for auditing
  • Block access to admin pages for non-admin users
Why not put security logic in every Managed Bean?
If you check login status in 20 different beans, you have to maintain the check in 20 places. A filter is one class that guards all pages automatically — or all pages matching a pattern like /admin/*.
Request arrives
Browser: GET /admin/users.xhtml
Filter runs
AuthFilter: Is user logged in?
If NO
→ Redirect to login.xhtml
If YES
→ chain.doFilter() — allow request through
FacesServlet
Processes the request normally
Page rendered
Response returned to browser ✓
20 / 20

Knowledge Check — FinalSections 3, 4 & 5

Q1: After a user successfully submits an "Add Order" form, your action method returns "orders" (without ?faces-redirect=true). The user presses the browser's Refresh button. What is the risk?
Without a redirect, the browser's current URL still points to the POST action. Pressing Refresh re-submits the same POST request, calling your action method again and potentially adding a duplicate record. Always use ?faces-redirect=true after any data-modifying action (the Post-Redirect-Get pattern).
Q2: You want to prevent any unauthenticated user from accessing any page under /dashboard/. Where is the most maintainable place to implement this check?
A single HTTP Filter with the URL pattern "/dashboard/*" intercepts all requests matching that path before they reach any FacesServlet or bean. If the session check fails, the filter redirects to the login page. One class, one responsibility — far more maintainable than repeating the check in every bean.

Table of Contents