Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Handling HTML Forms With Apache Struts 2 and Microservices

DZone's Guide to

Handling HTML Forms With Apache Struts 2 and Microservices

A series of code snippets detailing how to handle web forms using Apache Struts 2 and how web forms are sent within applications.

· Web Dev Zone
Free Resource

Start coding today to experience the powerful engine that drives data application’s development, brought to you in partnership with Qlik.

In the spirit of my previous article, this one is a quick guide showing how to handle HTML forms with Apache Struts 2, and how a web form is sent within a web application can be delegated to an external entity with a decoupling model. If we skip the validation step which can be done on the client-side with JQuery Form Validator, then let's see choice by choice the skeleton of how to manage the creation and the display of customers.

No Decoupling, No Ajax

This is the traditional model in which a form is displayed in its own page with a redirection to another page once the data is saved to display the list of entities.

Image title

Customer.java

public class Customer {

 private Long id;
 private String firstName;
 private String lastName;
 private String email;

 public Long getId() {
  return id;
 }
 public void setId(Long id) {
  this.id = id;
 }
 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 getEmail() {
  return email;
 }
 public void setEmail(String email) {
  this.email = email;
 }

}

CustomerAction.java

public class CustomerAction extends ActionSupport {

  private Customer customer;

  public String saveCustomer() {
   // save the customer in your storage of choice
   return SUCCESS;
  }

  public Customer getCustomer() {
   return customer;
  }

  public void setCustomer(Customer customer) {
   this.customer = customer;
  }

  public List<Customer> getCustomers() {
   // get the list of customers in your storage of choice
   return customers;
  }

}

struts.xml

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 

Struts Configuration 2.0//EN" 

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

     <package name="customers" namespace="/customers" extends="struts-default">

       <default-action-ref name="index"></default-action-ref>

        <action name="index" class="CustomerAction">

          <result>/modules/customers/index.jsp</result>

        </action>

        <action name="create">

          <result>/modules/customers/create.jsp</result>

        </action>

       <action name="save" class="CustomerAction" method="saveCustomer">

          <result type="redirect">/customers/</result>

        </action>

    </package>

</struts>

create.jsp

<!DOCTYPE html>
<html>

  <head>
    <title>Create Customer</title>
  </head>

  <body>
    <form action="save" method="post">
         <fieldset>
            <label>First Name:</label>
            <input type="text"  name="customer.firstName" placeholder="Enter the first name" />
            <br />
            <label>Last Name:</label>
            <input type="text"  name="customer.lastName"  placeholder="Enter the last name" />
            <br />
            <label>Email:</label>
            <input type="email" name="customer.email"     placeholder="Enter the email address" />
            <br />
            <input type="submit" value="Create" />
         </fieldset>
      </form>
  </body>

</html>

index.jsp

<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html>
<html>
  <head>
    <title>Module Customers</title>
  </head>
  <body>
  <table>
     <thead>
       <tr>
         <th>First Name</th>
         <th>Last Name</th>
         <th>Email</th>
       </tr>
     </thead>
     <tbody>
        <s:iterator value="customers">
         <tr id="${id}">
          <td>${firstName}</td>
          <td>${lastName}</td>
          <td>${email}</td>
        </tr>
       </s:iterator>
     </tbody>
   </table>
  </body>
</html>

No Decoupling, Ajax

This model involves the use of the JSON plugin, and it must be added in your classpath.

Image title

pom.xml

<dependency>
    <groupId>org.apache.struts</groupId>
    <artifactId>struts2-json-plugin</artifactId>
    <version>2.3.16</version>
</dependency>

And your module needs now to extend the json-default package for the serialization of the action to be done and the Customer class needs not to implement the Serializable interface.

struts.xml

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 

Struts Configuration 2.0//EN" 

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <package name="customers" namespace="/customers" extends="json-default">

       <default-action-ref name="index"></default-action-ref>

        <action name="index">

          <result>/modules/customers/index.jsp</result>

        </action>

       <action name="save" class="CustomerAction" method="saveCustomer">

          <result type="json">
             <param name="excludeProperties">
               customers
             </param>
          </result>

        </action>

        <action name="list" class="CustomerAction">

          <result type="json">
             <param name="excludeProperties">
               customer
             </param>
          </result>

        </action>


    </package>

</struts>

And for Single Page Behaviors, we can manage the creation and the display of customers using JQuery and Handlebars in one page.

index.jsp

<!DOCTYPE html>
<html>
  <head>
    <title>Module Customers</title>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.6/handlebars.min.js"></script>
    <script>
     $(document).ready(function(){

        $.get("list", function(data) {
             render(data.customers);
        });

        $("input[type=submit]").click(function(){
          $.post("save",$("form").serialize()).done(function(data) {
             render([data.customer]);
          });
          return false;
       });

       var render = function(data) {
            var source   = $("#entity-template").html();
            var template = Handlebars.compile(source);
            var html = template(data);
            $("tbody").append(html);
       };

     });
    </script>
  </head>
  <body>
    <form>
         <fieldset>
            <label>First Name:</label>
            <input type="text"  name="customer.firstName" placeholder="Enter the first name" />
            <br />
            <label>Last Name:</label>
            <input type="text"  name="customer.lastName"  placeholder="Enter the last name" />
            <br />
            <label>Email:</label>
            <input type="email" name="customer.email"     placeholder="Enter the email address" />
            <br />
            <input type="submit" value="Create" />
         </fieldset>
      </form>
  <table>
     <thead>
       <tr>
         <th>First Name</th>
         <th>Last Name</th>
         <th>Email</th>
       </tr>
     </thead>
     <tbody>
       <script id="entity-template" type="text/x-handlebars-template">
         {{#each this}}
          <tr id="{{id}}">
           <td>{{firstName}}</td>
           <td>{{lastName}}</td>
           <td>{{email}}</td>
         </tr>
        {{/each}}
       </script>
     </tbody>
   </table>
  </body>
</html>

Decoupling, Ajax

This is my favorite one and the modern way since we need no Java, no POJO, no Action and CRUD can be automated with JavaScript since we know how to serialize an HTML form and how to communicate with an external entity like Node.js through REST and CORS using the HTTP verbs.

Image title

struts.xml (Optional)

<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD 

Struts Configuration 2.0//EN" 

"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <package name="customers" namespace="/customers" extends="struts-default">

       <default-action-ref name="index"></default-action-ref>

        <action name="index">

          <result>/modules/customers/index.jsp</result>

        </action>

    </package>

</struts>

index.jsp

<!DOCTYPE html>
<html>
  <head>
    <title>Module Customers</title>
    <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.6/handlebars.min.js"></script>
    <script>
     $(document).ready(function(){

        var url = "http://env-4347792.mircloud.host/customers";

        $.get(url, function(customers) {
            render(customers);
        });

        $("input[type=submit]").click(function(){
          $.post(url,$("form").serialize()).done(function(customer) {
            render([customer]);
          });
          return false;
         });

         var render = function(data) {
            var source   = $("#entity-template").html();
            var template = Handlebars.compile(source);
            var html = template(data);
            $("tbody").append(html);
         };

       });
    </script>
  </head>
  <body>
    <form>
         <fieldset>
            <label>First Name:</label>
            <input type="text"  name="firstName" placeholder="Enter the first name" />
            <br />
            <label>Last Name:</label>
            <input type="text"  name="lastName"  placeholder="Enter the last name" />
            <br />
            <label>Email:</label>
            <input type="email" name="email"     placeholder="Enter the email address" />
            <br />
            <input type="submit" value="Create" />
         </fieldset>
      </form>
  <table>
     <thead>
       <tr>
         <th>First Name</th>
         <th>Last Name</th>
         <th>Email</th>
       </tr>
     </thead>
     <tbody>
       <script id="entity-template" type="text/x-handlebars-template">
         {{#each this}}
          <tr id="{{id}}">
           <td>{{firstName}}</td>
           <td>{{lastName}}</td>
           <td>{{email}}</td>
         </tr>
        {{/each}}
       </script>
     </tbody>
   </table>
  </body>
</html>

customerService.js

function CustomerService() {

 this.mount = function(router) {

   router.get('/customers', function(request, response) {
    response.send(customers);
   });

   router.get('/customers/:id', function(request, response) {
    response.send(customer);
   });

   router.post('/customers', function(request, response) {
    var customer = request.body;
    response.send(customer);
   });

   router.put('/customers', function(request, response) {
    var customer = request.body;
    response.send(customer);
   });

   router.delete('/customers/:id', function(request, response) {
    response.send({status : 1, message : "entity deleted"});
   });

 };

};
module.exports = new CustomerService();

server.js (Dynamic Service Mounting)

var express = require("express");
var http = require('http');
var bodyparser = require("body-parser");
var cors = require('cors');
var fs = require('fs');

var app = express();
app.use(bodyparser.urlencoded({extended : true}));
app.use(bodyparser.json());
app.use(cors());

var router =  express.Router();
var dir = './services';
fs.readdir(dir, function(err, files) {
  files.forEach(function(file,index) {
     var service = require(dir+'/'+file);
     service.mount(router);
  });
});

app.use("/",router);

var server = http.createServer(app);

server.listen(9090,function(){
 console.log('Server started on port ' + server.address().address + ' '+server.address().port);
});

Image title

Since its 4.0 version, Express comes with the new Router which is like a mini application to create modular, mountable route handlers and provides us with the routing APIs like use, get, post, put, delete, param and route. The cors module provides an Express/Connect middleware to enable Cross Origin Resource Sharing with various options. The extended option of the bodyparser module which parses incoming request bodies in a middleware before your handlers, allows to choose between parsing the URL-encoded data with the querystring library (when false) or the qs library (when true). The "extended" syntax allows for rich objects and arrays to be encoded into the URL-encoded format with square brackets [], allowing for a JSON-like experience with URL-encoded. 

 <form>
   <fieldset>
     <label>First Name:</label>
     <input type="text"  name="firstName"/>
     <br />
     <label>Last Name:</label>
     <input type="text"  name="lastName"/>
     <br />
     <label>Email:</label>
     <input type="email" name="email"/>
     <br />
     <label>Contact First Name:</label>
     <input type="text"  name="contact[firstName]"/>
     <br />
     <label>Contact Last Name:</label>
     <input type="text"  name="contact[lastName]"/>
     <br />
     <input type="submit" value="Create" />
   </fieldset>
</form>
<table>
   <thead>
     <tr>
       <th>First Name</th>
       <th>Last Name</th>
       <th>Email</th>
       <th>Contact</th>
     </tr>
   </thead>
   <tbody>
     <script id="entity-template" type="text/x-handlebars-template">
        {{#each this}}
          <tr id="{{id}}">
           <td>{{firstName}}</td>
           <td>{{lastName}}</td>
           <td>{{email}}</td>
           <td>{{contact.firstName}} {{contact.lastName}}</td>
         </tr>
       {{/each}}
     </script>
   </tbody>
</table>

Demonstration

A working demo of the module is available here and made with one line of code after automation:

module.init("customer");

And we can add the generation and the printing of a PDF document with pdfmake.

module.js

var doc = function(customer) {
   return {content: customer.firstName+" "+customer.lastName};
};
module.init("customer");

Image title

Create data driven applications in Qlik’s free and easy to use coding environment, brought to you in partnership with Qlik.

Topics:
javascript ,struts ,web dev ,ajax

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}