Course Content‎ > ‎

Section 13: Improving your Applications


Is Java the only "Javaish" answer?

It might seem a little strange to question the use of Java in the very last section of the course.  The benefits of Java are well-known, for example:
  • Platform/OS/Hardware Portability
  • Powerful existing APIs / Huge community
  • Object Oriented
  • Ease of development
  • ..many others..
However, there are a couple of disadvantages for developing web applications
  • Java is a general programming language not specifically aimed at web application development
  • Some expected simple tasks can take a considerable amount of code
  • Static typing can make coding somewhat more cumbersome
Aside Note: Static typing means that type checking is performed during compile-time rather than run-time.  Java is statically typed where the type of associated with the variable (e.g. String s;  explicitly forces s to always be a String type).  Dynamic typing, on the other hand, is where the value has a type but the variable does not.  For example:  def s = "Some String" creates a dynamically-typed variable whose value happens to be a String.   Static typed code tends to be a little more restrictive but avoids many of the runtime errors that can occur with dynamic typing.  For example:

def s = "Some String";   // A string
System.out.println(s.substring(0,2));  
s = 2020;   // A number
System.out.println(s.substring(0,2));  

Let us take an example of some Java code.  

A Java Coding Solution

In this example we are going to attempt to sort a collection of JavaBeans based upon the properties of those beans.  We will look at a Vector of all of our 'Customer' objects and we wish to sort the Vector based upon the surnames of each of the objects.  Let us look at some code:

1) Consider a 'Customer' bean similar to that we have encountered before.  
/** * Customer JavaBean */ public class Customer { private String ID; private String surname; private String firstname; private String email; // Constructor public Customer(long ID, String surname, String firstname, String email) { this.ID = ID; this.surname = surname; this.firstname = firstname; this.email = email; } // Now the get methods public String getID() { return ID; } public String getName() { return firstname + " " + surname; } public String getSurname() { return surname; } public String getFirstname() { return firstname; } public String getEmail() { return email; } // And some set methods public void setID(String value) { ID = value; } public void setSurname(String value) { surname = value; } public void setFirstname(String value) { firstname = value; } public void setEmail(String value) { email = value; } }


2) Now consider in a Java class we populate a Vector<Customer> of all of our Customer entities from a file or database.  We want to print out these records, but we want to 'sort' the vector so that it alphabetically lists Customers by surname in an ascending order.  Let us also assume that we are not going to use a database to do this for us - there are plenty of scenarios in code where we want to sort collections of objects not coming directly from a database.  In Java, we need to modify the Javabean class to implement Comparator, such as the following:
import java.util.Comparator;

public class Customer implements Comparator<Customer>, Comparable<Customer>{
private long ID;
private String surname;
private String firstname;
private String email;
// Constructor
public Customer(long ID, String surname, String firstname, String email) {
           this.ID = ID;
           this.surname = surname;
           this.firstname = firstname;
           this.email = email;
}
// Now the get methods
public long getID() { return ID; }
public String getName() { return firstname + " " + surname; }
public String getSurname() {   return surname; }
public String getFirstname() { return firstname;   }
public String getEmail() {    return email;   }
// And some set methods
public void setID(long value)  { ID = value; }
public void setSurname(String value)  { surname = value; }
public void setFirstname(String value)  { firstname = value; }
public void setEmail(String value)  { email = value; }
// Overriding the compareTo method
public int compareTo(Customer c1) {
  return (this.surname).compareTo(c1.surname);
}

// Overriding the compare method to sort the name 
public int compare(Customer c, Customer c1){
return (c.surname).compareTo(c1.surname);
}
}

3) Finally our code to populate the Vector and use this sorting facility we have written:
import java.util.Collections;
import java.util.Vector;

public class Compare {

public static void main(String[] args) {
Vector<Customer> dVector = new Vector<Customer>();
        dVector = setupCustomerVector(dVector);
        
        System.out.println("Before sorting: ");
        for (Customer c:dVector) {
        System.out.println("Customer: " + c.getSurname() + ", " + c.getFirstname());
        }
        // So now we want to sort this Vector by surname and firstname alphabetically
        Collections.sort(dVector);
        
        System.out.println("After sorting: ");
        for (Customer c:dVector) {
        System.out.println("Customer: " + c.getSurname() + ", " + c.getFirstname());
        }
        
}
private static Vector<Customer> setupCustomerVector(Vector<Customer> dVector) {
dVector.addElement(new Customer(1, "Molloy", "David", "david@david.com"));
        dVector.addElement(new Customer(2, "Molloy", "Derek", "derek@derek.com"));
        dVector.addElement(new Customer(3, "Smith", "Mary", "smithm@email.com"));
        dVector.addElement(new Customer(4, "Smith", "John", "smithj@email.com"));
        dVector.addElement(new Customer(5, "Jones", "Joe", "joejoes@ireland.com"));
        dVector.addElement(new Customer(6, "Peters", "Mark", "markpeters@google.com"));
        dVector.addElement(new Customer(7, "Jacobs", "John", "jacobsj@email.com"));
        return dVector;
    }    
}
Source: Compare.java

4) This will result in the following correct output:
Before sorting: 
Customer: Molloy, David
Customer: Molloy, Derek
Customer: Smith, Mary
Customer: Smith, John
Customer: Jones, Joe
Customer: Peters, Mark
Customer: Jacobs, John
After sorting: 
Customer: Jacobs, John
Customer: Jones, Joe
Customer: Molloy, David
Customer: Molloy, Derek
Customer: Peters, Mark
Customer: Smith, Mary
Customer: Smith, John
We have met the requirements for sorting our collection of Customer objects and the code works - however, from a coding perspective it could hardly be called 'graceful'.  It is quite cumbersome to write and takes quite some programming time and lines of code. 

  

An Alternative Programming Language Solution

Take a look at the following code.  Firstly, a new bean:

public class Customer {
def id
def firstname
def surname
def email
}

Secondly, the code to generate the same output:


class CompareGroovy {

public static main(args) {
def coll = createCollection()
println "Before sorting:"
coll.each { println "Customer: " + it.surname + ", " + it.firstname }
println "After sorting:"
coll = coll.sort { it.surname }
coll.each { println "Customer: " + it.surname + ", " + it.firstname }
}

private static ArrayList createCollection() {
ArrayList coll = new ArrayList()
coll.add(new Customer(id:1, surname:"Molloy", firstname:"David", email:"david@david.com"))
coll.add(new Customer(id:2, surname:"Molloy", firstname:"Derek", email:"derek@derek.com"))
coll.add(new Customer(id:3, surname:"Smith", firstname:"Mary", email:"smithm@email.com"))
coll.add(new Customer(id:4, surname:"Smith", firstname:"John", email:"smithj@email.com"))
coll.add(new Customer(id:5, surname:"Jones", firstname:"Joe", email:"joejoes@ireland.com"))
coll.add(new Customer(id:6, surname:"Peters", firstname:"Mark", email:"markpeters@google.com"))
coll.add(new Customer(id:7, surname:"Jacobs", firstname:"John", email:"jacobsj@email.com"))
coll
}
}


The Java version of the same program, ignoring linespacing and comments, had a total of 55 lines of code (28 in bean, 27 in Compare.java).  This code has a total of 27 lines of code, demonstrating a reduction of 50% in the number of lines required.  In fact, the actual process of performing the sort was 1 line of code, compared to 9 lines of code required for Java.

But this code isn't Java and it certainly won't compile!   Correct - welcome to Groovy.

Groovy

Groovy is a lightweight, dynamic, object-oriented language that runs in the Java Virtual Machine (JVM).  It has been derived from a number of other languages including Ruby and Python but preserves much of the syntax we are used to as Java programmers.  It compiles to Java bytecode and simply extends the existing Java API and libraries.
For development in Java, all you need is an additional Groovy JAR file and you're up and running!  Put simply, it's Java with some bonus content!

So let's take a look at our advantages and disadvantages of Java above and comment on them in relation to Groovy:

Advantages:
  • Platform/OS/Hardware Portability: Groovy still works on the JVM so this feature remains
  • Powerful existing APIs / Huge community: Builds on top of the Java community and includes a new Groovy community
  • Object Oriented: fully object-oriented
  • Ease of development: even easier than Java due to a range of new convenience methods
  • ..many others.. : virtually all of which apply to Groovy also
Disadvantages:
  • Java is a general programming language not specifically aimed at web application development: Groovy is not aimed entirely at web application development either.  However, a large number of the convenience methods, particularly dealing with Strings and Collections are hugely beneficial in building web applications
  • Some expected simple tasks can take a considerable amount of code: far less code required for Groovy (as demonstrated)
  • Static typing can make coding somewhat more cumbersome:  Groovy can be either dynamically typed or statically typed (like Java) potentially reaping the best of both worlds
Having mentioned all of these, we probably should add one disadvantage that Groovy presents:
  • Groovy is slower than Java:  in fact, a number of benchmarking tests have shown it to be considerably slower than Java.  With Groovy this isn't a major issue, as computational sensitive code can always still be written in Java.  In addition, Groovy++ is considerably faster than Groovy and performance has improved massively on recent releases of both Groovy and Groovy++.
As an experienced Java programmer, you might not wish to switch to an entirely new language.  With Groovy this is not necessary - the vast majority of your existing Java code can even be simply copy/pasted into your new Groovy files.  The learning curve is quick, it follows existing Java semantics and allows a number of shortcuts to be made in writing code (look at the code above for ';'s or for 'return' keywords.  

Groovy provides support for closures, native regular expressions, operator overloading, expressions embedded in Strings, safe object navigation using ? (eg. myUser?.getEmail() doesn't throw a NullPointerException if myUser is null) and a whole host of other features.  Unfortunately, if you'd like more information on these features you'll need to do your own research, as we do not have scope in this module for a second programming language.  However, we will encounter Groovy later in this section when we look at Grails - A web application development framework based around the Groovy programming language and Java. 



Web Application Architecture

From earlier sections in the notes, we have seen how Java Servlets and JSPs have solved most of the shortcomings of traditional CGI-based solutions. The benefits of Servlets are numerous and many of these have been discussed earlier in the Java Servlets chapter. Likewise, there are a number of benefits provided by JSP to complement Servlet technology: changes to JSP HTML can be made without recompilation and JSP solves the problem of presentation code (HTML) being embedded within lines of code in Servlets.

However, after having completed your assignment, you should now have a clearer understanding of the limitations of the design model you have used. It was precisely for this reason, that this chapter is left until the end of the notes. For a developer to fully understand why they might use a framework, such as Struts, they need to understand what was the problem in the first instance.

When Sun introduced JSP technology, it provided a development road map for working with it and defined two models for building JSP-based Web applications. These models are, quite innovatively, known as Model 1 and Model 2. The first of these, Model 1, is the simpler of the two and has been the primary solution implemented when servlets/JSPs were first introduced.


Model 1 Architecture

Look familiar?

Figure 13.1. Model 1 Architecture

In the Model 1 Architecture, a request is made to a JSP or servlet and then that JSP or servlet handles all responsibilities for the request, including processing the request, validating data, handling the business logic, manipulating data and generating a response. Servlets/JSPs are given responsibility to perform certain functions or certain related groups of functions. While this is might be acceptable for small-scale applications managed by one developer, it is certainly not conducive to large-scale application development. Under this architecture, a great deal of functionality is duplicated in each JSP. Also, Model 1 unnecessarily ties together the business logic and presentation logic, making it difficult to introduce a new "view" or access point in an application. For example, if your standard HTML customers subsequently requested a Wireless Markup Language (WML) interface to your application, Model 1 would require an unnecessary duplication of business logic with each instance of the presentation code. Likewise, with so much data content hardcoded into applications the model 1 architecture is not suitable for internationalisation of applications.


Model 2 Architecture

The Model 2 Architecture is most commonly referred to today as the Model-View-Controller (MVC) architecture. This model solves many of the inherent problems with the original Model 1 design, by providing a clear seperation of application responsibilities. Consider the following diagram:

Figure 13.2. Model-View-Controller (MVC) Architecture

In the MVC architecture, a central servlet, known as the Controllerreceives all requests for the application. The Controller then processes the request and works with the Model to prepare any data needed by the View (which is usually a JSP) and forwards the data to the JSP. The JSP then uses the data prepared by the Controller to generate a response to the browser. In this architecture, the business and presentation logic are seperated from each other. Hence, by seperating this logic, we can now accomodate multiple interfaces to our application (web, wireless, application etc). Additionally, this seperation provides excellent reuse of code and modular development.

The MVC Architecture has been around for quite a number of years, originally used to bulid user interfaces in Smalltalk-80, an early object-oriented programming system. It is often referred to as the 'Model-View-Controller Design Pattern'. By design pattern we mean a blueprint for constructing a time-tested solution to a given problem. It is not actually a concrete implementation but rather a high level design of how a problem should be solved.

Let us focus on our three layers of functionality:

  • Model Components: The data and business logic

  • View ComponentsThe presentation

  • Controller Components: The flow control


Model Components

In the MVC Architecture, model components provide access to the necessary business data as well as the business logic needed to manipulate that data. The Model typically has some means of interacting with the application persistent storage mechanism (most likely a DBMS) - to retrieve, add and update the data. This way, controller components don't necessarily embed code for manipulating an application's data. Instead, they communicate with the model components that perform the data access and manipulation. Model components come in many different forms and can be as simple as a Java bean or as intricate as Enterprise JavaBeans (EJBs) or Web services.

View Components

View components are responsible for displaying data from the Model to the user. Hence, a view component provides what the user sees. View components are typically simple JSPs (GSPs for Grails) or HTML pages. However, due to the model providing seperation of presentation and business logic, you can just as easily use an alternate view technology for this part of the architecture. This is one of the major advantages of MVC - if we wish to provide an alternative view to our application, it is implemented independently from the core business logic.

Controller Components

The controller components are at the very core of the MVC architecture. These components handle all requests from the user and selects the view to return. The Controller is commonly a servlet that receives requests for the application and manages the flow of data between the Model layer and the View layer. When the Controller receives a request, the Controller forwards the request to the appropriate handler, which interprets what action to take based on the request. The Controller calls on the Model to perform the desired function. After the Model has performed the function, the Controller selects the View to send back to the user based on the state of the Model's data.



Grails 

Grails is an open-source, MVC, web application framework which has been designed around the idea of faster development.  It is based on both the Java and Groovy languages, providing developers with the ability to use either language or to interchangeably use both within the same application.  Grails provides developers with the ability to quickly build an MVC (Model-View-Controller) application within a short timeframe, by providing automated "scaffolding".

Grails was previously known as 'Groovy on Rails', but was forced to rename to 'Grails' after a request by the founder of the 'Ruby on Rails' framework.  The first non-beta release of Grails was in 2008 and the community of developers has been growing steadily since.  

Grails uses a number of proven technologies and features to form its framework:
  • Java/Groovy: both highly supported and popular programming languages are supported
  • Hibernate: Grails uses a layer called GORM (Grails Object Relational Mapping) to interface with a standard Hibernate setup
  • Spring: currently (2011) the most popular Java-based framework
  • Tag Libraries: Grails uses GSPs (Groovy Server Pages) designed to build web pages easier
  • Ajax: Grails builds strong Ajax support
  • JavaScript: to build more user-friendly client interfaces Grails makes use of JavaScript libraries such as Script.aculo.us and jQuery
  • Development Environments: support (sometimes limited) available on IDEs such as SpringSource ToolSuite (Eclipse-based), NetBeans,IntelliJ IDEA and others.

Building a Grails Application

The notes here have been kept purposefully short, as this section is best covered through the use of a demonstration video.  The following video demonstrates the creation of a 'Banking System' using Grails.  If we were to exclude the explanation and discussion, the video literally demonstrates the creation of a basic system for managing Branches, Customers and Accounts within approximately 10 minutes (the actual video including everything is about 30 mins).  


While this section of notes is shorter than other sections, you are expected to understand the steps taken in the deployment of the application and to recognise the benefits of using a framework such as Grails.  You will not be asked to create a Grails application in an examination, but you are expected to understand each of the steps taking place and what basically occurs during each step.

Domain Class Code from Example

The following three snippets contains all of the code we have mostly manually "written".  The remaining configuration files, controllers, views, CSS and JavaScript were all automatically generated by the Grails application framework.  Of course, in reality, each application we build will have customised functionality which will require us to get involved in the modification of the automatically generated code.  We would typically need to modify the business logic in the controllers and the style, content and presentation shown in the views, represented in GSPs (Grails Server Pages).  However, while this falls outside the scope of the module there are a large number of Grails tutorials and books, which can provide further information in this regard.

For our example, we only modified the following three files and all other files were the default, auto-generated templates.

Branch.groovy
package com.mybank

class Branch {
String name
String county

    static constraints = {
name blank:false, size:5..30
county blank:false, size:4..20
    }
public String toString() {
name + " - " + county
}
}
There is nothing too complex to explain in any of these domain classes.  They are essentially standard JavaBeans written in the Groovy programming language.  Groovy domain classes do not require the developer to generate getter and setter methods as it dynamically provides that functionality.  However, is it possible to manually override any of these methods in the usual way.  You will notice that Groovy does not require ';' at the end of lines, one of the restrictions of Java.  

The static constraints defines the database (and view) constraints on each of the properties of the domain class.  For example, 'name blank:false, size:5..30'  will have two main effects.  
  1. The generated views will implement server-side validation on the form field representing the name whenever it occurs.  It will ensure, in this example, that the name field is not left blank and will have between 5 and 30 characters of data.  Otherwise, it will not be accepted.
  2. The resulting automatically generated SQL table (via Hibernate) will have the column set as 'NOT NULL' and will likely be defined as having type variable character string of max size 30.
Finally, the toString() method is provided to change the standard String view of the class from something like 'com.bank.Branch:1' to 'Swords - Dublin'.  This is encountered in the video when, on the creation of a new customer, the drop down box is changed to this more user-friendly format.

Customer.groovy
package com.mybank

class Customer {
String firstname
String surname
String username
String password
String email
Branch branch

    static constraints = {
firstname blank:false, size:2..30
surname blank:false, size:4..20
username blank:false, size:8..20
password blank:false, size:6..20
email blank:true
    }
}

Account.groovy
package com.mybank

class Account {
    String type
    float balance
    Customer customer    

    static constraints = {
    }
}

Question: Given the Groovy is dynamically typed, why do we simply not write 'def type', 'def balance' etc. ? (Hint: Hibernate)


Grails Common Commands

Grails provides a number of default commands used in the creation and deployment of applications.  Most of the following sample commands have been used in the demonstration video, but here is a short description of their functionality:

grails create-app <appname>

Creates a new grails application, setting up the directory structure, blank configuration files and a sample entry page for your application.

grails create-domain-class <domainclass>

Creates a new bean which will be persisted on the datasource defined in the configuration files (most likely a RDBMS).  This will create a blank bean and the corresponding test classes.

grails run-app          

Runs the Grails application on the embedded Jetty (similar to Tomcat) server which comes bundled with Grails.  After this command, it should be possible to navigate to http://localhost:8080/appname to view the root page of your Grails web application.

grails create-controller <domainclass>

Creates a new controller class which is designed to handle requests and prepare the response.  Controller classes are commonly generated using existing, previously-generated domain classes.  A default controller class would have the common CRUD operations for Creating, Updating, Deleting and Retrieving instances of the domain class.

grails generate-views <domainclass>

This will generate the actual GSPs which are responsible for presenting the CRUD views of the domain class.  For example, it would typically generate the following files: create.jsp, edit.jsp, list.jsp, show.jsp for a different view for the various CRUD operations (deleting would be typically handled inside the edit.jsp).

grails generate-all <domainclass>                  

Creates both the controllers and views for a specific domain class.  Similar to a combination of the two previous commands listed.

grails install-plugin <pluginname>

Installs a plugin from either a file, URL or the grails plugin repository (most commonly from the repository).  More on plugins below.


Grails Plugins

There are literally hundreds of plugins available for Grails.  These cover a range of areas including:
  • Rich client development
  • Security (such as the Spring Security Core plugin)
  • Ajax Development
  • Web Services
  • Performance Management
  • Data exporting
  • Mail Services
  • Cron Jobs (scheduling events for particular times/dates)
  • ... many many more
When building Grails applications, it is typically worth checking for available plugins to provide or aid with the functionality required.  The plugin structure provides great power and functionality to Grails applications, with minimal coding and configuration.

For a complete list of Grails plugins see: http://www.grails.org/plugin/category/all 

GORM (Grails Object Relational Mapping)

Already in the module, we have spent considerable time covering both JDBC and Hibernate, both facilities for persisting our data in a relational database management system.  GORM is Grails' Object Relational Mapping (ORM) implementation.  However, GORM sits on top of Hibernate 3, the structure of which we have already examined.  However, due to the dynamic nature, the reduced-code domain classes and the convention of Grails, there is considerably less work involved in the configuration of GORM.  

Let us take some examples demonstrating CRUD operations for the domain class we encountered earlier: Branch.groovy

Branch.groovy
package com.mybank

class Branch {
String name
String county

    static constraints = {
name blank:false, size:5..30
county blank:false, size:4..20
    }
public String toString() {
name + " - " + county
}
}

Create

def b = new Branch(name:"Swords", county:"Dublin") b.save()

Read

Grails will add an explicit ID to our domain classes to act as primary key.  Assuming a primary key of 1:
def b = Branch.get(1)

Update

To update, we simply retrieve the object, update the properties and save it again.
def b = Branch.get(1)
b.name = "Swords Main Street"
b.save()

Delete

def b = Branch.get(1)
b.delete()


Querying with GORM

Querying with GORM can be handled using dynamic finders, criteria or using direct Hibernate Query Language (HQL).  Here are a few short examples demonstrating the most basic of queries:

To find a single entity with a known primary key:
def aBranch = Branch.get(23)

To find all Branch entities:
def branches = Branch.list()
..with sorting:
def branches = Branch.list(sort:"name", order:"asc")

Using Dynamic Finders

GORM supports the idea of dynamic finders.  Dynamic finders look like methods which have been written - except that the code is never manually written.  Instead, the code is generated automatically based upon the properties of the given class.  This is probably best explained using a set of examples:
def branch = Branch.findByName("Swords")

branch = Branch.findAllByNameLike("Sw%")

branch = Branch.findAllByNameNotEqual("Santry")

branch = Branch.findAllByNameLikeAndCounty( "Sw%", "Dublin" )

branch = Branch.findAllByNameIsNullAndCountyIsNotNull()

branch = Branch.findAllByTitleLikeOrReleaseDateLessThan( "%Something%", someDate )

Consider the scenario where, in standard JDBC development, one would need to implement separate methods to provide the database functionality provided in each of these statements.  However, using GORM these dynamic finders already exist and can be used immediately in only one single line of code.

We won't look in anymore detail at GORM - plenty of information can be found on the Grails website if this framework is chosen by you for a future project.  


In Closing on Grails

Grails has been introduced to the course to provide an example of an effective framework that can be used for the rapid development of web applications.  It has tried and tested design approaches and should get developers building web applications faster than could possibly be managed using Struts or Spring, which typically involve considerable configuration setup.  There are a range of other similar frameworks out there to choose from, but the intention was just to provide one and let you make your own choices in potential future projects.  Hopefully, you can see the benefits of using frameworks and the potential they provide for speeding up the development of your applications in the medium term.


Internationalisation (i18n) and Localisation (l10n)

Internationalisation and Localisation are means of adapting computer software to different languages and regional differences.  The shortened pseudo acroynoms are formed by the first-letter and the last 'n' together with the number of letters between each (eg. 18 characters between the I and n in Internationalisation).

Naturally, faced with the prospect of supporting multiple languages, the software developer could merely create different instances of the code with embedded localised text.  However, this process would come with numerous problems,one of which would be multiple versions of almost identical code to update every time a change needed to be made.

Internationalisation is the process of designing a software application so that it can be adapted to various languages and regions without engineering changes.  Localization is the process of adapting internationalized software for a specific region or language by adding locale-specific components and translating text.

Typically, internationalisation and localisation are handled through a relatively simple process of abstracting language away from the underlying code.  Under most MVC frameworks, the developer can define one or more message resource bundles (text files with name-value pairs) for use within their application: (message.properties is the name used in Grails, ApplicationResources.properties in Struts)

#errors associated with the Login page error.username.required=username is required. error.password.required=password is required. error.login.invalid=Login Failure: The DCU Novell username/password combination you have entered is incorrect. #login page text login.title=Welcome to Module - Please Login login.message=Enter your username and password: login.username=Username: login.password=Password: login.button.commit=Login login.help.message=Login Help #loggedin page text loggedin.title=Login Project loggedin.msg=Welcome, {0}. You are now logged in.

If we place our language specific application statements into an application resources file, it has obvious benefits for Internationalisation. Consider, if we were to embed these statements directly within our servlets (as you probably have done in your assignment!) or JSPs. If your server side application were being extended to a global level, how would you go about creating a french language version? Typically, if you had embedded your resources into your business logic, it would require a complete rewrite, or a replica servlet/JSP for each language. By using application resource files, we simply need to create a seperate file message messages_fr.properties (Grails) or ApplicationResources_fr.properties (Struts). When making a request to a Web server, browsers pass along an HTTP header (Accept-Language) that indicates the user's Locale (country, language and other variants). Now by using this Locale in our business logic, the application can be pointed at this new file and our application is fully internationalised with no extra work (except for hiring someone to translate our resources file into french!).


Comments