A Simple MVC Example: Login Management


In this section, you look at a simple example to see the power of the MVC pattern. You are tasked with writing the login management interface into a Web site. Consumers of the Web site can log in via a standard Web browser or a PDA. Additionally, you present some services via SOAP-based Web services for consumption by other Web sites and consumers. These Web services require a login conceptually similar to the others for billing.

You want to make the maintenance of the login module as simple as possible. The initial design calls for usernames and passwords to be stored in a database, but they can be stored elsewhere in the future.

To start, try the JSP route. Because JSPs have access to the entire Java and J2EE library, they will work here.

JSP Login Handler

Your JSP login handler will check for the existence of a flag in the session to determine whether a user is logged in. If no user is logged in, the handler will display a username and password text box, along with a submit button to allow the user to log in. If the user is already logged in, the JSP page will display the user 's last login date. The username and password are stored in clear text (because this is just an example) in a database table named users .

Your JSP might look like Listing 19.1.

Listing 19.1 LoginExample.jsp
 <%@ page import="java.sql.*" %> <html> <head> <title>Welcome to my login example!</title> </head> <body> <% // has the user logged in yet? if so their name is in the session if( request.getSession().getAttribute( "username" ) == null ) { String userName = (String)request.getParameter( "username" ); String password = (String)request.getParameter( "password" ); if( (userName != null) && (password != null) ) { // read database, compare user name and password // if they don't match, display an error and redirect // back to self. if they do match, put user name into // the session with the name "username" and redirect // back to self. // for the sake of the example, this code just shows // the end result. request.getSession().setAttribute( "username", userName ); response.sendRedirect( "/mvcExample/LoginExample.jsp" ); } else %> <form name="login" method="post" action="/mvcExample/LoginExample.jsp"> User name: <input type="text" name="username" size="20"><br> Password: <input type="password" name ="password" size="20"><br> <input type="submit" value="Submit"> </form> <% } else { %> <b>Welcome back <%= request.getSession().getAttribute( "username" ) %></b> <% } %> </body> </html> 

The JSP in Listing 19.1 does what is required: It verifies the user information and logs in the user if the credentials are correct. However, this JSP is mostly Java. It doesn't even include the JDBC database access ”that would have increased the size even more.

Besides being mostly Java, this JSP suffers from other problems:

  • It outputs only HTML. A different output format would require a different JSP. Service consumers would have to be aware of which JSP they wanted to call to get the correct kind of output.

  • If the database changed, or if your company switched over to using something such as LDAP, you might have to change the JSP significantly. And again, if additional output formats were required, you would have to replicate that change in all versions of the JSP.

  • The display and the code are intertwined to such an extent that a developer would have to be fluent in both Java and HTML to modify or extend this JSP. Although the HTML in this particular example is trivial, a real page is likely to be much more complex.

Servlet Login Handler

The JSP example from the preceding section could be changed to a servlet. Because one of the objections to the JSP implementation is that there is too much Java, you could pull the functionality of the JSP into a servlet. Because there isn't very much HTML, you wouldn't be embedding that much HTML into the servlet.

A servlet login handler might look like Listing 19.2.

Listing 19.2 LoginServlet.java
 package mvcexample; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class LoginServlet extends HttpServlet { public void doGet( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { ServletOutputStream out = response.getOutputStream(); response.setContentType("text/html"); out.println( "<form name=\"login\" method=\"post\" action=\"/mvcExample/loginServlet\">" ); out.println( "User name: <input type=\"text\" name=\"username\" size=\"20\"><br>" ); out.println( "Password: <input type=\"password\" name=\"password\" size=\"20\"><br>" ); out.println( "<input type=\"submit\" value=\"Submit\">" ); out.println( "</form>" ); } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { ServletOutputStream out = response.getOutputStream(); String userName = request.getParameter( "username" ); String password = request.getParameter( "password" ); boolean loginGood = false; // // get a JDBC connection and verify the username and password // set loginGood = true if the username and password are good. // for this example just set it to true as if the login worked. // if( loginGood ) { request.getSession().setAttribute( "username", userName ); response.setContentType("text/html"); out.println( "<html><head><title>Welcome Back</title></head><body>" ); out.println( "<b>Welcome back " + userName + "</b>" ); out.println( "</body></html>" ); } else { // // if the login didn't work, redirect back to this servlet, which will // send us back to the doGet() method // response.sendRedirect( "/mvcExample/loginServlet" ); } } } 

As with the code in Listing 19.1 you have solved the problem for HTML output. But in reality, you've just flipped the problem around by moving this code into a servlet. Now when your HTML is extended, you have to recompile the servlet. Although you could encapsulate the JDBC access better, you're still mixing HTML and Java.

Servlet Login Handler with a JSP Front End

The problem with a JSP or servlet solution is that you're not using the technology available in the best manner. JSPs are meant to be used primarily for display, and servlets are meant to be used primarily for Java. Even though the line between the two can be a little fuzzy at times, this is the primary role for each of them.

One way to improve this situation is to use the servlet for form processing and the JSP for display rendering.

To satisfy accessing the backing store, you will create a simple Java class. This class knows how to access the backing store for the user login information. It will be the model in the MVC pattern.

So using a servlet for backend processing the JSP example from Listing 19.1 might look like Listing 19.3.

Listing 19.3 LoginExample2.jsp
 <html> <head> <title>Welcome to my login example!</title> </head> <body> <!-- username is in the session only when the user is logged in --> <% if( request.getSession().getAttribute( "username" ) == null ) { %> <form name="login" method="post" action="/mvcExample/loginServlet"> User name: <input type="text" name="username" size="20"><br> Password: <input type="password" name="password" size="20"><br> <input type="submit" value="Submit"> </form> <% } else { %> <b>Welcome back <%= request.getSession().getAttribute( "username" ) %></b> <% } %> </body> </html> 

The JSP in Listing 19.3 checks the session to see if the username is set. If so, it knows that the user is logged in and it displays the username. If not then it displays the HTML input text boxes for the username and password.

Listing 19.3 still uses some Java code, but it is a vast improvement over the JSP in Listing 19.1. How the username and password are validated is completely hidden. The servlet that gets the HTTP POST may use JDBC directly to a database or use an EJB. It may use LDAP. The point is that the HTML author doesn't know and probably doesn't care (or at least doesn't want to care).

The code for the servlet has some similarities to the servlet code in Listing 19.2. The biggest difference is that all HTML display output is removed. The modified Java code for the Login Servlet is shown in Listing 19.4.

Listing 19.4 LoginServlet2.java
 package mvcexample; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class LoginServlet2 extends HttpServlet { public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { String userName = request.getParameter( "username" ); String password = request.getParameter( "password" ); if( (userName == null)  (password == null) ) { response.sendRedirect( "/mvcExample/LoginExample2.jsp" ); } boolean loginGood = false; // // get a JDBC connection and verify the username and password // set loginGood = true if the username and password are good. // for this example just set it to true as if the login worked. // loginGood = true; if( loginGood ) { request.getSession().setAttribute( "username", userName ); } response.sendRedirect( "/mvcExample/LoginExample2.jsp" ); } } 

The version of the LoginServlet in Listing 19.4 removes all HTML from the code. In this example the JSP in Listing 19.3 is completely responsible for the display or the view. If the code in Listing 19.3 is changed the LoginServlet in Listing 19.4 does not have to change.

However, the code in Listing 19.4 suffers from a tight coupling with the underlying backing store. If the business requirements change to have an LDAP server store security- related information then Listing 19.4 will have to be changed to be able to use an LDAP server. The issue is that the LoginServlet in Listing 19.4 is acting as both the model and the controller . The view was separated out in the JSP in Listing 19.3 but there is still room for improvement.

Implementing the Model

The Model in the Model-View-Controller design pattern is the place to put logic that is not related to any display or user interaction technology. In this example it will be the interface to a backing store.

The code in Listing 19.5 shows the interface of the model portion of the MVC example. A Java interface is used to allow other implementations to use different backing stores.

Listing 19.5 SecurityModel.java
 package mvcexample; public interface SecurityModel { /** * Checks to see if the supplied userName and password is correct. */ public boolean isValidLogin( String userName, String password ); } 

Now you will need a concrete implementation of this interface. Listing 19.6 contains a simple class that uses JDBC for the security information.

Listing 19.6 JDBCSecurityModel.java
 package mvcexample; import java.io.*; import java.sql.*; public class JDBCSecurityModel implements SecurityModel { /** * Checks to see if the supplied userName and password is correct. This * simple example uses JDBC to check if the supplied username and password * are correct in a database table. */ public boolean isValidLogin( String userName, String password ) { // // get a JDBC connection and verify the username and password // return true if the username and password are good and false // if not. // // for the sake of the example, this method just returns true as // if the login was successful. // return( true ); } } 

Now the code in Listing 19.4 can be changed to use a SecurityModel. The particular implementation of the SecurityModel will be configured externally to the servlet. This makes it fairly easy to change from one type of backing store to another.

Listing 19.7 LoginServlet3.java
 package mvcexample; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class LoginServlet3 extends HttpServlet { private SecurityModel securityModel = null; public void init( ServletConfig config ) throws ServletException { // get the class name to really use from web.xml String securityModelClassName = config.getInitParameter( "securityModelClassName" ); if( securityModelClassName == null ) { throw( new UnavailableException( "securityModelClassName not set" ) ); } try { securityModel = (SecurityModel)Class.forName( securityModelClassName ).newInstance(); } catch( ClassNotFoundException cnfe ) { throw( new UnavailableException( "can't find the requested security model class" ) ); } catch( InstantiationException ie ) { ie.printStackTrace(); throw( new UnavailableException( "an instantiation error has occurred" ) ); } catch( IllegalAccessException iae ) { iae.printStackTrace(); throw( new UnavailableException( "an illegal access error has occurred" ) ); } } public void doPost( HttpServletRequest request, HttpServletResponse response ) throws ServletException, IOException { String userName = request.getParameter( "username" ); String password = request.getParameter( "password" ); if( (userName == null)  (password == null) ) { response.sendRedirect( "/mvcExample/LoginExample2.jsp" ); } if( securityModel.isValidLogin( userName, password ) ) { request.getSession().setAttribute( "username", userName ); } response.sendRedirect( "/mvcExample/LoginExample2.jsp" ); } } 

To tie together the login servlet, the JSP, and the security model you will need to create a Web application configuration file. The web.xml will look like the one in Listing 19.8.

Listing 19.8 web.xml
 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <display-name> Example MVC web application </display-name> <description> This web application is used to show the MVC pattern. </description> <servlet> <servlet-name>loginServlet</servlet-name> <servlet-class>mvcexample.LoginServlet3</servlet-class> <init-param> <param-name>securityModelClassName</param-name> <param-value>mvcexample.JDBCSecurityModel</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>loginServlet</servlet-name> <url-pattern>/loginServlet</url-pattern> </servlet-mapping> </web-app> 

For information about deploying this web application, please see the section in Chapter 17 titled "J2EE Packaging for Servlets ”Web Applications".

The EJB-to-JSP Integration Tool

One of the tools included with WebLogic Server 7.0 allows you to automatically generate JSP tag libraries from existing EJBs. The advantage of this EJB-to-JSP integration tool is that the fairly complicated work of finding and instantiating an EJB is handled by the tool.

To invoke this tool in graphical mode in Unix, run

 
 java -classpath $WL_HOME/server/lib/weblogic.jar weblogic.servlet.ejb2jsp.gui.Main 

For DOS, run

 
 java -classpath %WL_HOME%\server\lib\weblogic.jar weblogic.servlet.ejb2jsp.gui.Main 

This tool allows you to select a JAR file that contains one or more EJBs. For each method in each EJB in the JAR, a JSP tag is generated with the same name as the method. Each JSP tag expects the same number of attributes as the corresponding method in the EJB.

This tool does not replace good design work, however. A proper design may not allow access to all methods within an EJB. The EJB deployment descriptor can still be used as normal to allow or disallow access to particular methods . Figure 19.2 shows the EJB-to-JSP tool.

Figure 19.2. EJB-to-JSP tool.

graphics/19fig02.gif

For more information on EJB deployment descriptors, see "EJB Application Deployment Descriptors," p. 965 .


Managing Page-to-Page Relationships and Transitions

In a small site with relatively few pages, maintaining the flow from one page to another is fairly simple. However, in a large Web site, maintaining embedded information can be problematic . If you want to add another page in the chain, you have to update the JSP pages to point to the next page in the line. When you add in servlets that process the data from one page to another, the maintenance of these page relationships becomes problematic.

For example, in the login sample code, you redirected the user to the same page as the login form if the user's login failed. Although this approach does work, you may want to redirect the user to a registration page. On the registration page, you want one error page for usernames that already exist and one for database-related errors. By coding this page-to-page transition into the HTML or JSP, you increase the amount of work that needs to be done if another page is added to the page flow.

A better way would be to externalize this flow from page to page so that the HTML programmer can focus on the presentation of the page and not necessarily the page-to-page flow. This is what the WebLogic Portal does with its Webflow.

Webflow is a mechanism that helps build Web applications that more closely follow the MVC pattern. It centralizes flow from one page to another through a set of XML configuration files. This mechanism can help to reduce the maintenance of a site as the logic of page-to-page transitions is taken out of the code (HTML, JSP, and so on) and externalized. Webflow can also call specialized components to interact with business processes and systems on the back end of the server.

The advantage of the Webflow is that a user outside the HTML or Java world can use a graphical editor to determine the flow of pages from one to another without any direct programming experience. Figure 19.3 shows an overview of the Webflow architecture in WebLogic Portal.

Figure 19.3. Webflow architecture overview.

graphics/19fig03.gif

(Figure 19.3 is taken directly from http://edocs.bea.com/wlp/docs40/wflopipe/overview.htm.)

More information about Webflow can be found on the BEA documentation site at http://edocs.bea.com/. Webflow represents an important evolution in the design of complex Web-based systems.



BEA WebLogic Platform 7
BEA WebLogic Platform 7
ISBN: 0789727129
EAN: 2147483647
Year: 2003
Pages: 360

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net