Basic MVC Architecture
Model View Controller or MVC as it is popularly called, is a software design pattern for developing web applications. A Model View Controller pattern is made up of the following three parts:
- Model - The lowest level of the pattern which is responsible for maintaining data.
- View - This is responsible for displaying all or a portion of the data to the user.
- Controller - Software Code that controls the interactions between the Model and View.
MVC is popular as it isolates the application logic from the user interface layer and supports separation of concerns. Here the Controller receives all requests for the application and then works with the Model to prepare any data needed by the View. The View then uses the data prepared by the Controller to generate a final presentable response. The MVC abstraction can be graphically represented as follows.
The model
The model is responsible for managing the data of the application. It responds to the request from the view and it also responds to instructions from the controller to update itself.
The view
A presentation of data in a particular format, triggered by a controller's decision to present the data. They are script based templating systems like JSP, ASP, PHP and very easy to integrate with AJAX technology.
The controller
The controller is responsible for responding to user input and perform interactions on the data model objects. The controller receives the input, it validates the input and then performs the business operation that modifies the state of the data model.
Struts2 is a MVC based framework. In the coming chapters, let us see how we can use the MVC methodology within Struts2.
Struts 2 Overview
Struts2 is popular and mature web application framework based on the MVC design pattern. Struts2 is not just the next version of Struts 1, but it is a complete rewrite of the Struts architecture.
The WebWork framework started off with Struts framework as the basis and its goal was to offer an enhanced and improved framework built on Struts to make web development easier for the developers.
After some time, the Webwork framework and the Struts community joined hands to create the famous Struts2 framework.
Struts 2 framework features:
Here are some of the great features that may force you to consider Struts2:
- POJO forms and POJO actions - Struts2 has done away with the Action Forms that were an integral part of the Struts framework. With Struts2, you can use any POJO to receive the form input. Similarly, you can now see any POJO as an Action class.
- Tag support - Struts2 has improved the form tags and the new tags allow the developers to write less code.
- AJAX support - Struts2 has recognised the take over by Web2.0 technologies, and has integrated AJAX support into the product by creating AJAX tags, that function very similar to the standard Struts2 tags.
- Easy Integration - Integration with other frameworks like Spring, Tiles and SiteMesh is now easier with a variety of integration available with Struts2.
- Template Support - Support for generating views using templates.
- Plugin Support - The core Struts2 behaviour can be enhanced and augmented by the use of plugins. A number of plugins are available for Struts2.
- Profiling - Struts2 offers integrated profiling to debug and profile the application. In addition to this, Struts also offers integrated debugging with the help of built in debugging tools.
- Easy to modify tags - Tag markups in Struts2 can be tweaked using Freemarker templates. This does not require JSP or java knowledge. Basic HTML, XML and CSS knowledge is enough to modify the tags.
- Promote less configuration - Struts2 promotes less configuration with the help of using default values for various settings. You don't have to configure something unless it deviates from the default settings set by Struts2.
- View Technologies: - Struts2 has a great support for multiple view options (JSP, Freemarker, Velocity and XSLT)
The above are just the top ten features of Struts 2 that makes it an entreprise ready framework.
Struts 2 disadvantages:
Though Struts 2 comes with a list of great features but I would not forget to mention few negative points about Struts 2 and would need lots of improvments:
- Bigger learning curve - To use MVC with Struts, you have to be comfortable with the standard JSP, Servlet APIs and a large & elaborate framework.
- Poor documentation - Compared to the standard servlet and JSP APIs, Struts has fewer online resources, and many first-time users find the online Apache documentation confusing and poorly organized.
- Less transparent - With Struts applications, there is a lot more going on behind the scenes than with normal Java-based Web applications which makes it difficult to understand the framework.
Final note, A good framework should provide generic behavior that many different types of applications can make use of it. Struts 2 is one of the best web framework and being highly used for the development of Rich Internet Applications (RIA).
Struts 2 Environment Setup
Our first task is to get a minimal Struts 2 application running. This chapter will guide you on how to prepare a development environment to start your work with Struts 2. I assume that you already have have JDK (5+), Tomcat and Eclipse installed on your machine. If you do not have these components installed then follow the given steps on fast track:
Step 1 - Setup Java Development Kit (JDK):
You can download the latest version of SDK from Oracle's Java site: Java SE Downloads. You will find instructions for installing JDK in downloaded files, follow the given instructions to install and configure the setup. Finally set PATH and JAVA_HOME environment variables to refer to the directory that contains java and javac, typically java_install_dir/bin and java_install_dir respectively.
If you are running Windows and installed the SDK in C:\jdk1.5.0_20, you would have to put the following line in your C:\autoexec.bat file.
set PATH=C:\jdk1.5.0_20\bin;%PATH% set JAVA_HOME=C:\jdk1.5.0_20
Alternatively, on Windows NT/2000/XP, you could also right-click on My Computer, select Properties, then Advanced, then Environment Variables. Then, you would update the PATH value and press the OK button.
On Unix (Solaris, Linux, etc.), if the SDK is installed in /usr/local/jdk1.5.0_20 and you use the C shell, you would put the following into your .cshrc file.
setenv PATH /usr/local/jdk1.5.0_20/bin:$PATH setenv JAVA_HOME /usr/local/jdk1.5.0_20
Alternatively, if you use an Integrated Development Environment (IDE) like Borland JBuilder, Eclipse, IntelliJ IDEA, or Sun ONE Studio, compile and run a simple program to confirm that the IDE knows where you installed Java, otherwise do proper setup as given document of the IDE.
Step 2 - Setup Apache Tomcat:
You can download the latest version of Tomcat from http://tomcat.apache.org/. Once you downloaded the installation, unpack the binary distribution into a convenient location. For example in C:\apache-tomcat-6.0.33 on windows, or /usr/local/apache-tomcat-6.0.33 on Linux/Unix and create CATALINA_HOME environment variable pointing to these locations.
Tomcat can be started by executing the following commands on windows machine, or you can simply double click on startup.bat
%CATALINA_HOME%\bin\startup.bat or C:\apache-tomcat-6.0.33\bin\startup.bat
Tomcat can be started by executing the following commands on Unix (Solaris, Linux, etc.) machine:
$CATALINA_HOME/bin/startup.sh or /usr/local/apache-tomcat-6.0.33/bin/startup.sh
After a successful startup, the default web applications included with Tomcat will be available by visiting http://localhost:8080/. If everything is fine then it should display following result:
Further information about configuring and running Tomcat can be found in the documentation included here, as well as on the Tomcat web site: http://tomcat.apache.org
Tomcat can be stopped by executing the following commands on windows machine:
%CATALINA_HOME%\bin\shutdown or C:\apache-tomcat-5.5.29\bin\shutdown
Tomcat can be stopped by executing the following commands on Unix (Solaris, Linux, etc.) machine:
$CATALINA_HOME/bin/shutdown.sh or /usr/local/apache-tomcat-5.5.29/bin/shutdown.sh
Step 3 - Setup Eclipse (IDE)
All the examples in this tutorial has been written using Eclipse IDE. So I would suggest you have latest version of Eclipse installed on your machine.
To install Eclipse dDownload the latest Eclipse binaries from http://www.eclipse.org/downloads/. Once you downloaded the installation, unpack the binary distribution into a convenient location. For example in C:\eclipse on windows, or /usr/local/eclipse on Linux/Unix and finally set PATH variable appropriately.
Eclipse can be started by executing the following commands on windows machine, or you can simply double click on eclipse.exe
%C:\eclipse\eclipse.exe
Eclipse can be started by executing the following commands on Unix (Solaris, Linux, etc.) machine:
$/usr/local/eclipse/eclipse
After a successful startup, if everything is fine then it should display following result:
Step 4 - Setup Struts2 Libraries
Now if everything is fine, then you can proceed to setup your Struts 2 faremwork. Following are the simple steps to download and install Struts2 on your machine.
- Make a choice whether you want to install Struts2 on Windows, or Unix and then proceed to the next step to download .zip file for windows and .tz file for Unix.
- Download the latest version of Struts2 binaries from http://struts.apache.org/download.cgi.
- At the time of writing this tutorial, I downloaded struts-2.0.14-all.zip and when you unzip the downloaded file it will give you directory structure inside C:\struts-2.2.3 as follows.
Second step is to extract the zip file in any location, I downloaded & extracted struts-2.2.3-all.zip in c:\folder on my Windows 7 machine so that I have all the jar files into C:\struts-2.2.3\lib. Make sure you set your CLASSPATH variable properly otherwise you will face problem while running your application.
Struts 2 Architecture
From a high level, Struts2 is a pull-MVC (or MVC2) framework. The Model-View-Controller pattern in Struts2 is realized with following five core components:
- Actions
- Interceptors
- Value Stack / OGNL
- Results / Result types
- View technologies
Struts 2 is slightly different from a traditional MVC framework in that the action takes the role of the model rather than the controller, although there is some overlap.
The above diagram depicts the Model, View and Controller to the Struts2 high level architecture. The controller is implemented with a Struts2 dispatch servlet filter as well as interceptors, the model is implemented with actions, and the view as a combination of result types and results. The value stack and OGNL provide common thread, linking and enabling integration between the other components.
Apart from the above components, there will be a lot of information that relates to configuration. Configuration for the web application, as well as configuration for actions, interceptors, results, etc.
This is the architectural overview of the Struts 2 MVC pattern. We will go through each component in more detail in the subsequent chapters.
Request life cycle:
Based on the above digram, one can explain the user's request life cycle in Struts 2 as follows:
- User sends a request to the server for requesting for some resource (i.e pages).
- The FilterDispatcher looks at the request and then determines the appropriate Action.
- Configured interceptors functionalities applies such as validation, file upload etc.
- Selected action is executed to perform the requested operation.
- Again, configured interceptors are applied to do any post-processing if required.
- Finally the result is prepared by the view and returns the result to the user.
Struts 2 Hello World Example
As you learnt from the Struts 2 architecture, when you click on a hyperlink or submit an HTML form in a Struts 2 web application, the input is collected by the Controller which is sent to a Java class called Actions. After the Action is executed, a Result selects a resource to render the response. The resource is generally a JSP, but it can also be a PDF file, an Excel spreadsheet, or a Java applet window.
Assume you already build-up your development environment. Now let us proceed for building our firstHello World struts2 project. The aim of this project is to build a web application that collects the user's name and displays "Hello World" followed by the user name. We would have to create following four components for any Struts 2 project:
SN | Components & Description |
---|---|
1 | Action Create an action class which will contain complete business logic and conrol the interaction between the user, the model, and the view. |
2 | Interceptors Create interceptors if required, or use existing interceptors. This is part of Controller. |
3 | View Create a JSPs to interact with the user to take input and to present the final messages. |
4 | Configuration Files Create configuration files to couple the Action, View and Controllers. These files are struts.xml, web.xml, struts.properties. |
I am going to use Eclipse IDE, so all the required components will be created under a Dynamic Web Project. So let us start with creating Dynamic Web Project.
Create a Dynamic Web Project:
Start your Eclipse and then go with File > New > Dynamic Web Project and enter project name asHelloWorldStruts2 and set rest of the options as given in the following screen:
Select all the default options in the next screens and finally check Generate Web.xml deployment descriptor option. This will create a dynamic web project for you in Eclipse. Now go with Windows > Show View > Project Explorer, and you will see your project window something as below:
Now copy following files from struts 2 lib folder C:\struts-2.2.3\lib to our project's WEB-INF\lib folder. To do this, you can simply drag and drop all the following files into WEB-INF\lib folder.
- commons-fileupload-x.y.z.jar
- commons-io-x.y.z.jar
- commons-lang-x.y.jar
- commons-logging-x.y.z.jar
- commons-logging-api-x.y.jar
- freemarker-x.y.z.jar
- javassist-.xy.z.GA
- ognl-x.y.z.jar
- struts2-core-x.y.z.jar
- xwork-core.x.y.z.jar
Create Action Class:
Action class is the key to Struts 2 application and we implement most of the business logic in action class. So let us create a java file HelloWorldAction.java under Java Resources > src with a package name com.only4programmers.struts2 with the contents given below.
The Action class responds to a user action when user clicks a URL. One or more of the Action class's methods are executed and a String result is returned. Based on the value of the result, a specific JSP page is rendered.
package com.only4programmers.struts2; public class HelloWorldAction{ private String name; public String execute() throws Exception { return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
This is a very simple class with one property called "name". We have standard getters and setter methods for the "name" property and an execute method that returns the string "success".
The Struts 2 framework will create an object of the HelloWorldAction class and call the execute method in response to a user's action. You put your business logic inside execute method and finally returns the String constant. Simply saying for for each URL, you would have to implement one action class and either you can use that class name directly as your action name or you can map to some other name using struts.xml file as shown below.
Create a View
We need a JSP to present the final message, this page will be called by Struts 2 framework when a predefined action will happen and this mapping will be defined in struts.xml file. So let us create the below jsp file HelloWorld.jsp in the WebContent folder in your eclipse project. To do this, right click on the WebContent folder in the project explorer and select New >JSP File.
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Hello World</title> </head> <body> Hello World, <s:property value="name"/> </body> </html>
The taglib directive tells the Servlet container that this page will be using the Struts 2 tags and that these tags will be preceded by s. The s:property tag displays the value of action class property "name> which is returned by the method getName() of the HelloWorldAction class.
Create main page:
We also need to create index.jsp in the WebContent folder. This file will serve as the initial action URL where a user can click to tell the Struts 2 framework to call the a defined method of the HelloWorldAction class and render the HelloWorld.jsp view.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World From Struts2</h1> <form action="hello"> <label for="name">Please enter your name</label><br/> <input type="text" name="name"/> <input type="submit" value="Say Hello"/> </form> </body> </html>
The hello action defined in the above view file will be mapped to the HelloWorldAction class and its execute method using struts.xml file. When a user clicks on the Submit button it will cause the Struts 2 framework to run the execute method defined in the HelloWorldAction class and based on the returned value of the method, an appropriate view will be selected and rendered as a response.
Configuration Files
We need a mapping to tie the URL, the HelloWorldAction class (Model), and the HelloWorld.jsp (the view) together. The mapping tells the Struts 2 framework which class will respond to the user's action (the URL), which method of that class will be executed, and what view to render based on the String result that method returns.
So let us create a file called struts.xml. Since Struts 2 requires struts.xml to be present in classes folder. So create struts.xml file under the WebContent/WEB-INF/classes folder. Eclipse does not create the "classes" folder by default, so you need to do this yourself. To do this, right click on the WEB-INF folder in the project explorer and select New > Folder. Your struts.xml should look like:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success">/HelloWorld.jsp</result> </action> </package> </struts>
Few words about the above configuration file. Here we set the constant struts.devMode to true, because we are working in development environment and we need to see some useful log messages. Then, we defined a package called helloworld. Creating a package is useful when you want to group your actions together. In our example, we named our action as "hello" which is corresponding to the URL /hello.action and is backed up by the HelloWorldAction.class. The execute method ofHelloWorldAction.class is the method that is run when the URL /hello.action is invoked. If the outcome of the execute method returns "success", then we take the user to HelloWorld.jsp.
Next step is to create a web.xml file which is an entry point for any request to Struts 2. The entry point of Struts2 application will be a filter defined in deployment descriptor (web.xml). Hence we will define an entry oforg.apache.struts2.dispatcher.FilterDispatcher class in web.xml. The web.xml file needs to be created under the WEB-INF folder under WebContent. Eclipse had already created a skelton web.xml file for you when you created the project. So, lets just modify it as follows:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
We have specified index.jsp to be our welcome file. Then we have configured the Struts2 filter to run on all urls (i.e, any url that match the pattern /*)
Enable Detailed Log:
You can enabled complete logging functionality while working with Struts 2 by creatinglogging.properties file under WEB-INF/classes folder. Keep the following two lines in your property file:
org.apache.catalina.core.ContainerBase.[Catalina].level = INFO org.apache.catalina.core.ContainerBase.[Catalina].handlers = \ java.util.logging.ConsoleHandler
The default logging.properties specifies a ConsoleHandler for routing logging to stdout and also a FileHandler. A handler's log level threshold can be set using SEVERE, WARNING, INFO, CONFIG, FINE, FINER, FINEST or ALL.
That's it. We are ready to run our Hello World application using Struts 2 framework.
Execute the Application
Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Enter a value "Struts2" and submit the page. You should see the next page
Note that you can define index as an action in struts.xml file and in that case you can call index page as http://localhost:8080/HelloWorldStruts2/index.action. Check below how you can define index as an action:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="index"> <result >/index.jsp</result> </action> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success">/HelloWorld.jsp</result> </action> </package> </struts>
Struts 2 Configuration Files
This chapter will take you through basic configuration required for a Struts 2 application. Here we will see what will be configured in few important configuration files : web.xml, struts.xml, struts-config.xml and struts.properties
Honestly speaking you can survive using web.xml and struts.xml configuration files and you have seen in previous chapter that our example worked using these two files, but for your knowledge let me explain other files as well.
The web.xml file:
The web.xml configuration file is a J2EE configuration file that determines how elements of the HTTP request are processed by the servlet container. It is not strictly a Struts2 configuration file, but it is a file that needs to be configured for Struts2 to work.
As discussed earlier, this file provides an entry point for any web application. The entry point of Struts2 application will be a filter defined in deployment descriptor (web.xml). Hence we will define an entry ofFilterDispatcher class in web.xml. The web.xml file needs to be created under the folderWebContent/WEB-INF.
This is the first configuration file you will need to configure if you are starting without the aid of a template or tool that generates it (such as Eclipse or Maven2). Following is the content of web.xml file which we used in our last example.
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Note that we map the Struts 2 filter to /*, and not to /*.action which means that all urls will be parsed by the struts filter. We will cover this when we will go through the Annotations chapter.
The struts.xml file:
The struts.xml file contains the configuration information that you will be modifying as actions are developed. This file can be used to override default settings for an application, for examplestruts.devMode = false and other settings which are defined in property file. This file can be created under the folder WEB-INF/classes.
Let us have a look at the struts.xml file we created in the Hello World example explained in previous chapter.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success">/HelloWorld.jsp</result> </action> <-- more actions can be listed here --> </package> <-- more packages can be listed here --> </struts>
The first thing to note is the DOCTYPE. All struts configuration file need to have the correct doctype as shown in our little example. <struts> is the root tag element, under which we declare different packages using <package> tags. Here <package> allows separation and modularization of the configuration. This is very useful when you have a large project and project is divided into different modules.
Say, if your project has three domains - business_applicaiton, customer_application and staff_application, you could create three packages and store associated actions in the appropriate package. The package tag has the following attributes:
Attribute | Description |
---|---|
name (required) | The unique identifier for the package |
extends | Which package does this package extend from? By default, we use struts-default as the base package. |
abstract | If marked true, the package is not available for end user consumption. |
namesapce | Unique namespace for the actions |
The constant tag along with name and value attributes will be used to override any of the following properties defined in default.properties, like we just set struts.devMode property. Settingstruts.devMode property allows us to see more debug messages in the log file.
We define action tags corresponds to every URL we want to access and we define a class with execute() method which will be accessed whenever we will access corresponding URL.
Results determine what gets returned to the browser after an action is executed. The string returned from the action should be the name of a result. Results are configured per-action as above, or as a "global" result, available to every action in a package. Results have optional name and type attributes. The default name value is "success".
Struts.xml file can grow big over time and so breaking it by packages is one way of modularizing it, but struts offers another way to modularize the struts.xml file. You could split the file into multiple xml files and import them in the following fashion.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <include file="my-struts1.xml"/> <include file="my-struts2.xml"/> </struts>
The other configuration file that we haven't covered is the struts-default.xml. This file contains the standard configuration settings for Struts and you would not have to touch these settings for 99.99% of your projects. For this reason, we are not going into too much detail on this file. If you are interested, take a look into the at the default.properties file available in struts2-core-2.2.3.jar file.
The struts-config.xml file:
The struts-config.xml configuration file is a link between the View and Model components in the Web Client but you would not have to touch these settings for 99.99% of your projects. The configuration file basically contains following main elements:
SN | Interceptor & Description |
---|---|
1 | struts-config This is the root node of the configuration file. |
2 | form-beans This is where you map your ActionForm subclass to a name. You use this name as an alias for your ActionForm throughout the rest of the struts-config.xml file, and even on your JSP pages. |
3 | global forwards This section maps a page on your webapp to a name. You can use this name to refer to the actual page. This avoids hardcoding URLs on your web pages. |
4 | action-mappings This is where you declare form handlers and they are also known as action mappings. |
5 | controller This section configures Struts internals and rarely used in practical situations. |
6 | plug-in This section tells Struts where to find your properties files, which contain prompts and error messages |
Following is the sample struts-config.xml file:
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd"> <struts-config> <!-- ========== Form Bean Definitions ============ --> <form-beans> <form-bean name="login" type="test.struts.LoginForm" /> </form-beans> <!-- ========== Global Forward Definitions ========= --> <global-forwards> </global-forwards> <!-- ========== Action Mapping Definitions ======== --> <action-mappings> <action path="/login" type="test.struts.LoginAction" > <forward name="valid" path="/jsp/MainMenu.jsp" /> <forward name="invalid" path="/jsp/LoginView.jsp" /> </action> </action-mappings> <!-- ========== Controller Definitions ======== --> <controller contentType="text/html;charset=UTF-8" debug="3" maxFileSize="1.618M" locale="true" nocache="true"/> </struts-config>
For more detail on struts-config.xml file, kindly check your struts documentation.
The struts.properties file
This configuration file provides a mechanism to change the default behavior of the framework. Actually all of the properties contained within the struts.properties configuration file can also be configured in the web.xml using the init-param, as well using the constant tag in the struts.xml configuration file. But if you like to keep the things separate and more struts specific then you can create this file under the folder WEB-INF/classes.
The values configured in this file will override the default values configured in default.properties which is contained in the struts2-core-x.y.z.jar distribution. There are a couple of properties that you might consider changing using struts.properties file:
### When set to true, Struts will act much more friendly for developers struts.devMode = true ### Enables reloading of internationalization files struts.i18n.reload = true ### Enables reloading of XML configuration files struts.configuration.xml.reload = true ### Sets the port that the server is run on struts.url.http.port = 8080
Here any line starting with hash (#) will be assumed as a comment and it will be ignored by Struts 2.
Struts 2 Actions
Actions are the core of the Struts2 framework, as they are for any MVC (Model View Controller) framework. Each URL is mapped to a specific action, which provides the processing logic necessary to service the request from the user.
But the action also serves in two other important capacities. First, the action plays an important role in the transfer of data from the request through to the view, whether its a JSP or other type of result. Second, the action must assist the framework in determining which result should render the view that will be returned in the response to the request.
Create Action:
The only requirement for actions in Struts2 is that there must be one no-argument method that returns either a String or Result object and must be a POJO. If the no-argument method is not specified, the default behavior is to use the execute() method.
Optionally you can extend the ActionSupport class which implements six interfaces including Actioninterface. The Action interface is as follows:
public interface Action { public static final String SUCCESS = "success"; public static final String NONE = "none"; public static final String ERROR = "error"; public static final String INPUT = "input"; public static final String LOGIN = "login"; public String execute() throws Exception; }
Let us take a look at the action method in the Hello World example:
package com.only4programmers.struts2; public class HelloWorldAction{ private String name; public String execute() throws Exception { return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
To illustrate the point that the action method controls the view, let us make the following change to theexecute method and extend the class ActionSupport as follows:
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class HelloWorldAction extends ActionSupport{ private String name; public String execute() throws Exception { if ("SECRET".equals(name)) { return SUCCESS; }else{ return ERROR; } } public String getName() { return name; } public void setName(String name) { this.name = name; } }
In this example, we have some logic in the execute method to look at the name attribute. If the attribute equals to the string "SECRET", we return SUCCESS as the result otherwise we return ERROR as the result. Because we have extended ActionSupport, so we can use String constants SUCCESS and ERROR. Now, let us modify our struts.xml file as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success">/HelloWorld.jsp</result> <result name="error">/AccessDenied.jsp</result> </action> </package> </struts>
Create a View
Let us create the below jsp file HelloWorld.jsp in the WebContent folder in your eclipse project. To do this, right click on the WebContent folder in the project explorer and select New >JSP File. This file will be called in case return result is SUCCESS which is a String constant "success" as defined in Action interface:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Hello World</title> </head> <body> Hello World, <s:property value="name"/> </body> </html>
Following is the file which will be invoked by the framework in case action result is ERROR which is equal to String constant "error". Following is the content of AccessDenied.jsp
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Access Denied</title> </head> <body> You are not authorized to view this page. </body> </html>
We also need to create index.jsp in the WebContent folder. This file will serve as the initial action URL where the user can click to tell the Struts 2 framework to call the execute method of the HelloWorldAction class and render the HelloWorld.jsp view.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World From Struts2</h1> <form action="hello"> <label for="name">Please enter your name</label><br/> <input type="text" name="name"/> <input type="submit" value="Say Hello"/> </form> </body> </html>
That's it, there is no change required web.xml file, so let us use same web.xml which we had created in Examples chapter. Now we are ready to run our Hello World application using Struts 2 framework.
Execute the Application
Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Let us enter a word as "SECRET" and you should see the following page:
Now enter any word other than "SECRET" and you should see the following page:
Create Multiple Actions:
You will frequently define more than one actions to handle different requests and to provide different URLs to the users, accordingly you will define different classes as defined below:
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; class MyAction extends ActionSupport{ public static String GOOD = SUCCESS; public static String BAD = ERROR; } public class HelloWorld extends ActionSupport{ ... public String execute() { if ("SECRET".equals(name)) return MyAction.GOOD; return MyAction.BAD; } ... } public class SomeOtherClass extends ActionSupport{ ... public String execute() { return MyAction.GOOD; } ... }
You will configure these actions in struts.xml file as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorld" method="execute"> <result name="success">/HelloWorld.jsp</result> <result name="error">/AccessDenied.jsp</result> </action> <action name="something" class="com.only4programmers.struts2.SomeOtherClass" method="execute"> <result name="success">/Something.jsp</result> <result name="error">/AccessDenied.jsp</result> </action> </package> </struts>
As you can see in the above hypothetical example, the action results SUCCESS and ERROR are duplicated. To get around this issue, it is suggested that you create a class that contain the result outcomes.
Struts 2 Interceptors
Interceptors are conceptually the same as servlet filters or the JDKs Proxy class. Interceptors allow for crosscutting functionality to be implemented separately from the action as well as the framework. You can achieve the following using interceptors:
- Providing preprocessing logic before the action is called.
- Providing postprocessing logic after the action is called.
- Catching exceptions so that alternate processing can be performed.
Many of the features provided in the Struts2 framework are implemented using interceptors; examples include exception handling, file uploading, lifecycle callbacks and validation etc. In fact, as Struts2 bases much of its functionality on interceptors, it is not unlikely to have 7 or 8 interceptors assigned per action.
Struts2 Framework Interceptors:
Struts 2 framework provides a good list of out-of-the-box interceptors that come preconfigured and ready to use. Few of the important interceptors are listed below:
SN | Interceptor & Description |
---|---|
1 | alias Allows parameters to have different name aliases across requests. |
2 | checkbox Assists in managing check boxes by adding a parameter value of false for check boxes that are not checked. |
3 | conversionError Places error information from converting strings to parameter types into the action's field errors. |
4 | createSession Automatically creates an HTTP session if one does not already exist. |
5 | debugging Provides several different debugging screens to the developer. |
6 | execAndWait Sends the user to an intermediary waiting page while the action executes in the background. |
7 | exception Maps exceptions that are thrown from an action to a result, allowing automatic exception handling via redirection. |
8 | fileUpload Facilitates easy file uploading. |
9 | i18n Keeps track of the selected locale during a user's session. |
10 | logger Provides simple logging by outputting the name of the action being executed. |
11 | params Sets the request parameters on the action. |
12 | prepare This is typically used to do pre-processing work, such as setup database connections. |
13 | profile Allows simple profiling information to be logged for actions. |
14 | scope Stores and retrieves the action's state in the session or application scope. |
15 | ServletConfig Provides the action with access to various servlet-based information. |
16 | timer Provides simple profiling information in the form of how long the action takes to execute. |
17 | token Checks the action for a valid token to prevent duplicate formsubmission. |
18 | validation Provides validation support for actions |
Please loook into Struts 2 documentation for complete detail on above mentioned interceptors. But I will show you how to use an interceptor in general in your Struts application.
How to use Interceptors?
Let us see how to use an already existing interceptor to our "Hello World" program. We will use thetimer interceptor whose purpose is to measure how long it took to execute an action method. Same time I'm using params interceptor whose purpose is to send the request parameters to the action. You can try your example without using this interceptor and you will find that name property is not being set because parameter is not able to reach to the action.
We will keep HelloWorldAction.java, web.xml, HelloWorld.jsp and index.jsp files as they have been created in Examples chapter but let us modify the struts.xml file to add an interceptor as follows
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <interceptor-ref name="params"/> <interceptor-ref name="timer" /> <result name="success">/HelloWorld.jsp</result> </action> </package> </struts>
Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Now enter any word in the given text box and click Say Hello button to execute the defined action. Now if you will check the log generated, you will find following text:
INFO: Server startup in 3539 ms 27/08/2011 8:40:53 PM com.opensymphony.xwork2.util.logging.commons.CommonsLogger info INFO: Executed action [//hello!execute] took 109 ms.
Here bottom line is being generated because of timer interceptor which is telling that action took total 109ms to be executed.
Create Custom Interceptors
Using custom interceptors in your application is an elegant way to provide cross-cutting application features. Creating a custom interceptor is easy; the interface that needs to be extended is the followingInterceptor interface:
public interface Interceptor extends Serializable{ void destroy(); void init(); String intercept(ActionInvocation invocation) throws Exception; }
As the names suggest, the init() method provides a way to initialize the interceptor, and the destroy() method provides a facility for interceptor cleanup. Unlike actions, interceptors are reused across requests and need to be thread-safe, especially the intercept() method.
The ActionInvocation object provides access to the runtime environment. It allows access to the action itself and methods to invoke the action and determine whether the action has already been invoked.
If you have no need for initialization or cleanup code, the AbstractInterceptor class can be extended. This provides a default no-operation implementation of the init() and destroy() methods.
Create Interceptor Class:
Let us create following MyInterceptor.java in Java Resources > src folder:
package com.only4programmers.struts2; import java.util.*; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; public class MyInterceptor extends AbstractInterceptor { public String intercept(ActionInvocation invocation)throws Exception{ /* let us do some pre-processing */ String output = "Pre-Processing"; System.out.println(output); /* let us call action or next interceptor */ String result = invocation.invoke(); /* let us do some post-processing */ output = "Post-Processing"; System.out.println(output); return result; } }
As you notice, actual action will be executed using the interceptor by invocation.invoke() call. So you can do some pre-processing and some post-processing based on your requirement.
The framework itself starts the process by making the first call to the ActionInvocation object's invoke(). Each time invoke() is called, ActionInvocation consults its state and executes whichever interceptor comes next. When all of the configured interceptors have been invoked, the invoke() method will cause the action itself to be executed. Following digram shows the same concept through a request flow:
Create Action Class:
Let us create a java file HelloWorldAction.java under Java Resources > src with a package namecom.only4programmers.struts2 with the contents given below.
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class HelloWorldAction extends ActionSupport{ private String name; public String execute() throws Exception { System.out.println("Inside action...."); return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
This is a same class which we have seen in previous examples. We have standard getters and setter methods for the "name" property and an execute method that returns the string "success".
Create a View
Let us create the below jsp file HelloWorld.jsp in the WebContent folder in your eclipse project.
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Hello World</title> </head> <body> Hello World, <s:property value="name"/> </body> </html>
Create main page:
We also need to create index.jsp in the WebContent folder. This file will serve as the initial action URL where a user can click to tell the Struts 2 framework to call the a defined method of the HelloWorldAction class and render the HelloWorld.jsp view.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World From Struts2</h1> <form action="hello"> <label for="name">Please enter your name</label><br/> <input type="text" name="name"/> <input type="submit" value="Say Hello"/> </form> </body> </html>
The hello action defined in the above view file will be mapped to the HelloWorldAction class and its execute method using struts.xml file.
Configuration Files
Now we need to register our interceptor and then call it as we had called default interceptor in previous example. To register a newly defined interceptor, the <interceptors>...</interceptors> tags are placed directly under the <package> tag ins struts.xml file. You can skip this step for a default interceptors as we did in our previous example. But here let us register and use it as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <interceptors> <interceptor name="myinterceptor" class="com.only4programmers.struts2.MyInterceptor" /> </interceptors> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <interceptor-ref name="params"/> <interceptor-ref name="myinterceptor" /> <result name="success">/HelloWorld.jsp</result> </action> </package> </struts>
It should be noted that you can register more than one interceptors inside <package> tag and same time you can call more than one interceptors inside the <action> tag. You can call same interceptor with the different actions.
The web.xml file needs to be created under the WEB-INF folder under WebContent as follows:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Now enter any word in the given text box and click Say Hello button to execute the defined action. Now if you will check the log generated, you will find following text at the bottom:
Pre-Processing Inside action.... Post-Processing
Stacking multiple Interceptors:
As you can imagine, having to configure multiple interceptor for each action would quickly become extremely unmanageable. For this reason, interceptors are managed with interceptor stacks. Here is an example, directly from the struts-default.xml file:
<interceptor-stack name="basicStack"> <interceptor-ref name="exception"/> <interceptor-ref name="servlet-config"/> <interceptor-ref name="prepare"/> <interceptor-ref name="checkbox"/> <interceptor-ref name="params"/> <interceptor-ref name="conversionError"/> </interceptor-stack>
The above stake is called basicStack and can be used in your configuration as shown below. This configuration node is placed under the <package .../> node. Each <interceptor-ref .../> tag references either an interceptor or an interceptor stack that has been configured before the current interceptor stack. It is therefore very important to ensure that the name is unique across all interceptor and interceptor stack configurations when configuring the initial interceptors and interceptor stacks.
We have already seen how to apply interceptor to the action, applying interceptor stacks is no different. In fact, we use exactly the same tag:
<action name="hello" class="com.only4programmers.struts2.MyAction"> <interceptor-ref name="basicStack"/> <result>view.jsp</result> </action
The above registration of "basicStack" will register complete stake of all the six interceptors with hello action. This should be noted that interceptors are executed in the order, in which they have been configured. For example, in above case, exception will be executed first, second would be servlet-config and so on.
Struts 2 Results and Result Types
As mentioned previously, the <results> tag plays the role of a view in the Struts2 MVC framework. The action is responsible for executing the business logic. The next step after executing the business logic is to display the view using the <results> tag.
Often there is some navigation rules attached with the results. For example, if the action method is to authenticate a user, there are three possible outcomes. (a) Successful Login (b) Unsuccessful Login - Incorrect username or password (c) Account Locked.
In this scenario, the action method will be configured with the three possible outcome strings and three different views to render the outcome. We have already seen this in the previous examples.
But, Struts2 does not tie you up with using JSP as the view technology. Afterall the whole purpose of the MVC paradigm is to keep the layers separate and highly configurable. For example, for a Web2.0 client, you may want to return XML or JSON as the output. In this case, you could create a new result type for XML or JSON and achieve this.
Struts comes with a number of predefined result types and whatever we've already seen that was the default result type dispatcher, which is used to dispatch to JSP pages. Struts allow you to use other markup languages for the view technology to present the results and popular choices include Velocity, Freemaker, XSLT and Tiles.
The dispatcher result type:
The dispatcher result type is the default type, and is used if no other result type is specified. It's used to forward to a servlet, JSP, HTML page, and so on, on the server. It uses the RequestDispatcher.forward()method.
We saw the "shorthand" version in our earlier examples, where we provided a JSP path as the body of the result tag.
<result name="success"> /HelloWorld.jsp </result>
We can also specify the JSP file using a <param name="location"> tag within the <result...> element as follows:
<result name="success" type="dispatcher"> <param name="location"> /HelloWorld.jsp </param > </result>
We can also supply a parse parameter, which is true by default. The parse parameter determines whether or not the location parameter will be parsed for OGNL expressions.
The FreeMaker result type:
In this example we are going to see how we can use FreeMaker as the view technology. Freemaker is a popular templating engine that is used to generate output using predefined templates. Let us create a Freemaker template file called hello.fm with the following contents:
Hello World ${name}
Here above file is a template where name is a paramter which will be passed from outside using the defined action. You will keep this file in your CLASSPATH. Next, let us modify the struts.xml to specify the result as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success" type="freemarker"> <param name="location">/hello.fm</param> </result> </action> </package> </struts>
Let us keep our HelloWorldAction.java, HelloWorldAction.jsp and index.jsp files as we have created them in examples chapter. Now Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Enter a value "Struts2" and submit the page. You should see the next page
As you can see, this is exactly same as the JSP view except that we are not tied to using JSP as the view technology. We have used Freemaker in this example.
The redirect result type:
The redirect result type calls the standard response.sendRedirect() method, causing the browser to create a new request to the given location.
We can provide the location either in the body of the <result...> element or as a <param name="location"> element. Redirect also supports the parse parameter. Here's an example configured using XML:
<action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success" type="redirect"> <param name="location"> /NewWorld.jsp </param > </result> </action>
So just modify your struts.xml file to define redirect type as mentioned above and create a new file NewWorld.jpg where you will be redirected whenever hello action will return success. You can checkStruts 2 Redirect Action example for better understanding.
Struts 2 Value Stack/OGNL
The Value Stack:
The value stack is a set of several objects which keeps the following objects in the provided order:
SN | Objects & Description |
---|---|
1 | Temporary Objects There are various temporary objects which are created during execution of a page. For example the current iteration value for a collection being looped over in a JSP tag. |
2 | The Model Object If you are using model objects in your struts application, the current model object is placed before the action on the value stack |
3 | The Action Object This will be the current action object which is being executed. |
4 | Named Objects These objects include #application, #session, #request, #attr and #parameters and refer to the corresponding servlet scopes |
The value stack can be accessed via the tags provided for JSP, Velocity or Freemarker. There are various tags which we will study in separate chapters, are used to get and set struts 2.0 value stack. You can get valueStack object inside your action as follows:
ActionContext.getContext().getValueStack()
Once you have a ValueStack object, you can use following methods to manipulate that object:
SN | ValueStack Methods & Description |
---|---|
1 | Object findValue(String expr) Find a value by evaluating the given expression against the stack in the default search order. |
2 | CompoundRoot getRoot() Get the CompoundRoot which holds the objects pushed onto the stack. |
3 | Object peek() Get the object on the top of the stack without changing the stack. |
4 | Object pop() Get the object on the top of the stack and remove it from the stack. |
5 | void push(Object o) Put this object onto the top of the stack. |
6 | void set(String key, Object o) Sets an object on the stack with the given key so it is retrievable by findValue(key,...) |
7 | void setDefaultType(Class defaultType) Sets the default type to convert to if no type is provided when getting a value. |
8 | void setValue(String expr, Object value) Attempts to set a property on a bean in the stack with the given expression using the default search order. |
9 | int size() Get the number of objects in the stack. |
The OGNL:
The Object-Graph Navigation Language (OGNL) is a powerful expression language that is used to reference and manipulate data on the ValueStack. OGNL also helps in data transfer and type conversion.
The OGNL is very similar to the JSP Expression Language. OGNL is based on the idea of having a root or default object within the context. The properties of the default or root object can be referenced using the markup notation, which is the pound symbol.
As mentioned earlier, OGNL is based on a context and Struts builds an ActionContext map for use with OGNL. The ActionContext map consists of the following:
- application - application scoped variables
- session - session scoped variables
- root / value stack - all your action variables are stored here
- request - request scoped variables
- parameters - request parameters
- atributes - the attributes stored in page, request, session and application scope
It is important to understand that the Action object is always available in the value stack. So, therefore if your Action object has properties x and y there are readily available for you to use.
Objects in the ActionContext are referred using the pound symbol, however, the objects in the value stack can be directly referenced, for example if employee is a property of an action class then it can ge referenced as follows:
<s:property value="name"/>
instead of
<s:property value="#name"/>
If you have an attribute in session called "login" you can retrieve it as follows:
<s:property value="#session.login"/>
OGNL also supports dealing with collections - namely Map, List and Set. For example to display a dropdown list of colors, you could do:
<s:select name="color" list="{'red','yellow','green'}" />
The OGNL expression is clever to interpret the "red","yellow","green" as colours and build a list based on that.
The OGNL expressions will be used extensively in the next chapters when we will study different tags. So rather than looking at them in isolation, let us look at it using some examples in the Form Tags / Control Tags / Data Tags and Ajax Tags section.
ValueStack/OGNL Example:
CREATE ACTION:
Let us consider following action class where we are accessing valueStack and then setting few keys which we will access using OGNL in our view ie. JSP page.
package com.only4programmers.struts2; import java.util.*; import com.opensymphony.xwork2.util.ValueStack; import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionSupport; public class HelloWorldAction extends ActionSupport{ private String name; public String execute() throws Exception { ValueStack stack = ActionContext.getContext().getValueStack(); Map<String, Object> context = new HashMap<String, Object>(); context.put("key1", new String("This is key1")); context.put("key2", new String("This is key2")); stack.push(context); System.out.println("Size of the valueStack: " + stack.size()); return "success"; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Actually, Struts 2 adds your action to the top of the valueStack when executed. So, the usual way to put stuff on the Value Stack is to add getters/setters for the values to your Action class and then use <s:property> tag to access the values. But I'm showing you how exactly ActionContext and ValueStack work in struts.
CREATE VIEWS
Let us create the below jsp file HelloWorld.jsp in the WebContent folder in your eclipse project. This view will be displayed in case action returns success:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Hello World</title> </head> <body> Entered value : <s:property value="name"/><br/> Value of key 1 : <s:property value="key1" /><br/> Value of key 2 : <s:property value="key2" /> <br/> </body> </html>
We also need to create index.jsp in the WebContent folder whose content is as follows:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World From Struts2</h1> <form action="hello"> <label for="name">Please enter your name</label><br/> <input type="text" name="name"/> <input type="submit" value="Say Hello"/> </form> </body> </html>
CONFIGURATION FILES
Following is the content of struts.xml file:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success">/HelloWorld.jsp</result> </action> </package> </struts>
Following is the content of web.xml file:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Now enter any word in the given text box and click "Say Hello" button to execute the defined action. Now if you will check the log generated, you will find following text at the bottom:
Size of the valueStack: 3
and this will display following screen, which will display whatever value you will enter and value of key1 and key2 which we had put on ValueStack.
Struts 2 File Uploads
The Struts 2 framework provides built-in support for processing file uploads using "Form-based File Upload in HTML". When a file is uploaded it will typically be stored in a temporary directory and they should be processed or moved by your Action class to a permanent directory to ensure the data is not lost.
Note that servers may have a security policy in place that prohibits you from writing to directories other than the temporary directory and the directories that belong to your web application.
File uploading in Struts is possible through a pre-defined interceptor called FileUpload interceptor which is available through the org.apache.struts2.interceptor.FileUploadInterceptor class and included as part of the defaultStack. Still you can use that in your struts.xml to set various paramters as we will see below.
Create view files:
Let us start with creating our view which will be required to browse and upload a selected file. So let us create a index.jsp with plain HTML upload form that allows the user to upload a file:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>File Upload</title> </head> <body> <form action="upload" method="post" enctype="multipart/form-data"> <label for="myFile">Upload your file</label> <input type="file" name="myFile" /> <input type="submit" value="Upload"/> </form> </body> </html>
There are couple of points worth noting in the above example. First of all, the form's enctype is set tomultipart/form-data. This should be set so that file uploads are handled successfully by the file upload interceptor. The next point noting is the form's action method upload and the name of the file upload field - which is myFile. We need this information to create the action method and the struts configuration.
Next let us create a simple jsp file success.jsp to display the outcome of our file upload in case it becomes success.
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>File Upload Success</title> </head> <body> You have successfully uploaded <s:property value="myFileFileName"/> </body> </html>
Following will be the result file error.jsp in case there is some error in uploading the file:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>File Upload Error</title> </head> <body> There has been an error in uploading the file. </body> </html>
Create action class:
Next let us create a Java class called uploadFile.java which will take care of uploading file and storing that file at a secure location:
package com.only4programmers.struts2; import java.io.File; import org.apache.commons.io.FileUtils; import java.io.IOException; import com.opensymphony.xwork2.ActionSupport; public class uploadFile extends ActionSupport{ private File myFile; private String myFileContentType; private String myFileFileName; private String destPath; public String execute() { /* Copy file to a safe location */ destPath = "C:/apache-tomcat-6.0.33/work/"; try{ System.out.println("Src File name: " + myFile); System.out.println("Dst File name: " + myFileFileName); File destFile = new File(destPath, myFileFileName); FileUtils.copyFile(myFile, destFile); }catch(IOException e){ e.printStackTrace(); return ERROR; } return SUCCESS; } public File getMyFile() { return myFile; } public void setMyFile(File myFile) { this.myFile = myFile; } public String getMyFileContentType() { return myFileContentType; } public void setMyFileContentType(String myFileContentType) { this.myFileContentType = myFileContentType; } public String getMyFileFileName() { return myFileFileName; } public void setMyFileFileName(String myFileFileName) { this.myFileFileName = myFileFileName; } }
The uploadFile.java is a very simple class. The important thing to note is that the FileUpload interceptor along with the Parameters Intercetpor does all the heavy lifting for us. The FileUpload interceptor makes three parameters available for you by default. They are named in the following pattern:
- [your file name parameter] - This is the actual file that the user has uploaded. In this example it will be "myFile"
- [your file name parameter]ContentType - This is the content type of the file that was uploaded. In this example it will be "myFileContentType"
- [your file name parameter]FileName - This is the name of the file that was uploaded. In this example it will be "myFileFileName"
The three parameters are available for us, thanks to the Struts Interceptors. All we have to do is to create three parameters with the correct names in our Action class and automaically these variables are auto wired for us. So, in the above example, we have three parameters and an action method that simply returns "success" if everything goes fine otherwise it returns "error".
Configuration files:
Following are the Struts2 configuration properties that control file uploading process:
SN | Properties & Description |
---|---|
1 | struts.multipart.maxSize The maximum size (in bytes) of a file to be accepted as a file upload. Default is 250M. |
2 | struts.multipart.parser The library used to upload the multipart form. By default is jakarta |
3 | struts.multipart.saveDir The location to store the temporary file. By default is javax.servlet.context.tempdir. |
In order to change any of theses settings you can use constant tag in your applications struts.xml file, as I did to change the maximum size of a file to be uploaded. Let us have our struts.xml as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <constant name="struts.multipart.maxSize" value="1000000" /> <package name="helloworld" extends="struts-default"> <action name="upload" class="com.only4programmers.struts2.uploadFile"> <result name="success">/success.jsp</result> <result name="error">/error.jsp</result> </action> </package> </struts>
Because FileUpload interceptor is a part of the defaultStack of interceptors, we do not need to configure it explicity. But you can add <interceptor-ref> tag inside <action>. The fileUpload interceptor takes two parameters (a) maximumSize and (b) allowedTypes. The maximumSize parameter sets the maximum file size allowed (the default is approximately 2MB).The allowedTypes parameter is a comma-separated list of accepted content (MIME) types as shown below:
<action name="upload" class="com.only4programmers.struts2.uploadFile"> <interceptor-ref name="basicStack"> <interceptor-ref name="fileUpload"> <param name="allowedTypes">image/jpeg,image/gif</param> </interceptor-ref> <result name="success">/success.jsp</result> <result name="error">/error.jsp</result> </action>
Following is the content of web.xml file:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Now Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/upload.jsp. This will give you following screen:
Now select a file "Contacts.txt" using Browse button and click upload button which will upload the file on your serve and you should see the net page. You can check the uploaded file should be saved in C:\apache-tomcat-6.0.33\work.
Note that the FileUpload Interceptor deletes the uploaded file automatically so you would have to save uploaded file programatically at some location before it gets deleted.
Error Messages:
The fileUplaod interceptor uses several default error message keys:
SN | Error Message Key & Description |
---|---|
1 | struts.messages.error.uploading A general error that occurs when the file could not be uploaded. |
2 | struts.messages.error.file.too.large Occurs when the uploaded file is too large as specified by maximumSize. |
3 | struts.messages.error.content.type.not.allowed Occurs when the uploaded file does not match the expected content types specified. |
You can override the text of these messages in WebContent/WEB-INF/classes/messages.propertiesresource files.
Struts 2 Database Access
This chapter will teah you how to access a database using Struts 2 in simple steps. Struts is a MVC framework and not a database framework but it provides excellent support for JPA/Hibernate integration. We shall look at the hibernate integration in a later chapter, but in this chapter we shall use plain old JDBC to access the database.
The first step in this chapter is to setup and prime our database. I am using MySQL as my database for this example. I have MySQL installed on my machine and I have created a new database called "struts_tutorial". I have created a table called login and populated it with some values. Below is the script I used to create and populate the table.
My MYSQL database has the default username "root" and "root123" password
CREATE TABLE `struts_tutorial`.`login` ( `user` VARCHAR( 10 ) NOT NULL , `password` VARCHAR( 10 ) NOT NULL , `name` VARCHAR( 20 ) NOT NULL , PRIMARY KEY ( `user` ) ) ENGINE = InnoDB; INSERT INTO `struts_tutorial`.`login` (`user`, `password`, `name`) VALUES ('scott', 'navy', 'Scott Burgemott');
Next step is to download the MySQL Connector jar file and placing this file in the WEB-INF\lib folder of your project. After we have done this, we are now ready to create the action class.
Create Action:
The action class has the properties corresponding to the columns in the database table. We haveuser, password and name as String attribues. In the action method, we use the user and password parameters to check if the user exists, if so , we display the user name in the next screen. If the user has entered wrong information, we send them to the login screen again. Following is the content ofLoginAction.java file:
package com.only4programmers.struts2; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import com.opensymphony.xwork2.ActionSupport; public class LoginAction extends ActionSupport { private String user; private String password; private String name; public String execute() { String ret = ERROR; Connection conn = null; try { String URL = "jdbc:mysql://localhost/struts_tutorial"; Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(URL, "root", "root123"); String sql = "SELECT name FROM login WHERE"; sql+=" user = ? AND password = ?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, user); ps.setString(2, password); ResultSet rs = ps.executeQuery(); while (rs.next()) { name = rs.getString(1); ret = SUCCESS; } } catch (Exception e) { ret = ERROR; } finally { if (conn != null) { try { conn.close(); } catch (Exception e) { } } } return ret; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Create main page:
Now, let us create a JSP file index.jsp to collect the username and password. This username and password will be checked against the database.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Login</title> </head> <body> <form action="loginaction" method="post"> User:<br/><input type="text" name="user"/><br/> Password:<br/><input type="password" name="password"/><br/> <input type="submit" value="Login"/> </form> </body> </html>
Create Views:
Now let us create success.jsp file which will be invoked in case action returns SUCCESS, but we will have another view file in case of an ERROR is returned from the action.
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Successful Login</title> </head> <body> Hello World, <s:property value="name"/> </body> </html>
Following will be the view file error.jsp in case of an ERROR is returned from the action.
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Invalid User Name or Password</title> </head> <body> Wrong user name or password provided. </body> </html>
Configuration Files:
Finally, let us put everything together using the struts.xml configuration file as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="loginaction" class="com.only4programmers.struts2.LoginAction" method="execute"> <result name="success">/success.jsp</result> <result name="error">/error.jsp</result> </action> </package> </struts>
Following is the content of web.xml file:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Now, right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Enter a wrong user name and password. You should see the net page
Now enter scott as user name and navy as password. You should see the net page
Struts 2 Sending Email
This chapter will teach you how yYou can send an email using your Struts 2 application. For this exercise, you need to download and install the mail.jar from JavaMail API 1.4.4 and place the mail.jarfile in your WEB-INF\lib folder and then proceed to follow the standard steps of creating action, view and configuration files.
Create Action:
The next step is to create an Action method that takes care of sending the email. Let us create a new class called Emailer.java with the following contents.
package com.only4programmers.struts2; import java.util.Properties; import javax.mail.Message; import javax.mail.PasswordAuthentication; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import com.opensymphony.xwork2.ActionSupport; public class Emailer extends ActionSupport { private String from; private String password; private String to; private String subject; private String body; static Properties properties = new Properties(); static { properties.put("mail.smtp.host", "smtp.gmail.com"); properties.put("mail.smtp.socketFactory.port", "465"); properties.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); properties.put("mail.smtp.auth", "true"); properties.put("mail.smtp.port", "465"); } public String execute() { String ret = SUCCESS; try { Session session = Session.getDefaultInstance(properties, new javax.mail.Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication(from, password); }}); Message message = new MimeMessage(session); message.setFrom(new InternetAddress(from)); message.setRecipients(Message.RecipientType.TO, InternetAddress.parse(to)); message.setSubject(subject); message.setText(body); Transport.send(message); } catch(Exception e) { ret = ERROR; e.printStackTrace(); } return ret; } public String getFrom() { return from; } public void setFrom(String from) { this.from = from; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getTo() { return to; } public void setTo(String to) { this.to = to; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } public static Properties getProperties() { return properties; } public static void setProperties(Properties properties) { Emailer.properties = properties; } }
As seen in the source code above, the Emailer.java has properties that correspond to the form attributes in the email.jsp page given below. These attributes are
- from - The email address of the sender. As we are using Google's SMTP, we need a valid gtalk id
- password - The password of the above account
- to - Who to send the email to?
- Subject - subject of the email
- body - The actual email message
We have not considered any validations on the above fields, validations will be added in the next chapter. Let us now look at the execute() method. The execute() method uses the javax Mail library to send an email using the supplied parameters. If the mail is sent successfuly, action returns SUCCESS, otherwise it returns ERROR.
Create main page:
Let us write main page JSP file index.jsp, which will be used to collect email related information mentioned above:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Email Form</title> </head> <body> <em>The form below uses Google's SMTP server. So you need to enter a gmail username and password </em> <form action="emailer" method="post"> <label for="from">From</label><br/> <input type="text" name="from"/><br/> <label for="password">Password</label><br/> <input type="password" name="password"/><br/> <label for="to">To</label><br/> <input type="text" name="to"/><br/> <label for="subject">Subject</label><br/> <input type="text" name="subject"/><br/> <label for="body">Body</label><br/> <input type="text" name="body"/><br/> <input type="submit" value="Send Email"/> </form> </body> </html>
Create Views:
We will use JSP file success.jsp which will be invoked in case action returns SUCCESS, but we will have another view file in case of an ERROR is returned from the action.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Email Success</title> </head> <body> Your email to <s:property value="to"/> was sent successfully. </body> </html>
Following will be the view file error.jsp in case of an ERROR is returned from the action.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Email Error</title> </head> <body> There is a problem sending your email to <s:property value="to"/>. </body> </html>
Configuration Files:
Now let us put everything together using the struts.xml configuration file as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="emailer" class="com.only4programmers.struts2.Emailer" method="execute"> <result name="success">/success.jsp</result> <result name="error">/error.jsp</result> </action> </package> </struts>
Following is the content of web.xml file:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Now, right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Enter the required information and click Send Email button. If everything goes fine then you should see the following page
Struts 2 Validations Framework
Now we will look into how Struts's validation framework. At Struts's core, we have the validation framework that assists the application to run the rules to perform validation before the action method is executed.
Client side validation is usually achieved using Javascript. But one should not rely upon client side validation alone. Best practise suggests that the validation should be introduced at all levels of your application framework. Now let us look at two ways of adding validation to our Struts project.
Here we will take an example of Employee whose name and age would be captured using a simple page and we will put two validation to make sure that use always enters a name and age should be in between 28 and 65. So let us start with the main JSP page of the example.
Create main page:
Let us write main page JSP file index.jsp, which will be used to collect Employee related information mentioned above.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Employee Form</title> </head> <body> <s:form action="empinfo" method="post"> <s:textfield name="name" label="Name" size="20" /> <s:textfield name="age" label="Age" size="20" /> <s:submit name="submit" label="Submit" align="center" /> </s:form> </body> </html>
The index.jsp makes use of Struts tag, which we have not covered yet but we will study them in tags related chapters. But for now, just assume that the s:textfield tag prints a input field, and the s:submit prints a submit button. We have used label property for each tag which creates label for each tag.
Create Views:
We will use JSP file success.jsp which will be invoked in case defined action returns SUCCESS.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Success</title> </head> <body> Employee Information is captured successfully. </body> </html>
Create Action:
So let us define a small action class Employee, and then add a method called validate() as shown below in Employee.java file. Make sure that your action class extends the ActionSupport class, otherwise your validate method will not be executed.
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class Employee extends ActionSupport{ private String name; private int age; public String execute() { return SUCCESS; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public void validate() { if (name == null || name.trim().equals("")) { addFieldError("name","The name is required"); } if (age < 28 || age > 65) { addFieldError("age","Age must be in between 28 and 65"); } } }
As shown in the above example, the validation method checks whether the 'Name' field has a value or not. If no value has been supplied, we add a field error for the 'Name' field with a custom error message. Secondly, we check if entered value for 'Age' field is in between 28 and 65 or not, if this condition does not meet we add an error above the validated field.
Configuration Files:
Finally, let us put everything together using the struts.xml configuration file as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="empinfo" class="com.only4programmers.struts2.Employee" method="execute"> <result name="input">/index.jsp</result> <result name="success">/success.jsp</result> </action> </package> </struts>
Following is the content of web.xml file:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Now, right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Now do not enter any required information, just click on Submit button. You will see following result:
Enter the required information but enter a wrong From field, let us say name as "test" and age as 30, and finally click on Submit button. You will see following result:
How this validation works?
When the user presses the submit button, Struts 2 will automatically execute the validate method and if any of the if statements listed inside the method are true, Struts 2 will call its addFieldError method. If any errors have been added then Struts 2 will not proceed to call the execute method. Rather the Struts 2 framework will return input as the result of calling the action.
So when validation fails and Struts 2 returns input, the Struts 2 framework will redisplay the index.jsp file. Since we used Struts 2 form tags, Struts 2 will automatically add the error messages just above the form filed.
These error messages are the ones we specified in the addFieldError method call. The addFieldError method takes two arguments. The first is the form field name to which the error applies and the second is the error message to display above that form field.
addFieldError("name","The name is required");
To handle the return value of input we need to add the following result to our action node in struts.xml.
<result name="input">/index.jsp</result>
XML Based Validation:
The second method of doing validation is by placing an xml file next to the action class. Struts2 XML based validation provides more options of validation like email validation, integer range validation, form validation field, expression validation, regex validation, required validation, requiredstring validation, stringlength validation and etc.
The xml file needs to be named '[action-class]'-validation.xml. So, in our case we create a file calledEmployee-validation.xml with the following contents:
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> <field name="name"> <field-validator type="required"> <message> The name is required. </message> </field-validator> </field> <field name="age"> <field-validator type="int"> <param name="min">29</param> <param name="max">64</param> <message> Age must be in between 28 and 65 </message> </field-validator> </field> </validators>
Above XML file would be kept in your CLASSPATH ideally along with class file. Let us have our Employee action class as follows without having validate() method:
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class Employee extends ActionSupport{ private String name; private int age; public String execute() { return SUCCESS; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Rest of the setup will remain as it is i the previous example, now if you will run the application, it will produce same result what we recieved in previous example.
The advantage of having an xml file to store the configuration allows the separation of the validation from the application code. You could get a developer to write the code and a business analyst to create the validation xml files. Another thing to note is the validator types that are available by default. There are plenty more validators that come by default with Struts. Common validators include Date Validator, Regex validator and String Length validator.
Struts2 Localization, internationalization (i18n)
Internationalization (i18n) is the process of planning and implementing products and services so that they can easily be adapted to specific local languages and cultures, a process called localization. The internationalization process is sometimes called translation or localization enablement. Internationalization is abbreviated i18n because the word starts with an i and ends with an n, and there are 18 characters between the first i and the last n.
Struts2 provides localization ie. internationalization (i18n) support through resource bundles, interceptors and tag libraries in the following places:
- The UI Tags
- Messages and Errors.
- Within action classes.
Resource Bundles:
Struts2 uses resource bundles to provide multiple language and locale options to the users of the web application. You don't need to worry about writing pages in different languages. All you have to do is to create a resource bundle for each language that you want. The resource bundles will contain titles, messages, and other text in the language of your user. Resource bundles are the file that contains the key/value pairs for the default language of your application.
The simplest naming format for a resource file is:
bundlename_language_country.properties
Here bundlename could be ActionClass, Interface, SuperClass, Model, Package, Global resource properties. Next part language_country represents the country locale for example Spanish (Spain) locale is represented by es_ES and English (United States) locale is represented by en_US etc. Here you can skip country part which is optional.
When you reference a message element by its key, Struts framework searches for a corresponding message bundle in the following order:
- ActionClass.properties
- Interface.properties
- SuperClass.properties
- model.properties
- package.properties
- struts.properties
- global.properties
To develop your application in multiple languages, you would have to maintain multiple property files corresponding to those languages/locale and define all the content in terms of key/value pairs. For example if you are going to develop your application for US English (Default), Spanish, and Franch the you would have to create three properties files. Here I will use global.properties file only, you can make use of different property files to segregate different type of messages.
- global.properties: By default English (United States) will be applied
- global_fr.properties: This will be used for Franch locale.
- global_es.properties: This will be used for Spanish locale.
Access the messages:
There are several ways to access the message resources, including getText, the text tag, key attribute of UI tags, and the i18n tag. Let us see them in brief:
To display i18n text, use a call to getText in the property tag, or any other tag, such as the UI tags as follows:
<s:property value="getText('some.key')" />
The text tag retrieves a message from the default resource bundle ie. struts.properties
<s:text name="some.key" />
The i18n tag pushes an arbitrary resource bundle on to the value stack. Other tags within the scope of the i18n tag can display messages from that resource bundle:
<s:i18n name="some.package.bundle"> <s:text name="some.key" /> </s:i18n>
The key attribute of most UI tags can be used to retrieve a message from a resource bundle:
<s:textfield key="some.key" name="textfieldName"/>
Localization Example:
Let us target to create index.jsp from the previous chapter in multiple languages. Same file would be written as follows:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Employee Form with Multilingual Support</title> </head> <body> <h1><s:text name="global.heading"/></h1> <s:url id="indexEN" namespace="/" action="locale" > <s:param name="request_locale" >en</s:param> </s:url> <s:url id="indexES" namespace="/" action="locale" > <s:param name="request_locale" >es</s:param> </s:url> <s:url id="indexFR" namespace="/" action="locale" > <s:param name="request_locale" >fr</s:param> </s:url> <s:a href="%{indexEN}" >English</s:a> <s:a href="%{indexES}" >Spanish</s:a> <s:a href="%{indexFR}" >France</s:a> <s:form action="empinfo" method="post" namespace="/"> <s:textfield name="name" key="global.name" size="20" /> <s:textfield name="age" key="global.age" size="20" /> <s:submit name="submit" key="global.submit" /> </s:form> </body> </html>
We will create success.jsp file which will be invoked in case defined action returns SUCCESS.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Success</title> </head> <body> <s:property value="getText('global.success')" /> </body> </html>
Here we would need to create following two actions. (a) First action a to take care of Locale and display same index.jsp file with different language (b) Another action is to take care of submitting form itself. Both the actions will return SUCCESS, but we will take different actions based on return values because our purpose is different for both the actions:
ACTION TO TAKE CARE OF LOCALE:
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class Locale extends ActionSupport{ public String execute() { return SUCCESS; } }
ACTION TO SUBMIT THE FORM:
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class Employee extends ActionSupport{ private String name; private int age; public String execute() { return SUCCESS; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Now. let us create following three global.properties files and put the in CLASSPATH:
GLOBAL.PROPERTIES:
global.name = Name global.age = Age global.submit = Submit global.heading = Select Locale global.success = Successfully authenticated
GLOBAL_FR.PROPERTIES:
global.name = Nom d'utilisateur global.age = l'âge global.submit = Soumettre des global.heading = Sé lectionnez Local global.success = Authentifi é avec succès
GLOBAL_ES.PROPERTIES:
global.name = Nombre de usuario global.age = Edad global.submit = Presentar global.heading = seleccionar la configuracion regional global.success = Autenticado correctamente
We will create our struts.xml with two actions as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <constant name="struts.custom.i18n.resources" value="global" /> <package name="helloworld" extends="struts-default" namespace="/"> <action name="empinfo" class="com.onlyprogrammers.struts2.Employee" method="execute"> <result name="input">/index.jsp</result> <result name="success">/success.jsp</result> </action> <action name="locale" class="com.only4programmers.struts2.Locale" method="execute"> <result name="success">/index.jsp</result> </action> </package> </struts>
Following is the content of web.xml file:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Now, right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Now select any of the languages, let us say we select Spanish, it would display the following result:
You can try with Franch language as well. Finally, let us try to click Submit button when we are in Spanish Locale, it would display the following screen:
Congratulations, now you have a multi-lingual webpage, you can launch your website globally.
Struts 2 Type Conversion
Everything on a HTTP request is treated as a String by the protocol. This includes numbers, booleans, integers, dates, decimals and everything else. Every thing is a string according to HTTP. However, in the Struts class, you could have properties of any data types. Howe does Struts autowire the properties for you ?
Struts uses a variety of type converters under the covers to do the heavy lifting. For example, if you have an integer attribute in your Action class, Struts automatically converts the request parameter to the integer attribute without you doing anything. By default, Struts comes with a number of type converters. Some of them are listed below and if you are using any of them then you have nothing to worry about:
- Integer, Float, Double, Decimal
- Date and Datetime
- Arrays and Collections
- Enumerations
- Boolean
- BigDecimal
Some times when you are using your own data type, it is necessary to add your own converters to make Struts aware how to convert those values before displaying. Consider the following POJO classEnvironment.java.
package com.only4programmers.struts2; public class Environment { private String name; public Environment(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
This is a very simple class that has an attribute called name, so nothing special about this class. Let us create another class that contains information about the system - SystemDetails.java. For the purpose of this exercices, I have hardcoded the Environment to "Development" and the Operating System to "Windows XP SP3". In a real project, you would get this information from the system configuration. So let us have following action class:
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class SystemDetails extends ActionSupport { private Environment environment = new Environment("Development"); private String operatingSystem = "Windows XP SP3"; public String execute() { return SUCCESS; } public Environment getEnvironment() { return environment; } public void setEnvironment(Environment environment) { this.environment = environment; } public String getOperatingSystem() { return operatingSystem; } public void setOperatingSystem(String operatingSystem) { this.operatingSystem = operatingSystem; } }
Next let us create a simple JSP file System.jsp to display the Environment and the Operating System information.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>System Details</title> </head> <body> Environment: <s:property value="environment"/><br/> Operating System:<s:property value="operatingSystem"/> </body> </html>
Let us wire the system.jsp and the SystemDetails.java class together using struts.xml. The SystemDetails class has a simple execute() method that returns the string "SUCCESS".
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="system" class="com.only4programmers.struts2.SystemDetails" method="execute"> <result name="success">/System.jsp</result> </action> </package> </struts>
Right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/system.action. This will give you following screen:
What is wrong with the above output? Struts knows how to display and convert the string "Windows XP SP3" and other built-in data types, but it does not know what to do with the the property of Environmenttype. So, it simply called the toString() method on the class. To resolve this problem, let us now create and register a simple TypeConverter for the Environment class. Create a class calledEnvironmentConverter.java with the following.
package com.only4programmers.struts2; import java.util.Map; import org.apache.struts2.util.StrutsTypeConverter; public class EnvironmentConverter extends StrutsTypeConverter { @Override public Object convertFromString(Map context, String[] values, Class clazz) { Environment env = new Environment(values[0]); return env; } @Override public String convertToString(Map context, Object value) { Environment env = (Environment) value; return env == null ? null : env.getName(); } }
The EnvironmentConverter extends the StrutsTypeConverter class and tells Struts how to convert Environment to a String and vice versa by overriding two methods convertFromString() andconvertToString(). Let us now register this converter before we us it in our application. There are two ways to register a converter. If the converter will be used only in a particular action, then you would have to create a property file needs to be named as '[action-class]'-converstion.properties, So, in our case we create a file called SystemDetails-converstion.properties with the following registration entery:
environment=com.only4programmers.struts2.EnvironmentConverter
In the above example, "environment" is the name of the property in the SystemDetails.java class and we are telling Struts to use the EnvironmentConverter for converting to and from this property. However, we are not going to do this, Instead we are going to register this converter globally so that it can be used throughout the application. To do this, create a property file called xwork-conversion.properties in the WEB-INF/classes folder with the following line:
com.only4programmers.struts2.Environment = \ com.only4programmers.struts2.EnvironmentConverter
This simply registers the converter globally, so that Struts can automatically do the conversion every time it encounters an object of type Environment. Now, if you re-compile and re-run the program, you will get a better output as follows:
Obviously, now result is better which means our Struts convertor is working fine. This is how you can create multiple convertors and register them to use as per your requirements.
Struts2 Themes and Templates
Before starting actual tutorial for this chapter, let us look into few definition as given byhttp://struts.apache.org:
Term | Description |
---|---|
tag | A small piece of code executed from within JSP, FreeMarker, or Velocity. |
template | A bit of code, usually written in FreeMarker, that can be rendered by certain tags (HTML tags). |
theme | A collection of templates packaged together to provide common functionality. |
I would also suggest to go through the Struts2 Localization chapter because we will take same example once again to perform our excercise.
When you use a Struts 2 tag such as <s:submit...>, <s:textfield...> etc in your web page, the Struts 2 framework generates HTML code with a preconfigured style and layout. Struts 2 comes with three built-in themes:
Theme | Description |
---|---|
simple theme | A minimal theme with no "bells and whistles". For example, the textfield tag renders the HTML <input/> tag without a label, validation, error reporting, or any other formatting or functionality. |
xhtml theme | This is the default theme used by Struts 2 and provides all the basics that the simple theme provides and adds several features like standard two-column table layout for the HTML, Labels for each of the HTML, Validation and error reporting etc. |
css_xhtml theme | This theme provides all the basics that the simple theme provides and adds several features like standard two-column CSS-based layout, using <div> for the HTML Struts Tags, Labels for each of the HTML Struts Tags, placed according to the CSS stylesheet. |
As mentioned above, if you don' t specify a theme, then Struts 2 will use the xhtml theme by default. For example this Struts 2 select tag:
<s:textfield name="name" label="Name" />
generates following HTML markup:
<tr> <td class="tdLabel"> <label for="empinfo_name" class="label">Name:</label> </td><td> <input type="text" name="name" value="" id="empinfo_name"/> </td> </tr>
Here empinfo is the action name defined in struts.xml file.
Selecting themes:
You can specify the theme on a per Struts 2 tag basis or you can use one of the following methods to specify what theme Struts 2 should use:
- The theme attribute on the specific tag
- The theme attribute on a tag's surrounding form tag
- The page-scoped attribute named "theme"
- The request-scoped attribute named "theme"
- The session-scoped attribute named "theme"
- The application-scoped attribute named "theme"
- The struts.ui.theme property in struts.properties (defaults to xhtml)
Following is the syntax to specify a them at tag level if you are willing to use different themes for different tags:
<s:textfield name="name" label="Name" theme="xhtml"/>
Because it is not very much practical to use themes on per tag basis, so simply we can specify the rule in struts.properties file using the following tags:
# Standard UI theme struts.ui.theme=xhtml # Directory where theme template resides struts.ui.templateDir=template # Sets the default template type. Either ftl, vm, or jsp struts.ui.templateSuffix=ftl
Following is the result we picked up from localization chapter where we used default theme with a setting struts.ui.theme=xhtml in struts-default.properties file which comes by default in struts2-core.xy.z.jar file.
How a theme works?
For a given theme, every struts tag has an associated template like s:textfield -> text.ftl ands:password -> password.ftl etc. These template files come zipped in struts2-core.xy.z.jar file. These template files keep a pre-defined HTML layout for each tag. So Struts 2 framework generates final HTML markup code using Sturts tags and associated templates.
Struts 2 tags + Associated template file = Final HTML markup code.
Default templates have been written in FreeMarker and they have extension .ftl. You can design your templates using velocity or JSP and accordingly set the configuration in struts.properties usingstruts.ui.templateSuffix and struts.ui.templateDir.
Creating new themes:
The simplest way to create a new theme is to copy any of the existing theme/template files and do required modifications. So let us start with creating a folder called template in WebContent/WEB-INF/classes and a sub-folder with the name of our new theme, for example WebContent/WEB-INF/classes/template/mytheme. From here, you can start building templates from scratch, or you can copy the templates from the Struts2 distribution and modify them as needed.
We are going to modify existing default template xhtml for learning purpose. So now let us copy the content from struts2-core-x.y.z.jar/template/xhtml to our theme directory and modify onlyWebContent/WEB-INF/classes/template/mytheme/control.ftl file. When we open control.ftl it will have following lines:
<table class="${parameters.cssClass?default('wwFormTable')?html}"<#rt/> <#if parameters.cssStyle??> style="${parameters.cssStyle?html}"<#rt/> </#if> >
Let us change above file control.ftl to have the following content:
<table style="border:1px solid black;">
If you will check form.ftl then you will find that control.ftl is being used in this file, but form.ftl is refering this file from xhtml theme. So let us change it as follows:
<#include "/${parameters.templateDir}/xhtml/form-validate.ftl" /> <#include "/${parameters.templateDir}/simple/form-common.ftl" /> <#if (parameters.validate?default(false))> onreset="${parameters.onreset?default('clearErrorMessages(this);\ clearErrorLabels(this);')}" <#else> <#if parameters.onreset??> onreset="${parameters.onreset?html}" </#if> </#if> > <#include "/${parameters.templateDir}/mytheme/control.ftl" />
I assume you would not have much understanding of the FreeMarker template language, still you can get a pretty good idea of what needs to be done by looking at the.ftl files. However, let us save above changes, and go back to our localization example and create WebContent/WEB-INF/classes/struts.properties file with the following content:
# Customized them struts.ui.theme=mytheme # Directory where theme template resides struts.ui.templateDir=template # Sets the template type to ftl. struts.ui.templateSuffix=ftl
Now after this change, right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2. This will give you following screen:
You can see a border around the form component which is a result of the change we did in out theme after copying it from xhtml theme. If you put little effort in learning FreeMarker, then you will be able to create or modify your themes very easily. Atleast now you must have a basic understanding on Sturts 2 themes and templates, isn't it?
Struts 2 Exception Handling
Struts provides an easier way to handle uncaught exception and redirect users to a dedicated error page. You can easily configure Struts to have different error pages for different execeptions.
Struts makes the exception handling easy by the use of the "exception" interceptor. The "exception" interceptor is included as part of the default stack, so you don't have to do anything extra to configure it. It is available out-of-the-box ready for you to use. Let us see a simple Hello World example with some modification in HelloWorldAction.java file. Here we purposely introduced a NullPointer Exception in ourHelloWorldAction action code.
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; public class HelloWorldAction extends ActionSupport{ private String name; public String execute(){ String x = null; x = x.substring(0); return SUCCESS; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Let us keep the content of HelloWorld.jsp as follows:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib prefix="s" uri="/struts-tags" %> <html> <head> <title>Hello World</title> </head> <body> Hello World, <s:property value="name"/> </body> </html>
Following is the content of index.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Hello World</title> </head> <body> <h1>Hello World From Struts2</h1> <form action="hello"> <label for="name">Please enter your name</label><br/> <input type="text" name="name"/> <input type="submit" value="Say Hello"/> </form> </body> </html>
Your struts.xml should look like:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success">/HelloWorld.jsp</result> </action> </package> </struts>
Now right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Enter a value "Struts2" and submit the page. You should see the following page:
As shown in the above example, the default exception interceptor does a great job of handling the exception. Let us now create a dedicated error page for our Exception. Create a file called Error.jsp with the following contents:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title></title> </head> <body> This is my custom error page </body> </html>
Let us now configure Struts to use this this error page in case of an exception. Let us modify thestruts.xml as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <exception-mapping exception="java.lang.NullPointerException" result="error" /> <result name="success">/HelloWorld.jsp</result> <result name="error">/Error.jsp</result> </action> </package> </struts>
As shown in the example above, now we have configured Struts to use the dedicated Error.jsp for the NullPointerException. If you rerun the program now, you shall now see the following output:
In addition to this, Struts2 framework comes with a "logging" interceptor to log the exceptions. By enabling the logger to log the uncaught exceptions, we can easily look at the stack trace and work out what went wrong.
Global Exception Mappings:
We have seen how we can handle action specific exception. We can set an exception globally which will apply to all the actions. For example, to catch the same NullPointerException exceptions, we could add <global-exception-mappings...> tag inside <package...> tag and its <result...> tag should be added inside the <action...> tag in struts.xml file as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <constant name="struts.devMode" value="true" /> <package name="helloworld" extends="struts-default"> <global-exception-mappings> <exception-mapping exception="java.lang.NullPointerException" result="error" /> </global-exception-mappings> <action name="hello" class="com.only4programmers.struts2.HelloWorldAction" method="execute"> <result name="success">/HelloWorld.jsp</result> <result name="error">/Error.jsp</result> </action> </package> </struts>
Struts 2 Annotations
As mentioned previously, Struts provides two forms of configuration. The traditional way is to use thestruts.xml file for all the configurations. We have seen so many examples of that in the tutorail so far. The other way of configuring Struts is by using the Java 5 Annotations feature. Using the struts annotations, we can achieve Zero Configuration.
To start using annotations in your project, make sure you have included following jar files in yourWebContent/WEB-INF/lib folder:
- struts2-convention-plugin-x.y.z.jar
- asm-x.y.jar
- antlr-x.y.z.jar
- commons-fileupload-x.y.z.jar
- commons-io-x.y.z.jar
- commons-lang-x.y.jar
- commons-logging-x.y.z.jar
- commons-logging-api-x.y.jar
- freemarker-x.y.z.jar
- javassist-.xy.z.GA
- ognl-x.y.z.jar
- struts2-core-x.y.z.jar
- xwork-core.x.y.z.jar
Now let us see how you can do away with the configuration available in the struts.xml file and replace it with annotaions.
To explain the concept of Annotation in Struts2, we would have to reconsider our validation example explained in Struts2 Validations chapter.
Here we will take an example of Employee whose name and age would be captured using a simple page and we will put two validation to make sure that use always enters a name and age should be in between 28 and 65. So let us start with the main JSP page of the example.
Create main page:
Let us write main page JSP file index.jsp, which will be used to collect Employee related information mentioned above.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Employee Form</title> </head> <body> <s:form action="empinfo" method="post"> <s:textfield name="name" label="Name" size="20" /> <s:textfield name="age" label="Age" size="20" /> <s:submit name="submit" label="Submit" align="center" /> </s:form> </body> </html>
The index.jsp makes use of Struts tag, which we have not covered yet but we will study them in tags related chapters. But for now, just assume that the s:textfield tag prints a input field, and the s:submit prints a submit button. We have used label property for each tag which creates label for each tag.
Create Views:
We will use JSP file success.jsp which will be invoked in case defined action returns SUCCESS.
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Success</title> </head> <body> Employee Information is captured successfully. </body> </html>
Create Action:
This is the place where annotation will be used. Let us re-define action class Employee with annotation, and then add a method called validate() as shown below in Employee.java file. Make sure that your action class extends the ActionSupport class, otherwise your validate method will not be executed.
package com.only4programmers.struts2; import com.opensymphony.xwork2.ActionSupport; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Result; import org.apache.struts2.convention.annotation.Results; import com.opensymphony.xwork2.validator.annotations.*; @Results({ @Result(name="success", location="/success.jsp"), @Result(name="input", location="/index.jsp") }) public class Employee extends ActionSupport{ private String name; private int age; @Action(value="/empinfo") public String execute() { return SUCCESS; } @RequiredFieldValidator( message = "The name is required" ) public String getName() { return name; } public void setName(String name) { this.name = name; } @IntRangeFieldValidator(message = "Age must be in between 28 and 65", min = "29", max = "65") public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
We have used few annotations in this example. Let me go through them one by one:
- First, we have included the Results annotation. A Results annotation is a collection of results. Under the results annotation, we have two result annotations. The result annotations have thename that correspond to the outcome of the execute method. They also contain a location as to which view should be served corresponding to return value from execute().
- The next annotation is the Action annotation. This is used to decorate the execute() method. The Action method also takes in a value which is the URL on which the action is invoked.
- Finally, I have used two validation annotations. I have configured the required field validator onname field and the integer range validator on the age field. I have also specified a custom message for the validations.
Configuration Files:
We really do not need struts.xml configuration file, so let us remove this file and let us check the content of web.xml file:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Struts 2</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.FilterDispatcher </filter-class> <init-param> <param-name>struts.devMode</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
Now, right click on the project name and click Export > WAR File to create a War file. Then deploy this WAR in the Tomcat's webapps directory. Finally, start Tomcat server and try to access URL http://localhost:8080/HelloWorldStruts2/index.jsp. This will give you following screen:
Now do not enter any required information, just click on Submit button. You will see following result:
Enter the required information but enter a wrong From field, let us say name as "test" and age as 30, and finally click on Submit button. You will see following result:
Struts 2 Annotations Types:
Struts 2 applications can use Java 5 annotations as an alternative to XML and Java properties configuration. You can check the list of most important annotations related to different categories:
Struts 2 Control Tags
The Struts 2 tags have a set of tags that make it easy to control the flow of page execution. Following is the list of important Struts 2 Control Tags:
The if and else tags:
These tags perform basic condition flow found in every language. 'If' tag could be used by itself or with 'Else If' Tag and/or single/multiple 'Else' Tag as shown below:
<s:if test="%{false}"> <div>Will Not Be Executed</div> </s:if> <s:elseif test="%{true}"> <div>Will Be Executed</div> </s:elseif> <s:else> <div>Will Not Be Executed</div> </s:else>
The iterator tags:
These iterator will iterate over a value. An iterable value can be any of: java.util.Collection, java.util.Iterator. While iterating over an iterator, you can use Sort tag to sort the result or SubSet tag to to get a sub set of the list or array.
The following example retrieves the value of the getDays() method of the current object on the value stack and uses it to iterate over. The <s:property/> tag prints out the current value of the iterator.
<s:iterator value="days"> <p>day is: <s:property/></p> </s:iterator>
The merge tag:
These merge tag take two or more lists as parameters and merge them all together as shown below:
<s:merge var="myMergedIterator"> <s:param value="%{myList1}" /> <s:param value="%{myList2}" /> <s:param value="%{myList3}" /> </s:merge> <s:iterator value="%{#myMergedIterator}"> <s:property /> </s:iterator>
The append tag:
These append tag take two or more lists as parameters and append them all together as shown below:
<s:append var="myAppendIterator"> <s:param value="%{myList1}" /> <s:param value="%{myList2}" /> <s:param value="%{myList3}" /> </s:append> <s:iterator value="%{#myAppendIterator}"> <s:property /> </s:iterator>
The generator tag:
These generator tag generates an iterator based on the val attribute supplied. Following generator tag generates an iterator and print it out using the iterator tag.
<s:generator val="%{'aaa,bbb,ccc,ddd,eee'}"> <s:iterator> <s:property /><br/> </s:iterator> </s:generator>
Struts 2 Data Tags
The Struts 2 data tags are primarily used to manipulate the data displayed on a page. Listed below are the important data tags:
The action tag:
This tag enables developers to call actions directly from a JSP page by specifying the action name and an optional namespace. The body content of the tag is used to render the results from the Action. Any result processor defined for this action in struts.xml will be ignored, unless the executeResult parameter is specified.
<div>Tag to execute the action</div> <br /> <s:action name="actionTagAction" executeResult="true" /> <br /> <div>To invokes special method in action class</div> <br /> <s:action name="actionTagAction!specialMethod" executeResult="true" />
The include tag:
These include will be used to include a JSP file in another JSP page.
<-- First Syntax --> <s:include value="myJsp.jsp" /> <-- Second Syntax --> <s:include value="myJsp.jsp"> <s:param name="param1" value="value2" /> <s:param name="param2" value="value2" /> </s:include> <-- Third Syntax --> <s:include value="myJsp.jsp"> <s:param name="param1">value1</s:param> <s:param name="param2">value2</s:param> </s:include>
The bean tag:
These bean tag instantiates a class that conforms to the JavaBeans specification. This tag has a body which can contain a number of Param elements to set any mutator methods on that class. If the var attribute is set on the BeanTag, it will place the instantiated bean into the stack's Context.
<s:bean name="org.apache.struts2.util.Counter" var="counter"> <s:param name="first" value="20"/> <s:param name="last" value="25" /> </s:bean>
The date tag:
These date tag will allow you to format a Date in a quick and easy way. You can specify a custom format (eg. "dd/MM/yyyy hh:mm"), you can generate easy readable notations (like "in 2 hours, 14 minutes"), or you can just fall back on a predefined format with key 'struts.date.format' in your properties file.
<s:date name="person.birthday" format="dd/MM/yyyy" /> <s:date name="person.birthday" format="%{getText('some.i18n.key')}" /> <s:date name="person.birthday" nice="true" /> <s:date name="person.birthday" />
The param tag:
These param tag can be used to parameterize other tags. This tag has the following two paramters.
- name (String) - the name of the parameter
- value (Object) - the value of the parameter
<pre> <ui:component> <ui:param name="key" value="[0]"/> <ui:param name="value" value="[1]"/> <ui:param name="context" value="[2]"/> </ui:component> </pre>
The property tag:
These property tag is used to get the property of a value, which will default to the top of the stack if none is specified.
<s:push value="myBean"> <!-- Example 1: --> <s:property value="myBeanProperty" /> <!-- Example 2: -->TextUtils <s:property value="myBeanProperty" default="a default value" /> </s:push>
The push tag:
These push tag is used to push value on stack for simplified usage.
<s:push value="user"> <s:propery value="firstName" /> <s:propery value="lastName" /> </s:push>
The set tag:
These set tag assigns a value to a variable in a specified scope. It is useful when you wish to assign a variable to a complex expression and then simply reference that variable each time rather than the complex expression. The scopes available are application, session, request, page and action.
<s:set name="myenv" value="environment.name"/> <s:property value="myenv"/>
The text tag:
These text tag is used to render a I18n text message.
<!-- First Example --> <s:i18n name="struts.action.test.i18n.Shop"> <s:text name="main.title"/> </s:i18n> <!-- Second Example --> <s:text name="main.title" /> <!-- Third Examlpe --> <s:text name="i18n.label.greetings"> <s:param >Mr Smith</s:param> </s:text>
The url tag:
These url tag is used to create a URL.
<-- Example 1 --> <s:url value="editGadget.action"> <s:param name="id" value="%{selected}" /> </s:url> <-- Example 2 --> <s:url action="editGadget"> <s:param name="id" value="%{selected}" /> </s:url> <-- Example 3--> <s:url includeParams="get"> <s:param name="id" value="%{'22'}" /> </s:url>
No comments:
Post a Comment