Controller
In this chapter, we dig further into the Controller components of the Struts framework. We begin by looking at three distinct Struts Controller components, including the ActionServlet class, the Action class, Plugins, and the RequestProcesser. The goal of this chapter is to provide a solid understanding of the Struts Controller components, and how they can be used and extended to create a robust and easily extended Web application.The ActionServlet Class
The org.apache.struts.action.ActionServlet is the backbone of all Struts applications. It is the main Controller component that handles client requests and determines which org.apache.struts.action.Action will process each received request. It acts as an Action factory by creating specific Action classes based on the user’s request.
While the ActionServlet sounds as if it might perform some extraordinary magic, it is a simple servlet. Just like any other HTTP servlet, it extends the class javax.servlet.http.HttpServlet and implements each of the HttpServlet’s life-cycle methods, including the init(), doGet(), doPost(), and destroy() methods.
The special behavior begins with the ActionServlet’s process() method. The process() method is the method that handles all requests. It has the following method signature:
protected void process(HttpServletRequest request, HttpServletResponse response);
When the ActionServlet receives a request, it completes the following steps:
1. The doPost() or doGet() methods receive a request and invoke the process() method.
2. The process() method gets the current RequestProcessor, and invokes its process() method.
Note: If we intend to extend the ActionServlet, the most logical place for customization is in the RequestProcessor object. It contains the logic that the Struts controller performs with each request from the container.
3. The RequestProcessor.process() method is where the current request is actually serviced. The RequestProcessor.process() method retrieves, from the struts-config.xml file, the <action> element that matches the path submitted on the request. It does this by matching the path passed in the <html:form /> tag’s action element to the <action> element with the same path value. An example of this match is shown below:
<html:form action="/Lookup" name="lookupForm"
type="wiley.LookupForm" >
<action path="/Lookup" type="wiley.LookupAction"
name="lookupForm" >
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>4. When the RequestProcessor.process() method has a matching <action>, it looks for a <form-bean> entry that has a name attribute that matches the <action> element’s name attribute. The following code snippet contains a sample match:
<form-beans>
<form-bean name="lookupForm"
type="wiley.LookupForm"/>
</form-beans>
<action path="/Lookup"
type="wiley.LookupAction"
name="lookupForm" > <forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>5. When the RequestProcessor.process() method knows the fully qualified name of the FormBean, it creates or retrieves a pooled instance of the ActionForm named by the <form-bean> element’s type attribute, and populates its data members with the values submitted on the request.
6. After the ActionForm’s data members are populated, the RequestProcessor.process() method calls the ActionForm.validate() method, which checks the validity of the submitted values.
7. At this point, the RequestProcessor.process() method knows all that it needs to know, and it is time to actually service the request. It does this by retrieving the fully qualified name of the Action class from the <action> element’s type attribute, creating or retrieving the named class, and calling the Action.execute() method.
8. When the Action class returns from its processing, its execute() method returns an ActionForward object that is used to determine the target of this transaction. The RequestProcessor.process() method resumes control, and the request is then forwarded to the determined target.
9. At this point, the ActionServlet instance has completed its processing for this request and is ready to service future requests.
While the ActionServlet sounds as if it might perform some extraordinary magic, it is a simple servlet. Just like any other HTTP servlet, it extends the class javax.servlet.http.HttpServlet and implements each of the HttpServlet’s life-cycle methods, including the init(), doGet(), doPost(), and destroy() methods.
The special behavior begins with the ActionServlet’s process() method. The process() method is the method that handles all requests. It has the following method signature:
protected void process(HttpServletRequest request, HttpServletResponse response);
When the ActionServlet receives a request, it completes the following steps:
1. The doPost() or doGet() methods receive a request and invoke the process() method.
2. The process() method gets the current RequestProcessor, and invokes its process() method.
Note: If we intend to extend the ActionServlet, the most logical place for customization is in the RequestProcessor object. It contains the logic that the Struts controller performs with each request from the container.
3. The RequestProcessor.process() method is where the current request is actually serviced. The RequestProcessor.process() method retrieves, from the struts-config.xml file, the <action> element that matches the path submitted on the request. It does this by matching the path passed in the <html:form /> tag’s action element to the <action> element with the same path value. An example of this match is shown below:
<html:form action="/Lookup" name="lookupForm"
type="wiley.LookupForm" >
<action path="/Lookup" type="wiley.LookupAction"
name="lookupForm" >
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>4. When the RequestProcessor.process() method has a matching <action>, it looks for a <form-bean> entry that has a name attribute that matches the <action> element’s name attribute. The following code snippet contains a sample match:
<form-beans>
<form-bean name="lookupForm"
type="wiley.LookupForm"/>
</form-beans>
<action path="/Lookup"
type="wiley.LookupAction"
name="lookupForm" > <forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>5. When the RequestProcessor.process() method knows the fully qualified name of the FormBean, it creates or retrieves a pooled instance of the ActionForm named by the <form-bean> element’s type attribute, and populates its data members with the values submitted on the request.
6. After the ActionForm’s data members are populated, the RequestProcessor.process() method calls the ActionForm.validate() method, which checks the validity of the submitted values.
7. At this point, the RequestProcessor.process() method knows all that it needs to know, and it is time to actually service the request. It does this by retrieving the fully qualified name of the Action class from the <action> element’s type attribute, creating or retrieving the named class, and calling the Action.execute() method.
8. When the Action class returns from its processing, its execute() method returns an ActionForward object that is used to determine the target of this transaction. The RequestProcessor.process() method resumes control, and the request is then forwarded to the determined target.
9. At this point, the ActionServlet instance has completed its processing for this request and is ready to service future requests.
Extending the ActionServlet
Now that we have seen what the ActionServlet is and how it is configured, let’s look at how it can be extended to provide additional functionality. As we might have guessed, there are several different ways in which the ActionServlet can be extended, and we are going to examine just one of them. This examination, however, should provide the foundation we need to extend the ActionServlet for our own uses.
To develop our own ActionServlet, we must complete the following four steps. We will perform each of these steps when creating our custom ActionServlet.
1. Create a class that extends the org.apache.struts.action.ActionServlet class.
2. Implement the methods specific to our business logic.
3. Compile the new ActionServlet and move it into the Web application’s classpath.
4. Add a <servlet> element to the application’s web.xml file; name the new ActionServlet as the mapping to the .do extension.
In the 1.0x version of Struts, this was very common method of extending the ActionServlet. As of Struts 1.1, it is more appropriate to extend a RequestProcessor to modify the default ActionServlet processing.
To develop our own ActionServlet, we must complete the following four steps. We will perform each of these steps when creating our custom ActionServlet.
1. Create a class that extends the org.apache.struts.action.ActionServlet class.
2. Implement the methods specific to our business logic.
3. Compile the new ActionServlet and move it into the Web application’s classpath.
4. Add a <servlet> element to the application’s web.xml file; name the new ActionServlet as the mapping to the .do extension.
In the 1.0x version of Struts, this was very common method of extending the ActionServlet. As of Struts 1.1, it is more appropriate to extend a RequestProcessor to modify the default ActionServlet processing.
Configuring the ActionServlet
Now that we have a solid understanding of how the ActionServlet performs its duties, let’s take a look at how it is deployed and configured. The ActionServlet is like any other servlet and is configured using a web.xml <serlvet> element.
We can take many approaches when setting up an ActionServlet. We can go with a bare-bones approach or we can get more serious and include any combination of the available initialization parameters described --
The Initialization Parameters of the ActionServlet
bufferSize : Names the size of the input buffer used when uploading files. The default value is 4096 bytes. (optional)
Config : Names the context-relative path to the struts-config.xml file. The default location is in the /WEB-INF/struts-config.xml directory. (optional)
We can take many approaches when setting up an ActionServlet. We can go with a bare-bones approach or we can get more serious and include any combination of the available initialization parameters described --
The Initialization Parameters of the ActionServlet
bufferSize : Names the size of the input buffer used when uploading files. The default value is 4096 bytes. (optional)
Config : Names the context-relative path to the struts-config.xml file. The default location is in the /WEB-INF/struts-config.xml directory. (optional)
Content : Names the content type and character encoding to be set on each response.
The default value is text/html. (optional)
debug : Determines the debugging level for the ActionServlet. The default value is 0, which turns debugging off. (optional)
detail : Sets the debug level for the Digester object, which is used during ActionServlet initialization. The default value is 0. (optional)
factory : Names the fully qualified class name of the object used to create the application's MessageResources object. The default value is org.apache.struts.util.PropertyMessageResourcesFactory.
In most cases, the default class will handle our application needs.
(optional)
locale : If set to true and the requesting client has a valid session, then the Locale object is stored in the user's session bound to the key Action.LOCALE_KEY. The default value is true.
(optional)
mapping : Names the fully qualified class name of the ActionMapping implementation used to describe each Action deployed to this application. The default value is org.apache.struts.action.ActionMapping. We will create our own ActionMapping extension in this Chapter, "Creating Custom ActionMappings." (optional)
maxFileSize : Names the maximum file size (in bytes) of a file to be uploaded to a Struts application. This value can be expressed using K, M, or G, understood as kilobytes, megabytes, or gigabytes, respectively. The default size is 250M. (optional)
multipartClass: Names the fully qualified class of the MultipartRequestHandler implementation to be used when file uploads are being processed.
The default value is org.apache.struts.upload.DiskMultipartRequestHandler. (optional)
nocache : If set to true, will add the appropriate HTTP headers to every response, turning off browser caching. This parameter is very useful when the client browser is not reflecting our application changes. The
default value is false. (optional)
null : If set to true, will cause the Struts application resources to return null, as opposed to an error message, if it cannot find the requested key in the application resource bundle. The default value for this parameter is
true. (optional)
tempDir : Names a directory to use as a temporary data store when file uploads are being processed. The default value is determined by the container hosting the application. (optional)
validate : If set to true, tells the ActionServlet that we are using the configuration file format defined as of Struts 1.0. The default value is true.
(optional)
validating : If set to true, tells the ActionServlet that we want to validate the strutconfig.
xml file against its DTD. While this parameter is optional, it is highly recommended, and therefore the default is set to true.
While none of these initialization parameters are required, the most common ones include the config, application, and mapping parameters. It is also common practice to use a <load-onstartup> element to ensure that the ActionServlet is started when the container starts the Web application. An example <serlvet> entry, describing an ActionServlet, is shown in the following code snippet:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>mapping</param-name>
<param-value>wiley.WileyActionMapping</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
The default value is text/html. (optional)
debug : Determines the debugging level for the ActionServlet. The default value is 0, which turns debugging off. (optional)
detail : Sets the debug level for the Digester object, which is used during ActionServlet initialization. The default value is 0. (optional)
factory : Names the fully qualified class name of the object used to create the application's MessageResources object. The default value is org.apache.struts.util.PropertyMessageResourcesFactory.
In most cases, the default class will handle our application needs.
(optional)
locale : If set to true and the requesting client has a valid session, then the Locale object is stored in the user's session bound to the key Action.LOCALE_KEY. The default value is true.
(optional)
mapping : Names the fully qualified class name of the ActionMapping implementation used to describe each Action deployed to this application. The default value is org.apache.struts.action.ActionMapping. We will create our own ActionMapping extension in this Chapter, "Creating Custom ActionMappings." (optional)
maxFileSize : Names the maximum file size (in bytes) of a file to be uploaded to a Struts application. This value can be expressed using K, M, or G, understood as kilobytes, megabytes, or gigabytes, respectively. The default size is 250M. (optional)
multipartClass: Names the fully qualified class of the MultipartRequestHandler implementation to be used when file uploads are being processed.
The default value is org.apache.struts.upload.DiskMultipartRequestHandler. (optional)
nocache : If set to true, will add the appropriate HTTP headers to every response, turning off browser caching. This parameter is very useful when the client browser is not reflecting our application changes. The
default value is false. (optional)
null : If set to true, will cause the Struts application resources to return null, as opposed to an error message, if it cannot find the requested key in the application resource bundle. The default value for this parameter is
true. (optional)
tempDir : Names a directory to use as a temporary data store when file uploads are being processed. The default value is determined by the container hosting the application. (optional)
validate : If set to true, tells the ActionServlet that we are using the configuration file format defined as of Struts 1.0. The default value is true.
(optional)
validating : If set to true, tells the ActionServlet that we want to validate the strutconfig.
xml file against its DTD. While this parameter is optional, it is highly recommended, and therefore the default is set to true.
While none of these initialization parameters are required, the most common ones include the config, application, and mapping parameters. It is also common practice to use a <load-onstartup> element to ensure that the ActionServlet is started when the container starts the Web application. An example <serlvet> entry, describing an ActionServlet, is shown in the following code snippet:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>
org.apache.struts.action.ActionServlet
</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param>
<param-name>mapping</param-name>
<param-value>wiley.WileyActionMapping</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
The Action Class
The second component of a Struts Controller is the org.apache.struts.action. Action class must and will be extended for each specialized Struts function in our application. The collection of the Action classes that belong to our Struts application is what defines our Web application.
To begin our discussion of the Action class, we must first look at some of the Action methods that are more commonly overridden or leveraged when creating an extended Action class. The following sections describe five of these methods.
The execute() Method
The execute() method is where our application logic begins. It is the method that we need to override when defining our own Actions. The Struts framework defines two execute() methods.
The first execute() implementation is used when we are defining custom Actions that are not HTTP-specific. This implementation of the execute() method would be analogous to the javax.serlvet.GenericServlet class. The signature of this execute() method is
public ActionForward execute(ActionMapping mapping,ActionForm
form,ServletRequest request,ServletResponse response)
throws IOException, ServletExceptionWe will notice that this method receives, as its third and fourth parameter, a ServletRequest and a ServletResponse object, as opposed to the HTTP-specific equivalents HttpServletRequest and HttpServletResponse.
The second execute() implementation is used when we are defining HTTP- specific custom Actions. This implementation of the execute() method would be analogous to the javax.serlvet.http.HttpServlet class.
To begin our discussion of the Action class, we must first look at some of the Action methods that are more commonly overridden or leveraged when creating an extended Action class. The following sections describe five of these methods.
The execute() Method
The execute() method is where our application logic begins. It is the method that we need to override when defining our own Actions. The Struts framework defines two execute() methods.
The first execute() implementation is used when we are defining custom Actions that are not HTTP-specific. This implementation of the execute() method would be analogous to the javax.serlvet.GenericServlet class. The signature of this execute() method is
public ActionForward execute(ActionMapping mapping,ActionForm
form,ServletRequest request,ServletResponse response)
throws IOException, ServletExceptionWe will notice that this method receives, as its third and fourth parameter, a ServletRequest and a ServletResponse object, as opposed to the HTTP-specific equivalents HttpServletRequest and HttpServletResponse.
The second execute() implementation is used when we are defining HTTP- specific custom Actions. This implementation of the execute() method would be analogous to the javax.serlvet.http.HttpServlet class.
The signature of this execute() method is
public ActionForward execute(ActionMapping mapping,
ActionForm form,HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletExceptionWe will notice that this method receives, as its third and fourth parameter, a HttpServletRequest and a HttpServletResponse object, as opposed to the previously listed execute() method. This implementation of the execute() method is the implementation that we will most often extend.
Parameters of the Action.execute() method.
ActionMapping : Contains all of the deployment information for a particular Action bean. This class will be used to determine where the results of the LoginAction will be sent after its processing is complete.
ActionForm : Represents the Form inputs containing the request parameters from the View referencing this Action bean. The reference being passed to our LoginAction points to an instance of our LoginForm.
HttpServletRequest : Is a reference to the current HTTP request object.
HttpServletResponse : Is a reference to the current HTTP response object.
Extending the Action Class
Now that we have seen the Action class and some of its configuration options, let’s see how we can create our own Action class.
To develop our own Action class, we must complete the following steps. These steps describe the minimum actions that must be completed when creating a new Action:
public ActionForward execute(ActionMapping mapping,
ActionForm form,HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletExceptionWe will notice that this method receives, as its third and fourth parameter, a HttpServletRequest and a HttpServletResponse object, as opposed to the previously listed execute() method. This implementation of the execute() method is the implementation that we will most often extend.
Parameters of the Action.execute() method.
ActionMapping : Contains all of the deployment information for a particular Action bean. This class will be used to determine where the results of the LoginAction will be sent after its processing is complete.
ActionForm : Represents the Form inputs containing the request parameters from the View referencing this Action bean. The reference being passed to our LoginAction points to an instance of our LoginForm.
HttpServletRequest : Is a reference to the current HTTP request object.
HttpServletResponse : Is a reference to the current HTTP response object.
Extending the Action Class
Now that we have seen the Action class and some of its configuration options, let’s see how we can create our own Action class.
To develop our own Action class, we must complete the following steps. These steps describe the minimum actions that must be completed when creating a new Action:
1. Create a class that extends the org.apache.struts.action.Action class.
2. Implement the appropriate execute() method and our specific to our business logic.
3. Compile the new Action and move it into the Web application’s classpath.
4. Add an <action> element to the application’s struts-config.xml file describing the new Action.
An example execute() implementation is listed in the following snippet. We will be extending the Action class throughout the remainder of this text.
public ActionForward execute(ActionMapping mapping,ActionForm
form,HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException {
Double price = null;
// Default target to success
String target = new String("success");
if ( form != null ) {
// Use the LookupForm to get the request parameters
LookupForm lookupForm = (LookupForm)form;
String symbol = lookupForm.getSymbol();
price = getQuote(symbol);
}
// Set the target to failure
if ( price == null ) {
target = new String("failure");
}
else {
request.setAttribute("PRICE", price);
}
// Forward to the appropriate View
return (mapping.findForward(target));
}
2. Implement the appropriate execute() method and our specific to our business logic.
3. Compile the new Action and move it into the Web application’s classpath.
4. Add an <action> element to the application’s struts-config.xml file describing the new Action.
An example execute() implementation is listed in the following snippet. We will be extending the Action class throughout the remainder of this text.
public ActionForward execute(ActionMapping mapping,ActionForm
form,HttpServletRequest request,HttpServletResponse response)
throws IOException, ServletException {
Double price = null;
// Default target to success
String target = new String("success");
if ( form != null ) {
// Use the LookupForm to get the request parameters
LookupForm lookupForm = (LookupForm)form;
String symbol = lookupForm.getSymbol();
price = getQuote(symbol);
}
// Set the target to failure
if ( price == null ) {
target = new String("failure");
}
else {
request.setAttribute("PRICE", price);
}
// Forward to the appropriate View
return (mapping.findForward(target));
}
Configuring the Action Class
Now that we have seen the major methods of the Action class, let’s examine its configuration options. The Action class is a Struts-specific object, and therefore must be configured using the struts-config.xml file. The element that is used to configure a Struts action is an <action> element. The class that defines the <action> element’s attributes is the org.apache. struts.action.ActionMappings class. Next Table describes the attributes of an <action> element as they are defined by the default ActionMappings class.
Note When using an <action> element to describe an Action class, we are describing only one instance of the named Action class. There is nothing stopping we from using n-number of <action> elements that describe the same Action class. The only restriction is that the path attribute must be unique for each <action> element.
Path : Represents the context-relative path of the submitted request. The path must be unique and start with a / character. (required)
type : Names the fully qualified class name of the Action class being described by this ActionMapping. The type attribute is valid only if no include or forward attribute is specified. (optional)
name : Identifies the name of the form bean, if any, that is coupled with the Action being defined. (optional)
scope : Names the scope of the form bean that is bound to the described Action.
The default value is session. (optional)
input : Names the context-relative path of the input form to which control should be returned if a validation error is encountered. The input attribute is where control will be returned if ActionErrors are returned from
the ActionForm or Action objects. (optional)
className : Names the fully qualified class name of the ActionMapping implementation class to use in when invoking this Action class. If the className attribute is not included, the ActionMapping defined in
the ActionServlet's mapping initialization parameter is used. (optional)
forward : Represents the context-relative path of the servlet or JSP resource that will process this request. This attribute is used if we do not want an Action to service the request to this path. The forward attribute is valid only if no include or type attribute is specified. (optional)
include : Represents the context-relative path of the servlet or JSP resource that will process this request. This attribute is used if we do not want an Action to service the request to this path. The include attribute is valid only if no forward or type attribute is specified. (optional)
validate : If set to true, causes the ActionForm.validate() method to be called on the form bean associated to the Action being described. If the validate attribute is set to false, then the ActionForm.validate() method is not called. The default value is true. (optional)
A sample <action> subelement using some of the previous attributes is shown here:
<action-mappings>
<action path="/Lookup"
type="wiley.LookupAction"
name="lookupForm"
input="/index.jsp">
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>
</action-mappings>
Note When using an <action> element to describe an Action class, we are describing only one instance of the named Action class. There is nothing stopping we from using n-number of <action> elements that describe the same Action class. The only restriction is that the path attribute must be unique for each <action> element.
Path : Represents the context-relative path of the submitted request. The path must be unique and start with a / character. (required)
type : Names the fully qualified class name of the Action class being described by this ActionMapping. The type attribute is valid only if no include or forward attribute is specified. (optional)
name : Identifies the name of the form bean, if any, that is coupled with the Action being defined. (optional)
scope : Names the scope of the form bean that is bound to the described Action.
The default value is session. (optional)
input : Names the context-relative path of the input form to which control should be returned if a validation error is encountered. The input attribute is where control will be returned if ActionErrors are returned from
the ActionForm or Action objects. (optional)
className : Names the fully qualified class name of the ActionMapping implementation class to use in when invoking this Action class. If the className attribute is not included, the ActionMapping defined in
the ActionServlet's mapping initialization parameter is used. (optional)
forward : Represents the context-relative path of the servlet or JSP resource that will process this request. This attribute is used if we do not want an Action to service the request to this path. The forward attribute is valid only if no include or type attribute is specified. (optional)
include : Represents the context-relative path of the servlet or JSP resource that will process this request. This attribute is used if we do not want an Action to service the request to this path. The include attribute is valid only if no forward or type attribute is specified. (optional)
validate : If set to true, causes the ActionForm.validate() method to be called on the form bean associated to the Action being described. If the validate attribute is set to false, then the ActionForm.validate() method is not called. The default value is true. (optional)
A sample <action> subelement using some of the previous attributes is shown here:
<action-mappings>
<action path="/Lookup"
type="wiley.LookupAction"
name="lookupForm"
input="/index.jsp">
<forward name="success" path="/quote.jsp"/>
<forward name="failure" path="/index.jsp"/>
</action>
</action-mappings>
This <action> element tells the ActionServlet the following things about this Action instance:
=> The Action class is implemented by the wiley.LookupAction class
=> This Action should be invoked when the URL ends with the path /Lookup.
=> This Action class will use the <form-bean> with the name lookupForm.
=> The originating resource that submitted the request to this Action is the JSP index.jsp.
=> This Action class will forward the results of its processing to either the quote.jsp or the index.jsp.
The previous <action> element uses only a subset of the possible <action> element attributes, but the attributes that it does use are some of the more common.
=> The Action class is implemented by the wiley.LookupAction class
=> This Action should be invoked when the URL ends with the path /Lookup.
=> This Action class will use the <form-bean> with the name lookupForm.
=> The originating resource that submitted the request to this Action is the JSP index.jsp.
=> This Action class will forward the results of its processing to either the quote.jsp or the index.jsp.
The previous <action> element uses only a subset of the possible <action> element attributes, but the attributes that it does use are some of the more common.
Struts Plugins
Struts Plugins are modular extensions to the Struts Controller. They have been introduced in Struts 1.1, and are defined by the org.apache.struts.action.Plugin interface. Struts Plugins are useful when allocating resources or preparing connections to databases or even JNDI resources.
We will look at an example of loading application properties on startup later in this section. This interface, like the Java Servlet architecture, defines two methods that must be implemented by all used-defined Plugins: init() and destroy(). These are the life-cycle methods of a Struts Plugin.
init()
The init() method of a Struts Plugin is called whenever the JSP/Servlet container starts the Struts Web application containing the Plugin. It has a method signature as follows:
public void init(ApplicationConfig config)
throws ServletException;This method is convenient when initializing resources that are important to their hosting applications. As we will have noticed, the init() method receives an ApplicationConfig parameter when invoked. This object provides access to the configuration information describing a Struts application. The init() method marks the beginning of a Plugin’s life.
destroy()
The destroy() method of a Struts Plugin is called whenever the JSP/Servlet container stops the Struts Web application containing the Plugin. It has a method signature as follows:
public void destroy();
This method is convenient when reclaiming or closing resources that were allocated in the Plugin.init() method. This method marks the end of a Plugin’s life.
Creating a Plugin
We will look at an example of loading application properties on startup later in this section. This interface, like the Java Servlet architecture, defines two methods that must be implemented by all used-defined Plugins: init() and destroy(). These are the life-cycle methods of a Struts Plugin.
init()
The init() method of a Struts Plugin is called whenever the JSP/Servlet container starts the Struts Web application containing the Plugin. It has a method signature as follows:
public void init(ApplicationConfig config)
throws ServletException;This method is convenient when initializing resources that are important to their hosting applications. As we will have noticed, the init() method receives an ApplicationConfig parameter when invoked. This object provides access to the configuration information describing a Struts application. The init() method marks the beginning of a Plugin’s life.
destroy()
The destroy() method of a Struts Plugin is called whenever the JSP/Servlet container stops the Struts Web application containing the Plugin. It has a method signature as follows:
public void destroy();
This method is convenient when reclaiming or closing resources that were allocated in the Plugin.init() method. This method marks the end of a Plugin’s life.
Creating a Plugin
Now that we have discussed what a Plugin is, let’s look at an example Plugin implementation.
As we stated earlier, all Plugins must implement the two Plugin methods init() and destroy(). To develop our own Plugin, we must complete the following steps. These steps describe the minimum actions that must be completed when creating a new Plugin:
1. Create a class that implements the org.apache.struts.action.Plugin interface.
2. Add a default empty constructor to the Plugin implementation. We must have a default constructor to ensure that our Plugin is properly created by the ActionServlet.
3. Implement both the init() and destroy() methods and our implementation.
4. Compile the new Plugin and move it into the Web application’s classpath.
5. Add an <plug-in> element to the application’s struts-config.xml file describing the new Plugin.
5. An example Plugin implementation is listed in the following snippet.
package wiley;
import java.util.Properties;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ApplicationConfig;
import org.apache.struts.action.ActionServlet;
public class WileyPlugin implements PlugIn {
public static final String PROPERTIES = "PROPERTIES";
public WileyPlugin() {
}d
estroy()
public void init(ActionServlet servlet,
ApplicationConfig applicationConfig)
throws javax.servlet.ServletException {
System.err.println("---->The Plugin is starting<----");
Properties properties = new Properties();
try {
// Build a file object referening the properties file
// to be loaded
File file =
new File("PATH TO PROPERTIES FILE");
// Create an input stream
FileInputStream fis =
new FileInputStream(file);
// load the properties
properties.load(fis);
// Get a reference to the ServletContext
ServletContext context =
servlet.getServletContext();
// Add the loaded properties to the ServletContext
// for retrieval throughout the rest of the Application
context.setAttribute(PROPERTIES, properties);
}
catch (FileNotFoundException fnfe) {
throw new ServletException(fnfe.getMessage());
}
catch (IOException ioe) {
throw new ServletException(ioe.getMessage());
}
}
public void destroy() {
// We don't have anything to clean up, so
// just log the fact that the Plugin is shutting down
System.err.println("---->The Plugin is stopping<----");
}
}
As we stated earlier, all Plugins must implement the two Plugin methods init() and destroy(). To develop our own Plugin, we must complete the following steps. These steps describe the minimum actions that must be completed when creating a new Plugin:
1. Create a class that implements the org.apache.struts.action.Plugin interface.
2. Add a default empty constructor to the Plugin implementation. We must have a default constructor to ensure that our Plugin is properly created by the ActionServlet.
3. Implement both the init() and destroy() methods and our implementation.
4. Compile the new Plugin and move it into the Web application’s classpath.
5. Add an <plug-in> element to the application’s struts-config.xml file describing the new Plugin.
5. An example Plugin implementation is listed in the following snippet.
package wiley;
import java.util.Properties;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import org.apache.struts.action.PlugIn;
import org.apache.struts.config.ApplicationConfig;
import org.apache.struts.action.ActionServlet;
public class WileyPlugin implements PlugIn {
public static final String PROPERTIES = "PROPERTIES";
public WileyPlugin() {
}d
estroy()
public void init(ActionServlet servlet,
ApplicationConfig applicationConfig)
throws javax.servlet.ServletException {
System.err.println("---->The Plugin is starting<----");
Properties properties = new Properties();
try {
// Build a file object referening the properties file
// to be loaded
File file =
new File("PATH TO PROPERTIES FILE");
// Create an input stream
FileInputStream fis =
new FileInputStream(file);
// load the properties
properties.load(fis);
// Get a reference to the ServletContext
ServletContext context =
servlet.getServletContext();
// Add the loaded properties to the ServletContext
// for retrieval throughout the rest of the Application
context.setAttribute(PROPERTIES, properties);
}
catch (FileNotFoundException fnfe) {
throw new ServletException(fnfe.getMessage());
}
catch (IOException ioe) {
throw new ServletException(ioe.getMessage());
}
}
public void destroy() {
// We don't have anything to clean up, so
// just log the fact that the Plugin is shutting down
System.err.println("---->The Plugin is stopping<----");
}
}
As we look over our example Plugin, we will see just how straightforward Plugin development can be. In this example, we create a simple Plugin that extends the init() method, which contains the property loading logic, and the destroy() method, which contains no specialized implementation. The purpose of this Plugin is to make a set of properties available upon application startup. To make the wiley.WileyPlugin available to we Struts application, we need to move on to the following section on Plugin configuration.
Configuring a Plugin
Now that we have seen a Plugin and understand how they can be used, let’s take a look at how a Plugin is deployed and configured. To deploy and configure our wiley.WileyPlugin, we must
Configuring a Plugin
Now that we have seen a Plugin and understand how they can be used, let’s take a look at how a Plugin is deployed and configured. To deploy and configure our wiley.WileyPlugin, we must
1. Compile and move the Plugin class file into the classpath.
2. Add a <plug-in> element to our struts-config.xml file. An example <plug-in> entry, describing the previously defined Plugin, is shown in the following code snippet:
<plug-in className="wiley.WileyPlugin"/>
Note The <plug-in> element must follow all <message-resources /> elements in the strutsconfig.
xml.
3.Restart the Struts Web application.
When this deployment is complete, this Plugin will begin its life when the hosting application
restarts.
The RequestProcessor
2. Add a <plug-in> element to our struts-config.xml file. An example <plug-in> entry, describing the previously defined Plugin, is shown in the following code snippet:
<plug-in className="wiley.WileyPlugin"/>
Note The <plug-in> element must follow all <message-resources /> elements in the strutsconfig.
xml.
3.Restart the Struts Web application.
When this deployment is complete, this Plugin will begin its life when the hosting application
restarts.
The RequestProcessor
As we stated previously, the org.apache.struts.action.RequestProcessor contains the logic that the Struts controller performs with each servlet request from the container. The RequestProcessor is the class that we will want to override when we want to customize the processing of the ActionServlet.
Creating a New RequestProcessor
Now that we have discussed what the RequestProcessor is, let’s look at an example Plugin implementation. The RequestProcessor contains n-number of methods that we can override to change the behavior of the ActionServlet.
To create our own RequestProcessor, we must follow the steps described in the following list:
1. Create a class that extends the org.apache.struts.action.RequestProcessor class.
2.Add a default empty constructor to the RequestProcessor implementation.
3.Implement the method that we want to override. Our example overrides the processPreprocess() method.
In our example, we are going to override one of the more useful RequestProcessor methods, the processPreprocess() method, to log information about every request being made to our application.
The processPreprocess() method is executed prior to the execution of every Action.execute() method. It allows we to perform application-specific business logic before every Action. The method prototype for the processPreprocess() method is shown below:
protected boolean processPreprocess(HttpServletRequest request,
HttpServletResponse response)
The default implementation of the processPreprocess() method simply returns true, which tells the framework to continue its normal processing. we must return true from our overridden processPreprocess() method if we want to continue processing the request. Note If we do choose to return false from the processPreprocess() method, then the RequestProcessor will stop processing the request and return control back to the doGet() or
doPost() of the ActionServlet.To see how all of this really works, take a look at our example RequestProcessor implementation, which is listed in the following snippet.
package wiley;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import java.io.IOException;
Creating a New RequestProcessor
Now that we have discussed what the RequestProcessor is, let’s look at an example Plugin implementation. The RequestProcessor contains n-number of methods that we can override to change the behavior of the ActionServlet.
To create our own RequestProcessor, we must follow the steps described in the following list:
1. Create a class that extends the org.apache.struts.action.RequestProcessor class.
2.Add a default empty constructor to the RequestProcessor implementation.
3.Implement the method that we want to override. Our example overrides the processPreprocess() method.
In our example, we are going to override one of the more useful RequestProcessor methods, the processPreprocess() method, to log information about every request being made to our application.
The processPreprocess() method is executed prior to the execution of every Action.execute() method. It allows we to perform application-specific business logic before every Action. The method prototype for the processPreprocess() method is shown below:
protected boolean processPreprocess(HttpServletRequest request,
HttpServletResponse response)
The default implementation of the processPreprocess() method simply returns true, which tells the framework to continue its normal processing. we must return true from our overridden processPreprocess() method if we want to continue processing the request. Note If we do choose to return false from the processPreprocess() method, then the RequestProcessor will stop processing the request and return control back to the doGet() or
doPost() of the ActionServlet.To see how all of this really works, take a look at our example RequestProcessor implementation, which is listed in the following snippet.
package wiley;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import java.io.IOException;
import java.util.Enumeration;
import org.apache.struts.action.RequestProcessor;
public class WileyRequestProcessor extends RequestProcessor {
public WileyRequestProcessor() {
}
public boolean processPreprocess(HttpServletRequest request,
HttpServletResponse response) {
log("----------processPreprocess Logging--------------");
log("Request URI = " + request.getRequestURI());
log("Context Path = " + request.getContextPath());
Cookie cookies[] = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
log("Cookie = " + cookies[i].getName() + " = " +
cookies[i].getValue());
}
}
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName =
(String) headerNames.nextElement();
Enumeration headerValues =
request.getHeaders(headerName);
while (headerValues.hasMoreElements()) {
String headerValue =
(String) headerValues.nextElement();
log("Header = " + headerName + " = " + headerValue);
}
}
log("Locale = " + request.getLocale());
log("Method = " + request.getMethod());
log("Path Info = " + request.getPathInfo());
log("Protocol = " + request.getProtocol());
log("Remote Address = " + request.getRemoteAddr());
log("Remote Host = " + request.getRemoteHost());
log("Remote User = " + request.getRemoteUser());
log("Requested Session Id = "
+ request.getRequestedSessionId());
log("Scheme = " + request.getScheme());
log("Server Name = " + request.getServerName());
log("Server Port = " + request.getServerPort());
log("Servlet Path = " + request.getServletPath());
log("Secure = " + request.isSecure());
log("-------------------------------------------------");
return true;
}
}
import org.apache.struts.action.RequestProcessor;
public class WileyRequestProcessor extends RequestProcessor {
public WileyRequestProcessor() {
}
public boolean processPreprocess(HttpServletRequest request,
HttpServletResponse response) {
log("----------processPreprocess Logging--------------");
log("Request URI = " + request.getRequestURI());
log("Context Path = " + request.getContextPath());
Cookie cookies[] = request.getCookies();
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
log("Cookie = " + cookies[i].getName() + " = " +
cookies[i].getValue());
}
}
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName =
(String) headerNames.nextElement();
Enumeration headerValues =
request.getHeaders(headerName);
while (headerValues.hasMoreElements()) {
String headerValue =
(String) headerValues.nextElement();
log("Header = " + headerName + " = " + headerValue);
}
}
log("Locale = " + request.getLocale());
log("Method = " + request.getMethod());
log("Path Info = " + request.getPathInfo());
log("Protocol = " + request.getProtocol());
log("Remote Address = " + request.getRemoteAddr());
log("Remote Host = " + request.getRemoteHost());
log("Remote User = " + request.getRemoteUser());
log("Requested Session Id = "
+ request.getRequestedSessionId());
log("Scheme = " + request.getScheme());
log("Server Name = " + request.getServerName());
log("Server Port = " + request.getServerPort());
log("Servlet Path = " + request.getServletPath());
log("Secure = " + request.isSecure());
log("-------------------------------------------------");
return true;
}
}
In our processPreprocess() method, we are retrieving the information stored in the request and logging it to the ServletContext log. Once the logging is complete, the processPreprocess() method returns the Boolean value true, and normal processing continues. If the processPreprocess() method had returned false, then the ActionServlet would have terminated processing, and the Action would never have been performed.
Configuring an Extended RequestProcessor
Now that we have seen a Plugin and understand how it can be used, let’s take a look at how a Plugin is deployed and configured. To deploy and configure our wiley.WileyPlugin, we must
Configuring an Extended RequestProcessor
Now that we have seen a Plugin and understand how it can be used, let’s take a look at how a Plugin is deployed and configured. To deploy and configure our wiley.WileyPlugin, we must
1.Compile the new RequestProcessor and move it into the Web application’s classpath.
2.Add a <controller> element to the application’s struts-config.xml file describing the new RequestProcessor. An example <controller> entry, describing the our new RequestProcessor, is shown in the following code snippet:
<controller
processorClass="wiley.WileyRequestProcessor" />Note The <controller> element must follow the <action-mappings> element and precede the <message-resources /> elements in the struts-config.xml.
1. Restart the Struts Web application.
When this deployment is complete, the new RequestProcessor will take effect.
2.Add a <controller> element to the application’s struts-config.xml file describing the new RequestProcessor. An example <controller> entry, describing the our new RequestProcessor, is shown in the following code snippet:
<controller
processorClass="wiley.WileyRequestProcessor" />Note The <controller> element must follow the <action-mappings> element and precede the <message-resources /> elements in the struts-config.xml.
1. Restart the Struts Web application.
When this deployment is complete, the new RequestProcessor will take effect.
No comments:
Post a Comment