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

Writing Clear Servlets: Small Controllers, Big Wins

Posted on May 21, 2007 By Luis Fernandez
\n

Big controllers feel clever until the third bug report in the same week.

\n\n\n\n

Small servlets are quiet and boring and they keep your weekend free.

\n\n\n\n

We have Rails noise on every feed and still a lot of us ship on Java Servlets every day. Tomcat 6 runs steady, Spring and Struts are everywhere, and plenty of in house apps still ride plain HttpServlet with a splash of JSTL. The trick that saves teams is not a new framework but a simple habit. Keep controllers tiny and focused. One servlet does one thing and speaks one URL with a clear name. doGet and doPost should read like a short checklist and then hand off to a service that does the heavy lifting. When a controller gets fat, bugs love it and performance gets weird at the edges.

\n\n\n\n

Write a servlet that only parses input, calls a service, and chooses a view. That is it. All validation sits close to the parse step with clear messages so you do not chase stack traces across layers. Use strong parameter names, default values that make sense, and early returns when input is bad. Move business rules to a plain class and keep it free from servlet APIs so you can test it with JUnit in a snap. Keep the request scope clean and explicit so your JSP does not guess. This way your controller stays under fifty lines and you can read it without scrolling.

\n\n\n\n
public class CreateOrderServlet extends HttpServlet {\n  private final OrderService service = new OrderService();\n\n  @Override\n  protected void doPost(HttpServletRequest req, HttpServletResponse resp)\n      throws IOException, ServletException {\n\n    String customerId = req.getParameter("customerId");\n    String sku = req.getParameter("sku");\n    int qty = parseInt(req.getParameter("qty"), 1);\n\n    if (isBlank(customerId) || isBlank(sku) || qty < 1) {\n      req.setAttribute("error", "Please send customerId, sku, and a positive qty");\n      forward(req, resp, "/WEB-INF/views/orderForm.jsp");\n      return;\n    }\n\n    try {\n      Order order = service.create(customerId, sku, qty);\n      req.setAttribute("order", order);\n      forward(req, resp, "/WEB-INF/views/orderOk.jsp");\n    } catch (OutOfStock e) {\n      req.setAttribute("error", e.getMessage());\n      forward(req, resp, "/WEB-INF/views/orderForm.jsp");\n    }\n  }\n\n  private void forward(HttpServletRequest req, HttpServletResponse resp, String view)\n      throws ServletException, IOException {\n    req.getRequestDispatcher(view).forward(req, resp);\n  }\n\n  private boolean isBlank(String s) { return s == null || s.trim().isEmpty(); }\n  private int parseInt(String n, int def) { try { return Integer.parseInt(n); } catch(Exception e){ return def; } }\n}\n\nclass OrderService {\n  Order create(String customerId, String sku, int qty) {\n    // domain work here, no servlet imports\n    return new Order(customerId, sku, qty);\n  }\n}
\n\n\n\n

Small servlets shine when things change. A new rule lands from sales and you touch the service not the controller. A new field shows up and you only tweak the top of doPost or doGet and the view. You can even plug the same service into Spring or a Quartz job later without ripping wires. Keep URLs human and stable and your team starts to guess the mapping before opening code. Name your servlet class to match the action and the JSP to match the outcome. These boring names do more for a team than any clever abstraction.

\n\n\n\n
<!-- web.xml mapping that reads like a sentence -->\n<servlet>\n  <servlet-name>createOrder</servlet-name>\n  <servlet-class>com.acme.web.CreateOrderServlet</servlet-class>\n</servlet>\n<servlet-mapping>\n  <servlet-name>createOrder</servlet-name>\n  <url-pattern>/orders/create</url-pattern>\n</servlet-mapping>\n\n<!-- keep cross cutting in filters, not in controllers -->\n<filter>\n  <filter-name>charset</filter-name>\n  <filter-class>com.acme.web.EncodingFilter</filter-class>\n</filter>\n<filter-mapping>\n  <filter-name>charset</filter-name>\n  <url-pattern>/*</url-pattern>\n</filter-mapping>
\n\n\n\n

Filters carry cross cutting stuff so controllers stay tidy. Put character encoding in a single filter, same for auth checks and simple logging. Keep JSPs clean with JSTL and avoid secret work in scriptlets, since hidden logic always comes back to bite. For test flow, write thin tests that run the service by itself and a few Servlet API fakes to check wiring and view names. You can stub request params with a tiny helper and assert that the right JSP loaded and the right request attributes exist. A fast feedback loop beats any fancy tool right now and will probably still beat it next quarter.

\n\n\n\n
// a tiny test around the service\npublic class OrderServiceTest {\n  @org.junit.Test\n  public void createsOrder() {\n    OrderService s = new OrderService();\n    Order o = s.create("c123", "ABC", 2);\n    org.junit.Assert.assertEquals(2, o.getQty());\n  }\n}\n\n// a super small fake request for controller checks\nclass FakeRequest extends javax.servlet.http.HttpServletRequestWrapper {\n  private final java.util.Map<String,String> params = new java.util.HashMap<>();\n  private final java.util.Map<String,Object> attrs = new java.util.HashMap<>();\n  public FakeRequest() { super(new org.apache.catalina.connector.RequestFacade(new org.apache.catalina.connector.Request())); }\n  public void setParam(String k, String v) { params.put(k, v); }\n  @Override public String getParameter(String k) { return params.get(k); }\n  @Override public void setAttribute(String k, Object v) { attrs.put(k, v); }\n  @Override public Object getAttribute(String k) { return attrs.get(k); }\n}
\n\n\n\n

Small controllers age well and leave you more time to sip coffee than to chase ghosts.

\n
Content Management Systems Marketing Technologies 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