Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Migrating Struts to JSF: A Field Note

Posted on May 7, 2006 By Luis Fernandez

Why move from Struts to JSF at all

Struts has been the workhorse for years. Action classes, forwards, Tiles, and a pile of taglibs got many of us to production. It still ships on every team server I visit.

JSF is arriving with vendor love and a component story that feels closer to how users click. Backing beans, validators, converters, and view state can clean up a lot of glue code we wrote by hand.

What did we expect to gain

Fewer custom tags. Fewer ActionForm beans. Better reuse through UI components. A view story that plays nice with tools. And a path into Ajax without bolting scripts onto every page.

The short version. Struts to JSF buys us a cleaner UI layer and a simpler way to wire UI to services.

Start small and run both frameworks

Do not flip the whole app at once. Run Struts and JSF side by side. Keep the Struts controller for what already works and route new pages to the Faces servlet.

How do we wire that in web xml

<web-app>
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <servlet>
    <servlet-name>Faces Servlet</servlet-name>
    <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.jsf</url-pattern>
  </servlet-mapping>
</web-app>

Old flows keep using .do. New screens ride .jsf. No big bang. No extra pain.

Map Action to backing bean

In Struts we post to an Action, set stuff on a request, and forward. In JSF we bind to a managed bean and return an outcome string that picks a view.

What does that look like

// Struts action
public class LoginAction extends Action {
  public ActionForward execute(ActionMapping mapping, ActionForm form,
                               HttpServletRequest req, HttpServletResponse res) {
    LoginForm f = (LoginForm) form;
    boolean ok = authService.login(f.getUser(), f.getPass());
    if (ok) {
      req.getSession().setAttribute("user", f.getUser());
      return mapping.findForward("home");
    }
    req.setAttribute("error", "Bad login");
    return mapping.getInputForward();
  }
}
// JSF managed bean
public class LoginBean {
  private String user;
  private String pass;
  private AuthService authService;

  public String doLogin() {
    boolean ok = authService.login(user, pass);
    if (ok) {
      FacesContext.getCurrentInstance()
        .getExternalContext().getSessionMap().put("user", user);
      return "home";
    }
    FacesContext.getCurrentInstance().addMessage(null,
      new FacesMessage("Bad login"));
    return null; // stay on page
  }

  // getters and setters
}
<faces-config>
  <managed-bean>
    <managed-bean-name>login</managed-bean-name>
    <managed-bean-class>com.app.ui.LoginBean</managed-bean-class>
    <managed-bean-scope>request</managed-bean-scope>
  </managed-bean>

  <navigation-rule>
    <from-view-id>/login.jsp</from-view-id>
    <navigation-case>
      <from-outcome>home</from-outcome>
      <to-view-id>/home.jsp</to-view-id>
      <redirect/>
    </navigation-case>
  </navigation-rule>
</faces-config>

Swap tags screen by screen

Struts tags move to h and f tags. Keep JSP for now if that is what you ship. Facelets is tempting and already more pleasant than Tiles. More on that in a bit.

How do tags line up

<!-- Struts -->
<html:form action="/login.do">
  <html:text property="user"/>
  <html:password property="pass"/>
  <html:submit value="Login"/>
</html:form>

<!-- JSF -->
<h:form>
  <h:inputText value="#{login.user}"/>
  <h:inputSecret value="#{login.pass}"/>
  <h:commandButton value="Login" action="#{login.doLogin}"/>
  <h:messages />
</h:form>

Validation and conversion

Struts Validator has those xml rules. In JSF you wire Validator and Converter classes or use built ins.

What is the simplest example

public class EmailValidator implements javax.faces.validator.Validator {
  public void validate(FacesContext ctx, UIComponent comp, Object value)
      throws ValidatorException {
    String email = String.valueOf(value);
    if (!email.matches(".+@.+\\..+")) {
      throw new ValidatorException(
        new FacesMessage("Please enter a valid email"));
    }
  }
}
<h:inputText value="#{profile.email}">
  <f:validator validatorId="emailValidator"/>
</h:inputText>

Register the validator id in faces config and you are done. The page stays clean and the error shows up via h:message.

Tiles to Facelets

Tiles got us common layouts with definitions. Facelets does the same with less ceremony and much better templating.

How do we express a layout

<!-- template.xhtml -->
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets">
  <h:panelGroup layout="block"><ui:insert name="header"/></h:panelGroup>
  <h:panelGroup layout="block"><ui:insert name="content"/></h:panelGroup>
  <h:panelGroup layout="block"><ui:insert name="footer"/></h:panelGroup>
</ui:composition>

<!-- page.xhtml -->
<ui:composition template="/WEB-INF/templates/template.xhtml"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:ui="http://java.sun.com/jsf/facelets">
  <ui:define name="content">
    <h:outputText value="Hello Facelets"/>
  </ui:define>
</ui:composition>

If you are not ready for Facelets yet, you can still use JSP includes and keep moving. Do not block the migration waiting for the perfect view engine.

EL, JSTL, and when things run

JSTL runs before JSF. That surprises folks in the first week. Use the rendered attribute and keep JSTL for static bits.

What mistake bit us

<%-- This fails because c:if runs too early --%>
<c:if test="#{bean.show}">
  <h:inputText value="#{bean.name}"/>
</c:if>

<%-- Do this instead --%>
<h:inputText value="#{bean.name}" rendered="#{bean.show}"/>

State and redirects

JSF keeps a view state across requests. That reduces boilerplate but grows the page size. On big forms set state to server.

How do we flip the switch

<context-param>
  <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
  <param-value>server</param-value>
</context-param>

To avoid resubmits, return an outcome with redirect in faces config as shown above. It behaves like the Struts post redirect approach we used for years.

Messages and i18n

JSF can load a bundle once and make it available as bundle on every page. That cuts down on fussy includes.

What is the quick setup

<application>
  <message-bundle>com.app.i18n.Messages</message-bundle>
</application>
<h:outputText value="#{bundle.welcome}"/>

Libs and servers that worked

We had good results with MyFaces 1.1 on Tomcat 5.5 and with the Sun JSF RI as well. Tomahawk gives handy components like file upload and calendar.

What jars did we actually ship

faces api, faces impl or myfaces core, tomahawk if you want extra components, commons collections and friends. Keep your classpath tidy or you will chase class not found errors all night.

Ajax on top

The Ajax wave is real. You can sprinkle Prototype or Scriptaculous on JSF pages or try Ajax4jsf to bind events to server actions with less JavaScript.

Where is the safe line

Keep rules simple. Submit small areas. Avoid full page rerenders. If the form is huge, regular posts are still faster and less fragile.

Testing the move

For Struts we used StrutsTestCase. For JSF today we lean on Selenium or HtmlUnit to click and assert. Backing beans should be thin and push logic into services that you can unit test with JUnit.

What do we watch in CI

Page weight with client state. Bundle keys missing. Navigation cases that trap users on the same page with no message. These show up early if you put smoke tests in the build.

Migration flow that keeps sanity

Peel and replace one slice of UI at a time. Keep URLs stable. Keep Struts filters and security in place while JSF grows in a new folder like /faces or with the .jsf suffix.

Which screens should go first

Pick forms with repeatable controls and validation pain. Search forms, wizards, and admin pages usually pay back fast with JSF components.

Pitfalls we hit

Action classes that hide real business rules. Move that code into services before you migrate or you will just rename the mess. EL conflicts with JSP taglibs if the same prefix is reused. Keep namespaces clear.

What about SEO and bookmarks

If URLs matter, keep the same path and return redirect outcomes so the address bar shows the target page. Avoid view ids with random query strings. JSF can be friendly to crawlers if you keep links as real anchors and not only buttons.

Quick checklist to ship

Routing Struts and JSF side by side with clean mappings.

Beans Request scope by default. Session only for user stuff.

State Server state for big forms. Redirect after post.

Tags Replace Struts tags with h and f tags screen by screen.

Templates Move Tiles to Facelets when the team is ready.

Validation Use JSF validators and converters, keep messages in a bundle.

Testing Selenium for flows, JUnit for services.

Where does this leave Struts

Still fine for straight forms. But if you need components, richer widgets, and less glue code, JSF is a solid next step. The tool support that Sun and IDEs are putting out makes it feel like the center of gravity is moving.

Compact take away

Migrating Struts to JSF works best as a steady walk, not a sprint. Run both, swap tags, move logic into services, manage state on the server, and test the flows that make money.

Do that and you get cleaner pages, fewer custom tags, and a UI stack that fits the way we build web apps now. The path is clear and the lessons will age well.

Digital Transformation Software Architecture Software Engineering

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes