Wednesday, May 23, 2012
Tuesday, May 15, 2012
Collections frame work
Collections in Java: A Brief Introduction
Why use Collections?
If you want to store many values of the same type, why not just use an array? Well, an array may work just fine for your purposes--if you know exactly how large the array needs to be before you put anything in and this size will NEVER EVER change; and if you will never need to search for an element in the array without knowing its specific index.
But suppose you are retrieving records from a database that satisfy certain conditions--and you do not always know how many records you will get. Do you declare the size to be some really large number and just hope it will always be enough? That could mean a lot of memory waste. Do you really want to have to go through and count the number of results every time you run the query just to declare the array? Perhaps you could write the program so that it automatically reallocates the array with a larger size when it fills up. But that is also a lot of extra work.
Here are a few of the more common interfaces and classes within the Java Collections Framework that can make your programming tasks MUCH easier. There are MANY others; but these will help get you started.
. +---- Queue ---- Deque --+
. | |
. | |
. | +---- LinkedList
. | |
. Collection ----+---- List ----+
. | |
. | +---- ArrayList
. |
. |
. | +---- HashSet
. | |
. +---- Set -----+
. |
. +---- SortedSet ---- TreeSet
. | |
. | |
. | +---- LinkedList
. | |
. Collection ----+---- List ----+
. | |
. | +---- ArrayList
. |
. |
. | +---- HashSet
. | |
. +---- Set -----+
. |
. +---- SortedSet ---- TreeSet
The Collection Interface
This is the most general superinterface for Java’s predefined classes that store a general group of objects. By “object” we mean an instance of any subclass of the Object class, including Object itself. Unlike arrays, however, none of the Collections Framework classes can store primitives directly. To insert primitive values, you must use the wrapper classes (Boolean, Integer, Character, etc.). More on this later.
The Collection interface allows you to access methods you would use on any Collection, be it an ArrayList, TreeSet, etc. Some of the most common examples would be:
Iterator iterator() Returns an instance of a class that implements the Iterator interface. This allows you to move through and process all the elements of the collection one at a time.
boolean add(Object e) Attempts to add the given Object instance to the collection. The return value tells us whether or not the add was successful. The only way the add can fail is if the Collection happens to be a Set and we try to insert an Object that is already present.
boolean remove(Object o) Removes ONE occurrence of the given Object from the collection, if any are present. The return value tells us whether or not a removal actually occurred.
void clear() Empties the collection.
boolean contains(Object o) Tells us whether or not the collection has the given object as at least one of its elements.
int size() Returns the number of elements in the collection.
Object toArray() Returns an array which contains all the elements of the collection.
Be sure to understand the difference between the Collection interface and the Java Collections Framework. While the Collection interface and its predefined subtypes all belong to the Collections Framework, so do other interfaces and classes which are not subtypes of Collection, such as Map, Iterator, and Comparable.
The List Interface
This interface is the supertype for a group of objects stored as a sequential series. It inherits all the Collection methods and adds some new ones, most notably
Object get(int index)
Since we are dealing with a List and not a generalized Collection, we know that all the elements have been stored sequentially. So we can access the element at any given position in the List, provided it exists. (An IndexOutOfBoundsException will be thrown if it does not.) Remember that the List classes are indexed starting at zero, just like arrays. Because you can insert any Object at all into a List, this method must return the generalized type of Object. If you know that the List only contains objects of a more specific type and you want to use the element you are retrieving in a more type-specific manner, you must use a cast. A List also allows you to insert a new element at a position you specify (by calling add with an additional index parameter). If you do not wish to specify a certain position, the element will be appended to the end of the list by default. When you call the iterator() method on a List, you are guaranteed to receive an Iterator that moves through the List elements in increasing sequential order from beginning to end.
1) ArrayList
Chances are, of all the concrete subclasses of Collection, this is the one you will use most frequently. The ArrayList class implements the List interface using an array of references to the elements inserted. You can specify an initial size if you like, but it is not necessary. The ArrayList class will automatically expand the internal array dynamically (by copying the references to all the elements into a new larger array and then deleting the old array) whenever you try to insert a new element once the array is full. But if you know that the space will be used, it is advisable to go ahead and specify a large initial size rather than let the ArrayList class keep expanding the internal array over and over as you add a large number of elements. One very common use for an ArrayList is in the retrieval of multiple records from a database. The records are returned in an ArrayList and then processed similarly one at a time (displayed on the screen in a certain way, for example) using an Iterator.
2) LinkedList
The LinkedList class implements the List interface as well, but with a linked list instead of an array. LinkedList also implements the Deque interface (see Queue, discussed later).
By linked list we mean that each reference to an element is stored in a node, which contains the reference to the element and a reference to the next node. This means that whenever you want to retrieve an element according to its index, the LinkedList class must reference each node in turn starting from zero until it reaches the reference to the element at the desired position. But the advantage to this approach becomes evident when you consider what happens when you try to remove an element from the list or insert one at a specific position. ArrayList takes the bigger performance hit in this case, since we must shuffle elements either forward or backward in order to close the gap where one was deleted or to open a gap for an insertion. In practice, however, you may not encounter many situations where you need to remove an element from a List or insert one in the middle. Therefore you are likely to use the ArrayList class far more often in practice.
The Set Interface
This interface is the supertype for a group of objects stored as a set in the more mathematical sense. A set may be either sorted or unsorted, but it will not retain the order in which the elements are inserted and it will not allow the insertion of duplicates. Whether or not a prospective element is considered a “duplicate” of one that is already present will be decided using the element’s equals() function. Also, while an individual element of a List is accessed by the index at which its reference is located, an element of a Set is accessed using a reference to the element itself.
Calling the iterator() method on a Set guarantees that the Iterator you receive will process each element of the Set exactly once. There is no guarantee as to the order in which the elements will be processed. In fact, another call to iterator() on the same Set may return an Iterator that processes the elements in an entirely different order from before.
3) HashSet
The HashSet class implements the Set interface using a chained hash table. This means that the element references are stored in an array of slots (called buckets), where the bucket used to store each element is determined using a hash function. By a process called hashing, the hash function takes the object to be stored and converts it into an integer which is used for indexing the array. One could write an entire thesis on the subject of good hashing algorithms. Just remember that a good hash function (called hashCode() in Java) will always return the same value for the same object and any object that is considered equivalent.
For an explanation of what chained hash table means, think for a moment about the number of possible states that an object may take on. Because this number can be potentially huge, the number of buckets in the hash table will probably be much, much smaller. Therefore, it is natural to expect that the objects you insert into a HashSet will sometimes hash to a bucket that is already in use. This is known as a collision. There are other ways of implementing a hash table in which you would compute a different location to try in the event of a collision. But because we are dealing with a chained hash table, we work around collisions by essentially placing multiple elements in the same bucket. The new element is simply attached as part of a new node in linked list fashion to the last element that was added to that same bucket. So a chained hash table is really an array of linked lists.
The main advantage to using a HashSet is speed. Perhaps surprisingly, whether you are retrieving, inserting, or removing, the speed of a HashSet does not depend on its size! Rather, it depends on what is called the load factor, or the ratio of the number of elements currently inserted to the number of buckets allocated. The HashSet class will generally allow you to insert new elements until the load factor reaches about 75%, whereupon the next insertion will prompt the creation of a new larger storage table and the rehashing of the elements into this new table.
One disadvantage to using a HashSet is that it will never enforce any type of order. Whenever you iterate through the elements of a HashSet, always assume that they may be processed in any possible order--regardless of whatever order they may have been processed in before. Another thing the HashSet sacrifices in the name of speed is efficient use of memory. Because the HashSet class prevents the load factor from getting high enough to have a noticeably negative effect on performance, there will always be some unused space (and sometimes lots of it) in the storage table.
But if you want to work with the elements of your collection on a more individual basis instead of just dumping them all in a list all at once and then processing them all in the same way; if your main concern is speed; and if you do not need duplicates or any certain ordering, a HashSet or HashMap is the way to go. As a simple example, consider an extensive list of names and phone numbers. As with the ArrayList, a HashSet lets you specify an initial size if you desire--and you should if you know your collection will be large and you have some idea what the size will be.
The SortedSet Interface
This is a subinterface of the Set interface which, as you might expect, enforces a definite ordering of the elements inserted. If you are creating a SortedSet of Integer objects, of Double objects, or of String objects, for example, you can just insert the elements and they will be automatically sorted in ascending order. In fact, the elements you are inserting into a SortedSet must all be of a type that implements the Comparable interface--as many predefined classes already do. Otherwise the SortedSet will not be able to “look” at any given pair of elements and “know” which one comes “before” the other. However, there is an exception to this Comparable-type-element rule. You can define your own order in which the elements are to be sorted by means of a Comparator. In this case, your Comparator must be passed to the SortedSet on creation. (The Comparable and Comparator interfaces are discussed in more detail later.) When you apply the iterator() method to a SortedSet, you will receive an Iterator that will process the elements in their proper sorted order.
4) TreeSet
The TreeSet class implements the SortedSet interface using a red and black tree. That is, TreeSet is implemented internally as a binary tree in which each node is labelled as either “red” or “black”. A binary tree is like a linked list in that the references to the elements being inserted are again stored in nodes; but in this case each node can reference up to two additional nodes (called children) instead of just one. The purpose of the “red” and “black” tagging is to enforce a sense of “balance” (i.e. to maintain efficiency by ensuring that the tree and indeed each of its subtrees always has about the same depth on either side).
As with the LinkedList, no dynamic expansion and reinsertion is ever needed--additional memory is always allocated one new node at a time as the need arises. It is therefore never necessary or even helpful to specify an initial size. The power behind the TreeSet class comes from its structure--which allows retrievals, insertions, and deletions of its elements to all occur in logarithmic time while always maintaining some inherent ordering.
The Queue Interface
New in Java 1.5 (Oops, I mean Java 5!) a Queue enforces restricted access to its elements as well as a certain ordering of the elements. This ordering is typically FIFO (first-in-first-out, like a line at a checkout counter). But the ordering could also be LIFO (last-in-first-out, i.e. a stack) or based on the natural ascending order of the elements or a given Comparator (i.e. priority queue). You also have the option of either setting a maximum size for the Queue or letting it grow unrestricted.
Here are the new methods introduced in the Queue interface (which add to the methods inherited from Collection):
Object element() Retrieves the leading element of the Queue without removing it. Throws a NoSuchElementException if the Queue is empty.
Object peek() Same as element() but returns NULL for an empty Queue rather than throwing an exception.
boolean add(Object e) Inserts the given Object into the Queue according to the enforced ordering of the Queue’s elements. Throws an IllegalStateException if it happens that the Queue has a restricted capacity and is full. Note that this method overrides the add(…) method from Collection.
boolean offer(Object e) Same as add(…) but returns NULL if the Queue was full instead of throwing an exception.
Object remove() Same as element() but deletes the leading element from the Queue in the process.
Object poll() Same as remove() but simply returns NULL if the Queue is empty.
The Deque interface (short for double-ended queue) extends Queue and represents a more generalized queue--one that supports easy read, insert, and remove access to both the front and the back of the queue.
One popular application for a traditional queue (FIFO) is a call center. Calls enter the queue at the back in the order in which they arrive and exit the queue from the front each time an associate becomes available to take a new call.
For an example in which you might use a stack (or LIFO queue), consider a calculator that performs operations using postfix (or Reverse Polish) notation. If you were to simulate this using a Java application, you could push the numbers onto a stack as they are received from user input. But when the user enters an operator instead of a number (let’s say, +, -, *, /, %, or ^), you could then pop the top two numbers (i.e. the last two submitted) off the stack, apply the operator to these two numbers, and push the result back on the stack. If there were not enough operands available to perform the operation, you would probably wish to show the user an error message.
As for a priority queue, consider a series of documents sent by different users to the same printer. Rather than simply executing all the jobs on a first-come-first-serve basis, you may also wish to take into account the size and the level of criticality of each job. Naturally, you would need to specify the means by which to compute whether one job should take precedence over another. In Java, you would do this using the Comparable and/or Comparator interface. Once the priority queue knows how to “sort” its elements (print jobs), it will always pick up the “minimum element” (i.e. highest priority print job in this case) for the next execution once the last job is complete.
The Comparable and Comparator Interfaces
Neither of these is a subtype of the Collection interface. But both still belong to the Java Collections Framework and are used quite frequently in dealing with collections. The Comparable interface contains one method--int compareTo (Object o). This method returns a negative integer if the Object used to call the method is considered “less” than the Object passed in; a positive integer if the calling Object is considered “greater,” and zero if the two Objects are considered “equal.” Many predefined classes already have an inherent implementation of the compareTo method, such as Boolean, Integer, String, Calendar, etc., which works as you would intuitively expect. The compareTo method of a class is what is used to determine the so-called natural ordering for objects of the class.
The Comparator interface defines two methods, one of which is int compare(Object lhs, Object rhs). Much like the compareTo method, this one returns a negative, zero, or positive integer depending on whether the left Object is “less than,” “equal to,” or “greater than” the right Object.
Why use Comparator when we can define a method of ordering using Comparable? Suppose you wish to sort a List of objects of a class (call it class A) that someone else wrote. If A does not implement Comparable then there is no compareTo method available to tell the sort method how to compute the natural ordering. Using a Comparator allows you to define your own method of ordering objects of type A without the hassle of creating a “dummy” class that just extends A and implements Comparable. Not only that--what if A does implement Comparable but the compareTo method does not give the order you are looking for? You can define any number of Comparator objects, each designed to order objects of type A in its own unique way.
The Collections Interface
This class actually extends Object directly; but it contains many different methods that are useful when manipulating collections (including maps). Just like the Math class, all of the methods are static. But the Collections class is not final. Here are a few of the included methods:
int binarySearch(List list, Object key) Uses a binary search algorithm to determine whether the given List contains the given key. It is assumed that the given List is already sorted according to the natural order of its elements. Otherwise, a binary search may not work correctly and the method may return an erroneous result. The performance time is logarithmic provided that the List is “random access” (one that accesses any specified position in near-constant time). The index of the search key is returned if the key is found. If not, the return value is -p - 1, where p is the point at which the key would go if inserted according to the natural order.
Object max(Collection coll) Returns the maximal element of the given List, as determined by the natural ordering of the elements. As with the sort method, the elements must all be of a type that implements the Comparable interface.
Object min(Collection coll) Returns the minimal element of the given List, as determined by the natural ordering of the elements. As with the sort method, the elements must all be of a type that implements the Comparable interface.
boolean replaceAll(List list, Object oldValue, Object newValue) Replaces all occurrences of oldValue in the given List with newValue. Returns true or false based on whether or not oldValue was encountered.
void reverse(List list) Reverses the order of the elements in the given List.
void shuffle(List list) Rearranges the elements of the given List into some random (as defined by the default source of “randomness,” which is not completely unbiased) permutation.
void sort(List list) Sorts the elements of the List provided in ascending order according to their natural ordering. Actually, the elements must all be instances of a type that implements the Comparable interface. This is because the “natural ordering” will be determined using the compareTo methods of the elements. This method uses a Merge Sort algorithm that gives N log N performance. All the elements are actually dumped into an array and sorted from there--to avoid all the extra overhead of sorting a LinkedList in place.
The shuffle method above is overloaded to allow you to specify an alternate source of “randomness” when jumbling. Of the others shown above, most are overloaded to allow you to specify a Comparator as well. This means that the elements of the given List/Collection do not need to be of a subtype of Comparable; and the method will work using the ordering imposed by the given Comparator‘s compare method rather than the elements‘ “natural ordering.” Other than that, the methods are the same:
int binarySearch(List list, Object key, Comparator comp)
Object max(Collection coll, Comparator comp)
Object min(Collection coll, Comparator comp)
void sort(List list, Comparator comp)
+--- HashMap
Map ---+
+--- SortedMap --- TreeMap
The Map Interface
For our purposes, a map is a way of relating objects together in pairs. Instead of inserting, retrieving, or removing a single value as with a Set or List, you perform an insert by providing a key and its associated value; and you perform a retrieval by providing a key and getting back its associated value. While duplicate values are permitted, duplicate keys are not--so in this way a Map bears a bit of a resemblance to a Set. Uniqueness of keys assures us that a retrieval will never be ambiguous--every key gives us back ONE value (or no value at all if the key is not actually present). If you are mathematically inclined, you can think of a map as a function from the set of keys to the set of values. Each (key, value) pair is called an entry. Remember that although the Map interface does not extend the Collection interface in any way, shape, or form, it is still considered to be part of the Java Collections Framework. Many of the methods provided by Collection also appear and are similar in Map. For example:
Object put(Object key, Object value) Associates the given key with the given value in the Map, regardless of whether or not the key was previously associated with anything. The method will return either this previous value or NULL if there was no previous association.
Object remove(Object key) Removes the key and its associated value from the map, if present. The return value behaves like much as it does for put--it will be either the removed value or NULL if there was nothing to remove.
void clear() Empties the collection.
boolean containsKey(Object key) Tells us whether or not the map has the given object as one of its keys.
boolean containsValue(Object value) Tells us whether or not the map has the given object as at least one of its values. This method will generally be executed in linear time.
int size() Returns the number of entries in the map.
Set keySet() Returns a Set which contains all the keys of the map. Changes made to this Set will be reflected in the Map. For example, removing an element of this Set will cause the corresponding entry to be removed from the Map.
Collection values() Returns a Collection which contains all the values in the map. Changes made to this Collection will be reflected in the Map. For example, removing an element of this Collection will cause the corresponding entry to be removed from the Map.
5) HashMap
As you would probably guess after becoming familiar with HashSet, this class implements the Map interface using a chained hash table. The keys are converted into integers using the hashCode() method. The resulting integers are used to index the buckets in the hash table. The buckets store the corresponding values. One common application of this is a phone directory (list of names and phone numbers). Assuming each person has exactly one phone number, you would insert (name, phone) entries and retrieve using name keys. Were you inclined to allow multiple phone numbers for the same person, you might use a List of phone numbers for each value instead of a single phone number.
The SortedMap Interface
Naturally, this is Map’s counterpart to SortedSet. It is a subinterface of Map that enforces a definite ordering (by key) of the entries inserted. Of course, the SortedMap must also have a method by which to enforce the ordering. So you again have two choices: make sure all your elements are subtypes of Comparable OR provide your SortedMap with a Comparator on creation.
6) TreeMap
This, predictably, is a red and black tree implementation of the SortedMap interface. Again, there is no need to ever specify an initial size (in fact, you cannot). And any retrieve, insert, or remove operation generally occurs within logarithmic time. If you expect to continually update and print new copies of your phone directory from the previous example, you might consider implementing it using a TreeMap instead of a HashMap. That way you will maintain the sorted order with every insertion and avoid the overhead of repeatedly sorting the whole directory.
Think about it…
1. The sort and shuffle methods from Collections make sense only when applied to a Collection that happens to be a List. Why is this?
2. If you are familiar with postfix operations, you might try to trace through mentally or on paper what the internal stack described previously in the Queue application would look like (starting out empty) as the calculator processes the numbers and operations it receives. When you pop the top two numbers off the stack to apply an operator, use the first number off the top as the right operand. For example:
A) “3 4 5 + + 6 - * 2”
B) “22 23 + 3 3 / / 2 2 2 ^ ^ *”
3. Note that, unlike Collection, there is no iterator() method defined by the Map interface. How might you iterate through and process the entries of a Map despite this limitation?
4. A word of caution regarding the put and remove methods from the Map interface: a return value of NULL does not necessarily mean that the key that was provided did not exist previously in the map. It might be possible that the key was actually associated with the value NULL. How could one distinguish between these two cases?
5. Although a Map guarantees that the keys will all be unique, the values may not be. What do you think would happen if you were to call the values() method on an instance of Map having duplicate values--and then call the remove() method on the resulting Collection, passing in one of the duplicated values from the Map?
Tuesday, April 10, 2012
Struts2
Introduction/ History of Struts
The first version of Struts was released in June 2001. It was born out of the idea that JSPs and servlets could be used together to provide a clean separation between the view and the business or application logic of a web application. Before Struts, the most common options were to add business and application logic to the JSP, or to render the view from servlets using println() statements.
Since its release, Struts has become the de-facto standard for web application. With this popularity have come enhancements and changes - both to keep up with the ever-changing requirements for a web application framework, but also to match features with the ever-increasing number of competing frameworks available.
To that end, there have been several proposals for the next generation of Struts. The two alternatives that have become the most cohesive in the past year are Shale and Struts Ti. Shale is a component based framework, and just recently has become its own top-level Apache project, where Struts Ti continues the front controller or model-view-controller pattern that has made Struts so successful.
The WebWork project was started as a Struts revolution - as a fork of the Struts code to introduce new ideas, concepts and functionality that may not be compatible with the original Struts code - and it was first released in March 2002. WebWork is a mature framework, having undergone several minor and major releases.
In December 2005 it was announced that WebWork and the Struts Ti would join forces. Since that time, Struts Ti has become Struts Action Framework 2.0, and the successor to Struts.
Finally, it should be noted that neither the Struts nor WebWork projects are going away. While interest is high, and willing developers are available, both of these projects will continue - all the while having bugs fixed, and adding enhancements and new features.
To that end, there have been several proposals for the next generation of Struts. The two alternatives that have become the most cohesive in the past year are Shale and Struts Ti. Shale is a component based framework, and just recently has become its own top-level Apache project, where Struts Ti continues the front controller or model-view-controller pattern that has made Struts so successful.
The WebWork project was started as a Struts revolution - as a fork of the Struts code to introduce new ideas, concepts and functionality that may not be compatible with the original Struts code - and it was first released in March 2002. WebWork is a mature framework, having undergone several minor and major releases.
In December 2005 it was announced that WebWork and the Struts Ti would join forces. Since that time, Struts Ti has become Struts Action Framework 2.0, and the successor to Struts.
Finally, it should be noted that neither the Struts nor WebWork projects are going away. While interest is high, and willing developers are available, both of these projects will continue - all the while having bugs fixed, and adding enhancements and new features.
What is Struts?
According to the Apache Struts project page
"Apache Struts is a free open-source framework for creating Java web applications."
While that statement is certainly true, it belies the power and the complexity of Struts. There are a few adjectives I would add to this statement.
- Powerful
- Feature rich
- Widely accepted
- De-facto standard
- Modern (in the case of Struts2)
The Apache Struts Project offers two major versions of the Struts framework. Struts 1 is recognized as the most popular web application framework for Java. The 1.x framework is mature, well-documented, and widely supported.
Struts 2 was originally known as WebWork 2
. After working independently for several years, the WebWork and Struts communities joined forces
to create Struts 2. The 2.x framework is the best choice for teams who value elegant solutions to difficult problems.
What's so good about Struts2?
The bad old days
When the Servlet 1.0 API hit the scene in 1997 it was a major step forward in web application development. Before this, most developers were doing CGI in languages like C and Perl. Developers now had a clean API that closely followed the way HTTP works, but it still left the developer with a lot of work in terms of
You need a framework!
See Struts 1.x Vs Struts 2.x for a good rundown of Struts and a comparison between Struts 1 and Struts 2.
How do I get started?
What you need to know
The Struts2 Documentation
is referenced throughout this page. See the key technologies primer
to get up to speed.
The big picture
is always a good place to start.
HTTP and the Servlet API
For the purposes of this article, I assume that you have a working knowledge of HTTP and the Servlet API but it's worth mentioning that to write good Web applications you must know HTTP and to write good Java Web applications you must know the Servlet API. A thourough understanding of HTTP helps you design web apps that take advantage of HTTP and since all Java web application frameworks are built on Servlets, it's good to understand what's going on under the hood.
In Struts2 they have implemented a ServletFilter to act as the entry point into the framework. Struts2 applications typically configure the filter (
org.apache.struts2.dispatcher. FilterDispatcher) to wrap all requests.
OGNL
OGNL is Object Graph Navigation Language. The STruts2 documentation describes it very well here
. OGNL is basically used within Struts to provide access to what's called the "value stack". The value stack consists of several different objects including request parameters, attributes, session attributes, configuration etc. OGNL allows you to access all of those objects as if they were one object. Most of the interaction the code will have with OGNL is the use of OGNL expressions in JSP files.
Creating an Action
The requisite hello world example
shows how the Action and JSP interact in Struts2. Action classes typically extend com.opensymphony. xwork2.ActionSupport however Actions are only required to implement the execute method.
Key points:
- Action classes use Java Beans style getters and setters to get data to and from the jsp/request. Form element values are automatically set on the action.
- Return "success" from the execute method to invoke the default result.
- Return other values to control the flow of the application. The framework will perform the next configured task.
- Actions can be testable because they are POJOs.
Using Tags
Struts2 provides a comprehensive set of tags to handle form fields, links, messages, and more. The tag developer's guide
is where you can find documentation on these tags, also the tag reference
is helpful. Here's the basics:
The using tags
page also provides good information on tags. One important feature of Struts2 is that all of the JSP tags delegate to Freemarker templates to render their output. Freemarker is a templating engine from Apache. The templates can be customized and overridden which allows you to easily change the way the tags render.
Configuring Struts2
Struts2 is generally configured via the struts.xml file on the classpath. A basic configuration consists of action tags nested inside a package tag. Packages can be used to logically partition an appliction into different functional areas that may or may not have unique configuration needs. See configuration elements
in the documentation.
Constants can be set at the top of the configuration to control the overall behavior of the framework. The most common is the "struts.devMode" constant.
...
Struts 2 Architecture
Let's take a look at what the new architecture looks like by walking through the request processing.
As we walk through the request lifecycle you should notice one important fact - Struts2 is still a front controller framework. All of the concepts that you are familiar with will still apply.
This means:
- Actions will still be invoked via URL's Data is still sent to the server via the URL request parameters and form parameters.
- All those Servlet objects (request, response, session, etc.) are all still available to the Action.
From a high-level overview, this is how the request is processed:
The processing of a request can be broken into these 6 steps:
- A request is made and processed by the framework - the framework matches the request to a configuration so that the interceptors, action class and results to use are known.
- The request passes through a series of interceptors - interceptors, and interceptor stacks, can be configured at a number of different levels for the request. They provide pre-processing for the request as well as cross-cutting application features. This is similar to the Struts RequestProcessor class which uses the Jakarta Commons Chain component.
- The Action is invoked - a new instance of the action class is created and the method that is providing the logic for this request is invoked. We will discuss this in more detail in the second part of this series; however, in Struts2 the configuration of the action can specify the method of the action class to be invoked for this request.
- The Result is invoked - the result class that matches the return from processing the actions' method is obtained, a new instance created and invoked. One possible outcome of the result being processed is rendering of a UI template (but not the only one) to produce HTML. If this is the case, then Struts2 tags in the template can reach back into the action to obtain values to be rendered.
- The request returns through the Interceptors - the request passes back through the interceptors in reverse order, allowing any clean-up or additional processing to be performed.
- The response is returned to the user - the last step is to return control back to the servlet engine. The most common outcome is that HTML is rendered to the user, but it may also be that specific HTTP headers are returned or a HTTP redirect is invoked.
As you may have noticed, there are some differences. The most obvious one is that Struts2 is a pull-MVC architecture. What does this mean? From a developers perspective it means that data that needs to be displayed to the user can be pulled from the Action. This differs from Struts, where data is expected to be present in beans in either the HTTP page, request or session scopes.
Configuring the Framework
The first, and most important configuration, is the one that enables the web application framework within the servlet containers web.xml file.
The configuration that everyone should be familiar with for Struts is:
For Struts2 there are very few changes. The most significant is that the dispatcher has been changed from a servlet to a servlet filter. The configuration is just as easy as for a servlet, and shown here:
Similar to the servlet configuration, the filter configuration defines a name (for reference) and the class of the filter. A filter mapping links the name with the URI pattern that will invoke the filter. By default, the extension is ".action". This is defined in the default.properties file (within the Struts2 JAR file) as the "struts.action.extension" property.
For Struts, the servlet configuration provides an init-param tag that defines the names of the files used to configure Struts. Struts2 does not have such a configuration parameter. Instead, the default configuration file for Struts2 has the name "struts.xml" and needs to be on the classpath of the web application.
Deconstructing the Actions
Lets look at the differences between the structures of the actions in each framework.
Let's first review the general structure of the Struts action. The general form of the Struts action looks like this:
public class MyAction extends Action { public ActionForward execute(ActionMapping mapping,ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { // do the work return (mapping.findForward("success" )); }}
When implementing a Struts action, you need to be aware of the following items:
- All actions have to extend the Action base class.
- All actions have to be thread-safe, as only a single action instance is created.
- Because the actions have to be thread-safe, all the objects that may be needed in the processing of the action are passed in the method signature.
- The name of the method that is invoked for the processing of the action is "execute" (there is a DispatchAction class available in Struts which can re-route the method to be executed to another method in the same action, however the initial entry point from the framework into the action is still the "execute" method).
- An ActionForward result is returned using a method from the ActionMapping class, most commonly via the "findForward" method call.
In contrast, the Struts2 action provides a much simpler implementation. Here's what it looks like:
public class MyAction { public String execute() throws Exception { // do the work return "success"; }}
The first thing you may have noticed is that the action doesn't extend any classes or interfaces. In fact, it goes further than this. By convention, the method invoked in the processing of an action is the "execute" method - but it doesn't have to be. Any method that follows the method signature public String methodName() can be invoked through configuration.
Next, the return object is a String. If you don't like the idea of string literals in your code, there is a helper interface Action available that provides the common results of "success", "none", "error", "input" and "login" as constants.
Finally, and perhaps the most revolutionary difference from the original Struts implementation, is that the method invoked in the processing of an action (the "execute" method) has no parameter. So how do you get access to the objects that you need to work with? The answer lies in the "inversion of control" or "dependency injection" pattern. The Spring Framework has popularized this pattern, however, the predecessor to Struts2 (WebWork) started using the pattern around the same time.
To understand the inversion of control better, let's look at an example where the processing of the action requires access to the current requests HttpServerRequest object.
The dependency injection mechanism used in this example is interface injection. As the name implies, with interface injection there is an interface that needs to be implemented. This interface contains setters, which in turn are used to provide data to the action. In our example we are using the ServletRequestAware interface, here it is:
public interface ServletRequestAware { public void setServletRequest(HttpServletR equest request);}
When we implement this interface, our simple action from above becomes a little more complex - but now we have a HttpServerRequest object to use.
public class MyAction implements ServletRequestAware { private HttpServletRequest request; public void setServletRequest(HttpServletR equest request) { this.request = request; } public String execute() throws Exception { // do the work using the request return Action.SUCCESS; }}
There are now class level attributes in the action - which, although not thread-safe, is actually okay. In Struts2, an action instance is created for each request. It's not shared and it's discarded after the request has been completed.
There is one last step left, and that is to associate the ServletConfigInterceptor interceptor with this action. This interceptor provides the functionality to obtain the HttpServletRequest and inject it into actions that implement the ServletRequestAware interface. The interceptor and the interface work hand-in-hand to provide the dependency injection to the action.
The benefit to this design is that the action is completely de-coupled from the framework. The action becomes a simple POJO that can also be used outside of the framework. And for those that encourage unit testing, testing a Struts2 action is going to be significantly easier than wrestling to get a Struts action into a StrutsTestCase or a MockStrutsTestCase unit test.
Differences b/w Struts 1 and Struts 2
| Feature | Struts 1 | Struts 2 |
| Action classes | Struts 1 requires Action classes to extend an abstract base class. A common problem in Struts 1 is programming to abstract classes instead of interfaces. | An Struts 2 Action may implement an Action interface, along with other interfaces to enable optional and custom services. Struts 2 provides a base ActionSupport class to implement commonly used interfaces. Albeit, the Action interface is not required. Any POJO object with a execute signature can be used as an Struts 2 Action object. |
| Threading Model | Struts 1 Actions are singletons and must be thread-safe since there will only be one instance of a class to handle all requests for that Action. The singleton strategy places restrictions on what can be done with Struts 1 Actions and requires extra care to develop. Action resources must be thread-safe or synchronized. | Struts 2 Action objects are instantiated for each request, so there are no thread-safety issues. (In practice, servlet containers generate many throw-away objects per request, and one more object does not impose a performance penalty or impact garbage collection.) |
| Servlet Dependency | Struts 1 Actions have dependencies on the servlet API since the HttpServletRequest and HttpServletResponse is passed to the execute method when an Action is invoked. | Struts 2 Actions are not coupled to a container. Most often the servlet contexts are represented as simple Maps, allowing Actions to be tested in isolation. Struts 2 Actions can still access the original request and response, if required. However, other architectural elements reduce or eliminate the need to access the HttpServetRequest or HttpServletResponse directly. |
| Testability | A major hurdle to testing Struts 1 Actions is that the execute method exposes the Servlet API. A third-party extension, Struts TestCase, offers a set of mock object for Struts 1. | Struts 2 Actions can be tested by instantiating the Action, setting properties, and invoking methods. Dependency Injection support also makes testing simpler. |
| Harvesting Input | Struts 1 uses an ActionForm object to capture input. Like Actions, all ActionForms must extend a base class. Since other JavaBeans cannot be used as ActionForms, developers often create redundant classes to capture input. DynaBeans can used as an alternative to creating conventional ActionForm classes, but, here too, developers may be redescribing existing JavaBeans. | Struts 2 uses Action properties as input properties, eliminating the need for a second input object. Input properties may be rich object types which may have their own properties. The Action properties can be accessed from the web page via the taglibs. Struts 2 also supports the ActionForm pattern, as well as POJO form objects and POJO Actions. Rich object types, including business or domain objects, can be used as input/output objects. The ModelDriven feature simplifies taglb references to POJO input objects. |
| Expression Language | Struts 1 integrates with JSTL, so it uses the JSTL EL. The EL has basic object graph traversal, but relatively weak collection and indexed property support. | Struts 2 can use JSTL, but the framework also supports a more powerful and flexible expression language called "Object Graph Notation Language" (OGNL). |
| Binding values into views | Struts 1 uses the standard JSP mechanism for binding objects into the page context for access. | Struts 2 uses a "ValueStack" technology so that the taglibs can access values without coupling your view to the object type it is rendering. The ValueStack strategy allows reuse of views across a range of types which may have the same property name but different property types. |
| Type Conversion | Struts 1 ActionForm properties are usually all Strings. Struts 1 uses Commons-Beanutils for type conversion. Converters are per-class, and not configurable per instance. | Struts 2 uses OGNL for type conversion. The framework includes converters for basic and common object types and primitives. |
| Validation | Struts 1 supports manual validation via a validate method on the ActionForm, or through an extension to the Commons Validator. Classes can have different validation contexts for the same class, but cannot chain to validations on sub-objects. | Struts 2 supports manual validation via the validate method and the XWork Validation framework. The Xwork Validation Framework supports chaining validation into sub-properties using the validations defined for the properties class type and the validation context. |
| Control Of Action Execution | Struts 1 supports separate Request Processors (lifecycles) for each module, but all the Actions in the module must share the same lifecycle. | Struts 2 supports creating different lifecycles on a per Action basis via Interceptor Stacks. Custom stacks can be created and used with different Actions, as needed. |
123passportphoto is a very easy to use passport photo website that provides six enhanced photos. I have never had an issue while using this ...
-
Effective October 31, 2014, Embassy Kingston does not accept interview applications from third country nationals Effective October...
-
Introduction/ History of Struts The first version of Struts was released in June 2001. It was born out of the idea that JSPs and servle...
-
123passportphoto is a very easy to use passport photo website that provides six enhanced photos. I have never had an issue while using this ...

