|
As we have seen, JSPs are a convenient choice for generating HTTP browser based user
interfaces. Interaction with the Model (beans) is easy via the built-in beans tags.
The Controller
The Controller provides the glue to the MVC architecture. It is responsible for
receiving events, determining the appropriate handler, invoking the handler, and finally
triggering the generation of the appropriate response.
Note: With the full power of Java available to us, Servlets are an ideal selection for a Controller technology.
In an MVC architecture, the Controller (servlet) acts as a dispatcher. This presents
some challenges that must be addressed. Specifically, the controller must handle the
following tasks:
- SecurityPerform security related tasks such as ensuring authentication (you are who
you say you are) and authorization (you are allowed to trigger this event). Some or all of
these tasks might be delegated to the Servlet engine. We will defer a discussion of
security until Chapter 16.
- Event IdentificationIdentify the specific event that is to be executed.
This can be as simple as through the use of a common parameter in the request or part
of the request URL itself.
- Prepare the ModelEnsure the availability of required Model components
such as the instantiation of required JavaBeans.
- Process the EventMap the request to an appropriate event handler and
invoke it. This may be implemented with a table lookup or with more advanced techniques.
- Handle ErrorsHandle any errors generated by the handler. This may be
achieved through the use of exception handlers. The Controller can then forward control
to an Error Page, as we have seen in previous chapters.
- Trigger the ResponseForward control to the response generator. Typically
this is implemented by invoking the RequestDispatcher.forward() method to pass control
to a JSP.
Design Issues
Implementing MVC with Servlets, JSP, and beans introduces several technical and
logistical hurdles. Ideally, we would like to reduce coupling between the components.
Where we cannot reduce it, we would like to implement it in such a way as to remove any
pain associated with the coupling.
For example, the View must provide event information to the display in such a manner
as to allow the event generated by the display (say, a button push) to be uniquely
identified by the Controller. The Controller must then decide on the appropriate handler
and response generator for the event.
Thus the View is coupled to the Controller by the event information, and the Controller
is coupled to both the Model (the Action components) and the View by the same information.
We can overcome most of the pain associated with this coupling through the use of an
initialization file, by employing Java's built-in class mechanisms, Java's reflection
capabilities, or some combination of the three. We will see an example of this strategy
in the next section.
As you will see, through the use of these techniques, the Controller can become very
efficient and elegant.
An Example of MVC
Returning to our time entry system from previous chapters, we will now apply the
design principles of MVC. Our directory structure for this example will be as follows,
all under <webapps>:
- HTML ch06\
- JSPs ch06\jsp\
- web.xml ch06\WEB-INF\
- Properties file ch06\WEB-INF\classes\
- Controller ch06\WEB-INF\classes\com\wrox\projsp\ch06\
- Event Handlers ch06\WEB-INF\classes\com\wrox\projsp\ch06\event
- Beans ch06\WEB-INF\classes\com\wrox\projsp\ch06\time\beans
The Controller
The Controller will be implemented as a Servlet. The init() method will be used to
establish any configuration information needed to process requests. The doGet() method
will call doPost(), so either HTTP method will produce the same response.
Design Overview
As discussed above, the Controller must employ an efficient technique to decouple
itself and the rest of the system from the event identification and management. There
are a variety of methods to use here, ranging from a simple table driven approach to
quite complex techniques. For our example, we will choose something in the middle. The
architecture is shown below:
Controller Architecture
In our design we utilize a HashMap to hold all known event names (the key) and the
associated handler class (the event). Each handler class must implement a process() and
forward() method. We will revisit these methods when we discuss the Model in detail. For
now we only need to know the following:
- The process() method takes a request and response parameter (of type
HttpServletRequest and HttpServletResponse) and handles the event
- The forward() method takes a request and response parameter and produces the
response
The Controller Class
The complete source code for our Controller example is
shown here. Notice how little coupling there is with the other components of the system:
package com.wrox.projsp.ch06;
import com.wrox.projsp.ch06.Constants;
import com.wrox.projsp.ch06.event.EventHandlerBase;
import com.wrox.projsp.ch06.event.UnknownEventHandler;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.ResourceBundle;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class Controller extends HttpServlet {
The HashMap events is used to hold the event definitions:
protected HashMap events = new HashMap();
As shown in the above picture, the init() method reads the events from a properties
file and inserts them into the event handler table. An event definition consists of an
event name and the corresponding handler class:
public void init() throws ServletException {
Debug.init();
The Event.properties file is read to determine the events that can be processed. This
file is described after the source code:
// get the event values and save them into events
ResourceBundle bundle = ResourceBundle.getBundle("Event");
Enumeration e = bundle.getKeys();
while(e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = bundle.getString(key);
It is worth taking a more detailed look at the mapping of an event to its associated
handler class. Within the init() method of our example, we use the java.lang.Class class.
Class has a forName() method that allows you to specify a class name. Then you can use the
newInstance() method to instantiate a new object of the supplied class name. The
newInstance() method assumes the existence of a no-argument constructor. The event
object is then stored in the events HashMap, to be used during the processing of actions. As we have seen, JSPs are a convenient choice for generating HTTP browser based user
interfaces. Interaction with the Model (beans) is easy via the built-in beans tags.
The Controller
The Controller provides the glue to the MVC architecture. It is responsible for
receiving events, determining the appropriate handler, invoking the handler, and finally
triggering the generation of the appropriate response.
Note: With the full power of Java available to us, Servlets are an ideal selection for a Controller technology.
In an MVC architecture, the Controller (servlet) acts as a dispatcher. This presents
some challenges that must be addressed. Specifically, the controller must handle the
following tasks:
- SecurityPerform security related tasks such as ensuring authentication (you are who
you say you are) and authorization (you are allowed to trigger this event). Some or all of
these tasks might be delegated to the Servlet engine. We will defer a discussion of
security until Chapter 16.
- Event IdentificationIdentify the specific event that is to be executed.
This can be as simple as through the use of a common parameter in the request or part
of the request URL itself.
- Prepare the ModelEnsure the availability of required Model components
such as the instantiation of required JavaBeans.
- Process the EventMap the request to an appropriate event handler and
invoke it. This may be implemented with a table lookup or with more advanced techniques.
- Handle ErrorsHandle any errors generated by the handler. This may be
achieved through the use of exception handlers. The Controller can then forward control
to an Error Page, as we have seen in previous chapters.
- Trigger the ResponseForward control to the response generator. Typically
this is implemented by invoking the RequestDispatcher.forward() method to pass control
to a JSP.
Design Issues
Implementing MVC with Servlets, JSP, and beans introduces several technical and
logistical hurdles. Ideally, we would like to reduce coupling between the components.
Where we cannot reduce it, we would like to implement it in such a way as to remove any
pain associated with the coupling.
For example, the View must provide event information to the display in such a manner
as to allow the event generated by the display (say, a button push) to be uniquely
identified by the Controller. The Controller must then decide on the appropriate handler
and response generator for the event.
Thus the View is coupled to the Controller by the event information, and the Controller
is coupled to both the Model (the Action components) and the View by the same information.
We can overcome most of the pain associated with this coupling through the use of an
initialization file, by employing Java's built-in class mechanisms, Java's reflection
capabilities, or some combination of the three. We will see an example of this strategy
in the next section.
As you will see, through the use of these techniques, the Controller can become very
efficient and elegant.
An Example of MVC
Returning to our time entry system from previous chapters, we will now apply the
design principles of MVC. Our directory structure for this example will be as follows,
all under <webapps>:
- HTML ch06\
- JSPs ch06\jsp\
- web.xml ch06\WEB-INF\
- Properties file ch06\WEB-INF\classes\
- Controller ch06\WEB-INF\classes\com\wrox\projsp\ch06\
- Event Handlers ch06\WEB-INF\classes\com\wrox\projsp\ch06\event
- Beans ch06\WEB-INF\classes\com\wrox\projsp\ch06\time\beans
The Controller
The Controller will be implemented as a Servlet. The init() method will be used to
establish any configuration information needed to process requests. The doGet() method
will call doPost(), so either HTTP method will produce the same response.
Design Overview
As discussed above, the Controller must employ an efficient technique to decouple
itself and the rest of the system from the event identification and management. There
are a variety of methods to use here, ranging from a simple table driven approach to
quite complex techniques. For our example, we will choose something in the middle. The
architecture is shown below:
Controller Architecture
In our design we utilize a HashMap to hold all known event names (the key) and the
associated handler class (the event). Each handler class must implement a process() and
forward() method. We will revisit these methods when we discuss the Model in detail. For
now we only need to know the following:
- The process() method takes a request and response parameter (of type
HttpServletRequest and HttpServletResponse) and handles the event
- The forward() method takes a request and response parameter and produces the
response
The Controller Class
The complete source code for our Controller example is
shown here. Notice how little coupling there is with the other components of the system:
package com.wrox.projsp.ch06;
import com.wrox.projsp.ch06.Constants;
import com.wrox.projsp.ch06.event.EventHandlerBase;
import com.wrox.projsp.ch06.event.UnknownEventHandler;
import java.io.IOException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.ResourceBundle;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class Controller extends HttpServlet {
The HashMap events is used to hold the event definitions:
protected HashMap events = new HashMap();
As shown in the above picture, the init() method reads the events from a properties
file and inserts them into the event handler table. An event definition consists of an
event name and the corresponding handler class:
public void init() throws ServletException {
Debug.init();
The Event.properties file is read to determine the events that can be processed. This
file is described after the source code:
// get the event values and save them into events
ResourceBundle bundle = ResourceBundle.getBundle("Event");
Enumeration e = bundle.getKeys();
while(e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = bundle.getString(key);
It is worth taking a more detailed look at the mapping of an event to its associated
handler class. Within the init() method of our example, we use the java.lang.Class class.
Class has a forName() method that allows you to specify a class name. Then you can use the
newInstance() method to instantiate a new object of the supplied class name. The
newInstance() method assumes the existence of a no-argument constructor. The event
object is then stored in the events HashMap, to be used during the processing of actions.
|