Skip to main content

skip to main content

developerWorks  >  Java technology  >

Build better Web sites using the Translator pattern

Using the Translator pattern to convert input Strings

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


New site feature

Check out our new article design and features. Tell us what you think.


Rate this page

Help us improve this content


Level: Intermediate

Donald BellIBM

21 Feb 2001

This article gives an introduction to the Translator pattern and describes how to use the Translator pattern in a JSP technology and servlet environment. After reading this article, you will be able to successfully implement the pattern using the code examples provided.

When building Web applications using JSP files and servlets, the application's interface is most likely going to be HTML. The HTML that is rendered by the browser is nothing but a big string. Business objects that make up an application have a few attributes that are strings, but the rest are dates, numbers, and even other business objects. When building Web applications, there is a big problem of how to get the information contained within business objects translated into HTML that the browser can understand. Almost every application also collects information in HTML and that information is sent to the server as a string. So now there is also the problem of how to get the submitted information translated into values that the business objects understand.

The Translator pattern solves these problems by having a Translator object that works with a JSP file, a servlet, and the business objects. The Translator object glues the three different objects together, which allows each object to specialize on a given task. The Translator object is able to be this glue because it encapsulates all translating logic needing to be done. The JSP file specializes in displaying information by communicating with the Translator object. This makes the JSP file "clean," meaning there is very little Java code inside the JSP file. The servlet specializes in persistence of the business objects and the navigation flow between screens. With the servlet specializing in these tasks, the JSP file needs less Java code, which makes the JSP file even cleaner.

More details please

As mentioned earlier there are three basic parts in the Translator pattern. There is a specialized Translator object, a servlet, and a JSP file.

The JSP file's sole responsibility is to be a user interface class. This is possible because the JSP file gets preformatted string values from a Translator object (discussed later). The JSP file should have as little Java code in it as possible, because the Web page designers normally responsible for the development of JSP files typically have little or no knowledge about the Java programming language. Having the least amount of Java code as possible embedded in the JSP file makes the JSP file look much more like a plain HTML page. A plain HTML page is far easier to modify than a JSP file that has Java code intermingled throughout it.

The Translator object is a special class that is similar to a model class in the MVC pattern. The Translator object glues the business objects to the display fields in the JSP file. A Web page designer will call a getter method on the Translator object to display dynamic information in the JSP file. The Translator object will return a string that is preformatted, so that all the Web page designer needs to do is send it to the output stream. The Translator object is able to provide this information because it stores all the values that need to be displayed in internal variables. These variables are set by the methods syncGuiToModel() and processForm(). These methods specialize in the synchronization of information between the screen and the Translator. The Java developers in the group build the Translator object.

Finally, the servlet specializes in the navigation flow and persistence of business objects. When a servlet receives a submitted form, it will get an instance of the Translator object and delegate the parsing of the form to the Translator object using the processForm() method. After the form has been parsed, the servlet will have the Translator object synchronize the business object's values to the values submitted in the form using the syncModelToGui() method. After successfully translating the submitted values and setting them on the business object, the servlet will persist the business object and redirect the Web site visitor to a confirmation page.



Back to top


Bring me my microscope

Let's look closely at the three parts of the Translator pattern: the JSP file, the servlet, and the Translator object. (The examples in this article are built around the Servlet 2.1 spec and JSP 1.0 spec. The examples were built and tested with WebSphere 3.02 and WebSphere 3.5.)

The JSP file
A typical JSP entry form using the Translator pattern looks like this:


<%@ page extends="com.ibm.igs.ispkcm.translator.JspBase"
         import="com.ibm.developerworks.translatorpattern.LoanTranslator, 
                 Java.util.Hashtable"%>

<HTML>
<%
   LoanTranslator ltLoan = LoanTranslator.getInstance(request);
   Hashtable htErrors = ltLoan.getErrors();
%>
<HEAD>
</HEAD>
<BODY>
<%= displayErrors(htErrors) %>
<action="/servlet/com.ibm.developerworks.translatorpattern.LoanRegistrationServlet">
   <%= hightLightErrors(ltLoan.BORROWER_LAST_NAME, htErrors) %>Borrower Last Name:
   <INPUT name="<%= ltLoan.BORROWER_LAST_NAME %>" 
          value="<%= ltLoan.getBorrowerLastName() %>">
</FORM>
</BODY>
</HTML>

In the Translator pattern, all forms are JSP files and not HTML so that the entry fields' values can be dynamic. This is important because our Web site visitors are real people who make entry mistakes. The only thing worse than having a Web site tell you it is not accepting your input is for it tell you that it is not accepting your input in one field and then make you re-key the other 20 fields because some programmer was too busy checking his stock quotes to make his entry forms user-friendly. In the Translator pattern each entry form's input fields_ values are dynamically set, so if the Web site visitor needs to be shown the entry page again because they made a mistake, they see their mistake highlighted but they also see their other entries preserved.

Remember that a Web site designer who knows little or no Java code usually writes and maintains the JSP file. Because of this fact, we want to keep as little Java code as possible in the JSP file. But what we described in the previous paragraph sounds like a lot of Java programming. The example JSP file has a little bit of Java code, but the majority of that is in an Expression (<%= x %>). We are able to have this large amount of functionality because the JSP file inherits this Java code from its super class of com.ibm.igs.ispkcm.translator.JspBase and by pushing a large chunk into the Translator object.

The first thing to notice in this JSP file is that it has a page directive tag. This is because it needs to inherit from a super class and it needs to import two things. The JSP file inherits from the super class of com.ibm.igs.ispkcm.translator.JspBase because the JspBase includes some nice utility functions that allow the JSP file to have a smaller amount of code. The main utility functions the JSP file uses are displayErrors() and highLightErrors(). The page directive imports LoanTranslator and Hashtable because these classes are referenced in the JSP file and it is cleaner to import the classes so the names do not have to be fully qualified later on in the JSP code. This code shows an example of the import statement:


<%@ page extends="com.ibm.igs.ispkcm.translator.JspBase"
      import="com.ibm.developerworks.translatorpattern.LoanTranslator, 
              Java.util.Hashtable"%>

The JSP file's first real Java code gets an instance of the Translator object and then gets the Hashtable of errors that belong to that instance of the Translator object. Because a JSP file (after it is compiled) is a servlet, it is a stateless service object. Translator objects will maintain any state information that needs to be maintained between the different round trips to a particular JSP file or servlet. The state information should be limited to the values entered by the Web site visitor and any processing errors that need to be displayed to the Web site visitor. Because an instance of the Translator object is associated with a specific Web site visitor, the JSP file calls the Translator object's getInstance(HttpServletRequest) method. Because the method is passed an HttpServletRequest object, the method will be able to retrieve the Translator instance associated with the Web site visitor's HttpSession.


<%
   LoanTranslator ltLoan = LoanTranslator.getInstance(request);
   Hashtable htErrors = ltLoan.getErrors();
%>

In the example JSP file, all processing errors are displayed to the Web site visitor at the top of the form. The HTML that displays these error messages is written out when using the <%= displayErrors(htErrors) %> expression. The displayErrors() method is inherited from the JSP file's super class JspBase. By having displayErrors(), all logic for displaying error messages is centrally located and easier to maintain.

For each entry field on the form, this code template applies:


<%= hightLightErrors(ltLoan.BORROWER_LAST_NAME, htErrors) %>Borrower Last Name:
<INPUT name="<%= ltLoan.BORROWER_LAST_NAME %>" 
       value="<%= ltLoan.getBorrowerLastName() %>">

The highLightErrors() is inherited from the JSP file's super class JspBase. This method highlights an entry field's label if the entry field has an error associated with it. The method accepts two parameters: a String and a Hashtable. The Hashtable is the Hashtable of errors that is retrieved from the instance of the Translator. String is the name of the entry field that is being checked to see if it has any errors. If there are errors that correspond with the field, then highLightErrors() returns HTML that highlights the entry field's label.

An important thing to notice in this code template is that the INPUT tag's name attribute is being set by an expression that is using the constant BORROWER_LAST_NAME on the LoanTranslator object. By using a constant when referring to this field name in the JSP file, Translator object and servlet debugging becomes easier. Using a constant is easier because whenever developers fat-finger the name of the field, they get a compile error instead of a run-time error. Compile errors are a lot easier to find because the compiler will complain immediately, whereas run-time errors have to be found during debugging and testing.

The last and most important thing to notice about this code template is that the INPUT tag's value attribute is being set by an expression that is using a Translator's getter method. The value="<%= ltLoan.getBorrowerLastName() %> fragment is one of the most important parts of the pattern because it is what defaults the entry field's value back to the value that the Web site visitor originally entered. By defaulting the value to what the Web site visitor entered originally, the visitor can see what he entered originally and fix his mistake easily. This saves time for the Web site visitor, giving him a better user experience.

The servlet
A typical abbreviated servlet looks something like this:


public void doPost(HttpServletRequest request, HttpServletResponse response)
{
  // Default the user back to the entry page.
  String sRedirect = LOAN_JSP;

  // Get the correct instance of the Translator
  LoanTranslator ltTrans = LoanTranslator.getInstance(request);

  // Now that we have an instance of the Translator
  ltTrans.processForm(request);

  // Logic for getting correct version of Loan
  Loan lnTheLoan = null;
  if (ltTrans.isNew () == true)
  {
     // Create new Loan
  }
  else
  {
     // Get existing loan
  }
  // Sync the Loan object values to values in the submitted form.
  ltTrans.syncModelToGui(lnTheLoan);
  // Make sure no errors occurred
  if (ltTrans.hasErrors() == false)
  {
    // Commit the Loan info and then set the redirect to the correct
    // following page
    sRedirect = LOAN_CONFIRMATION;
  }
 
  // Redirect the web site visitor to the correct page.
  try
  {
    response.sendRedirect(sRedirect);
  }
  catch (Exception e)
  {
    // Error logic
  }
}

The servlet's main purpose is to control navigation flow between JSP files and to persist the business objects. The servlet's code is very simple.

  • The first thing the servlet does is get the correct instance of the Translator object from the HttpServletRequest.
  • The servlet then delegates the processing of the submitted HTML form to the Translator object using the processForm() method.
  • After the Translator has parsed the form, the servlet will determine if a business object (a Loan in the example) needs to be created or retrieved from secondary storage.
  • After the servlet has an instance of the business object, the servlet calls the Translator object's syncModelToGui(). The syncModelToGui() will translate all the values submitted by the Web site visitor into values that the business object understands.
  • After the Translator has finished synchronizing the values, the servlet will check to see if Translator logged any errors while trying to translate the values the Web site visitor entered.
    • If no errors were logged, then the servlet redirects the Web site visitor to the confirmation page (usually another JSP file that can also display the values from the Translator).
    • If errors were logged, then the servlet redirects the Web site visitor to the entry form where he can correct his mistakes.

The Translator object
Because the Translator object is the glue between the JSP file, servlet, and business objects, it needs to be stateful and it needs to be stateful across HTTP requests (or threads). To meet this criteria, the Translator objects need to act like a pseudo singleton. There are five main sections to a Translator class and a previously unmentioned library of Object Translator classes.

  • The getInstance()
  • The syncGuiToModel()
  • The getter methods
  • The processForm()
  • The syncModelToGui()
  • A library of Object Translators

To get an instance of a Translator object, all callers call the static method getInstance(HttpServletRequest). The getInstance() method will determine if a new Translator instance should be returned or if an instance should be re-used out of the HttpSession. The method does this by looking at a custom parameter called action. This parameter is passed along with the HTTP request (for example, http://localhost/registerLoan.jsp?action=new). This sample code fragment shows what the getInstance() looks like:


public static LoanRegistrationTranslator getInstance(HttpServletRequest request)
{
   // Declare the Return Value variable.
   LoanRegistrationTranslator lrtRV = null;
	
   // The HttpSession is always needed so get it out here.
   HttpSession session = request.getSession();

   // Retrieve the action parameter off of the request object.
   String sAction = parseString(request, ACTION);

   // Determine what kind of Translator we need to return back.
   if (ACTION_PROCESS.equals(sAction) == true)
   {
      // Since the action parameter was set to process that means
      // we are processing an existing Translator so grab the translator
      // out of the HttpSession.
      lrtRV = (LoanRegistrationTranslator)
         session.getValue(HttpSessionValueKeys.LOAN_REGISTRATION_TRANSLATOR);
   }
   else if (sAction == null || "".equals(sAction) == true || 
            ACTION_NEW.equals(sAction) == true)
   {
      // Since either the action was not passed at all or it was not set
      // the default action is to create a new.  The other possibility is
      // that the action was "new"
      lrtRV = new LoanRegistrationTranslator();
      session.putValue(HttpSessionValueKeys.LOAN_REGISTRATION_TRANSLATOR, lrtRV);
   }
   else
   {
      // Since the action did not meet any of the previous checks that
      // means the action value is an ID for an existing Loan that is in
      // secondary storage.  So this time create a LoanRegistrationTranslator
      // whose values are preset to the values in the saved loan.
      lrtRV = new LoanRegistrationTranslator(sAction);
      session.putValue(HttpSessionValueKeys.LOAN_REGISTRATION_TRANSLATOR, lrtRV);
   }
   // Return the instance of the Translator object.
   return lrtRV;
}

The Translator has a getter method for each of the business object values to be displayed in a JSP file. These getter methods are called by the JSP file. There is one getter method for each of the Business Object's attributes that has to be displayed in a JSP file. The getter method always returns a String. The returned String's value is pre-formatted to be directly displayed in a JSP file. The reason for pre-formatting the String is so that there is as little Java code in a JSP file as possible. The formatting of these values instead should be handled in the syncGuiToModel().

The Translator uses processForm() to clear the previously displayed errors and to parse the information out of a submitted form. When a servlet receives a submitted form, it delegates the processing of the form to the Translator. During this delegation, the Translator parses the values of the submitted form and stores the values in String variables. These holding variables are then later translated into the business object's values using syncModelToGui(), which is called by the servlet.

The syncGuiToModel() and syncModelToGui() are similar methods, but as their names imply, one synchronizes values in one direction and the other synchronizes the values in the reverse direction. The syncGuiToModel() takes the values out of the business object's attributes and pre-formats each attribute's value using an Object Translator. The Object Translator pre-formats the value to the value that will be displayed on the screen. After that, it sets the Translator's corresponding String variable to that value. The syncModelToGui() does the same thing, but in the reverse direction. A fragment from a typical syncGuiToModel() is:


DoubleTranslator dtDouble = new DoubleTranslator();
String sTemp = dtDouble.translate(loan.getInterestRate());
setInterestRate(sTemp);

To get maximum reuse out of the translation code and to provide a common translation throughout the site, both the syncGuiToModel() and syncModelToGui() use Object Translator classes. The Object Translators work so closely with the Translator object that they could almost be considered rules of the Translator object. An Object Translator class is a simple class whose sole purposes are to translate a data type to a formatted string and to translate a formatted string into its data type. A very simple Object Translator class looks like this sample code:


public class DoubleTranslator extends ObjectTranslator
{
   public String translate(double doubleValue)
   {
      return Double.toString(doubleValue);
   }
   public double translate(String stringToBeTranslated) throws Exception
   {
      double dRV = 0.0;

      try
      {
         Double dbDouble = Double.valueOf(stringToBeTranslated);
         dRV = dbDouble.doubleValue();
      }
      catch(Exception e)
      {
         Exception eTranslation = 
           new Exception("Please enter a numeric value like 1.0 or 1.25");
         throw eTranslation;
      }

      return dRV;
   }
}

When this sample Object Translator translates the double into a preformatted String, it merely calls Double.toString(). This method could be made more powerful, for example, by adding a few lines of code so that when it formats a large double like 1000.25 it pre-formats it to display 1,000.25.

An important thing to note about DoubleTranslator.translate(String) is that it throws a nicely worded exception instead of the standard Java.lang.NumberFormatException. The JSP file will display this exception's message directly to the Web site visitor, so it is important that the exceptions thrown by this method are easily understandable by an average Web site visitor.



Back to top


Conclusion

The benefits of using the Translator pattern framework range from lower site costs to higher customer satisfaction. The site becomes cheaper to build because the different components are broken out into specialized tasks. These specialized tasks can then be assigned to productive teams, like an HTML team or a Java programmer team. Because the Translator pattern has Object Translators that are reused throughout the site, all formatting and parsing is done in the same manner. Because everything is done in the same manner, Web site visitors are able to consistently see information throughout your site. This leads to higher customer satisfaction.




Back to top


Download

DescriptionNameSizeDownload method
Sample codej-jsppatt.zip46 KBHTTP
Information about download methods


Resources



About the author

Donald Bell is an IT Specialist in IBM Global Services, where he works with IBM's customers to design and develop J2EE based software solutions.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top