|
Articles Index
By Tony Squier and Steve Meloan
(January 1998)
Note: Thanks to our ever astute JDC membership
for finding a synchronization problem in the
UserRegistryJdbcImpl
class. A new version of the file with the issues resolved is now available.
Please download the new Register.zip file to
get the changes.
The previous "behind the scenes" look at the
Java Developer
Connection (JDC) discussed how the
site handles session management. While that article went into some detail
as to how the login process itself works, it didn't cover the specifics
of how members become registered to begin with.
This edition of "Backstage at the JDC" focuses on the actual
registration process--the classes involved, and in particular, the
UserRegistry abstraction. If you'd like to go further, you can download
the Register.zip file in order to obtain all
the classes and sample code featured in the
article.
Overview
All JDC member information is maintained within an Oracle database. Because
of a key abstraction known as the UserRegistry, however, none of the JDC
software depends upon the specifics of any one registry implementation.
Rather than having components such as RegisterServlet make database queries
directly, the UserRegistry instead provides the necessary abstraction for
retrieving and updating member information. The following sections outline
the specific classes used in registration, and discuss in greater detail
the abstractions found within the JDC.
Why Abstractions?
One key feature of object-oriented software development is the concept of
abstractions. Simply stated, an abstraction removes implementation-specific
dependencies from your software. In the case of the JDC, having
database-specific dependencies strewn throughout the code would make it
very difficult to change or modify a given implementation after the fact.
Such modifications might then require changes within areas that shouldn't
really "care" about the specifics of the database being used.
Within the JDC, the UserRegistry provides this data access abstraction
(in the form of an interface) for all dependent code.
Another excellent reason for developing abstractions, is that it forces the
software engineer to think clearly about the classes being built. In the
process, the developer is required to ask, "What are the important
operations this abstraction needs to provide?" Thinking about such
abstractions helps one to see beyond the mere implementation, and to start
viewing the system at a much higher level.
For more information about object-oriented software engineering, see
the list of recommended titles at the end of this article.
Basic Components
The UserRegistry abstraction provides two essential methods--getMember
and setMember. As the name implies, the getMember method
simply retrieves an existent JDCMember instance from the
UserRegistry. The setMember method, meanwhile, either
updates an already existent member record, or creates a new entry entirely.
There are three key classes relevant to JDC registration: JDCMember,
RegisterServlet, and UserRegistry.
-
JDCMember--
provides the methods and variables specific to an individual JDC member instance.
-
RegisterServlet--
provides the big-picture logic and data checking inherent to registration processing.
-
UserRegistry--
provides the relevant member access abstraction
Together, the JDCMember and UserRegistry classes
are used for updating, registering, and authenticating (the Authenticator
implementation used in the SessionServlet is an implementation of
UserRegistry), as well as for processing JDC "Duke Dollars."
In short, the two classes are key to the smooth functioning of the JDC
infrastructure as a whole.
Note: The UserRegistryHashtableJdbcImpl is an
alternative implementation to using a JDBC database.
Registration
Given a well-defined abstraction for accessing member information,
Registration then becomes a much easier process. For this article,
the original RegisterServlet is simplified to exclude certain complexities
pertaining to multiple fields and wild cards. Yet hopefully, the examples
still provide sufficient coverage to serve as the basis for a more complete
and individualized registration servlet implementation.
The RegisterServlet has two basic responsibilities: to create new entries
in the UserRegistry, and to prevent duplicate entries from occurring. The
production version of the RegisterServlet presents a variety of
registration-related pages. For simplicity's sake, however, this version
uses a simple HTML error page. Thus, for any invalid data (such as a
duplicate userid), the code simply returns an SC_INTERNAL_SERVER_ERROR
(error type 500), along with an appropriate text message. The actual JDC
implementation provides a far more elegant registration experience, but
the important basics presented here remain the same.
Within the main block of the RegisterServlet (the service method),
the code first ensures that a userid and password are entered by the user
(line 66). Next, assuming
the userId and password variables contain data, the
database is checked for a possible duplicate
(lines 71-76). Notice that
the getMember method potentially returns an array of JDC members.
This is a processing artifact from the JDC production usage of
getMember (involving wild card searches), where multiple member
objects can conceivably be returned.
Assuming a duplicate (already existent) record is found, the code returns
an SC_INTERNAL_SERVER_ERROR, along with an appropriate text message.
If no duplicate exists, however, we simply add the JDCMember
instance to the UserRegistry (thus granting membership), and
redirect processing to the SessionServlet
(line 84).
That's all there is to registration! But as previously mentioned, most
of the work is done within the UserRegistry's
JDBC implementation.
Fun with JDBC
Using JDBC makes life a lot easier when accessing a database. JDBC, in
a nutshell, is a Java API for an interface to a database. With the JDBC
API, it's no longer necessary to write one program to access a Sybase database,
another program to access an Oracle database, and so on. One can write
a single program using the JDBC API, and the program (given the appropriate
JDBC driver) will then be able to issue statements to any given database.
There are a great many
articles available
on how to
use
JDBC, and several available within the JDC.
Also, the book
JDBC
Database Access with Java is available as part of The Java
Series.
The first thing executed in the UserRegistryJdbcImpl (implementation
of the UserRegistry interface) is to load the JDBC driver
(line 58), and
then initiate a connection to the database
(line 230). This
is done within a static initializer so that there is only one driver for
the multiple instances of the class. As you might suspect, this design
decision is a simple matter of conserving system resources. In reality,
there is generally only one instance of the UserRegistryJdbcImpl
created within the servlets in the Java
Web Server, but in practice there could
always be more than one. Note that the user and password values found in
the database connection code, as well as those for the driver and URL
(lines 34-37), are
specific to this particular demo database implementation. Your own values
will, of course, differ depending upon the individual database configuration.
As part of initialization, three prepared statements are also created:
_insertStatement, _updateStatement, and
_queryStatement (lines
47-49). Once properly loaded with their respective SQL-based text
and data, these three statements provide all necessary access to the database.
Getting a Member
The getMember method
(line 75) performs
a simple query to the database to retrieve data pertaining to a given
JDCMember instance. Here, the code is significantly simplified
from the actual JDC production version, which anticipates possible wild
cards and case sensitivities. The demo method simply takes the
passed userId value and sets it in the _queryStatement.
The results are returned in an array of JDCMember (the array being
an artifact of wild card logic). If no member is found, the method returns
a 0 length array.
Note that the _queryStatement parameters are cleared in the
finally clause of the try block
(line 113). This
operation is performed for all three PreparedStatement variables
(line 230),
and should be done before reissuing any of the commands. Failure to do
so may produce unexpected results.
Setting a Member
The setMember method
(line 143) returns
current data if an entry already exists for that user. By checking whether
an entry exists (line
150), setMember implicitly determines whether the given
operation will be an insert or an update. For an already existent value,
an update operation is performed using the _updateStatement.
Otherwise, a new record is added using the _insertStatement.
In both the setMember and getMember methods, an open
database connection is first confirmed before processing further. If
for whatever reason the connection is closed, reconnection is first
established, along with re-creation of the three prepared statements
(_insertStatement, _updateStatement, and
_queryStatement), see the _init method
(line 226.)
Another Abstraction.
Some interested readers may not have a database available. If so, one
simple way to get around the problem is to create an alternate UserRegistry
implementation--one using files for storage, or a
Java Hashtable serialized to disk. The
specifics of such an implementation are left to the reader as an exercise.
Once the new storage system is in place, however, the RegisterServlet
need then only create a new instance of the implementation. After that,
the code should work just fine! Such is the elegance of the abstraction
concept. Note, however, that if you are using the SessionServlet code
from the previous article in the "Backstage" series, be sure
that the Authenticator uses the same underlying storage medium
as the records you create with the RegisterServlet. One way to
ensure data consistency between the Authenticator and
UserRegistry is to have the UserRegistry interface extend the
Authenticator interface. Then, simply have your UserRegistry
extension implement the isAuthorized method.
Conclusion
As with much of the JDC sample code, you can download the Registration
Servlet classes and use them for your own web site. But be sure to first
purchase the Java Web Server (or some other servlet-compatible product).
The Registration Management servlet code runs with JWS 1.0.x. If you are
using JWS 1.1 Beta, the code may not work correctly. You can download a
copy of JWS 1.0.3 at
http://java.sun.com/products/java-server/webserver/index.html.
Further Reading
There are many excellent books on the subject of object-oriented software
engineering. The JDC encourages all Java developers to explore the relevant
literature. Some recommended titles are:
-
Bertrand Meyer.
Object-Oriented Software Construction.
Series in Computer Science.
Prentice Hall, NJ, 1988.
-
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.
Design Patterns - Elements of Reusable Object-Oriented Software.
Addision-Wesley Professional Computing Series.
Addision-Wesley Publishing Company, 1995.
-
James Rumbaugh, Michael Blaha, William Premerlani,
Fredrick Eddy, and William Lorensen.
Object-Oriented Modeling and Design.
Prentice Hall, NJ. 1991.
-
Ivar Jacobson, Magnus Christerson, Patrik Jonsson, Gunnar Overgaard.
Object-Oriented Software Engineering - A Use Case Driven Approach.
Addision-Wesley, 1992.
-
Grady Booch.
Object-Oriented Software Engineering.
Prentice Hall, NJ, 1993.
Tony Squier is a JDC tools engineer. He developed the JDC
registration-management code. He invites any feedback you have about
the article or the code, such as enhancements or potential bugs. If
you have comments please send them to:
Tony Squier
Steven Meloan is a writer, journalist, and former software developer.
His work has appeared in Wired, Rolling Stone, BUZZ, San Francisco Examiner,
ZDTV's "The Site," and American Cybercast's "The
Pyramid."
|